iot-manager Usage Reference¶
The following contains information on the IoTManager module’s classes such as their inputs and member methods, how to connect to a the IoTManager using clients, and how to use the IoTManager class to interface with connected IoT Clients.
iot-manager Classes Documentation¶
Manager Class Documentation¶
This section details how the Manager class is written and what parameters and member methods it has.
class iot_manager.Manager(**params, **kwargs)
Parameters
- valid_types (list) -A list of strings that are the type of a type of device which a client can identify as, below is an example of such a list that could be passed.
types = ["light", "lock", "thermostat"]
- ssl_context (Optional[ssl_context])[None] -A TLS/SSL wrapper context for securing socket communications over unsecured networks.
- host (Optional[string])[“127.0.0.1”] -The host IP the server is running on. For example “0.0.0.0” on windows and “127.0.0.1” on Linux.
- backlogged_connections (Optional[int])[10] -The number of backlogged connections the listening socket can have before it refuses new clients.
- logging_level (Optional[logging.LEVEL])[logging.INFO] -Changes the level of logging output generated by the Manager.
Class Members
- host(string)The IP of the host connection, always ‘0.0.0.0’.
- port(int)The port clients will use to connect to the Manager. Changing this value will not update the connection port so it should be treated as read-only.
- logger(logging.Logger)The logger object used by the Manager to do logging.
- heartbeat_rate(int)The frequency, in seconds, at which the Manager will check the heartbeat of all connected clients.
Class Methods
- get_client_data()
{ "uuid": (uuid of the client connection)[str], "type": (client's type)[str], "data": (client's data)[dict] }
Client Class Documentation¶
This section details what members and member methods the Client class has.
class iot_manager.Client(**params, **kwargs)
Parameters
- connection(socket)An active socket which is connected to a device, used to communicate with the device.
- address(tuple)The address info of the client as a (IP, PORT) pair.
Class Members
# acquire the lock
with client.connection_lock:
# request some data
client.send_string("get_data")
# get some data (10 second timeout)
response = client.recv_string(10)
# release the lock
Class Methods
- end(raise_exception[boolean](False))Calling this method will end the client’s socket connection with the server after sending the client the “endn” message. Finally raising a ConnectionEnd error when finished if raise_exception is set to true.
- send(bytes)Similar to the socket.sendall method, however rather than raising exceptions it will return None instead of the # of bytes sent.
- recv(timeout[int](15) )Similar to the socket.recv method, however rather than raising exceptions it will return None instead of the data received. Timeout is the delay before a socket timeout is triggered in seconds (The data received from this method is a bytes object)
- get_data(timeout[int](15) )Initiates a handshake procedure with the client, asking for the clients information such as UUID, type, and data. This handshake is imitated by the Manager when the client first connects and does not need to be used after, but can if needed if edit’s to the Client’s data are made while a ongoing connection exists. Can raise a ConnectionEnd and InvalidInfo exception if the client fails to answer or provides invalidly formatted data.
- heartbeat(timeout[int](15) )A method used by the Manager’s heartbeat checking protocol to see if clients are still connected and responsive. It is a handshake procedure similar to the get_data method which has no real use outside of the Manager but is provided for convenience. Can raise a ConnectionEnd exception if the client fails to complete the handshake correctly.
Class Errors
Data Class Documentation¶
This section details what members and member methods the Data class has.
class iot_manager.Data(**params, **kwargs)
Parameters
- client_uuid(string)The UUID of the Client used for identifying a given device or sub process on a device. Devices can have many Client connections open with the server at once as long as each has its own UUID. Must be 32 bytes long or the server will reject the Client, and if another Client is already connected to the server with the same UUID the old Client’s connection will be terminated and replaced by the new Client.
Class Members
Class Methods
Manager Events¶
This section explains how to make make use of events provided by the Manager, this including how to define your own events as well as examples showing you how they can work.
Event Explanation Events are a way of handling connections and messages from clients to the server. For example on_connect handlers will trigger when a client connects so the user can do additional connection management when the device connects to the manager. Another example is the on_message handler which allows the user to handle messages from a client to the server.
Manager Connection Protocol¶
This section details how to write a client that is able to connect to the IoTManager properly. As well giving as a basic client written in python which can connect to a IoT Manager.
The following steps occur when a client connects to the IoTManager an
The Manager sends the client the getinfo command as the leading byte 0B00000001 or x01.
The client must respond to the manager with a string containing the following information separated by a “##” delimiter.
UUID - A 36 char UUID string used to identify a client. Must be unique as clients that have conflicting UUIDs will cause one another to disconnect.
Type - The type of the device the client is, must match a value from the valid_types list entered when the Manager was created, if it does not match one of those values the client will be rejected.
Device Data - Extra data that can be supplied by the device, will be formatted into a dictionary when being read by the Manager. Below is an example of how this data should be formatted when sent from the client:
data = '{ "key_1": "data_1", "key_2": "data_2" }'
The example above would be converted to the following dictionary when read by the IoT Manager:
data = { "key_1": "data_1", "key_2": "data_2" }
This above information must be formatted into a single line with each piece of information separated by a :. The following string is an example of a valid response to the getinfo command.
response = '1862cfe1-5dab-4d8e-a946-50d6728f830f##light##{ "key_1": "data_1", "key_2": "data_2" }'
The client will now be accepted by the Manager but an additional step must be taken to ensure a continued connection.
The Manager will send out a heartbeat request signified by the command heart as the byte 0B00000010 or x02), the client must respond to this command by sending back the matching beat command (as a string). If the client fails to respond to the manager within 15 seconds the client’s connection to the manager will be terminated.
iot-manager Examples¶
Example Client Code in Python¶
The following code will connect successfully to a running Manager, it does nothing but connect and print out every command received from the server.
import socket
import uuid
# the IP of the machine running the IoTManager
# ("localhost" if both the server can client are running on the same machine)
host = "localhost"
# the port the IoTManager is using to listen for devices
port = 8595
# device info (used for handshaking with the server, make sure the type is valid)
info = {
"uuid": uuid.uuid1(),
"type": "test",
"data": '{ "name": "Test Client" }'
}
# formatted info so it can be sent to the IoTManager
formatted_info = (info["uuid"] + "##" + info["type"] + "##" + info["data"])
# the socket object that will be used to connect and communicate with the server
connection_socket = socket.socket()
# connect the to the IoTManager
connection_socket.connect((host, port))
print("Connected to IoTManager! Starting main loop...")
# main client loop
while True:
# wait to receive a command from the server
command = connection_socket.recv(4096).decode()
# some debug output so we can see what command was received
if command != "":
print("Command Received: " + command)
# respond to the 'getinfo' command with this clients info
if command == "\x01":
print("Sending Device Data: '" + formatted_info + "'")
connection_socket.sendall(formatted_info.encode())
# respond to the 'heart' command with the 'beat' response
elif command == "\x02":
connection_socket.sendall("beat".encode())
# respond to the hello client command
elif command == "hello client":
connection_socket.sendall("hello server".encode())
# go back to waiting for another command from the server
Manager Example Code¶
The following is an example showing how to instantiate the Manager as well as send commands using both events and the send function. The example uses a flask web server as the interface with the Manager and shows how the Manager can be used to create a basic IoT Web App. Below is both the flask server and required html file.
from flask import Flask, render_template, request
import logging
import iotmanager
# init a basic logging config
logging.basicConfig()
# list of valid device types
types = ["test"]
# create the manager (faster heartbeat rate and debug level verbose for demonstration purposes)
device_manager = iotmanager.Manager(types, heartbeat_rate=30, logging_level=logging.DEBUG)
# flask app
app = Flask("iot-manager example")
# web page to test device communication
@app.route('/', methods=['GET', 'POST'])
def hello():
# get the client data so we can serve the page
client_data = device_manager.get_client_data()
# command form submission handling
if request.method == 'POST' and "command" in request.form:
# send the command
device_manager.send_string(request.form["uuid"], request.form["command"])
# command_all form submission handling
if request.method == 'POST' and "command_all" in request.form:
# send the command to all devices
device_manager.send_all_string(request.form["command_all"])
# html template rendering
return render_template("index.html", data=client_data)
# handler for messaging test client on connection
@device_manager.event
def on_connect_test(client):
client.send_string("hello client")
# handler for client sending messages to the server
@device_manager.event
def on_message_test(client, message):
print("Message Received from Client: ")
print(message)
# run the flaks app
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IoT Light Manager</title>
</head>
<body>
<h1>Connected Devices:</h1>
<hr color="black">
<ul>
{% for client in data %}
<li><b>{{ client.data.name }}</b>
<ul>
<li>
<form method="post">
<input type="text" name="command" value="" />
<input type="hidden" name="uuid" value="{{ client.uuid }}" />
<input type="submit" value="Send Command" />
</form>
</li>
</ul>
</li>
{% endfor %}
</ul>
<hr color="black">
<h3>Communicate to Every Client</h3>
<ul>
<li>
<form method="post">
<input type="text" name="command_all" value="" />
<input type="submit" value="Send Custom Command to All Devices" />
</form>
</li>
</ul>
</body>
</html>