I found the best way to have Home Assistant manage the heater is to give Home Assistant full control of the heater. Have a read through this post (of mine) above and the five that follow it.
After implementing the generic thermostat in 2022, I’ve never looked back.
If that can work for your usage, it makes voice automations simple.
Hi all - I’m finding my way back to this thread after a few years. My integration has been working really well up until a most recent upgrade to Home Assistant 2025.5.3. Curious if others here are finding the aqualogic integration failing recently
Here’s the error in my logs: Detected blocking call to import_module with args (‘custom_components.aqualogic.sensor’,)
Going to start digging, but thought I’d post to see if someone has already solved this!
I’m not seeing any errors and I’m on 2025.5.3. You can give my fork a shot. I did do a bunch of cleanup and fixing errors. Or if you find the problem in the code you have, look to see if it’s different in mine.
Thanks for the share - I was able to hack my way through it - looking at your code I think you made some potentially more elegant fixes than I did. The const imports in my sensor.py file definitely needed cleaning up (it was throwing errors attempting to use “POWER_WATT”, “TEMP_CELSIUS”, and “TEMP_FAHRENHEIT”.
Thanks for this stuff. Everything is working great under HA, but I cannot get the Configuration Menu to unlock. I press the menu button to get to the “Configuration Menu-Locked”, then press and release the right arrow. Nothing happens. Am I missing something?
Are you using my code base (aqualogic_p4p8)? If so, it should work. There’s special code to handle this. When the menu reads “Configuration Menu-Locked”, there’s code that looks for a right key press and then unlocks it. Which do you have, a p4 or p8? I’ve only tested it with a P4. I just tried it and it unlocked. It take a few seconds to happen though.
I wonder if the P8 uses a different code than the P4 then?
This is the code that is getting called:
# MOD Begin
self._append_data(frame, self.FRAME_TYPE_LOCAL_WIRED_KEY_EVENT)
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
if self._configmenu and key.value == Keys.RIGHT:
self._append_data(frame, b'\x05\x00\x05\x00')
self._configmenu = False
else:
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
# MOD End
It’s basically just waiting for a Right key to be pressed and then sends self._append_data(frame, b’\x05\x00\x05\x00’). I don’t have a P8, so it’s hard to say what it’s expecting. If you turn on debugging and then do it at the keypad, it might show you the code that was sent.
The other thing to look for is that it says “Configuration Menu-Locked” on the display. That’s what the code is watching for.
I made two small changes to try out. I changed keys.py to add the LEFTRIGHT key and core.py to change the Config Menu logic to use the LEFTRIGHT key. It was hardcoded before.
Download those two files or make the changes below:
Add LEFTRIGHT to keys.py
LEFT = 0x0004
LEFTRIGHT = 0x0005
SERVICE = 0x0008
In core.py starting on line 480, replace the MOD block with this
# MOD Begin
if self._configmenu and key.value == Keys.RIGHT:
#self._append_data(frame, b'\x05\x00\x05\x00') #Added LEFTRIGHT to keys.py
key = Keys.LEFTRIGHT
self._configmenu = False
self._append_data(frame, self.FRAME_TYPE_LOCAL_WIRED_KEY_EVENT)
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
else:
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
# MOD End
Not sure if that will do it or not, but it’s worth a try.
Something doesn’t work, as the whole integration goes wonky. I’m editing my existing files, and when I apply the changes in the integration, it causes delays and doesn’t work correctly. Change back and all good.
Make sure the else: is above the #MOD Begin. It looks like the code in Github has an extra carriage return in it compared to my local code, so the MOD block would start on line 481 in your code. The else is on line 480.
The change is pretty minor and shouldn’t affect anything else. It’s working for me.
Here’s the full code block:
def _get_key_event_frame(self, key):
frame = bytearray()
frame.append(self.FRAME_DLE)
frame.append(self.FRAME_STX)
if key.value > 0xffff:
self._append_data(frame, self.FRAME_TYPE_WIRELESS_KEY_EVENT)
self._append_data(frame, b'\x01')
self._append_data(frame, key.value.to_bytes(4, byteorder='little'))
self._append_data(frame, key.value.to_bytes(4, byteorder='little'))
self._append_data(frame, b'\x00')
else:
# MOD Begin
if self._configmenu and key.value == Keys.RIGHT:
#self._append_data(frame, b'\x05\x00\x05\x00') #Added LEFTRIGHT to keys.py
key = Keys.LEFTRIGHT
self._configmenu = False
self._append_data(frame, self.FRAME_TYPE_LOCAL_WIRED_KEY_EVENT)
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
else:
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
# MOD End
crc = 0
for byte in frame:
crc += byte
self._append_data(frame, crc.to_bytes(2, byteorder='big'))
frame.append(self.FRAME_DLE)
frame.append(self.FRAME_ETX)
return frame
I was pretty sure my edits were good, but I opted to download your two files to make sure. Now running your core and keys files. When I press the a button the integration hangs. When I revert to original core and keys files everything works fine (without the option to unlock config menu).
Strange. I just downloaded the core.py and keys.py just to make sure and they work fine for me. It almost looks like it’s using the P4 keys instead of the P8.
Can you diff the original working core.py and the new one to see if there are any other differences? Or send them to me and I’ll look.
I see the issue. When I moved the If statement for the Config menu up in the code, I left the else. It should have been removed. I was still working OK because I don’t have a P8 and was dropping to the else. If you have a P8 it wouldn’t append the key press because it would skip the else.
Try the code below.
# MOD Begin
if self._configmenu and key.value == Keys.RIGHT:
#self._append_data(frame, b'\x05\x00\x05\x00') #Added LEFTRIGHT to keys.py
key = Keys.LEFTRIGHT
self._configmenu = False
self._append_data(frame, self.FRAME_TYPE_LOCAL_WIRED_KEY_EVENT)
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
self._append_data(frame, key.value.to_bytes(2, byteorder='little'))
if self._p4p8 == 'p8':
self._append_data(frame, b'\x00\x00')
# MOD End
I’ve started digging in a little deeper. I noticed you were missing an else yesterday, but I decided to explore another route.
I checked the AquaLogic manuals, and it appears both the P4 and P8 use the same code to unlock the Configuration Menu. The manual says you need to hold the Left+Right buttons for 5 seconds. When I try this on the wired keypad, that’s exactly what happens — if I just tap the buttons, it doesn’t unlock. So it’s not surprising that a single simulated keypress doesn’t work either.
I added a loop to send the frame 25 times with a 200ms delay between each one, but it still doesn’t unlock. I’m starting to wonder if the EW11 is buffering or slowing things down, making it look like 25 quick press/release cycles instead of a continuous hold.
Based on what I found (and confirmed with ChatGPT), simulating a key hold should involve repeatedly sending the exact same frame — essentially mimicking the key being held down. But even with that, I haven’t gotten it to work.
Try the code above. I think it might work. The old code wouldn’t work with the P8 because it was bypassing the section that added b’\x00\x00 to the start and end of the key that the P8 requires. This new code should do that.