Lay-Z-Spa Hot Tub wi-fi pump automation

I’m particularly interested in the incoming traffic to the pump. I’m currently trying to figure out how to capture that. It would be preferable if you could communicate locally to the pump from HA or your own app rather than sending API calls up to the cloud and back down. This might be ambitious.

I’m a novice at capturing this data so my first attempts are in fact to capture outgoing app data and responses and trying to replicate that. Any pointers on how to get started are much appreciated. FWIW I Am Not A Coder but I do have a lifetime of experience debugging things for coders and as a result understanding what I’m addressing quite well.

I’m guessing you would need something like Wireshark. But I’ve never got that deep into things either !

I’ve installed WS and am planning to see what I can come up with as soon as I have time. I’ll update here as and when. I have no idea when that might be!

1 Like

Hi guys, great research here! Thanks a lot for all the investigations.
I have a Bestway Lay-z-Spa Milan too and searched for MQTT/OpenHAB modules.

Since I am using the Bestway app too, i read your posts about the Bestway app and the sniffed traffic; I did some simple curl research on top of your informations and think found a big puzzle piece:

X-Gizwits-Application-Id:
Seems so be a general App Id on the Gizwits IoT platform for „Bestway“ IoT device or in other words the reference to the „Bestway“ app. So this should be the same for all Bestway app users - i had no time to sniff the traffic between my app and the server yet - so its just a guess and needs to be confirmed.

The API is fully disclosed here:
https://docs.gizwits.com/en-us/cloud/OpenAPI.html

and useful informations here aswell for the device „Heatzy“

E.g. page 13 show a login procedure.

Again: I couldn‘t do any tests until now. Try it next week.

But this should be the key to make it work - hopefully :slight_smile:

That api doc looks more like the la z spa app method.
Doesn’t look to match what the Bestway app is doing.
Happy to be proved wrong.

Thanks.

Hi Bruce, you are right :slight_smile: and that’s good, because it seems it is simply the same :slight_smile:
Just the layzspa method has some kind of gateway in between the communication with gizwits as it seems. You need additionally the AppID in the Bestway App case.

It was quite easy even without sniffing the traffic. I took the Bestway Android APK and unzipped it. Find relevant informations in assets/www/main.js :wink:

The relevant ones:

  • The first IDs block works for me and eu server
    First:
iosAppSecret:"13482b824b934003a8d95106d3d28eeb"
androidAppSecret:"1d69eda66af64662a6f1b31f0b42eab2"
androidAppID:"98754e684ec045528b073876c34c7348"
iosAppID:"43424551921d4ee18dd279140e11e198"
  • Alternative - maybe for other servers?:
iosAppID:"f50095a20a2f4152a3685a01a863ec5a"
iosAppSecret:"20438225160440b7b5a8d87ed8abcad7"
androidAppID:"afb8f41dbb274cb19d0fdf472945fbf3"
androidAppSecret:"3d81fcd4c22e48a286c26a6b3bf29a5b"

Then just use curl for a first test - see the heatzy document in my last post:
1.
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348' -d '{ "username": "[email protected]", "password": "YOURPASSWORD", "lang": "en" }' 'https://euapi.gizwits.com/app/login'

and receive

{ {"token": "YOUR_TOKEN", "uid": "YOUR_UID", "expire_at": } }

  1. get bindings infos with token
    curl -X GET --header 'Accept: application/json' --header 'X-Gizwits-User-token: YOUR_TOKEN' --header 'X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348' 'https://euapi.gizwits.com/app/bindings?limit=20&skip=0'

and receive something like that

{"devices": [{"protoc": 3, "ws_port": 8080, "port_s": 8883, "is_disabled": false, "gw_did": null, "wifi_soft_version": "WIFI_SOFT_VERSION", "dev_alias": "Milan", "mesh_id": null, "is_online": true, "host": "eum2m.gizwits.com", "sleep_duration": 0, "dev_label": [], "port": 1883, "remark": "", "did": "YOUR_DID", "mac": "YOUR_MAC", "product_key": "YOUR_PRODUCT_KEY", "wss_port": 8880, "state_last_timestamp": TIMESTAMP, "role": "owner", "is_sandbox": false, "passcode": "YOURPASSCODE", "type": "normal", "product_name": "SPA", "is_low_power": false}]}

  1. get the supported Data points
    curl -X GET --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348' 'https://euapi.gizwits.com/app/datapoint?product_key=YOUR_PRODUCT_KEY'

and receive the supported data type and attributes.

  1. get the current values of the attributes e.g. temp_now
    curl -X GET --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348' 'https://euapi.gizwits.com/app/devdata/YOUR_DID/latest'

  2. e.g. set temperature to 23 degrees (temp_set)

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-User-token: YOUR_TOKEN' --header 'X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348' -d '{"attrs": {"temp_set": 23} } ' 'https://euapi.gizwits.com/app/control/YOUR_DID'

With those infos it should not be that big deal to get back control :slight_smile:

Btw the whole thing here looks like MQTT…port 1883…eum2m.gizwits.com…maybe MQTT-Broker M2M…
-> inline update: yes mitt…mentioned here: https://docs.gizwits.com/en-us/cloud/OpenAPI.html
Would be a direct MQTT connection possible?! Perhaps a local connection to the “egg”? I’ll do some research on that…

Enjoy :slight_smile:

Best Chris

P.S.: Maybe its possible to get back control of your app with the informations and the api calls

2 Likes

Thank you.
This looks great, I’ll give it a go and report back.

Amazing stuff Chris!

Btw the whole thing here looks like MQTT…port 1883…eum2m.gizwits.com…maybe MQTT-Broker M2M…
→ inline update: yes mitt…mentioned here: Gizwits Open API - Gizwits
Would be a direct MQTT connection possible?! Perhaps a local connection to the “egg”? I’ll do some research on that…

This is precisely what I was looking for. Ideally HA will make calls directly to the “egg” bypassing the cloud entirely. That way if you’re internet goes away scheduling will work as normal :slight_smile:

I’ll see what I can do myself but very interested in the progress.

Graham

Additionally to sikksakks lyzspa2mqtt we could easily adapt the heatzy MQTT gateway here

to the new Bestway method. Everything’s there incl. openhab how to - even if we are here on the HA forum :slight_smile: Thanks a lot Nicolas Bernaerts :slight_smile:

http://www.bernaerts-nicolas.fr/iot/362-linux-setup-heatzy-pilote-mqtt-gateway

This is all great ! Thanks for the pointers in the right direction.
It’s weird, as it doesn’t match the calls the iOS app seems to make, but it works so I’m very happy !

I’ve adapted my Lay-Z-Spa code into Bestway code and it is available on

Please let me know if you have any questions.

1 Like

Very stupid question but I’m unsure where to place the file and code to make this work?

I have all my code split into many packages to make it easier to read and share.
If all your is in configuration.yaml (as it is by default) then you will add the code there.

First you need a sensor to give you your token:

sensor:
  - platform: rest
    name: hottub_login
    scan_interval: 172000
    resource: "https://euapi.gizwits.com/app/login"
    headers:
      Content-Type: application/json
      X-Gizwits-Application-Id: "98754e684ec045528b073876c34c7348"
    method: POST
    payload: '{ "username": "[email protected]", "password": "xxxxxxxx", "lang": "en" }'
    value_template: "{{ value_json.token }}"    
    json_attributes:
      - uid
      - expire_at

The value of this sensor can then be used in the next sensor…

sensor:
  - platform: rest
    name: hottub_bindings
    scan_interval: 172000
    resource: "https://euapi.gizwits.com/app/bindings?limit=20&skip=0"
    headers:
      Content-Type: application/json
      X-Gizwits-Application-Id: "98754e684ec045528b073876c34c7348"
      X-Gizwits-User-token: "xxxxxxxxxxxxxxxx" 
      # xxxxxxxxxxxx is The token from the login sensor
    method: GET
    value_template: "{{ value_json.devices[0].did }}"    
    json_attributes_path: "$.devices[0]"
    json_attributes:
      - dev_alias
      - mac
      - product_key
      - passcode
      - product_name

With the did you get from that sensor, you can make this sensor…

sensor:
  - platform: rest
    name: hottub_status
    resource: https://euapi.gizwits.com/app/devdata/xxxxxxxxxxxx/latest  
    # xxxxxxxxxxxx is the did from the bindings sensor
    headers:
      Content-Type: application/json
      X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348
    method: GET
    value_template: "{% if value_json.attr.temp_set > 0%}online{% else %}offline{%endif%}"    
    json_attributes_path: "$.attr"
    json_attributes:
      - system_err2
      - wave_appm_min
      - heat_timer_min
      - heat_power
      - earth
      - wave_timer_min
      - system_err6
      - system_err7
      - system_err4
      - system_err5
      - heat_temp_reach
      - system_err3
      - system_err1
      - system_err8
      - system_err9
      - filter_timer_min
      - heat_appm_min
      - power
      - temp_set_unit
      - filter_appm_min
      - temp_now
      - wave_power
      - locked
      - filter_power
      - temp_set

Once you’ve got to his point, you don’t need the login or bindings sensors any more.

Let me know how you get on and I can explain the rest if required.

I think that’s enough to get me started figuring it out for myself. Thanks for your patience!

Brilliant. Got that working which gives me a reading I can use. I can start working the rest out now I think.

that’s great.
Let me know if you have any more questions.
Some of my stuff might be a bit over complicated, but hopefully you can get what you need from it.

We will have to figure out how to link the two methods into the component!

1 Like

I’ve now got switches for power, heat, filter, bubbles and reports on current and set temperature but what I’m struggling with is how to actually set the target temperature. I’ve added the automations but I can’t quite see how I would set the temp using them in the UI. Is there something basic I’m missing?

Graham

Made it in the end. Here’s what I did:

  • Added the various yaml from @BruceH5200’s repo
  • Created a thermostat (config below)
  • Installed HACS and then installed Scheduler card/custom component
  • Set a scheduler card to do a heating cycle that suits my needs and can be altered easily
  • Made a dashboard :slight_smile: - The cards are mostly to prove what can be done

Thermostat code:

climate:
  - platform: generic_thermostat
    name: Hottub
    heater: switch.hottub_heat
    target_sensor: sensor.hottub_pump_temp
    min_temp: 21
    max_temp: 40
    target_temp: 35
    precision: 1.0

Notes:

All of this works on the basis that the target temp is set to 40 degrees C and left that way. I’ve not actually figured out a way to set that explicitly through the UI. The thermostat reads the pump temp and turn the heating off or on based on that reading. Thus if your target temp is set to 30 and the thermo is trying to heat to 40 it’s never going to turn off. This will work better if the thermostat is actually manipulating the pumps target temp rather than just switching the heater on or off. It also doesn’t control the filter.

To do

  • work out if the target temp can be set from the generic thermostat setting
  • understand the automations better as the idea of turning on the pump for a few minutes to sample temp and then turning off if it’s high enough seems good. Ditto running the filter on it’s own regularly.
  • look at the Octopus agile automations.

For now I’ve got much more than I had. A easy to use scheduler that actually works and is probably trustworthy!

Thank you all for getting me started one this :slight_smile:

You shouldn’t need scheduler or the climate component.
All of this can be done with just my code.
You need to create the helpers that the code mentions.

Eg.
Input_number.hottub_water_temp

The main automation : pump every 30 or similar then handles the schedule to get the tub to a certain temperature by a defined fine.

If you use octopus agile for your electric it will use the cheapest electric price slots too.

This is my interface :slight_smile: