A Device is your IoT device as the Dash app sees it: its controls, its layouts and its settings. It runs in its own thread, answers the app, and sends control values out through its connections (connections.md).
device = dashio.Device("Greenhouse", "gh-7f3a", "Greenhouse", store=dashio.DashStore("greenhouse.db"))
tcp = dashio.TCPConnection()
tcp.add_device(device)
dash = dashio.DashConnection(username, password)
dash.add_device(device)
device.add_control(dial)| Parameter | Default | |
|---|---|---|
device_type |
The same for every device running this code, e.g. "TempSensor". The app uses it to find devices and match layouts |
|
device_id |
Unique to this device, e.g. a UUID or MAC address | |
device_name |
What the app shows. The user can change it if you let them (below) | |
cfg_dict |
None | Layouts to load: {1: phone_cfg64, 3: tablet_cfg64}, or one decoded layout. See layouts.md |
context |
The shared zmq.Context.instance() |
The ZMQ context the device and its connections talk over |
store |
None | A DashStore for time graph, event log and map history |
certificates |
None | DeviceCertificates, to log in to the Dash server with the device's own certificate |
device.close() stops the device. It leaves the shared ZMQ context running, for other devices and connections.
| Method | |
|---|---|
add_control(control) |
Adds a control. Controls in a loaded layout are added for you |
get_control(control_type, control_id) |
The device's control, e.g. get_control(dashio.ControlName.KNOB, "aKNB") |
remove_control(control) |
Removes a control |
is_control_loaded(control_type, control_id) |
Whether the device has the control |
storage_enable(control_type, control_id) |
Has the Dash server keep a TimeGraph's, EventLog's or Map's history. See storage.md |
When an app connects, it asks for the state of every control. The device answers with its name, then the controls' values, packaged into a few PKG messages rather than one message each. Controls that are only in Menus and ButtonGroups are left out: their values are sent when the Menu or ButtonGroup is opened (layouts.md).
The app's provisioning menu lets the user change the device's settings: its name, WiFi, Dash server login, and your own settings in a Menu in the provisioning menu. Set a callback for each setting the device lets the user change. Setting one tells the app the device has it.
Each callback receives the message from the app, split on tabs, and returns whether it took the change (the name callback returns the new name). The device then confirms it to the app.
def name_changed(msg):
save_setting("name", msg[2])
return msg[2]
def dash_login(msg):
dash.set_connection(msg[2], msg[3])
save_setting("dash", (msg[2], msg[3]))
return True
device.set_name_callback(name_changed)
device.set_dashio_callback(dash_login)| Method | The app sends | |
|---|---|---|
set_name_callback(callback) |
msg[2]: the name |
Return the new name. The device answers requests for its name with or without one |
set_wifi_callback(callback) |
msg[2]: country code, msg[3]: SSID, msg[4]: password |
Return True if it took them |
set_dashio_callback(callback) |
msg[2]: username, msg[3]: password |
The Dash server login. Return True if it took them |
set_tcp_callback(callback) |
msg[2]: port |
Return True if it took it |
set_mqtt_callback(callback) |
The MQTT settings | Return True if it took them |
Each has an unset_..._callback() that clears it, and stops offering the setting.
def reset(msg):
if len(msg) > 2 and msg[2] == "FRST":
clear_saved_settings() # A factory reset
threading.Timer(1.0, restart).start() # So the reply reaches the app first
device.set_reset_callback(reset)set_reset_callback(callback) lets the user reset the device from the app. The device replies to the app, as the protocol requires, then calls the callback, which should reset the device, e.g. after a short delay so the reply gets out. msg[2] == "FRST" asks for a factory reset. unset_reset_callback() clears it.
The layout's deviceSetup tells the app what the device supports: first its connections, added as you add the device to them (BLE, TCP, and MQTT for a DashConnection), then the settings it lets the user change, from the callbacks above (NAME, WIFI, DASH, RST and so on), e.g. TCP,MQTT,NAME,DASH. It's part of the layout, so work out cfgRev after adding the device's connections and callbacks (layouts.md).
device.store_and_forward = True adds SAF, which lets the user turn on the Dash server's store and forward for the device: the server keeps the app's messages until the device connects, and asks it for its values when it does. It's for devices that only connect now and then (storage.md). With it on, the server also keeps a copy of the device's layout, so the app can show the device while it's offline: when the server doesn't have the current one, it asks the device for it when it connects, and the device sends it.
| Method or value | |
|---|---|
device_name |
The device's name. Setting it sends it to the app |
config_revision, inc_config_revision() |
cfgRev. See layouts.md |
number_of_device_views |
The number of DeviceViews in the layout generated from the controls |
These need a DashConnection.
| Method | |
|---|---|
request_clock() |
Asks the Dash server for the time. The reply goes to set_clock_callback(callback): msg[3] is the time in seconds since the epoch |
request_ota_build_number() |
Asks the server for the latest firmware build number. The reply goes to set_ota_callback(callback) |
request_ota(build_number, chunk_number) |
Asks the server for a block of firmware. The reply goes to the OTA callback |
set_store_sync_callback(callback) |
Called when each upload of the device's DashStore to the server ends: True when the server has everything, False when it failed |
set_saf_end_callback(callback) |
With store_and_forward, called each time the device connects, once the messages the server kept for it have arrived (True), or when the server's SAF END doesn't come within saf_end_timeout seconds, 5 by default (False) (storage.md) |
Each has an unset_..._callback().