Skip to content

Releases: marklynch/pool-controller-code

v1.6.0

16 Jun 06:19
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

What's Changed

  • feat: add findings about cmd 0x17, add device name for 0x72 by @lawther in #32
  • Update docs on the second heater setpoint by @marklynch in #33
  • refactor: Introduce reg_id_t enum for named register IDs (closes #36) by @lawther in #37
  • [ADDED] - Global protocol-error counters alongside the decoded/unknown by @marklynch in #38
  • Add support for second heater and pool spa setpoints by @marklynch in #34
  • [FIXED] Fixed phantom "Chlorine Output Level", "Pump Speed" and pH/ORP by @marklynch in #40

Full Changelog: v1.5.1...v1.6.0

v1.6.0-rc1

10 Jun 05:56
Immutable release. Only release title and notes can be modified.
18d13cd

Choose a tag to compare

Note - some of the entities have changed - so it is recommended to delete the MQTT device and it will then automatically add the correct entities back.

What's Changed

  • feat: add findings about cmd 0x17, add device name for 0x72 by @lawther in #32
  • Update docs on the second heater setpoint by @marklynch in #33
  • refactor: Introduce reg_id_t enum for named register IDs (closes #36) by @lawther in #37
  • [ADDED] - Global protocol-error counters alongside the decoded/unknown by @marklynch in #38
  • Add support for second heater and pool spa setpoints by @marklynch in #34
  • [FIXED] Fixed phantom "Chlorine Output Level", "Pump Speed" and pH/ORP by @marklynch in #40

Full Changelog: v1.5.1...v1.6.0-rc1

v1.5.1 - retry wifi for longer

25 May 10:09
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.5.1] - 2026-05-19

Changed

  • Increase the timeout and retry count if wifi is not available to 15 second retry and 10 attempts. #25

Full Changelog: v1.5.0...v1.5.1

v1.5.0 - adding more devices

25 May 09:59
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.5.0] - 2026-05-19

Added

  • Added tests runner for message decoder to make it easier to track changes and prevent regressions
  • Added GitHub Actions workflow that runs the host-based test suite on every push and pull request
  • Added many new unknown registers to PROTOCOL.md
  • Added Viron XT Variable Speed Pump support (device 0x00A0): decodes speed telemetry (CMD 0x3B)
  • Added pump physical speed preset button presses (CMD 0x1B, mapping Low/Med/High), and controller-to-pump speed commands (CMD 0x18, mapping Low/Med/High)

Changed

  • Renamed device 0x00A0 from "Internal Salt Cell" to "Viron XT Pump"
  • Renamed CMD 0x18 from "Chlorinator Cell Mode" to "Pump Speed Command" in the command name table
  • Updated temperature samples by @lawther

Fixed

  • Fixed mixed-validity temperature logging (CMD 0x16 / 0x31): when one of temp1 / temp2 is INVALID the still-valid temperature now logs its decoded °C value rather than a generic OK (raw 0xXX) label
  • Re-enabled the test_message_decoder and test_mqtt_commands host-test suites that had drifted out of sync with the current decoder/state and bus_send_bytes/s_pool_state interfaces, and cleared the SKIP_LIST in run_tests.sh
  • Fixed: ICI Gas Heater setpoints not being saved to pool state by @lawther

What's Changed

  • docs: add unknown command 0x2B by @lawther in #15
  • Add message decoding tests on build and fix existing tests by @marklynch in #16
  • Fix: ICI Gas Heater setpoints not being saved to pool state by @lawther in #14
  • Rename test suite workflows - and make them work with node 24 by @marklynch in #17
  • Add Viron XT Variable Speed Pump support (device 0x00A0) by @lawther in #19
  • tests: updated temperature samples by @lawther in #20
  • docs: add observed unknown registers to PROTOCOL.md by @lawther in #21
  • Add VX 11S v3 Salt Chlorinator support (ID 0x0081) by @lawther in #13
  • feat: add Viron XT pump button activity and controller speed command decoding by @lawther in #24

Full Changelog: v1.4.1...v1.5.0

v1.4.1 - add ICI Gas Heater

19 May 05:51
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

What's Changed

  • Add support for ICI Gas Heater (0x0074) by @lawther in #12

New Contributors

Full Changelog: v1.4.0...v1.4.1

v1.4.0 - support multiple temperatures

19 May 04:52
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

Note - this changes MQTT/Home assistant id's so worth deleting from MQTT after reflashing. It will automatically repopulate with new IDs.

[1.4.0] - 2026-05-19

Added

  • Per-source temperature storage: temp1, temp2, temp1_valid, temp2_valid and single_sensor_source are now stored directly on each seen_device_t entry. Multiple temperature sources (Connect 8/10 + Genus Heater variants) are kept distinct rather than overwriting a single global field.
  • HTTP /status: each device entry in the devices[] array now carries temperature1 (and temperature2 for multi-sensor sources like the Connect 8/10). Devices that don't broadcast CMD 0x16 get no temperature fields at all.
  • New per-source MQTT temperature entities, discovered lazily on first reading from each (source, sensor) pair. Friendly names: "Temp - Genus Heater" for single-sensor sources, "Temp 1 - Connect 8/10" / "Temp 2 - Connect 8/10" for multi-sensor. Entity IDs namespaced by controller, e.g. sensor.pool_controller_<mac>_connect_8_10_temp_1.
  • Invalid-temperature sentinel: water temperature readings (CMD 0x16 and 0x31) with a raw value >= 0xA0 (160°C) are treated as a disconnected sensor — logged as a warning and skipped instead of being stored or published.
  • Picked up the previously-unhandled 0x0072 Genus Heater variant of CMD 0x16 (same single-byte layout as the 0x0070 Genus Heater).

Changed

  • Pulled out the water temperature reading (CMD 0x16) to be source-agnostic — single unified handle_temp_reading dispatched on the CMD byte and routed by payload length: 2-byte {temp1, temp2} from 0x0062 Connect 8/10 (LEN 0x0E); 1-byte {temp1} from 0x0070/0x0072 Genus Heater family (LEN 0x0D). Removes the MSG_TYPE_TEMP_READING and MSG_TYPE_GENUS_HEATER_TEMP_READING patterns and the dedicated handle_genus_heater_temp_reading.
  • Merged CMD 0x31 (Water Temperature Reading alt) into the unified handle_temp_reading()0x16 and 0x31 share the same {temp1, temp2} field layout. CMD 0x16 is the canonical source (writes onto the device entry and publishes MQTT); CMD 0x31 is log-only to avoid dual MQTT updates for the same reading. Removes the MSG_TYPE_TEMP_READING2 pattern and the dedicated handle_temp_reading2.
  • MQTT topic split: setpoints (pool_sp, spa_sp, scale) moved from pool/<id>/temperature/state to a dedicated pool/<id>/setpoints/state. Per-sensor temperature readings publish to pool/<id>/temperature/<slug>/<index>/state (multi-sensor) or pool/<id>/temperature/<slug>/state (single-sensor), where <slug> is derived from get_device_name() (e.g. connect_8_10, genus_heater). Breaking change for MQTT consumers: the old pool/<id>/temperature/state topic is gone; any HA automation bound to the old "Temperature" sensor entity will need to rebind to the new per-source entities.
  • HA discovery for setpoint number entities (Pool Setpoint, Spa Setpoint) updated to read from the new setpoints/state topic.

Removed

  • pool_state.current_temp and pool_state.temp_valid — temperatures now live per-source on seen_device_t. Consumers (MQTT publish, HTTP status, HA discovery) migrated to the per-device fields.
  • Global "Temperature" HA sensor entity (publish_temperature_discovery) — replaced by per-source entities discovered lazily on first CMD 0x16 reading.
  • HTTP /status field temperature.current — replaced by temperature1/temperature2 on each device entry.

Fixed

  • HA MQTT discovery entity IDs: switched from the unrecognised default_entity_id field to object_id across all entity types (temperatures, setpoints, pH/ORP, heaters, channels, lights, valves, mode, favourites), so HA now derives stable entity IDs like sensor.pool_controller_<mac>_orp instead of falling back to sensor.unnamed_device_<n>
  • CMD 0x16 (Connect 8/10 variant): byte 11 was previously labelled "unknown — always 0x00", now decoded as a second water temperature (temp2).
  • CMD 0x31 byte 11: was previously labelled "unknown — always 0xA6 in observed samples", now decoded as the same temp2 field as CMD 0x16 byte 11. The >= 0xA0 values (0xA6, 0xAD, 0xAF observed) are the disconnected-sensor sentinel — confirmed by paired captures where CMD 0x16 byte 11 reads 0x00 in the same cycle.

Full Changelog: v1.3.1...v1.4.0

v1.3.1 - extracting more commands

18 May 07:18
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.3.1] - 2026-05-18

Changed

  • Pulled out the CMD 0x38 and 0x39 to be handled as commands rather than patterns as other devices send these now.
  • Pulled out the temperature setpoint command (CMD 0x19) to be source agnostic as the Genus Heater also sends this.
  • Consolidated the chlorinator pH/ORP setpoint (CMD 0x1D) and reading (CMD 0x1F) dispatch into source-agnostic CMD-byte routing in dispatch_message, so the existing four handle_chlor_{ph,orp}_{setpoint,reading} handlers now fire for both 0x0090 RolaChem and 0x0084 Viron sources — payload shape {channel, value_lo, value_hi} is identical across the two variants, only the source address (and resulting checksum1 byte) differed. Removed the MSG_TYPE_CHLOR prefix pattern and the four CHLOR_*_{SETPOINT,READING} sub-type pattern constants

Full Changelog: v1.3.0...v1.3.1

v1.3.0 - rework commands and per device version and message counts

18 May 00:24
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.3.0] - 2026-05-18

Added

  • Added handler and PROTOCOL.md §32 entry for the Chlorinator Status Broadcast (CMD 0x12 from both 0x0090 and 0x0084 variants) — 1-byte mode payload, tentatively mapped to the standard 0x00=Off / 0x01=Auto / 0x02=On channel-state convention (marked ⚠️ pending an Off↔Auto↔On transition capture); handle_chlor_status logs the resolved mode name and populates new pool_state->chlor_mode / chlor_mode_valid fields. Updates the §Known Command Bytes 0x12 shared-device table to list five devices and the Chlorinator broadcasts table to include 0x12
  • Added handler and PROTOCOL.md §33 entry for the Chlorinator Firmware Version (CMD 0x0A from 0x0084) — structurally identical to the §17 Gateway and §21 Touchscreen firmware-version messages with a 2-byte {major, minor} payload; handle_chlor_version populates new pool_state->chlor_version_major / _minor / _valid fields and logs the version. Updates the §Known Command Bytes 0x0A shared-device table to list three devices and the Chlorinator broadcasts table to include 0x0A
  • Added PROTOCOL.md §34 entry for the Temp Sensor Firmware Version (CMD 0x0A from 0x0062) — same {major, minor} payload shape as the other firmware-version broadcasts; observed sample is v2.6 (02 06 08). Populates new pool_state->temp_sensor_version_* fields via the consolidated firmware-version handler. Updates the §Known Command Bytes 0x0A shared-device table to list five devices (now also covering 0x0070 Heatpump explicitly) and the Temperature sensor broadcasts table to include 0x0A
  • Added a devices section to the /status JSON listing every source address observed on the bus, with resolved name, firmware version (when a CMD 0x0A has been seen), and per-device message_counts.decoded / unknown counters — backed by a new pool_state->seen_devices[] registry populated in decode_message; broadcast (0xFFFF) is excluded from the registry

Changed

  • Changed get_device_name() to format a self-describing Unknown 0xHHLL fallback into a caller-supplied buffer instead of returning NULL, removing the four-branch NULL-handling block in decode_message()
  • Moved the global decoded/unknown message counters from tcp_bridge.c into pool_state_t (messages_decoded_total / messages_unknown_total) and unified global + per-device counter updates in a single mutex-held block at the end of decode_message; tcp_bridge_get_decoded_count() / _unknown_count() and the result-branching at the decode call site removed. Side effect: frames that fail the very first sanity checks (len < 12, missing 0x02/0x03 framing) no longer count as "unknown" — counts now reflect protocol-level decoded-vs-unknown only, not frame-integrity glitches
  • Consolidated the four per-device firmware-version handlers (handle_touchscreen_version, handle_heatpump_version, handle_gateway_version, handle_chlor_version) into a single source-agnostic handle_firmware_version dispatched on data[7] == 0x0A regardless of source — the payload layout ({major, minor}) is identical across all five known sources, so per-source patterns and handlers were redundant. The handler switches on source address to populate the appropriate pool_state->*_version_* field (touchscreen/temp-sensor/chlorinator/gateway have dedicated fields; heatpump and any future sources are log-only). Removed the four MSG_TYPE_*_VERSION pattern constants and the four scattered dispatch entries
  • Replaced the eight per-grouping shared-CMD tables in PROTOCOL.md §Known Command Bytes (Register protocol, Device status 0x12, Firmware version 0x0A, Temperature reading 0x16, Temperature setpoint 0x17, Chlorinator cell mode 0x18, plus per-source broadcast tables for touchscreen/temp-sensor/chlorinator/gateway) with a single master "Known Commands" table — one row per CMD byte, with Direction, Variants / Notes, Section(s), and a new In code? column that distinguishes handlers actually implemented in message_decoder.c from CMDs that are documented only. Surfaces four doc-vs-code gaps (0x0F, 0x18, 0x25, 0x28) at a glance. First step in a planned restructure toward a command-centric doc
  • Consolidated the four per-device firmware-version sections in PROTOCOL.md (§17 Internet Gateway, §21 Touchscreen, §33 Chlorinator, §34 Temp Sensor) into a single generic §17 "Firmware Version" with a unified Known Sources table covering all five observed sources (Touchscreen 0x0050, inbuilt heater 0x0062, Heatpump 0x0070, Chlorinator 0x0084, Gateway 0x00F0). §21, §33, and §34 fully removed — TOC entries, Quick Reference rows, and the section bodies themselves — leaving numbering gaps that will resolve when the doc is restructured to a command-centric layout. Master CMD table's 0x0A row now points only to §17. Removes ~125 lines of duplicated content

Fixed

  • Corrected device-address labels in get_device_name() and across PROTOCOL.md to reflect new understanding: 0x0062 Temp Sensor → Connect 8/10 Controller (now framed as controller-sourced heater/water-temperature broadcasts rather than messages from an "inbuilt heater"), 0x006F Controller → Internal Channels (logical destination tag), 0x0070 Heatpump → Genus Heater, 0x0084 Chlorinator → Viron Chlorinator, 0x0090 Chlorinator → RolaChem, 0x00A0 Salt Cell → Internal Salt Cell, 0x00F0 Internet GW → Internet Gateway; renamed pool_state->temp_sensor_version_* to controller_version_*, MSG_TYPE_HEATPUMP_* to MSG_TYPE_GENUS_HEATER_*, and handle_heatpump_* to handle_genus_heater_* to match

What's Changed

Full Changelog: v1.2.1...v1.3.0

v1.2.1 - working on second heater support

17 May 02:15
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.2.1] - 2026-05-17

Added

  • Added register-dispatch entries for Heater 1 state (0xE6 slot 0x00) and tentative Heater 2 state (0xE9 slot 0x00) so touchscreen CMD 0x38 broadcasts log as Heater 1/2 state - Off/On instead of falling through to "Unhandled register"; both are log-only — authoritative Heater 1 state still flows through the CMD 0x12 path that updates pool_state->heaters[0]
  • Added PROTOCOL.md Appendix A entries for the suspected Heater 2 trio (0xE9 State, 0xEA Pool Setpoint, 0xEB Spa Setpoint) in slot 0x00 — marked tentative (⚠️) based on structural symmetry with the Heater 1 0xE6/0xE7/0xE8 trio, a confirmed gateway register-write to 0xEA = 27°C (CMD 0x3A), and the matching H2 value in the heater's CMD 0x17 [H1, H2] broadcast

Fixed

  • Corrected the §5 Configuration message byte 10 bit 3 interpretation from "heater count (single/two)" to "heater currently active (Off/On)" after direct same-system observation showed bit 3 toggling with heater on/off transitions (heater On → 0x09, heater Off → 0x01); handle_config now logs heater=Off/On and the §5 byte 10 example table is updated accordingly

Full Changelog: v1.2.0...v1.2.1

v1.2.0- improve handle_unknown messages

17 May 00:14
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

[1.2.0] - 2026-05-17

Added

  • Decoded the Internet Gateway Status Broadcast (CMD 0x12 from 0x00F0) — payload is {major, minor, embedded_checksum} where the third byte equals major + minor; handle_gateway_status now logs the firmware version and validates the embedded checksum, while firmware-version state population remains with §17. PROTOCOL.md §18 promoted from ⚠️ to ✅, with two confirming samples (5.1 → 05 01 06, 5.0 → 05 00 05) and a note clarifying that byte 13 is the standard frame checksum, not a data field

Changed

  • Improved unhandled-message logging in handle_unknown to include the resolved source/destination device names (via addr_info), the CMD byte plus a human-readable name (get_cmd_name table covering 0x05–0xFD; CMDs not in the table render as "Unknown CMD 0xXX"), the declared length byte, and the payload section only — making unknown messages triagable from a single log line without duplicating the raw frame already printed as "RX MSG"

Full Changelog: v1.1.0...v1.2.0