Releases: marklynch/pool-controller-code
Releases · marklynch/pool-controller-code
v1.6.0
Immutable
release. Only release title and notes can be modified.
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
Immutable
release. Only release title and notes can be modified.
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
Immutable
release. Only release title and notes can be modified.
[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
Immutable
release. Only release title and notes can be modified.
[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 (CMD0x3B) - Added pump physical speed preset button presses (CMD
0x1B, mapping Low/Med/High), and controller-to-pump speed commands (CMD0x18, mapping Low/Med/High)
Changed
- Renamed device
0x00A0from "Internal Salt Cell" to "Viron XT Pump" - Renamed CMD
0x18from "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 oftemp1/temp2is INVALID the still-valid temperature now logs its decoded °C value rather than a genericOK (raw 0xXX)label - Re-enabled the
test_message_decoderandtest_mqtt_commandshost-test suites that had drifted out of sync with the current decoder/state andbus_send_bytes/s_pool_stateinterfaces, and cleared theSKIP_LISTinrun_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
Immutable
release. Only release title and notes can be modified.
v1.4.0 - support multiple temperatures
Immutable
release. Only release title and notes can be modified.
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_validandsingle_sensor_sourceare now stored directly on eachseen_device_tentry. 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 thedevices[]array now carriestemperature1(andtemperature2for multi-sensor sources like the Connect 8/10). Devices that don't broadcast CMD0x16get 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
0x16and0x31) 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
0x0072Genus Heater variant of CMD0x16(same single-byte layout as the0x0070Genus Heater).
Changed
- Pulled out the water temperature reading (CMD
0x16) to be source-agnostic — single unifiedhandle_temp_readingdispatched on the CMD byte and routed by payload length: 2-byte{temp1, temp2}from0x0062Connect 8/10 (LEN0x0E); 1-byte{temp1}from0x0070/0x0072Genus Heater family (LEN0x0D). Removes theMSG_TYPE_TEMP_READINGandMSG_TYPE_GENUS_HEATER_TEMP_READINGpatterns and the dedicatedhandle_genus_heater_temp_reading. - Merged CMD
0x31(Water Temperature Reading alt) into the unifiedhandle_temp_reading()—0x16and0x31share the same{temp1, temp2}field layout. CMD0x16is the canonical source (writes onto the device entry and publishes MQTT); CMD0x31is log-only to avoid dual MQTT updates for the same reading. Removes theMSG_TYPE_TEMP_READING2pattern and the dedicatedhandle_temp_reading2. - MQTT topic split: setpoints (
pool_sp,spa_sp,scale) moved frompool/<id>/temperature/stateto a dedicatedpool/<id>/setpoints/state. Per-sensor temperature readings publish topool/<id>/temperature/<slug>/<index>/state(multi-sensor) orpool/<id>/temperature/<slug>/state(single-sensor), where<slug>is derived fromget_device_name()(e.g.connect_8_10,genus_heater). Breaking change for MQTT consumers: the oldpool/<id>/temperature/statetopic 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 newsetpoints/statetopic.
Removed
pool_state.current_tempandpool_state.temp_valid— temperatures now live per-source onseen_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 CMD0x16reading. HTTP /statusfieldtemperature.current— replaced bytemperature1/temperature2on each device entry.
Fixed
- HA MQTT discovery entity IDs: switched from the unrecognised
default_entity_idfield toobject_idacross all entity types (temperatures, setpoints, pH/ORP, heaters, channels, lights, valves, mode, favourites), so HA now derives stable entity IDs likesensor.pool_controller_<mac>_orpinstead of falling back tosensor.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
0x31byte 11: was previously labelled "unknown — always 0xA6 in observed samples", now decoded as the sametemp2field as CMD0x16byte 11. The>= 0xA0values (0xA6,0xAD,0xAFobserved) are the disconnected-sensor sentinel — confirmed by paired captures where CMD0x16byte 11 reads0x00in the same cycle.
Full Changelog: v1.3.1...v1.4.0
v1.3.1 - extracting more commands
Immutable
release. Only release title and notes can be modified.
[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 (CMD0x1F) dispatch into source-agnostic CMD-byte routing indispatch_message, so the existing fourhandle_chlor_{ph,orp}_{setpoint,reading}handlers now fire for both0x0090RolaChem and0x0084Viron sources — payload shape{channel, value_lo, value_hi}is identical across the two variants, only the source address (and resulting checksum1 byte) differed. Removed theMSG_TYPE_CHLORprefix pattern and the fourCHLOR_*_{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
Immutable
release. Only release title and notes can be modified.
[1.3.0] - 2026-05-18
Added
- Added handler and PROTOCOL.md §32 entry for the Chlorinator Status Broadcast (CMD
0x12from both0x0090and0x0084variants) — 1-byte mode payload, tentatively mapped to the standard0x00=Off /0x01=Auto /0x02=On channel-state convention (marked⚠️ pending an Off↔Auto↔On transition capture);handle_chlor_statuslogs the resolved mode name and populates newpool_state->chlor_mode/chlor_mode_validfields. Updates the §Known Command Bytes0x12shared-device table to list five devices and the Chlorinator broadcasts table to include0x12 - Added handler and PROTOCOL.md §33 entry for the Chlorinator Firmware Version (CMD
0x0Afrom0x0084) — structurally identical to the §17 Gateway and §21 Touchscreen firmware-version messages with a 2-byte{major, minor}payload;handle_chlor_versionpopulates newpool_state->chlor_version_major/_minor/_validfields and logs the version. Updates the §Known Command Bytes0x0Ashared-device table to list three devices and the Chlorinator broadcasts table to include0x0A - Added PROTOCOL.md §34 entry for the Temp Sensor Firmware Version (CMD
0x0Afrom0x0062) — same{major, minor}payload shape as the other firmware-version broadcasts; observed sample is v2.6 (02 06 08). Populates newpool_state->temp_sensor_version_*fields via the consolidated firmware-version handler. Updates the §Known Command Bytes0x0Ashared-device table to list five devices (now also covering0x0070Heatpump explicitly) and the Temperature sensor broadcasts table to include0x0A - Added a
devicessection to the/statusJSON listing every source address observed on the bus, with resolved name, firmware version (when a CMD0x0Ahas been seen), and per-devicemessage_counts.decoded/unknowncounters — backed by a newpool_state->seen_devices[]registry populated indecode_message; broadcast (0xFFFF) is excluded from the registry
Changed
- Changed
get_device_name()to format a self-describingUnknown 0xHHLLfallback into a caller-supplied buffer instead of returning NULL, removing the four-branch NULL-handling block indecode_message() - Moved the global decoded/unknown message counters from
tcp_bridge.cintopool_state_t(messages_decoded_total/messages_unknown_total) and unified global + per-device counter updates in a single mutex-held block at the end ofdecode_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, missing0x02/0x03framing) 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-agnostichandle_firmware_versiondispatched ondata[7] == 0x0Aregardless 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 appropriatepool_state->*_version_*field (touchscreen/temp-sensor/chlorinator/gateway have dedicated fields; heatpump and any future sources are log-only). Removed the fourMSG_TYPE_*_VERSIONpattern 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 newIn code?column that distinguishes handlers actually implemented inmessage_decoder.cfrom 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 heater0x0062, Heatpump0x0070, Chlorinator0x0084, Gateway0x00F0). §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's0x0Arow 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:0x0062Temp Sensor → Connect 8/10 Controller (now framed as controller-sourced heater/water-temperature broadcasts rather than messages from an "inbuilt heater"),0x006FController → Internal Channels (logical destination tag),0x0070Heatpump → Genus Heater,0x0084Chlorinator → Viron Chlorinator,0x0090Chlorinator → RolaChem,0x00A0Salt Cell → Internal Salt Cell,0x00F0Internet GW → Internet Gateway; renamedpool_state->temp_sensor_version_*tocontroller_version_*,MSG_TYPE_HEATPUMP_*toMSG_TYPE_GENUS_HEATER_*, andhandle_heatpump_*tohandle_genus_heater_*to match
What's Changed
- Rework protocol.md by command by @marklynch in #11
Full Changelog: v1.2.1...v1.3.0
v1.2.1 - working on second heater support
Immutable
release. Only release title and notes can be modified.
[1.2.1] - 2026-05-17
Added
- Added register-dispatch entries for Heater 1 state (
0xE6slot0x00) and tentative Heater 2 state (0xE9slot0x00) so touchscreen CMD0x38broadcasts log asHeater 1/2 state - Off/Oninstead of falling through to "Unhandled register"; both are log-only — authoritative Heater 1 state still flows through the CMD0x12path that updatespool_state->heaters[0] - Added PROTOCOL.md Appendix A entries for the suspected Heater 2 trio (
0xE9State,0xEAPool Setpoint,0xEBSpa Setpoint) in slot0x00— marked tentative (⚠️ ) based on structural symmetry with the Heater 10xE6/0xE7/0xE8trio, a confirmed gateway register-write to0xEA = 27°C(CMD0x3A), and the matching H2 value in the heater's CMD0x17[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_confignow logsheater=Off/Onand the §5 byte 10 example table is updated accordingly
Full Changelog: v1.2.0...v1.2.1
v1.2.0- improve handle_unknown messages
Immutable
release. Only release title and notes can be modified.
[1.2.0] - 2026-05-17
Added
- Decoded the Internet Gateway Status Broadcast (CMD
0x12from0x00F0) — payload is{major, minor, embedded_checksum}where the third byte equalsmajor + minor;handle_gateway_statusnow 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_unknownto include the resolved source/destination device names (viaaddr_info), the CMD byte plus a human-readable name (get_cmd_nametable 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