Skip to content

Releases

3.14.2

Choose a tag to compare

@jboulton jboulton released this 11 Nov 23:43

Fix: Python built with OpenSSL 4

Python built with OpenSSL 4, such as Homebrew's Python 3.14.10, has no ssl.PROTOCOL_TLSv1_2. Creating a DashConnection with a password login raised AttributeError: module 'ssl' has no attribute 'PROTOCOL_TLSv1_2', and so did the dashio_data_exporter command.

Both now use a TLS context with TLS 1.2 as the minimum (ssl.create_default_context()). It checks the Dash server's certificate and host name against the system's CAs, as before.

Full changes: 3.14.1...3.14.2

3.14.1

Choose a tag to compare

@jboulton jboulton released this 11 Nov 11:11

Fix: setting the clock from the Dash app

The Dash app sends CLK with a request ID before the time. 3.14.0 took only the Dash server's srv_clk as a request ID, so it read the app's ID as the time: the clock callback wasn't called, and the app never got CLK back to close its Setting Time popup.

The time is now taken from the last field, with or without a request ID. Every request ID except srv_clk is the app's, and gets the CLK reply when the callback returns True.

Full changes: 3.14.0...3.14.1

3.14.0

Choose a tag to compare

@jboulton jboulton released this 11 Nov 10:58

Setting the clock from the Dash app

set_clock_callback(callback) now lets the user set the device's clock from the Dash app. It adds CLK to the layout's deviceSetup, so the app offers it in the device's provisioning menu. See Provisioning from the app.

  • Breaking: the callback gets the time, not the message. It's called with an int, whole seconds since 1970 UTC, whether the time comes from the app or from the Dash server's reply to request_clock(). Before, it got the raw message.
  • Return True if the clock was set. The device then sends the app the CLK reply it waits for. The server's reply gets no answer, because a CLK sent to the server asks it for the time again.
  • unset_clock_callback() stops offering the setting.

Fixes

  • set_ota_callback() set the clock callback, and unset_clock_callback() cleared the OTA callback. Each now sets or clears its own.

Full changes: 3.13.0...3.14.0

3.13.0

Choose a tag to compare

@jboulton jboulton released this 11 Nov 10:00

The layout hash: no more raising cfgRev

The Dash app fetches a device's layout again only when the device reports a different layout ID. Until now that was cfgRev, which had to be raised by hand every time the layout changed. A device now reports # and the 64-bit FNV-1a hash of its layout instead, so the app notices a changed layout by itself. See How layouts work.

  • On by default. device.layout_id is what the device reports.
  • Older Dash apps: app versions from before the hash take the device as having no layout. Update the app, or use Device(..., layout_hash=False) to report cfgRev as before.
  • When cfgRev is still used: a device with layouts loaded for more than one column count (one hash can't describe them all), with layout_hash=False, and after use_cfg(). In those cases, keep raising cfgRev as before (Keeping cfgRev up to date).
  • The Dash server checks the layout a store and forward device sends it against the hash, and they match.

TextBox colours

  • Setting color or caption_color now sends it. Before, the new colour only went to the app with the next text.
  • When an app connects, a TextBox sends its text and its caption, each in its colour. Before, the app showed the text in white after every reconnect, and didn't get the caption.

Full changes: 3.12.0...3.13.0

3.12.0

Choose a tag to compare

@jboulton jboulton released this 10 Nov 08:44

Device certificates

A device can log in to the Dash server with its own certificate on port 8885, rather than your username and password. Your password isn't kept on the device, and you can cut one device off by revoking it in the Dash app. See Device certificates.

certificates = dashio.DeviceCertificates("certs", state_callback=certificate_state)
device = dashio.Device("Greenhouse", "gh-7f3a", "Greenhouse", certificates=certificates)
  • Getting a certificate: logged in with your password, the device gets a certificate from the Dash server. It then logs in with the certificate and erases the password. The Dash app can also read the device's certificate status and provision it, over any connection.
  • Renewal: certificates are renewed in their last year.
  • Fallback: a new certificate the server refuses falls back to the password.
  • Recovery: setting the device's Dash login again from the app gives it a new key.
  • Encrypted key: key_passphrase keeps the private key encrypted. On Linux, the passphrase can come from a systemd credential (Encrypting the key).
  • One device per connection: a DashConnection with certificates has one device.

Store and forward

For devices with store_and_forward on:

  • SAF END: the Dash server sends SAF END after the messages it kept for the device. device.set_saf_end_callback() is called when it arrives, or after saf_end_timeout seconds without it. A device that connects now and then can therefore disconnect as soon as it has its messages (storage.md).
  • Stored layouts: the device sends its layout when the Dash server asks for it, so the app can show the device while it's offline.
  • disconnect(): it publishes what the device has already sent before going offline.

History upload

  • Data Updater spec: the upload to the Dash server now follows the Data Updater spec.
  • delete_history(): new on TimeGraph, EventLog and Map. It deletes a line's, log's or track's history on the device straight away, and on the Dash server at the next upload.
  • Current position marker: MapTrack(replace=True), added with Map.add_track(), is a marker for a current position. The server keeps only its latest location.

BLE

BLEConnection was rewritten on dbus-fast (the ble extra, Linux with BlueZ), and it looks to the app like DashIO firmware:

  • Each phone gets its own notify socket and MTU.
  • Messages are split to fit what BlueZ delivers.
  • It works with iPhones and Android phones.

Breaking: BLEControl and the ble_uuid parameter have been removed. See BLEConnection.

Other changes

  • Older layouts: layouts exported from older Dash apps load, with defaults for the keys the app added later.
  • Long control IDs: add_cfg64 names every control ID over 7 characters in one error.
  • Charts: a Chart's control type is CHRT, and ControlName.GRPH is kept as another name for it.
  • Fix: adding a device to a connection straight after creating it no longer fails.
  • Event logs: an EventData keeps at most 25 lines.
  • Docs: the docs are now in docs/. There's also a Claude Code skill for library users in skills/python-dashio.
  • New dependency: cryptography.

Full changes: 3.11.0...3.12.0

3.10.1

Choose a tag to compare

@jboulton jboulton released this 13 Oct 08:40

Fixes

  • Store and forward to the Dash server works. In 3.10.0, a DashStore's SDU UPDATE packets were sent to the device's data topic, but the Dash server handles SDU on the announce topic, so every sync failed after the ACK timeouts. They now go to the announce topic.
  • DashStore's age limit (max_days) is applied from the first entry. Before, it wasn't applied in the first minute after the machine booted.

See Documentation.md.

3.10.0

Choose a tag to compare

@jboulton jboulton released this 13 Oct 08:27

Changed

  • History over local connections. For a TimeGraph, EventLog or Map with storage_enable(), the device now only answers history requests that come from the Dash server. The Dash server is the app's source of truth for that history: answered over TCP, the app took the device's history as complete and didn't fetch older data from the server. Live updates still go to every connection.
  • deviceSetup in the CFG now lists the device's connections (TCP for a TCPConnection, MQTT for a DashConnection, BLE), then the settings the Dash app can change. The options are upper case, as the app writes them, and the Dash login option is DASH (was dashio). Adding a connection changes the CFG, so work out cfgRev from get_cfg_hash() after adding the device's connections. The examples do this; expect cfgRev to go up once on their next start.
  • DashConnection reconnects with a growing wait after a refused login, up to 15 minutes, and doesn't try to connect without a username and password. Before, it retried about every second.

New

  • DashStore: local storage, and store and forward to the Dash server. Device(..., store=dashio.DashStore("device.db")) keeps the data of the device's TimeGraph, EventLog and Map controls in an sqlite database, with limits on the number of entries and their age. History requests are answered from it, including data from before a restart. When the device connects to the Dash server, it sends the server the data it missed, with the DashIO firmware's store and forward (SDU) protocol.
  • Devices that only connect now and then, e.g. on LTE to save power:
    • DashConnection(auto_connect=False), connect() and disconnect(). During a planned disconnect, device messages are dropped rather than sent as a backlog when the connection is made; requests to the server, such as STE, are kept.
    • device.set_store_sync_callback() is called when each sync with the Dash server ends, so the device can disconnect.
    • device.store_and_forward = True lets the Dash app turn on store and forward (SAF) for the device on the Dash server.
  • dashio.CaptionMode and dashio.DeviceViewStyle are exported.

Examples

  • Examples/intermittent_connection.py: a data logger that connects to the Dash server on a schedule and disconnects when its store has synced. The Dash app provisions it over TCP on the local network: its name, the Dash login, and the connection and record intervals.
  • The greenhouse keeps its history in a DashStore (greenhouse.db).

See Documentation.md, in particular Local Storage.

3.9.0

Choose a tag to compare

@jboulton jboulton released this 12 Oct 06:53

Changed

  • How numbers are sent. Numbers are now formatted to fit the controls.
    • Dial and Table: sent to 6 significant digits, e.g. 19.123456 is sent as 19.1235. The Dash app applies the control's precision, which the user can change in the app. Before, a Dial or Table with precision Off sent the number unchanged.
    • Other controls, such as Knob, Slider and TimeGraph: sent to 5 significant digits, like the DashIO firmware.
    • Very large or small numbers are sent in exponent notation, and values smaller than 1e-11 are sent as 0.
    • Setting a value to None sends NA, so the app shows that the value isn't available.

New

  • Batched publishes to the Dash server. DashConnection publishes messages for the same topic sent within batch_delay (default 0.05 seconds) together, so a device updating several controls makes one publish instead of one for each. Alarms are still published straight away. batch_delay=0 turns batching off.
  • Numbers in Table rows: a TableRow's columns can be numbers as well as text.

Documentation and examples

  • Factory reset: the reset callback receives the app's message, and msg[2] == "FRST" is a request for a factory reset.
  • The greenhouse example only sends its button states when they change.

See Documentation.md.

3.8.0

Choose a tag to compare

@jboulton jboulton released this 12 Oct 05:10

Breaking changes

  • Control IDs can be at most 7 characters, as the Dash app requires. Creating a control with a longer control_id now raises ValueError. Layouts exported from the Dash app already use short IDs.

New

  • Menu and ButtonGroup updates. When the user taps a Menu or ButtonGroup, the Dash app asks for the values of its controls. The device now replies with the state of each control in it.
  • Smaller replies with PKG messages. The device packages several messages into one PKG message, which matters most over the Dash server.
    • Menu and ButtonGroup updates are sent as PKG.
    • STATUS replies, sent when the app connects, are sent as PKG. Controls that are only inside Menus or ButtonGroups are left out, as their values are sent when the Menu or ButtonGroup is tapped. A control on a DeviceView in any layout is always included.
    • dashio.package.make_packages() builds PKG messages.
  • Device reset: device.set_reset_callback() tells the Dash app the device can be reset. When the app asks, the device replies first, as the protocol requires, then calls the callback.
  • Name requests: the device answers the Dash app's request for its current name, with or without a name callback.

Fixes

  • The announce includes cfgRev. Before, the device announced itself without it, which the Dash app takes as cfgRev 0, so the app could load an out of date layout.
  • TCPConnection(use_zero_conf=False): add_device() no longer crashes.
  • A message with only a device ID no longer stops the device.

Documentation and examples

  • README: improved, with an example of connecting through the Dash server.
  • generate_cfg64(): documented, with examples.
  • The greenhouse example: its lights times are Date/Time text boxes.
  • Control IDs: all the examples use short IDs.

See Documentation.md.

3.7.0

Choose a tag to compare

@jboulton jboulton released this 11 Oct 08:46

Breaking changes

  • EG800Q and Lte767xConnection are no longer exported from dashio while the modem connections are reworked. Import them from their modules, e.g. from dashio import lte_767x_connection.
  • DashConnection connects from its connection thread, a moment after it is created, instead of in its constructor. Add your devices straight after creating the connection, as the examples do.
  • Receive callbacks that return a string now send it to the Dash app as a reply. Callbacks that return anything else behave as before.

New

  • DashConnection tells the Dash app when devices go online and offline:
    • ONLINE is sent for each device when the connection is made, and for a device added later.
    • close() sends OFFLINE for each device before disconnecting.
    • If the connection is lost, e.g. the program stops unexpectedly, the Dash server publishes OFFLINE for the first device on the connection (its MQTT Last Will). The new offline_delay parameter (default 10 seconds) delays it, so reconnecting within that time cancels it. An MQTT connection has a single will, so it only covers the first device.

Fixes

  • Devices answer the Dash app's history requests again. Since January 2023 a device never replied to the app's TimeGraph, EventLog or Map history requests, so the app only had the data it had received live, unless the data was stored on the Dash server. Replies from receive callbacks were also lost.
  • Map history no longer crashes, a bad request time is ignored, and each track keeps its track ID.
  • EventLog history sends nothing when there are no events to send.

See Documentation.md for DashConnection.