Add manual credential entry, SLX/FLX colour control, and lighting state parser (fw 2.7+ workaround)#2
Conversation
- Expose async_step_manual via a Setup method picker on async_step_user. Lets users on chlorinator firmware that requires server-mediated pairing (Play Integrity / App Attest) bypass BLE pairing entirely when they have cloud credentials obtained out-of-band. - Add Light Colour select for AstralPool SLX/FLX lights (lighting model 0). Uses the existing 0x01F5 LIGHT_CMD_ID with the SetZoneColour app action (action=5), payload [zone, colour_value]. 12 colours/patterns matching LightColour.cs constants. Write-only for now - state read for lighting colour is a separate follow-up.
The previous mapping {1: 'Off', 2: 'On', 3: 'Auto'} sent action=1
(SetZoneModeToManual in BusinessObjects.Lighting.AppActions) when the
user picked 'Off' — which kept lights on in manual mode rather than
turning them off.
Map to the correct enum values:
- Off -> TurnOffZone (3)
- On -> TurnOnZone (4)
- Auto -> SetZoneModeToAuto (2)
Also send the zone byte in the payload (was missing), matching the
SetZoneColour write format and the official app's behaviour.
Adds a parser for the cloud lighting state characteristic, identified by sniffing the official app's traffic during a colour change. Layout: bytes 0-3: ZoneModes[4] (0=default/Off, 1=Auto, 2=Manual) bytes 4-7: ZoneColours[4] (model-specific value, SLX/FLX = 0..11) byte 8: ZoneStateFlags (bit 0 = Zone1On) Updates HaloData with light_colour, light_zone_modes, light_zone_colours, light_zone_on_flags. Polls cmd 300 in the initial vomit batch and refreshes it after light mode / colour writes so the select entities reflect changes made via phone app or chlorinator screen, not just HA. Wires light_colour into the Light Colour select's value_fn so the entity shows current state instead of None.
The 'Manual credential path' subsection previously described itself as a hidden fallback. With BLE pairing blocked on fw 2.7+ by Play Integrity attestation, manual entry is the only viable path on affected hardware. Document the actual extraction: - Hardware/software prerequisites (paired phone, adb, USB debugging). - adb logcat filter targeting the official app's PID. - Where in the logcat output the (sn, username, password) credentials appear. - Bearer-secret warning so users don't paste credentials into issues or screenshots. - Step-through for adding the integration in Home Assistant. - The known one-concurrent-connection-per-chlorinator wrinkle and the disable/re-enable workaround.
|
Hi @davidbell81, this is excellent. The firmware-2.7+ context is significant new information for this project I new AstralPool had moved BLE pairing, didnt know about the app attest side of things. I havent moved from 2.3 myself as paranoid i wouldnt be able to fix it. The decompile trace from The Light Mode action-code mapping bug looks like a real defect My quick early morning thoughts:
Once preview.1 is published this weekend I'll come back here with a concrete rebase plan rather than handwaving. Thanks again also a question how confident are you on the manual-cred extraction path? You mention |
|
Thanks Rob. On manual-cred extraction confidence — what I actually know:
Proposed README section for the manual-credential path — happy to commit this onto
On the decompile findings for SECURITY.md: the On the rewrite collision: makes sense. The manual-cred path is small enough to lift onto preview.1 without much pain — happy to rebase once it's tagged. On 0x012C parser comparison: keen to see yours. Mine is the minimum needed to surface |
TL;DR
Two related changes that make the integration usable on Halo Chlor firmware 2.7+, where Astralpool moved BLE pairing behind Google Play Integrity / Apple App Attest and the existing BLE pair flow can't complete:
async_step_userso users with already-known cloud credentials can skip BLE pairing entirely.Light Colourselect with all 12 SLX/FLX colours/patterns, plus a parser for cmd0x012C(lighting state) so HA reflects external colour/mode changes.Plus a bug fix to existing
Light Modemapping (Off / On / Auto were sending the wrong action codes; "Off" actually selected manual mode and "Auto" turned lights off).All changes validated end-to-end against a real Halo Chlor 25 on fw 2.7 with FLX lights — colour picks, on/off, and mode changes round-trip correctly.
Why this matters: the fw 2.7+ wall
Decompiling the official Halo Chlor Go Android app (Xamarin,
au.com.fabtronics.halochlorgo):AstralPoolService.BusinessObjects.Helpers/Crypto.cs— local AES path matchespychlorinator-cloudbyte-for-byte. Existing BLE crypto is correct for older firmware.AstralPoolService.BusinessObjects/Device.cs:895-905— new behaviour gate:if (DeviceProtocolRevision == 0) { use local AES } else { use server-side MacHelper.RequestMac }. fw 2.7 advertises a non-zeroDeviceProtocolRevision, taking the server path.BusinessObjects.Helpers/MacHelper.cs— server path POSTs tohttps://halo.connectmypool.com.au:443/halo/bluetooth-auth-key/version-1/{generate-challenge,request-mac}with a Play Integrity / App Attest token. Third-party clients can't replicate this — the Astralpool server refuses any request without a valid attestation token signed by Google/Apple, and that token can only be produced by the genuine, store-installed app on a non-rooted device.Net effect: the existing BLE pairing flow times out silently on fw 2.7+, with logs showing session-key read OK, auth write OK, and then no notification ever returning. The chlorinator's protocol-revision gate is the culprit.
The manual credential path in this PR is the cleanest workaround — users who pair via the official app retain working cloud credentials in the app's data; those credentials are extractable (e.g., via
adb logcatof the app'sConsole.WriteLine("Credentials Message: ...")output, which surfaces serial/username/password as JSON). Once they have those, this PR's manual flow lets them set up the integration without ever touching BLE.What's in the PR
1. Manual credential entry (
config_flow.py,strings.json,translations/en.json)async_step_usernow shows a Setup method picker:async_step_ble_discovery.async_step_manual.selector.setup_methodtranslation strings for the radio options.The hidden
async_step_manualis otherwise unchanged — same(serial_number, username, password)form, samequery_availabilityvalidation, same handoff toasync_step_device_details. Just reachable from the UI now.2. Light Colour select (
select.py,websocket_client.py)New
LIGHT_COLOUR_SELECT_DESCRIPTIONexposing the 12 AstralPool SLX/FLX colour names directly fromBusinessObjects.Light.LightColour:New
set_light_colour(colour_value, zone=0)onHaloWebSocketClient. Sends cmd0x01F5(LIGHT_CMD_ID) with payload[5, zone, colour_value]— matchingChlorinatorDevice3.SendAppAction_LightingSetColourexactly:entity_registry_enabled_default=Falseconsistent with other lighting controls.3. Light Mode action-mapping fix (
websocket_client.py)Pre-existing
LIGHT_MODES = {1: "Off", 2: "On", 3: "Auto"}doesn't match the actualBusinessObjects.Lighting.AppActionsenum:The previous mapping sent
1for "Off" →SetZoneModeToManual(lights stayed on in manual mode). My local testing confirmed: picking "Auto" actually turned lights off (3=TurnOffZone). New mappingLIGHT_MODE_ACTIONS = {"Off": 3, "On": 4, "Auto": 2}aligns user-facing labels with the protocol semantics.Also:
set_light_modenow sends[action, zone](was sending just[action]), matching the same payload format asSetZoneColourand the official app.4. Lighting state parser (
websocket_client.py)Adds
_parse_lighting_statefor cmd0x012C/ 300, identified by sniffing the official app's WebSocket traffic during a deliberate colour change:Layout:
HaloDatagetslight_colour,light_zone_modes,light_zone_colours,light_zone_on_flags. The Light Colour entity now exposesvalue_fn=lambda data: data.light_colourinstead of None.Cmd 300 added to
_request_all_data'svomit_cmdsfor initial state. Bothset_light_modeandset_light_colournow refresh(0x0068, 0x012C)after writes — keeps HA in sync after external changes (phone app, chlorinator screen) within the next refresh cycle.Testing
End-to-end on a Halo Chlor 25 fw 2.7 + AstralPool FLX pool light:
Tested HA core: 2026.4.2.
Caveats / open questions
AstralPool_SLX/FLX,Delta,Hayward_ColorLogic_CrystalLogic,Pentair_Intellibrite_5G,JJElectronics_ColorSplash_XG,SpaElectrics,LumiPower). I can extend to model-aware naming in a follow-up if you'd prefer that done before merge — wanted to keep this PR focused.Happy to revise based on review.