Hayward Pools LOCAL integration announcement!

I have been working for the past few months on a custom HACS integration for Hayward OmniLogic/Hub/PL pool controllers. If you have one of these, I know what you are thinking, why make another integration? there is already one in core, and another one in HACS… well, this one offers LOCAL API control. This has been a feature that I have seen requested multiple times, and it’s finally here! I worked with the creator of the previous (and excellent) integrations, as well as a project manager at Hayward to get this developed.

Before we get too far, lets cover a few things that you should know up front before using it:

Now the fun part:

The integration code is available at GitHub - cryptk/haomnilogic-local: A Home Assistant integration for Hayward OmniLogic/OmniHub pool controllers using the local UDP api, and it utilizes an open source library available at GitHub - cryptk/python-omnilogic-local: Python Omnilogic Library for local access, everything is licensed Apache 2.0

There is quite a bit of functionality in the integration already, this is covered under GitHub - cryptk/haomnilogic-local: A Home Assistant integration for Hayward OmniLogic/OmniHub pool controllers using the local UDP api, but here is a quick rundown of the highlights:

  • Multiple devices for your backyard and each body of water
  • Control of variable speed pumps including buttons to activate your speed presets
  • ColorLogic light control
  • Relay control
  • Heater control
  • Chlorinator control (timed-percent mode only, need to find someone with an ORP sensor to implement that)

This announcement is going in a few places, and while I will try my best to keep an eye on the various spots, I have enabled GitHub Discussions to hopefully centralize any questions/discussion about the integration, you can find those at cryptk/haomnilogic-local · Discussions · GitHub

If you run into any problems, please feel free to open an issue at Issues · cryptk/haomnilogic-local · GitHub

6 Likes

This is awesome ! Thank you ! Will be adding to mine to start testing !

Seriously - THANK YOU for this! I am so excited to no longer be dependent on Hayward’s flakey servers!

I installed a few hours ago, and so far it is looking great. I did have to change a few automations.

For example, this:

      - service: omnilogic.set_pump_speed
        data:
          entity_id: switch.pool_pump
          speed: 25

Had to change to this:

      - service: number.set_value
        data:
          entity_id: number.pool_pump_speed
          value: 25

But otherwise it seems stable, and definitely faster than the cloud version.

I know it is still early, but curious - Do you have any plans to try to eventually move this into HA core?

Sorry for the late reply, right after I made the announcement I had some family issues that I had to take care of.

That change is expected, wherever possible, I’m attempting to use Home Assistant native entities rather than custom services (and so far I haven’t needed any).

Getting it into core eventually is not off the table, but it’s not the primary focus yet. I want to make sure it’s stable before I work on that.

1 Like

Been using this integration since the announcement with no issues.

Great work.

First, thanks for you work.

A simple question, its this integration compatible with Hayward AQUARITE FLO ADVANCED, im desperated with this controller.

Thanks.,

I am still a newbie, the functionality looks very nice but I am not sure how to configure. The repository is now in HACS. I have a static IP for the controller.

Does the configuration.yaml need to be edited?

For a more simple solution does this work with any cards?

The integration is awesome and works much better than the cloud integration. Has anyone with lovelace skills put together any snazzy display cards that work with the interface?

@cryptk I just want to thank you again for creating the local integration! I appreciate it now more than ever!

I just came across this after having problems with the cloud integration and wow this is amazing work! It’s fast and has more functionality that the cloud integration. Kudos and thank you cryptk for making this.

Hello,

Could you please let me know if the plugin you’re developing works with the Hayward heat pump that uses the EyesPool Inverter Connect app?

Hello,

This is a fantastic update—local API control is a huge win for privacy and speed. Great work collaborating with IPASS Illinois and Hayward!

Hi. I echo the love for this integration. One question is around the service mode entity. It doesn’t seem to reflect the actual state of my system. Any idea what i’m doing wrong? Thanks!

Just converted to the local integration and it’s fantastic. However, I’m having an issue when changing BOWs. Appears to be caused by an inability to parse telemetry for Filter.whyFilterIsOn and an input of 22. Appears the codes only go to 20.

i saw the latest version that came out.

I just want to appreciate the work you are putting into it. Thank ou!

and, now it stopped working. I am getting nothing. :confused:

I get an "Error -5 while decompressing data incomplete or truncated stream".
I rolled back to 1.5.0 then 1.4.1 and now i cannot even get a connection to the same device I had full control before.
The Hayward has no internet access. So no updates to it.
something about decompressing, a non-compressed packet or some such?
Do you want me to put the log here, or on github?

And, thank you!

homeassistant.exceptions.ConfigEntryNotReady: Error -5 while decompressing data: incomplete or truncated stream
2026-06-03 18:23:34.644 DEBUG (MainThread) [pyomnilogic_local.api.protocol] connection lost: None
2026-06-03 18:33:20.827 ERROR (MainThread) [custom_components.fpl] 'balancesDrilldown'
2026-06-03 18:33:35.106 DEBUG (MainThread) [pyomnilogic_local.api.api] Sending RequestTelemetryData with body: <?xml version='1.0' encoding='utf-8'?>
<Request xmlns="http://nextgen.hayward.com/api"><Name>RequestTelemetryData</Name></Request>
2026-06-03 18:33:35.107 DEBUG (MainThread) [pyomnilogic_local.api.protocol] connection established
2026-06-03 18:33:35.108 DEBUG (MainThread) [pyomnilogic_local.api.protocol] transmitting message ID: 1601, type: GET_TELEMETRY (attempt 1/6)
2026-06-03 18:33:35.116 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 1601, Type: MSP_ACK, Compressed: False, Client: OMNI, Body: 
2026-06-03 18:33:35.125 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 314, Type: MSP_TELEMETRY_UPDATE, Compressed: True, Client: OMNI
2026-06-03 18:33:35.125 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 314
2026-06-03 18:33:35.126 DEBUG (MainThread) [pyomnilogic_local.api.api] Received response for RequestTelemetryData: <?xml version="1.0" encoding="UTF-8" ?>
<STATUS version="1.12">
    <Backyard systemId="0" statusVersion="12" airTemp="83" state="1" ConfigChksum="2961726" mspVersion="R0502000" />
    <BodyOfWater systemId="1" waterTemp="-1" flow="1" />
    <Filter systemId="3" filterState="0" filterSpeed="0" valvePosition="2" whyFilterIsOn="0" fpOverride="0" reportedFilterSpeed="0" power="0" lastSpeed="50" />
    <VirtualHeater systemId="4" Current-Set-Point="80" enable="1" SolarSetPoint="81" Mode="0" SilentMode="0" whyHeaterIsOn="1" />
    <Heater systemId="5" heaterState="0" temp="83" enable="1" priority="0" maintainFor="24" />
    <Chlorinator systemId="6" status="0" instantSaltLevel="0" avgSaltLevel="0" chlrAlert="0" chlrError="0" scMode="0" operatingState="1" Timed-Percent="35" operatingMode="1" enable="0" />
    <ColorLogic-Light systemId="8" lightState="0" currentShow="0" speed="4" brightness="4" specialEffect="0" />
    <BodyOfWater systemId="2" waterTemp="96" flow="1" />
    <Filter systemId="10" filterState="1" filterSpeed="100" valvePosition="2" whyFilterIsOn="14" fpOverride="0" reportedFilterSpeed="100" power="1210" lastSpeed="50" />
    <VirtualHeater systemId="11" Current-Set-Point="96" enable="1" SolarSetPoint="97" Mode="0" SilentMode="0" whyHeaterIsOn="1" />
    <Heater systemId="12" heaterState="0" temp="83" enable="1" priority="0" maintainFor="24" />
    <Chlorinator systemId="13" status="0" instantSaltLevel="0" avgSaltLevel="3900" chlrAlert="0" chlrError="0" scMode="0" operatingState="1" Timed-Percent="0" operatingMode="1" enable="0" />
</STATUS>

2026-06-03 18:33:35.127 DEBUG (MainThread) [pyomnilogic_local.api.api] Sending RequestConfiguration with body: <?xml version='1.0' encoding='utf-8'?>
<Request xmlns="http://nextgen.hayward.com/api"><Name>RequestConfiguration</Name></Request>
2026-06-03 18:33:35.129 DEBUG (MainThread) [pyomnilogic_local.api.protocol] connection lost: None
2026-06-03 18:33:35.129 DEBUG (MainThread) [pyomnilogic_local.api.protocol] connection established
2026-06-03 18:33:35.129 DEBUG (MainThread) [pyomnilogic_local.api.protocol] transmitting message ID: 39914, type: REQUEST_CONFIGURATION (attempt 1/6)
2026-06-03 18:33:35.133 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 39914, Type: MSP_ACK, Compressed: False, Client: OMNI, Body: 
2026-06-03 18:33:35.186 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 316, Type: MSP_LEADMESSAGE, Compressed: True, Client: OMNI
2026-06-03 18:33:35.187 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 316
2026-06-03 18:33:35.187 DEBUG (MainThread) [pyomnilogic_local.api.protocol] reassembling 4-block response (compressed=True)
2026-06-03 18:33:35.193 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 318, Type: MSP_BLOCKMESSAGE, Compressed: False, Client: OMNI
2026-06-03 18:33:35.193 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 318
2026-06-03 18:33:35.194 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received block 1/4
2026-06-03 18:33:35.200 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 320, Type: MSP_BLOCKMESSAGE, Compressed: False, Client: OMNI
2026-06-03 18:33:35.201 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 320
2026-06-03 18:33:35.201 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received block 2/4
2026-06-03 18:33:35.206 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 322, Type: MSP_BLOCKMESSAGE, Compressed: False, Client: OMNI
2026-06-03 18:33:35.206 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 322
2026-06-03 18:33:35.206 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received block 3/4
2026-06-03 18:33:35.210 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received from ('10.2.30.3', 10444): ID: 324, Type: MSP_BLOCKMESSAGE, Compressed: False, Client: OMNI
2026-06-03 18:33:35.211 DEBUG (MainThread) [pyomnilogic_local.api.protocol] sent XML_ACK for message ID 324
2026-06-03 18:33:35.211 DEBUG (MainThread) [pyomnilogic_local.api.protocol] received block 4/4
2026-06-03 18:33:35.212 INFO (MainThread) [custom_components.omnilogic_local] Config entry 'Omnilogic' for omnilogic_local integration not ready yet: Error -5 while decompressing data: incomplete or truncated stream; Retrying in 600 seconds
2026-06-03 18:33:35.212 DEBUG (MainThread) [custom_components.omnilogic_local] Full exception
Traceback (most recent call last):
  File "/config/custom_components/omnilogic_local/__init__.py", line 59, in async_setup_entry
    await omni.refresh()
  File "/usr/local/lib/python3.14/site-packages/pyomnilogic_local/omnilogic.py", line 174, in refresh
    self.mspconfig = await self._api.async_get_mspconfig()
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/site-packages/pyomnilogic_local/api/api.py", line 176, in async_get_mspconfig
    resp = await self.async_send_and_receive(MessageType.REQUEST_CONFIGURATION, req_body)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/site-packages/pyomnilogic_local/api/api.py", line 146, in async_send_and_receive
    resp = await protocol.async_send_and_receive(message_type, message)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/site-packages/pyomnilogic_local/api/protocol.py", line 336, in async_send_and_receive
    return self._decode_payload(raw_data, compressed)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/site-packages/pyomnilogic_local/api/protocol.py", line 287, in _decode_payload
    data = zlib.decompress(data.rstrip(b"\x00"))
zlib.error: Error -5 while decompressing data: incomplete or truncated stream

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 796, in __async_setup_with_context
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/omnilogic_local/__init__.py", line 61, in async_setup_entry
    raise ConfigEntryNotReady from error
homeassistant.exceptions.ConfigEntryNotReady: Error -5 while decompressing data: incomplete or truncated stream
2026-06-03 18:33:35.215 DEBUG (MainThread) [pyomnilogic_local.api.protocol] connection lost: None

And, because I have no idea what I am talking about this is what the genAI is suggesting:

Symptom: omnilogic_local integration fails at setup with zlib.error: Error -5 while decompressing data: incomplete or truncated stream, raised as ConfigEntryNotReady. Telemetry works fine; only the MSP config fetch fails.
Failing location: pyomnilogic_local/api/protocol.py:287 → data = zlib.decompress(data.rstrip(b"\x00"))
Library version: python-omnilogic-local==3.1.0 (also reproduced via integration 2.0.0; not version-specific to the integration)
Controller firmware: mspVersion="R0502000"
Reproduces outside Home Assistant: clean-room CLI/script repro on a fresh install, same LAN, no HA involved — so it's not an HA, container, or config-entry issue.
Network/transport is fine: full handshake completes every time — MSP_ACK, MSP_LEADMESSAGE, all 4 MSP_BLOCKMESSAGE blocks received and ACKed, reassembled. Nothing truncated on the wire.
Payload is a valid zlib stream: reassembled compressed config is 3604 bytes, header 78 9c (standard zlib).
Root cause: one-shot zlib.decompress() fails on this payload, but streaming zlib.decompressobj() succeeds and inflates the full, valid 48,106-byte XML.
Proposed fix (verified against a live controller):

python  _d = zlib.decompressobj()
  data = _d.decompress(data.rstrip(b"\x00")) + _d.flush()

Why it "used to work": telemetry (single-block) never hits this path; the bug surfaces on larger/multi-block config payloads, so it's a latent flaw exposed once the config grew, not a regression in the integration code.

aaaand, now it works. I had the integration off since my last post. Turn it back on - all working...