Viron Astral Pool ChlorinatorGo integration

Thanks, but it wasn’t all me.
Even though eQ and HALO are different, they have some simularities. @pbutterworth’s work on the Viron eQ was a good kickstart. Especially with the HA custom component. Personally, I’m new to the python programming language, so the Source code will likely reflect that.

Halo plugin via BLE is ready!
Make sure you have a ESP32 setup with esphome BLE proxy (Simple as browsing here, with your esp32 connected via usb Ready-Made Projects — ESPHome and selecting Bluetooth proxy. Join it to your wifi, and then adopt it into Home assistant)

Best experiance is to install with HACS.
In Hacs, add a Intergration Repo pointing at this github.

Then search for Halo.

There’s currently 2 outstanding issues.

  • The initial pairing process configured in configflow is using a new “progress_task” argument that in the Home assistant dev branch, and hasnt made it to the stable release branch which allows async events to trigger gui changes. (Changes to FlowManager.async_show_progress | Home Assistant Developer Docs). As a result, pairing gui fails to behave correctly, but follow these steps should get you configured.

When the halo device is detected, click configure.

  • Put your HALO into pairing mode.
  • Press submit to confirm add device.
  • it will either error, or the circle will just spin “forever”. if it errors, hit config / add / confirm again (whilst the Halo is still in pairing mode). If it is spinning, just wait approx 30 seconds, then cancel it, and hit configure again, it should then ask you what area the Halo is located, and be added to HA.

The other bug is associated around Mode / Speed control. Currently is it DISABLED, ie, you can’t change the mode or speed until I confirm i’ve fixed the bug. (the halo would change to medium manual mode at midnight… lol)

However, it will pull data every minute or so.

Note, During the HA Polling time, you will not be able to use your mobile app to connect to the halo either via Bluetooth or Cloud.
However, you are not totally locked out. If you open your mobile app, and look for the “blue” dot next to your chlorinator. if it isnt there, HA is polling for data(takes 20 seconds tops). as soon as it appears, you will be able to connect to it from your mobile.

3 Likes

Just got my EQ35 setup in what felt like 1 usb-c cable plug and 4 clicks - excellent work guys.

Only comment is that both proj have the same readme that involves a bunch of steps to copy in the custom config where you can just click the 3 dots top right in the HACS screen and choose Custom Repositories and paste the github link and choose integration - boom!

Yeah I agree, although you can manually install it like that into the custom component folder, hacs is the easier way.
Some people also don’t want to install hacs for just one plugin. But those people would likely know how to manually install it as well.

Hey @nagyOUT, great work!

Have you seen the Pool Monitor Card?

Could be a really great way to visualise the results.

I discovered a hidden system menu on the Halo Chlorinator that allows you to display the actual ORP value on the Halo screen and also on the app screen.

I made a video on how to access the menu.

3 Likes

That’s pretty cool. We just need more sensors! Lol

Oh yes, I discovered this already from a astralzodiac YouTube video! Pretty handy with the other settings it has too.

Just released Halo update v0.1.1. it seemed to be closing connection before all data was retrieved. As a result, some items were being marked as unknown randomly.
(pychlorinator updated to v0.2.6 dependancy).
Thanks @bowler for making me aware of this issue.

1 Like

Can someone link me to a suitable BLE dongle for this integration please?
Ideally something like a WiFi ↔ BLE bridge that I can mount somewhere close to the Chlorinator and not have to run a data hard-wire anywhere.
Thanks in advance :slight_smile:

Edit:
will this one work?
https://www.amazon.com.au/ESP32-WROOM-32E-Board-Bluetooth-Antenna-Boards/dp/B0BZN9R3V1

I assume the hard-wire refers to network only, not power?

I used the atome lite and the esphome stuff done here

So easy, hardest part was waiting for delivery!

1 Like

Nice, I have removed my attempt at just a basic rebrand. Thanks for working through this.

This is what I used

ATOM Lite ESP32 IoT Development Kit | m5stack-store

Plug into a PC and then load up as a ESPhome BT Proxy through this link

Ready-Made Projects — ESPHome

1 Like

That should work.

No problems.
Btw, I named this intergration exactly as per the “brand” images that you merged requested into Homeassistant / brands.

1 Like

Awesome work @nagyOUT!
Got this working on the weekend with very little drama.
Is there more data available to pick up from the chlorinator and maybe control? For example whether the heater is running, valve states, other equipment etc…? Is it a case of running the Halo Parser scripts and seeing the other data structures available?

1 Like

Thanks!
The parser library pulls all data out from the Halo. Just need to add them to the HA Intergration.
If you send me a copy of the Halo debug output, I can add them in.

Got this installed via the ESPHome Bluetooth proxy running on a LilyGO POE ESP32 dev board.

Brilliant work @nagyOUT, I have been trying to get my pool sensor data integrated into Home Assistant for months!! Thanks so much for all your efforts!!

1 Like

Here is the output:

2024-01-29 15:17:45.695 DEBUG (MainThread) [custom_components.astralpool_halo_chlorinator.coordinator] halo_ble_client finish: {'DeviceType': <DeviceType.Chlorinator: 1>, 'DeviceVersion': 4, 'DeviceProtocol': <DeviceProtocol.NextGen: 2>, 'DeviceProtocolRevision': 0, 'FirmwareVersionMajor': 1, 'FirmwareVersionMinor': 3, 'BootloaderVersionMajor': 0, 'BootloaderVersionMinor': 1, 'HardwareVersion': 1, 'SerialNumber': 123456789, 'PhControlType': <PhControlTypes.Automatic: 2>, 'OrpControlType': <ChlorineControlTypes.Automatic: 2>, 'MinimumManualAcidSetpoint': 0, 'MinimumManualChlorineSetpoint': 0, 'MinimumOrpSetpoint': 100, 'MinimumPhSetpoint': 3.0, 'MaximumManualAcidSetpoint': 10, 'MaximumManualChlorineSetpoint': 8, 'MaximumOrpSetpoint': 800, 'MaximumPhSetpoint': 10.0, 'ph_control_type': <PhControlTypes.Automatic: 2>, 'ChlorineControlType': <ChlorineControlTypes.Automatic: 2>, 'chlorine_control_type': <ChlorineControlTypes.Automatic: 2>, 'HighestPhMeasured': 8.2, 'LowestPhMeasured': 0.0, 'HighestOrpMeasured': 860, 'LowestOrpMeasured': 0, 'HeaterEnabled': 1, 'FilterPumpThreeSpeed': 0, 'HeaterPumpThreeSpeed': 0, 'HeaterPumpInstalled': 0, 'HeaterPumpTimerBit': 0, 'SolarEnabled': 1, 'EquipmentEnabled': 1, 'FilterPumpMode': 1, 'ModeGPO1': 255, 'ModeGPO2': 0, 'ModeGPO3': 1, 'ModeGPO4': 0, 'ModeValve1': 255, 'ModeValve2': 255, 'ModeValve3': 0, 'ModeValve4': 0, 'ModeRelay1': 255, 'ModeRelay2': 255, 'StateBitfield': 75, 'AutoEnabledBitfield': 2027, 'mode': <Mode.Auto: 1>, 'StateFilterPump': True, 'pump_is_operating': True, 'AutoEnabledFilterPump': True, 'GPO1_Mode': <GPOMode.NotEnabled: 255>, 'GPO1_State': True, 'GPO1_AutoEnabled': True, 'GPO2_Mode': <GPOMode.Off: 0>, 'GPO2_State': False, 'GPO2_AutoEnabled': False, 'GPO3_Mode': <GPOMode.Auto: 1>, 'GPO3_State': True, 'GPO3_AutoEnabled': True, 'GPO4_Mode': <GPOMode.Off: 0>, 'GPO4_State': False, 'GPO4_AutoEnabled': False, 'Valve1_Mode': <GPOMode.NotEnabled: 255>, 'Valve1_State': False, 'Valve1_AutoEnabled': True, 'Valve2_Mode': <GPOMode.NotEnabled: 255>, 'Valve2_State': True, 'Valve2_AutoEnabled': True, 'Valve3_Mode': <GPOMode.Off: 0>, 'Valve3_State': False, 'Valve3_AutoEnabled': True, 'Valve4_Mode': <GPOMode.Off: 0>, 'Valve4_State': False, 'Valve4_AutoEnabled': True, 'Relay1_Mode': <GPOMode.NotEnabled: 255>, 'Relay1_State': False, 'Relay1_AutoEnabled': True, 'Relay2_Mode': <GPOMode.NotEnabled: 255>, 'Relay2_State': False, 'Relay2_AutoEnabled': True, 'CellReversalCount': 32, 'CellRunningTime': 142, 'LowSaltCellRunningTime': 0, 'PreviousDaysCellLoad': 26, 'DosingPumpSecs': 164, 'FilterPumpMins': 263, 'General': <GeneralValues.DisplayORP|DosingEnabled|PumpProtectEnable|EnableCleaningInterlock|DisplayPH: 6752>, 'CellModel': <CellModelValues.Model_35: 2>, 'ReversalPeriod': 4, 'AIWaterTurns': 10, 'AcidPumpSize': 55, 'FilterPumpSize': 0, 'DefaultManualOnSpeed': 2, 'VolumeUnits': <VolumeUnitsValues.Litres: 0>, 'PoolVolume': 50000, 'SpaVolume': 2800, 'PoolLeftFilter': 25040, 'SpaLeftFilter': 2800, 'Flag': 64, 'PowerBoardRuntime': (126,), 'PhControlSetpoint': 7.6, 'OrpControlSetpoint': 650, 'PoolChlorineControlSetpoint': 4, 'AcidControlSetpoint': 5, 'SpaChlorineControlSetpoint': 1, 'ph_control_setpoint': 7.6, 'chlorine_control_setpoint': 650, 'Flags': 2, 'RealCelllevel': 4, 'CellCurrentmA': 3293, 'MainText': <MainTextValues.Sanitising: 1>, 'SubText1Chlorine': 3, 'ORPMeasurement': 648, 'SubText2Ph': 3, 'PhMeasurement': 7.6, 'SubText3TimerInfo': 0, 'SubText3BytesData': [b'\x00\x00'], 'SubText4ErrorInfo': 0, 'ph_measurement': 7.6, 'IsInPoolSelection': False, 'IsCellRunning': True, 'cell_is_operating': True, 'IsCelLReversed': False, 'IsCoolingFanOn': False, 'IsLightOutputOn': False, 'DosingPumpOn': False, 'CellIsReversing': False, 'AIModeActive': False, 'info_message': <MainTextValues.Sanitising: 1>, 'SubText1': <SubText1Values.ORPIsGreen: 3>, 'chlorine_control_status': <SubText1Values.ORPIsGreen: 3>, 'SubText2': <SubText2Values.PHIsGreen: 3>, 'SubText3': <SubText3Values.NoneValue: 0>, 'DoseDisableTimeMins': 0, 'MaintenanceTaskState': <TaskStatesValues.NoTask: 0>, 'MaintenanceTaskReturnCode': <TaskReturnCodesValues.OK: 0>, 'TaskTimeRemaining': 0, 'ValueToDisplay': 0, 'CalibrateState': <CalibrateStatesValues.Idle: 0>, 'ModeAfterComplete': <Mode.Off: 0>, 'AcidDosingDisabled': False, 'ZoneModes': b'\x00\x00\x00\x00', 'ZoneColours': b'\x00\x00\x00\x00', 'ZoneStateFlags': 0, 'LightingMode_1': <Mode.Off: 0>, 'LightingMode_2': <Mode.Off: 0>, 'LightingMode_3': <Mode.Off: 0>, 'LightingMode_4': <Mode.Off: 0>, 'LightingState_1': <ZoneStateFlagsValues: 0>, 'LightingState_2': <ZoneStateFlagsValues: 0>, 'LightingState_3': <ZoneStateFlagsValues: 0>, 'LightingState_4': <ZoneStateFlagsValues: 0>, 'LightingColour_1': 0, 'LightingColour_2': 0, 'LightingColour_3': 0, 'LightingColour_4': 0, 'LightingEnabled': 0, 'OnBoardLightEnabled': 0, 'Model': 0, 'NumZonesInUse': 1, 'ZoneIsMulticolourFlags': <ZoneIsMulticolourFlagsValues.Zone1IsMulticolour|Zone2IsMulticolour|Zone3IsMulticolour|Zone4IsMulticolour: 15>, 'ZoneNames': b'\x00\x01\x07\x07', 'LightingZoneName_1': <ZoneNamesValues.Pool: 0>, 'LightingZoneName_2': <ZoneNamesValues.Spa: 1>, 'LightingZoneName_3': <ZoneNamesValues.Other: 7>, 'LightingZoneName_4': <ZoneNamesValues.Other: 7>, 'IsFahrenheit': 0, 'TempSupports': <TempSupportsValues.BoardTemp|WaterTemp|SolarWater|SolarRoof|Heater: 59>, 'BoardTemp': 49.1, 'WaterTemp': 27.0, 'ChloroWater': 6519.8, 'SolarWater': 27.2, 'WaterTempValid': <TempValidEnum.IsValid: 1>, 'SolarRoof': 55.2, 'Heater': 27.0, 'TempDisplayed': <TempDisplayedValues.BoardTemp|ChloroWater: 5>, 'HeaterPumpEnabled': 0, 'HeaterMinPumpSpeed': <SpeedLevels.Low: 0>, 'HeaterStatusFlag': 2, 'HeaterPumpMode': <Mode.Auto: 1>, 'HeaterMode': <Mode.Off: 0>, 'HeaterSetpoint': 30, 'HeatPumpMode': <HeatpumpModeValues.Heating: 1>, 'HeaterForced': <HeaterForcedEnum.NotForced: 0>, 'HeaterForcedTimeHrs': 0, 'HeaterForcedTimeMins': 0, 'HeaterWaterTempValid': <TempValidEnum.IsValid: 1>, 'HeaterWaterTemp': 27.0, 'HeaterError': 0, 'HeaterOn': False, 'HeaterPressure': True, 'HeaterGasValve': False, 'HeaterFlame': False, 'HeaterLockout': False, 'GeneralServiceRequired': False, 'IgnitionServiceRequired': False, 'CoolingAvailable': False, 'HeaterCooldownEventOccurredFlag': 0, 'HeaterCooldownState': 0, 'Ignore': 0, 'TargetMode': 0, 'RemainingCooldownTime': 0, 'TotalHeaterCooldownTime': 300, 'SolarPumpStartHR': 20, 'SolarPumpStartMin': 0, 'SolarPumpStopHR': 7, 'SolarPumpStopMin': 0, 'SolarEnableFlush': 1, 'SolarFlushTimeHR': 14, 'SolarFlushTimeMin': 0, 'Differential': 70, 'SolarEnableExclPeriod': 1, 'SolarRoofTemp': 552, 'SolarWaterTemp': 272, 'SolarTemp': 32, 'SolarSeason': 1, 'SolarMode': <Mode.Auto: 1>, 'SolarFlag': 17, 'SolarRoofTempValid': <TempValidEnum.IsValid: 1>, 'SolarWaterTempValid': <TempValidEnum.IsValid: 1>, 'SolarSpecTemp': 0, 'SolarMessage': <SolarMessageValues.SolarHeatingActive: 2>, 'SolarPumpState': True, 'SolarFlushActive': False, 'FilterPumpSpeed': 2, 'ParameterGPO1': 0, 'ParameterGPO2': 0, 'ParameterGPO3': 0, 'ParameterGPO4': 0, 'ParameterValve1': 0, 'ParameterValve2': 0, 'ParameterValve3': 0, 'ParameterValve4': 0, 'ParameterRelay1': 0, 'ParameterRelay2': 0, 'pump_speed': <SpeedLevels.High: 2>, 'Index': 3, 'OutletEnabled': 1, 'GPOFunction': <GPOFunctionValues.Equipment: 0>, 'GPOName': <GPONameValues.Blower: 7>, 'GPOLightingZone': 0, 'UseTimers': 1, 'RelayEnabled': 0, 'RelayName': <RelayNameValue.Relay1: 0>, 'RelayAction': 0, 'ValveEnabled': 1, 'ValveName': <ValveNameValue.NoneValue: 0>}
type or paste code here

image

So i’ve just added Heater Mode, and Heater on status for now.

I’ve worked out how to dynamically add entities based on the returned data from the Halo. So, The Heater stuff above will only appear if the halo reports Heater is enabled (which yours does).

I want to refactor the code further removing unneeded settings for those who have ORP / PH / Dosers, vs those who do not. So some metrics will disappear likely on the next update, based on the Halo configuration.

Controlling the heater mode not implemented yet, as I have to work out the appropriate command to construct and send. (This is on the todo list with Lighting also)

Your log shows you have Solar enabled as well.

Can you explain your pump(s) configuration?
ie, 1 / 2 / 3 pumps?
1 - filter
2 - solar
3 - heat pump

or is Solar combined with filter pump, or heatpump pump?
And what controls that pump? hub? xpand? heatpump?

Can discuss this offline if you wish.

1 Like