Hologram's Python SDK is an easy-to-use interface for communicating with the Hologram cloud, other cloud services, and SMS destinations.
It's designed to be run on small Linux devices such as Raspberry Pi, which are connected to the internet using a USB Cellular Modem such as the Hologram Nova.
Source code is available on GitHub.
Don't want to have to write your own scripts? Just need to send data from your device to your apps?
Hologram's Command-Line Interface (CLI) is takes the most useful features of the Python SDK and provides one-line commands you can use from your console.
We recommend using the SDK with a Raspberry Pi running Raspbian Buster. If you want to use other Linux distributions, make sure to stop the modem manager or other tools that might interfere with the serial port.
We wrote scripts to ease the installation process.
Please run this command to download the script that installs the Python SDK:
curl -L hologram.io/python-install | bash
Please run this command to download the script that updates the Python SDK:
curl -L hologram.io/python-update | bash
If everything goes well, you're done and ready to use the SDK.
Older versions of the SDK that work with Python 2 are still available on PyPI. You can install these directly with pip:
bash sudo pip install hologram-python~=0.8.3
You might be missing some dependencies that would normally get installed by our install script above. If you get any errors, try installing some more packages:
bash sudo apt install python pip ppp libpython2.7-dev
And then try running the pip install command again.
The credentials dictionary can be used to specify the 8 character device key of your Hologram Device. For instructions on finding your device key, please refer to this guide for more details. This is generally used for HologramCloud purposes and is explained in more detail below. You shouldn't need to specify the devicekey parameter unless you're using CSRPSK Authentication. This is described in detail below.
Credentials Dictionary Keys: * devicekey (string) -- The 8 character device key obtained from your dashboard.
Example:
from Hologram.HologramCloud import HologramCloud
credentials = {'devicekey': '12345a7b'}
Cloud is a base class that acts as a fundamental interface in which CustomCloud and HologramCloud is built on.
Properties:
Example:
cloud = HologramCloud(dict(), network='cellular')
print cloud.version # Prints 0.7.0
print cloud.network_type # Prints either 'Network Agnostic Mode' or 'Cellular'
HologramCloud is a subclass of CustomCloud and is the main way to use our cloud via the SDK. The Hologram cloud parameters/configs are used to populate properties in CustomCloud. In other words, the HologramCloudproperty values are:
Note: The HologramCloud inbound functionality, which utilizes both receivehost and receiveport, will only work if the SDK is used in a Hologram cellular connected device. Please use with caution.
Properties:
The HologramCloud constructor is responsible for initializing many of SDK components selected by the user.
Parameters:
Network Interface Options
These are cellular network interfaces (strings) that you can use to choose which Network interface you want to use for the connectivity layer in the Hologram SDK.
Example:
from Hologram.HologramCloud import HologramCloud
credentials = {'devicekey': '1234abcd'}
hologram = HologramCloud(credentials, network='cellular') # 1st example with cellular network interface.
This method sends a message to the specified host. This will also broadcast the message.sent event if the message is sent successfully.
Parameters: * message (string) -- The message that will be sent. * topics (string array, optional) -- The topic(s) that will be sent. * timeout (int) -- A timeout period in seconds for when the socket should close if it doesn't receive any response. The default timeout is 5 seconds.
Returns: A Hologram response code (int).
There are specific error codes that will be returned:
# Send message with topics. This has a 5 sec socket timeout
recv = cloud.sendMessage("hi there!", topics=["TOPIC1","TOPIC2"])
# recv will be 0 if message is sent successfully
# Send message with a timeout of 7 seconds
recv2 = cloud.sendMessage("hi again!", topics=["TOPIC1","TOPIC2"], timeout=7)
Cloud messages are buffered if the network is down (on a network.disconnected event). Once the network is reestablished (a broadcast on network.connected), these messages that failed to send initially will be sent to the cloud again.
Parameters: * destination_number (string) -- The destination number. This must be a E.164 formatted string with a '+' sign. * message (string) -- The SMS body. This SMS must be less than or equal to 160 characters in length
Returns: A message response description (string)
Example:
recv = cloud.sendSMS("+11234567890", "Hello, Python!") # Send SMS to destination number
Note: The sendSMS interface only works when the authentication type is set to CSRPSK.
Start listening on incoming SMS and buffer them internally.
Parameters: None
Returns: None
Stops listening to incoming SMS.
Parameters: None
Returns: None
Whenever the inbound SMS feature is enabled and SMS(es) are received, they will be appended to a SMS received buffer. This receive buffer acts as a queue for inbound SMS, and a .popReceivedSMS() call will pop and return the oldest SMS payload that is still currently in the queue. Returns None if the received buffer is empty.
Parameters: None
Returns: A SMS payload (SMS)
The SMS object contains the following properties:
Properties:
The Timestamp object contains the following properties:
Properties:
Example:
# 1. someone sends a "hey there!" SMS
# After a while...
# 2. someone sends another "bye!" SMS
sms_obj = hologramCloud.popReceivedSMS()
print sms_obj.message # prints "hey there!"
sms_obj = hologramCloud.popReceivedSMS()
print sms_obj.message # prints "bye!"
sms_obj = hologramCloud.popReceivedSMS() # trying to pop more returned SMS.
# prints None because the receive buffer is empty.
print sms_obj
You can also subscribe to when messages are being received via sms.received. This event will get broadcasted whenever an inbound SMS has been received, and you can choose to have event handlers/callback functions registered to it. This is explained in more detail under the Event section below.
Note: Since we utilize Python threads within the inbound SMS receive feature, your callback function needs to be thread safe to avoid race conditions in your application. In addition to that, we can only guarantee that the 'sms.received' broadcast happens after a message is received but not exactly when since those are decoupled and handled in separate threads. If you're doing multiple sends at the same time, we also can't guarantee that the first broadcast that happens come from the first received message (once again, since it's threaded)
Example:
1st Example:
def sayHelloReceived():
print "hello! I received something!"
# ...
hologramCloud.event.subscribe('sms.received', sayHelloReceived)
# someone sends a "this is the payload" SMS
# "hello! I received something!" is printed here
# Note that the received buffer still contains "this is a payload"
2nd Example:
The user can choose to pop the message within his/her event handler function too.
# this function now pops the received message and prints it out.
def sayHelloReceivedAndPopMessage():
print "hello! I received something!"
sms_obj = hologramCloud.popReceivedSMS()
print sms_obj.message
# ...
hologramCloud.event.subscribe('sms.received', sayHelloReceivedAndPopMessage)
# someone sends a "this is the payload" message
# prints "hello! I received something!"
# prints "this is the payload"
sleep(100)
sms_obj = hologramCloud.popReceivedSMS() # prints None
This method takes in a result code and prints the appropriate description.
Parameters:
Returns: A message response description (string).
These are specific error descriptions that will be returned based on the given result_code:
# Send message with topics. This has a 5 sec socket timeout
recv1 = cloud.sendMessage("hi there!", topics=["TOPIC1","TOPIC2"])
# This will print 'Message sent successfully' if recv1 is 0.
print 'RESPONSE MESSAGE: ' + cloud.getResultString(recv1)
# Send message with a timeout of 7 seconds
recv2 = cloud.sendMessage("hi again!", topics=["TOPIC1","TOPIC2"], timeout=7)
print 'RESPONSE MESSAGE: ' + cloud.getResultString(recv2)
CustomCloud is a subclass of Cloud, and it allows the user to make inbound and outbound connections via the SDK by specifying the host and port parameters.
If you would like to use the Hologram cloud, please check out the HologramCloud subclass instead as it'll populate the appropriate host, port and any other required parameters for you.
The CustomCloud constructor is responsible for initializing many of SDK components selected by the user.
Parameters:
Note: You must set send_host and send_port if you choose to use the CustomCloud class to send messages (make outbound connection) in your application. Likewise, receive_host and receive_port must be set if you plan to enable inbound connection.
Network Interface Options
These are cellular network interfaces (strings) that you can use to choose which Network interface you want to use for the connectivity layer in the Hologram SDK.
Example:
from Hologram.CustomCloud import CustomCloud
# send example
customCloud1 = CustomCloud(dict(), send_host='my.example.host.com', send_port=9999)
# receive example with cellular network interface
customCloud2 = CustomCloud(dict(), receive_host='0.0.0.0', receive_port=57750, network='cellular')
This method sends a message to the specified host. This will also broadcast the message.sent event if the message is sent successfully.
Parameters:
Returns: The response from the remote host (string)
Example:
recv = customCloud.sendMessage("hello")
recv2 = customCloud.sendMessage("hello again with 7 sec timeout", timeout = 7)
Cloud messages are buffered if the network is down (on a network.disconnected event). Once the network is reestablished (a broadcast on network.connected), these messages that failed to send initially will be sent to the cloud again.
This method sends periodic message(s) every interval seconds to the specified host. This will also broadcast the message.sent event if each message is sent successfully. You cannot have more than 1 periodic message at once.
Parameters:
Returns: None
Example:
recv = customCloud.sendPeriodicMessage(10, 'This is a periodic message',
topics=['PERIODIC MESSAGES'],
timeout=6)
print 'sleeping for 20 seconds...'
time.sleep(20)
customCloud.stopPeriodicMessage()
Disables and stop sending background periodic messages.
Parameters: None
Returns: None
Initializes, opens and binds an inbound TCP socket connection. This method is also responsible for spinning up another thread for the blocking socket accept call, which is used to process incoming connections.
Parameters: None
Example:
inboundCloud = CustomCloud(dict(), send_host = 'my.example.host.com', send_port = 9999,
receive_host = '0.0.0.0', receive_port = 57750)
inboundCloud.openReceiveSocket()
hologram = HologramCloud(dict(), network='cellular')
hologram.openReceiveSocket()
Closes the inbound TCP socket connection.
Parameters: None
Example:
# Closes the inbound socket connection. You can reopen this again via the .openReceiveSocket()
# call.
inboundCloud = CustomCloud(dict(), send_host = 'my.example.host.com', send_port = 9999,
receive_host = '0.0.0.0', receive_port = 57750)
# Open and then close the socket conneciton.
inboundCloud.openReceiveSocket()
# do something...
inboundCloud.closeReceiveSocket()
# Reopen the socket connection.
inboundCloud.openReceiveSocket()
hologram = HologramCloud(dict(), network='cellular')
hologram.openReceiveSocket()
# do something...
hologram.closeReceiveSocket()
Whenever the inbound message feature is enabled and message(s) are received, they will be appended to a received buffer. This receive buffer acts as a queue for inbound data, and a .popReceivedMessage() call will pop and return the oldest data payload that is still currently in the queue. Returns None if the received buffer is empty.
Parameters: None
Returns: A received data payload (string)
Example:
# 1. someone sends a "hey there!" message
# 2. someone sends another "bye!" message
recv = customCloud.popReceivedMessage()
print recv # prints "hey there!"
recv = customCloud.popReceivedMessage()
print recv # prints "bye!"
recv = customCloud.popReceivedMessage() # trying to pop more returned response.
# prints None because the receive buffer is empty.
print recv
You can also subscribe to when messages are being received via message.received. This event will get broadcasted whenever an inbound message has been received, and you can choose to have event handlers/callback functions registered to it. This is explained in more detail under the Event section below.
Note: Since we utilize Python threads within the inbound message receive feature, your callback function needs to be thread safe to avoid race conditions in your application. In addition to that, we can only guarantee that the 'message.received' broadcast happens after a message is received but not exactly when since those are decoupled and handled in separate threads. If you're doing multiple sends at the same time, we also can't guarantee that the first broadcast that happens come from the first received message (once again, since it's threaded)
Example:
1st Example:
def sayHelloReceived():
print "hello! I received something!"
# ...
customCloud.event.subscribe('message.received', sayHelloReceived)
# someone sends a "this is the payload" message
# "hello! I received something!" is printed here
# Note that the received buffer still contains "this is a payload"
2nd Example:
The user can choose to pop the message within his/her event handler function too.
# this function now pops the received message and prints it out.
def sayHelloReceivedAndPopMessage():
print "hello! I received something!"
recv = customCloud.popReceivedMessage()
print recv
# ...
customCloud.event.subscribe('message.received', sayHelloReceivedAndPopMessage)
# someone sends a "this is the payload" message
# prints "hello! I received something!"
# prints "this is the payload"
sleep(100)
recv = customCloud.popReceivedMessage() # prints None
Note: Although you can have multiple event handlers subscribed to the 'message.received' event, we cannot guarantee which event handler will run first. Hence, you might run into a race condition and cause undefined behavior, especially so when your event handler function(s) are not thread safe. Once again, we highly recommend that you keep them thread safe so you don't run into an undefined behavior like this.
The NetworkManager class is responsible for defining and managing the networking interfaces of Hologram SDK. There are interfaces here that allow you to, for example, connect and disconnect from a network of your choice. This NetworkManager interface lives within a Cloud instance. The NetworkManager is responsible for picking the right interface based on the argument set in the Cloud constructors.
Note: The NetworkManager interface is in beta, and the interface may change in the future.
Note: The networking functionality has only been tested on a Raspberry Pi 3 running Raspbian Jessie. It should work on other Debian-based Linux installations. Please post in our community forum if you have any issues.
We also provide a non-network mode that can be used to disable the Hologram SDK networking functionality. You may want to do this if you just want to interact with the interfaces in the Hologram SDK, but not deal with the details on how to connect/disconnect the device itself via the SDK.
Example:
customCloud = CustomCloud(dict(), send_host = 'my.example.host.com', send_port = 9999)
The NetworkManager interface allows you to choose between 2 different network options: 1. Cellular 2. None(non-network mode)
Non-network mode allows you to use the Python SDK independent of the network used by your machine. This assumes that you have figured out the network connectivity layer required by the SDK.
Connects to the specified network by establishing a PPP session. This will also broadcast the network.connectedevent if sucessful. Default timeout is 200 seconds.
Parameters:
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Disconnect from an active network by tearing down an existing PPP session. This will also broadcast the network.disconnected event. This is meant to be called by a subclass that implements the Network interface, and not to be called directly.
Parameters: None
Reconnects to the specified network. This is meant to be called by a subclass that implements the Network interface, and not to be called directly.
Parameters: None
Returns a list of available network interfaces supported by the Python SDK. This will be a subset of the following strings:
Returns: List of available interfaces (list())
Example:
print 'Available network interfaces:'
print hologram.network.listAvailableInterfaces() # ['cellular']
The Cellular interface allows you to use the Hologram SDK to connect/disconnect from the network. We currently support two modes of operation, which are PPP and Serial mode. The operation modes used varies with the cellular modem that you're using. For example, in PPP mode, you can use the Hologram Nova (U201/R404), Huawei E303 or MS2131 modems as part of your cellular connectivity solution.
The Cellular interface requires root permissions to connect/disconnect from a given address. We strongly recommend running your scripts with sudo privileges.
Properties:
The Location object has the following properties:
Properties:
Connect to a cellular connection by establishing a PPP session. This will also broadcast the cellular.connectedevent.
Parameters:
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Example:
hologram.network.connect(timeout=7) # Connect with 7 second timeout.
Disconnect from an active cellular connection by tearing down a PPP session. This will also broadcast thecellular.disconnected event.
Parameters: None
Returns: Bool -- True if successful, False otherwise.
Returns the cell network connection status. This is represented by the following return codes:
Connection Status Code:
Parameters: None
Returns: int -- The connection status codes.
Example:
# prints 1 if there's an active connection.
print 'CONNECTION STATUS: ' + str(hologram.network.getConnectionStatus())
The Modem interface allows you to interact directly with the modem. This is a base class for the Hologram Nova (U201/R404), Huawei MS2131 and E303 modem interfaces.
Properties:
The Nova class implements the Modem interface and is designed to interact with a Hologram Nova U201 modem.
Properties: with the SDK. We only support 'ppp' for now.
Parameters:
You can obtain a sample chatscript that works with the Nova here
Note: The chatscript file must be an executable for the SDK to load it. You can do this by running:
sudo chmod +x <file></file>
Connect to a cellular connection by establishing a PPP session.
Parameters:
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Disconnect from an active cellular connection by tearing down a PPP session.
Parameters: None
Returns: Bool -- True if successful, False otherwise.
The NovaM_R404 class implements the Modem interface and is designed to interact with a Hologram Nova R404 modem.
Properties:
If you're using a R404 modem, you might need to run the following commands whenever your machine is rebooted:
sudo modprobe option
sudo sh -c 'echo -n 05c6 90b2 > /sys/bus/usb-serial/drivers/option1/new_id'
Parameters:
You can obtain a sample chatscript that works with the Nova here
Note: The chatscript file must be an executable for the SDK to load it. You can do this by running:
sudo chmod +x <file></file>
Connect to a cellular connection by establishing a PPP session.
Parameters: * timeout (int) -- A timeout period in seconds for when the connection should close if it fails to connect. Default is 200 seconds.
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Disconnect from an active cellular connection by tearing down a PPP session.
Parameters: None
Returns: Bool -- True if successful, False otherwise.
The E303 class implements the Modem interface and is designed to interact with a Huawei E303 modem.
Properties: with the SDK. We only support 'ppp' for now. * localIPAddress (string) -- The local IP address. It'll be None if the cell network is inactive. * remoteIPAddress (string) -- The remote IP address. It'll be None if the cell network is inactive.
Parameters: * device_name (string) -- The device name. * baud_rate (string) -- The baud rate of the serial line. * chatscript_file (string) -- Path to chatscript file.
You can obtain a sample chatscript that works with the E303 here
Note: The chatscript file must be an executable for the SDK to load it. You can do this by running sudo chmod +x <file>.</file>
Connect to a cellular connection by establishing a PPP session.
Parameters: * timeout (int) -- A timeout period in seconds for when the connection should close if it fails to connect. Default is 200 seconds.
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Disconnect from an active cellular connection by tearing down a PPP session.
Parameters: None
Returns: Bool -- True if successful, False otherwise.
The MS2131 class implements the Modem interface and is designed to interact with a Huawei MS2131 modem.
Properties:
Parameters:
You can obtain a sample chatscript that works with the MS2131 here
Note: The chatscript file must be an executable for the SDK to load it. You can do this by running:
sudo chmod +x <file></file>
Connect to a cellular connection by establishing a PPP session.
Parameters:
Returns: Bool -- True if successful, False otherwise.
Note: You shouldn't have to make .connect() and .disconnect() calls unless one or more of these is true: 1) You want to send messages/SMS via PPP. 2) You are sending messages/SMS with the E303 or MS2131 modem.
Disconnect from an active cellular connection by tearing down a PPP session.
Parameters: None
Returns: Bool -- True if successful, False otherwise.
This Hologram SDK allows the developer to publish/subscribe to certain events via the Event interface. Registered event handlers based on predefined strings below will get executed when the event occurs. Some of these predefined strings are as follows:
Registers an event handler function to the specific event.
Parameters:
Example:
def message_sent():
print 'hurray!'
# do something
def temp():
# message_sent() will execute whenever a broadcast happens on message.sent
# (or whenever a message is sent)
cloud.event.subscribe('message.sent', message_sent)
Unregisters an event handler function to the specific event.
Parameters:
Example:
# message_sent() will no longer be executed when a message is sent.
cloud.event.unsubscribe('message.sent', message_sent)
We use the standard Python logger framework to report all SDK internal messages. We also define custom exceptions such as HologramError, PPPError and AuthenticationError for SDK specific errors.
Note: You must define a custom Python handler in order to see the raised exceptions. This can be done using the Python logging framework by calling something such as logging.basicConfig(). Check the Python logging framework documentation for more information.
We use the pytest testing framework, and unit tests can be found under the /tests folder.
To run them from the top level directory, go ahead and type make test via the Makefile we provided. This will run all unit tests under /tests.
Hologram's multi-factor authentication (MFA) helps developers and businesses verify IoT device identity, maintain privacy, and re-issue security keys to restore trust with devices following a security incident or breach. Currently available via Hologram secure SIMs with the Hologram Python SDK or CLI, Hologram multi-factor authentication enables key rotation, signing, and message authentication codes, such as Time-based One-Time Password for Machines (TOTP-M), all through a few lines code.
Available now in private beta. To get started, simply request more information by contacting our sales team.
Setup
The HologramCloud constructor initializes the authentication type used for message validation in the Python SDK. By default, a single factor One-time Password (OTP) based on time is used. Enabling multi-factor authentication through a combination of the default OTP and Hologram MFA element in Hologram's secure IoT SIMs requires only initializing the authentication type parameter with authentication_type='sim-otp'.
Example:
from Hologram.HologramCloud import HologramCloud
credentials = {'devicekey': '1234abcd'}
hologram = HologramCloud(credentials, network='cellular', authentication_type='sim-otp')