Ylianst / ESP-IQ2020

Connect a Hot Tub with a IQ2020 control board to Home Assistant and make it smart. Monitor and control temperature, jets, lights, power usage and more.
Apache License 2.0
11 stars 0 forks source link

Protocol Decoding & Discussion #2

Closed wolfson292 closed 1 day ago

wolfson292 commented 1 month ago

I've been working extensively on reverse engineering the IQ2020 firmware from a hex file found online, and have identified the RS485 address (0x1F) of their internet gateway and some of the functions. Followed you on Twitter, and happy to collaborate.

Commands from address 0x1F to IQ2020 at 0x01 have 2 bytes to identify the command, and optional additional bytes. I've identified the following so far.

Get Versions 0x01 0x00

[18:50:41][I][iq2020.component:097]: sendCmd_ 1F -> 01 Length:02 Operation:40 Data:01:00 Checksum:63
[18:50:41][I][iq2020.component:349]: readline_ Full Packet 01 -> 1F Length:17 Operation:80 RESP Data:01:00:57:52:34:2E:30:34:64:65:31:63:45:30:30:32:44:4B:34:2E:30:30:06 DataS:..WR4.04de1cE002DK4.00. Checksum:Valid 4D=4D

Get/Set Timestamps 0x02 0x4C Seconds ... if Seconds >60, don't set, just return values

[18:50:41][I][iq2020.component:097]: sendCmd_ 1F -> 01 Length:03 Operation:40 Data:02:4C:FF Checksum:B0
[18:50:41][I][iq2020.component:349]: readline_ Full Packet 01 -> 1F Length:0A Operation:80 RESP Data:02:4C:37:12:11:0B:08:D1:07:01 DataS:.L7....... Checksum:Valid C1=C1 SinceLast:0.3s

Get lots of data 0x02 0x55

[18:50:41][I][iq2020.component:097]: sendCmd_ 1F -> 01 Length:02 Operation:40 Data:02:55 Checksum:B9
[18:50:41][I][iq2020.component:349]: readline_ Full Packet 01 -> 1F Length:75 Operation:80 RESP Data:02:55:00:08:40:04:00:00:06:04:00:0A:06:20:72:13:00:20:1C:20:1C:20:1C:84:03:60:54:00:00:00:00:31:30:32:46:10:66:09:00:C0:1C:06:00:61:A5:D9:02:1D:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:21:A3:06:00:02:00:2C:A4:D9:02:00:00:00:00:00:00:00:00:31:30:30:46:31:30:32:46:78:00:07:01:07:01:00:00:00:00:00:00:00:00:00:00:00:00:3A:00:00:00:00:00 DataS:.U..@........ r.. . . ...`T....102F.f......a............
joshs85 commented 3 weeks ago

FYI, The hot tub I have is the HotSpring HotSpot SX.. if it matters.

Ylianst commented 3 weeks ago

@joshs85 Oh yes, ESP-home Econet is amazing, just like my hottub, I use it to dynamically change temperature levels to avoid using electricity at high prices and maximize use at cheap prices. My graphs for both integrations below.

I added your hot tub to the tested list here. What is your version string? What ESP32 do you use? Also, if there is anything you want to add to the tested page, let me know.

image

image

joshs85 commented 3 weeks ago

@joshs85 Oh yes, ESP-home Econet is amazing, just like my hottub, I use it to dynamically change temperature levels to avoid using electricity at high prices and maximize use at cheap prices. My graphs for both integrations below.

I added your hot tub to the tested list here. What is your version string? What ESP32 do you use? Also, if there is anything you want to add to the tested page, let me know.

image

image

Version string is: 112T b5e1A002 v3.0 Using the m5stack atom lite https://shop.m5stack.com/products/atom-lite-esp32-development-kit

With the atomic rs485 base https://shop.m5stack.com/products/atomic-rs485-base

Ylianst commented 3 weeks ago

@joshs85 I got two updated for you. First, a new sensor in the latest integration update that should match your loop value. Let me know if this works.

sensor:
    lights_main_loop_speed:
      name: Lights Main Loop Speed

Also, I updated the DataViewer to display decoding errors instead of crashing. If you run it again, send over the error you see and I will fix correctly.

joshs85 commented 3 weeks ago

Awesome. I was able to pull in the extra sensor and its working correctly. I'll check out the windows app later. for what its worth it wasn't even giving me an error code before but just exiting.

Ylianst commented 3 weeks ago

@joshs85 Understood, yes, it would just quit before... it should give an error message now. Thanks.

joshs85 commented 3 weeks ago

Aah ok, so the error was because mine doesn't have a way to set the time so its probably not a valid value that's convertible to a datetime object.

 <-- 1F 01 80 02560000000000000404000A0A2040160E201C201C201C840360540000000020393646DA1B9B00E0C60802BF4AD2146200000000000000000000000000000000000000AF4F8B00030000000000CEB0F101000000003130324631303146E700E700E700000000000000000000000000000000000000003C001E00006C00000000000000DC0701
System.ArgumentOutOfRangeException: Year, Month, and Day parameters describe an un-representable DateTime.
   at System.DateTime.DateToTicks(Int32 year, Int32 month, Int32 day)
   at System.DateTime..ctor(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second)
   at DataViewer.MainForm.processData(Byte[] data, Int32 len)
Ylianst commented 3 weeks ago

@joshs85 Oh! Makes sense. I will add code to handle this case.

joshs85 commented 3 weeks ago

@joshs85 Oh! Makes sense. I will add code to handle this case.

Can you post this as a new github project with an esp home component for the tcp listener? I'd love to use this CS app to reverse my PoolSync (to control my pool pump, heater, and salt cell) and may compile it for .net on osx if possible.

Ylianst commented 3 weeks ago

@joshs85 For the RS485 to TCP in ESP-Home, take a look at esphome-stream-server. It's super easy to get yourself just the RS485 relay. For the C# app, it's located in this GitHub repo here. I have not cleaned up the code, it's a bit ugly. I don't think I need to put it in a separate repo, the entire source code is there. Let me know if that helps.

joshs85 commented 3 weeks ago

@joshs85 For the RS485 to TCP in ESP-Home, take a look at esphome-stream-server. It's super easy to get yourself just the RS485 relay. For the C# app, it's located in this GitHub repo here. I have not cleaned up the code, it's a bit ugly. I don't think I need to put it in a separate repo, the entire source code is there. Let me know if that helps.

Ok, cool.. that works for me.

Ylianst commented 3 weeks ago

@joshs85 I just updated DataViewer with your fix. Thanks for reporting this and the exact error.

dvanderb commented 3 weeks ago

Just wanted to confirm that this is working great with a 2022 Caldera Niagara(Utopia Series). Reports power and everything..

Ylianst commented 3 weeks ago

@dvanderb Thanks for confirming. Can you also provide your version string? It's something like "WR2.02ad1bE002DK2.02". I will add it to the tested list.

dvanderb commented 3 weeks ago

@dvanderb Thanks for confirming. Can you also provide your version string? It's something like "WR2.02ad1bE002DK2.02". I will add it to the tested list.

EU2.039018E0020.00.0

Ylianst commented 3 weeks ago

Thanks @dvanderb for the contribution, you are the first one with a Caldera. I just updated the tested list.

Ylianst commented 2 weeks ago

I am getting close to having every possible feature for this integration I can think of. I just added one more, the ACE Module Emulation feature. It allows you to see this screen on your spa remote:

image

This is only for people that do not already have the ACE or Freshwater module. You can then use added Home Assistant sensors in your automatons to do anything you like.

I did have to change the audio_buttons sensor to buttons. So, if you get an error, just remote audio_ and that will fix it.

sensor:
  - platform: iq2020
    buttons:
      name: Buttons

I did that since both the audio emulation and ACE emulation use the buttons sensor. Feedback appreciated.

npinguin commented 2 weeks ago

I see the new ace emulation with boost. Did you also manage to get boost working/decoded for tubs with ace?

One feature I am really missing is the color setting. That would really help for doorbell scenarios

Ylianst commented 2 weeks ago

@npinguin I just managed to get ACE boost decoded and probably working, but I can't truly test it because I don't have a real ACE module. I updated the documentation and the 3 ACE sensors are below. The salt_power did not change, still 0 to 10. salt_content did change, it's now a value from 0 to 7 where 3 or 4 are ideal. This is same as the yellow/green/red indicator on the remote in the ACE screen. Lastly, salt_system_boost is the new one and should indicate if you are in boost mode or not and allow you to change your boost setting from Home Assistant. If anyone can confirm this works. Note that if you change the boost setting in Home Assistant, you need to go back to the main page and return to ACE on the remote to see the updated state. If you change boost on the remote, it should update in Home Assistant right away.

number:
  - platform: iq2020
    id: salt_power
    name: Salt Power
    datapoint: 5

sensor:
    salt_content:
      name: Salt Content

switch:
  - platform: iq2020
    name: Salt Boost
    id: salt_system_boost
    datapoint: 8
Ylianst commented 2 weeks ago

FYI. I just figured out how to change light brightness and colors! I am still paying around with this.

--> 01 1F 40 17020102
                 NNAA
NN = Light Number 0x00 to 0x03 for individual light, 0x04 for all lights.
AA = Action
  02 = Lower Brightness (0 to 5)
  03 = Increase Brightness (0 to 5)
  04 = Previous color (1 to 7 and cycles back)
  05 = Next color (1 to 7 and cycles back)
  06 = Previous Color Loop ?
  07 = Next Color Loop ?
Ylianst commented 2 weeks ago

Full control over the light colors, intensity and cycle speed has been added to the integration. You can look at the top of the extras section for details on how to add this. Note that I only tested this on my hot tub and that I may need to fix things for other hot tub models. So please let me know if it does not work right.

image

Once nice extra is that I can now control the cycle speed of the lights, something I can't do from the remote.

dvanderb commented 2 weeks ago

Full control over the light colors, intensity and cycle speed has been added to the integration. You can look at the top of the extras section for details on how to add this. Note that I only tested this on my hot tub and that I may need to fix things for other hot tub models. So please let me know if it does not work right.

Very cool.. my Caldera only has one controllable RGB light(it simply affects all lights in the tub).. so do we think that would just be data point 1?

jbwalkup920 commented 2 weeks ago

Working on a Hot Springs Limelight Flash 2018 here. Version is 1.14D6ce5E002. Light control colors are off by one (violet=blue, etc.) and wheel doesn't cycle, although the control panel flashes the light icon like it does when cycling normally. No audio or salt systems to test.

Ylianst commented 2 weeks ago

@dvanderb Yes, that's correct. Only use select with data point 1. That would be ok for you. Let me know what your exact model and version number is, I will add it to the tested page with that note.

@jbwalkup920 Thanks for reporting this. It does seem like there is a lot of variations on what colors are set to what number for different tubs. Go ahead and change the color options to fix you hot tub. If you send back your exact color order yaml, I will add it to the tested page. I will also work on cycling at some point to get that working, but I may need some data capture from various tubs.

fricker-ben commented 2 weeks ago

Just tested the lights on my Hot Spring Sovereign. The lights are off by one colour & the descriptions are also off (Exterior control pillows, bar top control water fall etc) This is the current working YAML with everything correct:

select:
  - platform: iq2020
    name: Color Underwater
    id: lights1_color
    datapoint: 1
    options:
      - Blue
      - Cyan
      - Green
      - White
      - Yellow
      - Red
      - Magenta
      - Cycle
  - platform: iq2020
    name: Color Waterfall
    id: lights2_color
    datapoint: 2
    options:
      - Blue
      - Cyan
      - Green
      - White
      - Yellow
      - Red
      - Magenta
      - Cycle
  - platform: iq2020
    name: Color Bar Top
    id: lights3_color
    datapoint: 3
    options:
      - Blue
      - Cyan
      - Green
      - White
      - Yellow
      - Red
      - Magenta
      - Cycle
  - platform: iq2020
    name: Color Pillow
    id: lights4_color
    datapoint: 4
    options:
      - Blue
      - Cyan
      - Green
      - White
      - Yellow
      - Red
      - Magenta
      - Cycle
  - platform: iq2020
    name: Color Cycle Speed
    id: lights_cycle_speed
    datapoint: 5

number:
  - platform: iq2020
    id: lights1_intensity
    name: Intensity Underwater
    datapoint: 7
    maximum: 5
  - platform: iq2020
    id: lights2_intensity
    name: Intensity Waterfall
    datapoint: 8
    maximum: 5
  - platform: iq2020
    id: lights3_intensity
    name: Intensity Bar Top
    datapoint: 9
    maximum: 5
  - platform: iq2020
    id: lights4_intensity
    name: Intensity Pillow
    datapoint: 10
    maximum: 5
fricker-ben commented 2 weeks ago

Also, not sure what 'Small heater power' actually is, but mine sits at an average of 115W, except for then the pump is on 'high' then its approx 50W. The 115W reading cannot be correct, as I have power monitoring on the supply to the Hot Tub & it reads around 85W when just the small filter pump is running (the rating of the pump), so unsure what that data point is.

Also on my Hot Spring Sovereign, heater power & pump power both work correctly, but heater current shows on L2 & pump current on L1, 'Current heater' never reads above 0

fricker-ben commented 2 weeks ago

@fricker-ben That is interesting. I will take note of this. The lifetime encoding is very basic with not much room for error, basically, the value is directly obtained from the hot tub. If you even want to investigate this further, you can send me a packet capture like #4 to see what is going on, but not something that is crucial.

The response to an 'Ask Status 0x256' command: 1F 01 80 02560008000400000404000A0900405600201C201C201C840360540000000033352E30A17AB7065752A3004C9F31374000000000000000000000000000000000000000B3FB830003003B652A37AFCC35000000000033352E3033352E30F200F200F200000000000000000000000000760000000000003C001E0000664500240F0613FFD00701

Ylianst commented 2 weeks ago

@fricker-ben Thanks for the reports. I just added your light yaml to the tested page, but now I am thinking I am going to create a new page just for controlling lights as this is going to be a crazy topic.

For the power/voltage/amps sensors... that too seems to vary between tubs and it's a still a mystery to me. The files I have seen show the following sensors in command 0x0256 in this order:

voltage_l1
voltage_heater
voltage_l2
voltage_jet3
current_l1
current_heater
current_l2
current_jet3
power_l1
power_heater
power_l2
power_jet3

But for me, l2 is clearly the main heater and I have not idea what "heater" is, so, I called it "small heater" as a stand it until it's figured out. For your 0x0256 command, in DataViewer, the last 0x0256 command is always decoded in the "State" tab. In my case, it looks like this:

image

If you can figure out what the values map to in your hot tub, that would be wonderful.

fricker-ben commented 2 weeks ago

This is mine currently, I still can't work out what the 'Small heater power' value could be, is it definitely a power figure, or could it be something else? Possibly heater flow/pressure?

Hex:
1F01868002560008000400000404000A0900405600201C201C201C840360540000000033352E30A17AB7065752A3003DED31374000000000000000000000000000000000000000B3FB830003002CB32A37AFCC35000000000033352E3033352E30F000F000F000000000000000000000000000790000000000003C001E00006544000D2D110206E80701DA1C
0008     - Unknown
00       - Flags
04       - Flags
00000404000A090040 - Unknown
5600     - Flags, 0x4000 is Celsius
201C201C201C - Unknown
84036054 - Unknown
00000000 - Unknown
33352E30 - Exaust Temperature string, "35.0"
A17AB706 - Heater total runtime in seconds, 112687777
5752A300 - Jets 1 total runtime in seconds, 10703447
3DED3137 - Lifetime in seconds, 926018877
40000000 - Power on / Boot counter
00000000 - Flags
00000000 - Jet 2 runtime
00000000 - Jet 3 runtime
00000000 - Blower runtime
B3FB8300 - Lights runtime
0300     - SPA state & Light state
2CB32A37 - Circulation pump Lifetime
AFCC3500 - Jets 1 low operation Lifetime
00000000 - Jets 2 low operation Lifetime
33352E30 - Target Temperature String, "35.0"
33352E30 - Current Temperature String, "35.0"
F000     - Voltage L1
F000     - Voltage Heater
F000     - Voltage L2
0000     - Voltage Jet3
0000     - Current L1
0000     - Current Heater
0000     - Current L2 (Heater)
0000     - Current Jet3
0000     - Power L1
7900     - Power Heater
0000     - Power L2 (Heater)
0000     - Power Jet3
003C001E00006544 - Unknown
000D2D11 - Time hh:mm:ss, 17:45:13
0206E807 - Date, 2-7-2024
01       - Clock Status
joshs85 commented 2 weeks ago

So, I just tested out the ability to change the lights for the underwater lights. If I set cycle speed to Pause, it does nothing (it was normal previously). If I change to fast it works.. as soon as I change the color from whatever it was, the lights stop cycling and change color (expected). The cycle speed stays on normal.. I can't get the lights to change back to cycling even if I set the color to Cycle or change the speed. Also, the colors don't match the UI. I think I posted the correct mapping of color to ID earlier in our previous communication.

Ylianst commented 2 weeks ago

The light colors seems to be very different depending on the model, so, changing the options to the right value is going to be needed. For cycling, I coded the integration to work for my hot tub, but to debug get the DataViewer working and use "Ask Lights Status" in the "Commands" menu to ask for the current status.

image

The light status starts with 1F 01 80 1705... like here:

 <-- 1F 01 80 17050000000000000000000000000001010101000000

The lights status seems to decode like this:

<-- 1F 01 80 17050003000000010000000300000003010101010000
                 FFAABBCCDD        LLMMNNOOWWXXYYZZOO

FF = Flags (0x01 = Lights Timer On)
AA = Underwater light intensity (0x05 = High ... 0x01 = Low, 0x00 = Off)
BB = Bartop light intensity
CC = Pillow light intensity
DD = Exterior light intensity
LL = Underwater light cycle speed - Main loop speed
MM = Bartop light cycle speed
NN = Pillow light cycle speed
OO = Exterior light cycle speed
WW = Underwater light color     (0x01 = Violet, 0x02 = Blue, 0x03 = Cyan, 0x04 = Green, 0x05 = White, 0x06 = Yellow, 0x07 = Red, 0x08 = Color Cycle)
XX = Bartop light color
YY = Pillow light color
ZZ = Exterior light color
OO = 00 for lights are off, 01 for lights are on.

However, if you can get the possible valid states for your tub tub, I will fix the integration. It's technically possible to set different cycle speeds for each light group, but on my hot tub, lights will never run at different speeds for each other.

joshs85 commented 2 weeks ago

My tub only has underwater lights. Here's the decode. Version: 010031313254202062356531413030322076332E30200A

Blue.  Paused:
17050003000000000000000000000001010101010000
Cyan Paused:
17050003000000000000000000000002010101010000
Green Paused:
17050003000000000000000000000003010101010000
White Paused:
17050003000000000000000000000004010101010000
Yellow Paused:
17050003000000000000000000000005010101010000
Red Paused:
17050003000000000000000000000006010101010000
Violet Paused:
17050003000000000000000000000007010101010000
Cycle Speed 1:
17050003000000010000000100000005010101010000
Cycle Speed 2:
17050003000000010000000200000005010101010000
Cycle Speed 3:
17050003000000010000000300000005010101010000

Intensity 1 Paused:
17050001000000000000000000000001010101010000
Intensity 2 Paused:
17050002000000000000000000000001010101010000
Intensity 3 Paused:
17050003000000000000000000000001010101010000

These are the light color values that seemed to work earlier: 1 = Blue 2 = Cyan 3 = Green 4 = White 5 = Yellow 6 = Red 7 = Violet

joshs85 commented 2 weeks ago

There's also an LCD intensity setting with values 1-3. Not sure where that value lives. It doesn't seem to affect the light status.

Ylianst commented 2 weeks ago

@joshs85 Wow! Thank you. Yes, based on your data, I have a good idea on how to fix the integration. I will put in a fix in the next few days. Question: It looks like you can enabling cycling for any color? In your data, you changed the cycling speed while having yellow selected... but I imagine any color could be selected?

Or when cycling, is the "05" at this location the only possible value?

17050003000000010000000100000005010101010000
                              ^^
joshs85 commented 2 weeks ago

@joshs85 Wow! Thank you. Yes, based on your data, I have a good idea on how to fix the integration. I will put in a fix in the next few days. Question: It looks like you can enabling cycling for any color? In your data, you changed the cycling speed while having yellow selected... but I imagine any color could be selected?

Or when cycling, is the "05" at this location the only possible value?

17050003000000010000000100000005010101010000
                              ^^

No, any color will work it seems.
This is me setting it to loop 2 when it was red 17050003000000010000000200000006010101010000

Ylianst commented 2 weeks ago

@joshs85 - Excellent. Got it. Thanks. I need to redesign my controls a bit because it's possible to cycle while adjusting the colors in your case... so having a "cycle" color is not going to work. I think I need to have a "Off" on the cycle selector instead and have a cycle selector for each light group.

joshs85 commented 2 weeks ago

Can you just make it so that if the color is changed while a cycle is running the cycle setting also gets set to 0?

Ylianst commented 2 weeks ago

@joshs85 - Ok, I got a new version of the integration ready. The key difference now is that if you do not specify the 8th color and only put 7 colors in the options, the code will assume that you can system on any color and change the Color Cycle Speed selector to make it do that. If you specify the 8th color, the integration will assume that you can only cycle when the 8th color is selected. Give that a try, remove the 8th color from all options and let me know if it works.

select:
  - platform: iq2020
    name: Audio Source
    id: audio_source
    datapoint: 0
  - platform: iq2020-dev
    name: Color Underwater
    id: lights1_color
    datapoint: 1
    options:
      - "Violet"
      - "Blue"
      - "Cyan"
      - "Green"
      - "White"
      - "Yellow"
      - "Red"
#     - "Cycle"
  - platform: iq2020
    name: Color Bartop
    id: lights2_color
    datapoint: 2
    options:
      - "Violet"
      - "Blue"
      - "Cyan"
      - "Green"
      - "White"
      - "Yellow"
      - "Red"
#     - "Cycle"
  - platform: iq2020
    name: Color Pillow
    id: lights3_color
    datapoint: 3
    options:
      - "Violet"
      - "Blue"
      - "Cyan"
      - "Green"
      - "White"
      - "Yellow"
      - "Red"
#     - "Cycle"
  - platform: iq2020
    name: Color Exterior
    id: lights4_color
    datapoint: 4
    options:
      - "Violet"
      - "Blue"
      - "Cyan"
      - "Green"
      - "White"
      - "Yellow"
      - "Red"
#     - "Cycle"
  - platform: iq2020
    name: Color Cycle Speed
    id: lights_cycle_speed
    datapoint: 5
    options:
      - "Off"
      - "Slow"
      - "Normal"
      - "Fast"
Ylianst commented 1 day ago

Closing this since it's long and things have gotten pretty stable. Please open a new issue with any new data, suggestions or bug reports.