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

That was it. Im now getting a response (via Curl) that shows a token, UID, and expiry.

Now to the next steps, and see if I can make this work.

Thank you so much, I did not even think or know to do that! I assumed the site would stay the same regardless of locale, and only would need to change the language. I will let you know how i progress.

Excellent. Glad you’re making progress.

I can get the hot tub status. But if I use any other of the template sensors, I get unavailable. Any ideas?

Hi guys, cleverspa tub here.

I have sent musicman2005 a message as he seems to have it working but wondered if anyone has come across this problem where it doesn’t show any devices.

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73' -d '{ "username": "xxxxxx", "password": "xxxxxx", "lang": "en" }' 'https://api.gizwits.com/app/users'

Lets me login and gives me a token and uid

curl -X GET --header 'Accept: application/json' --header 'X-Gizwits-User-token: xxxxxxxxx' --header 'X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73' 'https://api.gizwits.com/app/bindings?limit=20&skip=0'

returns

{"devices": []}

But I am logged in on my phone and working fine.

I am in the UK, if I point at the euapi it doesn’t login and looking at the main.js it seems the app is pointing at api.gizwits.com

Cheers

Maybe the application id is different for api.
Can you capture it with proxy or similar ?

I think I know what I have done…

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73' -d '{ "username": "xxxxxx", "password": "xxxxxx", "lang": "en" }' 'https://api.gizwits.com/app/users'

That first command has created a new user

I have just tried registering a new user with a different email address and it doesn’t let me login to the cleverspa app with the right command

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73' -d '{ "username": "xxxxxx", "password": "xxxxxx", "lang": "en" }' 'https://api.gizwits.com/app/login'

So I think there is just an account there now that doesn’t really exist.

So the main.js has this as the first mention of gizwits

HomePage.prototype.getToken = function () {
        var _this = this;
        var httpOptions = {
            headers: new __WEBPACK_IMPORTED_MODULE_4__angular_common_http__["c" /* HttpHeaders */]({
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'X-Gizwits-Application-Id': '805cc6a3f41b48aeae471e2fcb6ebc73'
            })
        };
        var user = this.db.object("users/" + this.user).valueChanges();
        user.subscribe(function (usr) {
            console.log(usr);
            _this.token = usr['token'];
            var timestamp = Math.round((new Date()).getTime() / 1000);
            console.log(usr['expire_at'] + ' | ' + timestamp);
            if (usr['expire_at'] >= timestamp) {
                if (_this.did != undefined) {
                    _this.getDeviceStatus();
                }
                else {
                    _this.getDevices();
                }
            }
            else {
                var postData = {
                    "phone_id": _this.user
                };
                _this.http.post("https://api.gizwits.com/app/users", postData, httpOptions)
                    .subscribe(function (data) {
                    console.log(data);
                    var itemRef = _this.db.object('users/' + _this.user);
                    itemRef.set(data);
                    _this.token = data['token'];
                    if (_this.did != 'undefined') {
                        _this.getDeviceStatus();
                    }
                    else {
                        _this.getDevices();
                    }
                }, function (error) {
                    console.log(error);
                });
            }
        });
    };
    HomePage.prototype.getDeviceStatus = function () {
        var _this = this;
        var headers = new __WEBPACK_IMPORTED_MODULE_4__angular_common_http__["c" /* HttpHeaders */]({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-Gizwits-Application-Id': '805cc6a3f41b48aeae471e2fcb6ebc73'
        });
        if (this.locked == 0) {
            this.http.get("https://api.gizwits.com/app/devdata/" + this.did + "/latest", { headers: headers })
                .subscribe(function (data) {
                _this.rotateme = false;
                //console.log(data);
                if (_this.locked == 0) {
                    if (_this.bubblesbtn != data['attr']['Bubble']) {
                        _this.loadingbubbles = 0;
                    }
                    _this.bubblesbtn = data['attr']['Bubble'];
                    if (_this.filterbtn != data['attr']['Filter']) {
                        _this.loadingfilter = 0;
                    }
                    _this.filterbtn = data['attr']['Filter'];
                    if (_this.heaterbtn != data['attr']['Heater']) {
                        _this.loadingheater = 0;
                    }
                    _this.heaterbtn = data['attr']['Heater'];
                    _this.tempbtn = data['attr']['Current_temperature'];
                    _this.bubbles = data['attr']['Bubble'];
                    _this.filter = data['attr']['Filter'];
                    _this.heater = data['attr']['Heater'];
                    _this.temp = data['attr']['Current_temperature'];
                    if (_this.oldVal != data['attr']['Temperature_setup'] && _this.oldVal != 0 && _this.oldVal < 43) {
                        _this.targetTemp = _this.oldVal;
                    }
                    else {
                        if (data['attr']['Temperature_setup'] > 42) {
                            _this.targetTemp = 42;
                        }
                        else {
                            _this.targetTemp = data['attr']['Temperature_setup'];
                        }
                    }
                    _this.filterChange = data['attr']['Time_filter'];
                    if (data['attr']['Time_filter'] > 167 * _this.filterCount) {
                        _this.filterNoti();
                    }
                    if (_this.firsttime != 1) {
                        _this.updateValues('bubbles');
                        _this.firsttime = 1;
                        localStorage.setItem('firsttime', '1');
                    }
                    if (data == [] || !_this.temp) {
                        _this.offline = 1;
                    }
                    else {
                        _this.offline = 0;
                    }
                    if (_this.heater == 1) {
                        _this.filter = 1;
                        _this.filterbtn = 1;
                    }
                    _this.disabled = 0;
                    _this.loader.dismiss();
                }
            }, function (error) {
                //console.log(error);
            });
        }
    };

Which looks like the appid makes sense but it looks like it posts “phone_id” as _this.user

Do you have a picture where the esp-wroom-2us visible and how it is connected?

My current YAML which is working. It’s all based on what Bruce has done but changed where needed to work with the clever spa.

Once i’ve got all the functions working i’ll get a full package of code together.

#####################################
sensor:
  - platform: rest
    name: hottub_status
    scan_interval: 30
    timeout: 20    
    resource: "https://api.gizwits.com/app/devdata/xxxxxxxxxxxx/latest"  
    # xxxxxxxxxxxx is the did from the bindings sensor
    device_class: timestamp
    headers:
      Content-Type: application/json
      X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73
    method: GET
    value_template: "{{ value_json.updated_at | timestamp_custom ('%Y-%m-%dT%H:%M:%S+00:00') }}"
    json_attributes_path: "$.attr"
    json_attributes:
      - Heater
      - 03
      - Undercooling
      - Current_temperature
      - Filter
      - Temperature_setup
      - Superheat
      - Bubble
      - Overtime_filter
      - Timing
      - Check
      - Time_filter

  - platform: template
    sensors:
      hottub_pump_temp:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        friendly_name: "HotTub Water Temperature"
        unit_of_measurement: '°C'
        value_template: "{{ state_attr('sensor.hottub_status', 'Current_temperature') | int }}"

      hottub_water_temp:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        friendly_name: "HotTub Water Temperature"
        unit_of_measurement: '°C'        
        value_template: "{{ states('input_number.Temperature_setup') | int }}"

      hottub_summary:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        friendly_name: "HotTub Status"
        value_template: "{% if is_state('binary_sensor.hottub_online','off') %}offline{% elif is_state('sensor.hottub_error','on') %}error{% elif is_state('input_boolean.hottub_scheduled','on') and is_state('switch.hottub_filter','off') %}scheduled{% elif is_state('switch.hottub_power','off') %}off{% elif state_attr('sensor.hottub_status','heat_temp_reach')==1 %}at temperature{% elif is_state('switch.hottub_heat','on') %}heating{% elif is_state('switch.hottub_filter','off') %}on{% elif is_state('switch.hottub_heat','off') %}filtering{% elif is_state('switch.hottub_bubbles','off') %}heating{% else %}bubbling{% endif %}"

  - platform: history_stats
    name: HotTub Heating This Week
    entity_id: sensor.hottub_summary
    state: "heating"
    type: time
    end: "{{ now().replace(hour=0, minute=0, second=0) }}"
    duration:
      days: 7

rest_command:
  hottub_command: 
    method: POST
    headers:
      content_type: "application/json"
      X-Gizwits-Application-Id: "805cc6a3f41b48aeae471e2fcb6ebc73"
      X-Gizwits-User-token: #####your user token#####
    url: "https://api.gizwits.com/app/control/ZUnVqKmSwvvgGkVtOFxS9v"    
    payload: "{{hottub_command}}"

binary_sensor:
  - platform: template
    sensors:
      hottub_online:
        friendly_name: "HotTub Online"
        value_template: "{{ (now() | as_timestamp - states('sensor.hottub_status') | as_timestamp ) < 2000.0 }}"
        
switch:
  - platform: template
    switches:
      hottub_heat:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        unique_id: Heater
        friendly_name: Heater      
        value_template: "{% if state_attr('sensor.hottub_status', 'Heater') == 1 %}on{% else %}off{% endif %}"
        turn_on:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"Heater": 1} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status
        turn_off:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"Heater": 0} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status
  
      hottub_filter:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        unique_id: hottub_filter
        friendly_name: Filter  
        value_template: "{% if state_attr('sensor.hottub_status', 'filter') == 1 %}on{% else %}off{% endif %}"
        turn_on:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"filter": 1} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status
        turn_off:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"filter": 0} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status

      hottub_bubbles:
        availability_template: "{{ states('binary_sensor.hottub_online') }}"
        unique_id: Bubbles
        friendly_name: Bubbles      
        value_template: "{% if state_attr('sensor.hottub_status', 'Bubble') == 1 %}on{% else %}off{% endif %}"
        turn_on:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"Bubble": 1} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status
        turn_off:
          - service: rest_command.hottub_command
            data_template: 
              hottub_command: >
                {"attrs": {"Bubble": 0} }   
          - delay: 00:00:05
          - service: homeassistant.update_entity
            entity_id: sensor.hottub_status

Ok…I’m stuck, and just can’t make things work. I’ve got the basic rest working, and am getting the following for hot tub status via the rest api.


So its working, and I can see changes in state when I use the buttons on the tub controllers.

Using the following in my sensor.yaml file (which works):

  • platform: rest
    name: hottub_status
    resource: https://usapi.gizwits.com/app/devdata/youdontneedtoknow/latest
    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

but when I attempt to use any template sensors, they all show unavailable.

Like this one:

binary_sensor:

  • platform: template
    sensors:
    hottub_online:
    friendly_name: “HotTub Online”
    value_template: “{{ (now() | as_timestamp - states(‘sensor.hottub_status’) | as_timestamp ) < 2000.0 }}”

None of them work from Bruce’s code, they all say unavailable. I must be missing something stupid, and am pulling my hair out. Am I placing some of the code in the wrong place (configuration vs sensor YAML files, I have tried both doesn’t seem to matter). Any help would be appreciated!

I’ve changed some of my code since then.

It’s expecting the hottub_status sensor to show a timestamp as it’s state:

sensor:
  - platform: rest
    name: hottub_status
    scan_interval: 30
    timeout: 20    
#    resource: https://euapi.gizwits.com/app/devdata/{my did}/latest
    resource: !secret bestway_status_url
    device_class: timestamp
    headers:
      Content-Type: application/json
      X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348
    method: GET
    value_template: "{{ value_json.updated_at | timestamp_custom ('%Y-%m-%dT%H:%M:%S+00:00') }}"
    json_attributes_path: "$.attr"
    json_attributes:
      - temp_now          # Temperature of Water in Pump
      - temp_set          # Target Temperature
      - temp_set_unit     # Temperature displayed in C or F
      - power             # Power - 1:On, 0:Off
      - filter_appm_min   # Start Filter in x minutes
      - filter_power      # Power - 1:On, 0:Off
      - filter_timer_min  # Filter for x minutes      
      - heat_appm_min     # Start Heat in x minutes
      - heat_power        # Heater - 1:On, 0:Off
      - heat_timer_min    # Heat for x minutes
      - heat_temp_reach   # Target Temperature Reached
      - wave_power        # Bubbles - 1:On, 0:Off
      - wave_appm_min     # Start Bubbles in x minutes
      - wave_timer_min    # Bubbles for x minutes
      - locked            # Pump Keypad Locked
      - earth             # Earth Fault
      - system_err1       # Error 1 - Sensor Error / Water Flow Error / Debris Buildup
      - system_err2       # Error 2 - Water Flow Error
      - system_err3       # Error 3 - Temperature too low / Thermostat broken
      - system_err4       # Error 4 - Temperature too high / Thermostat broken / Thermostat needs reset
      - system_err5       # Error 5 - Temperature too high / Thermostat broken / Thermostat needs reset
      - system_err6       # Error 6 - Electrical Fault (Current Variation)
      - system_err7       # Error 7 - Electrical Fault
      - system_err8       # Error 8 - Thermostat needs reset
      - system_err9       # Error 9 - Internal Fuse Failure
      
# https://support.bestwayaftersales.co.uk/faq/get-help-with-your-lay-z-spa-pump/

I put the code above in. That part works. But again, any template sensors are not working correctly. I’m taking small chunks to make it easier to debug.

I took your code of template sensors for HotTub Online and HotTub Error. You can see them in the background. Hottub Online shows Off regardless of status, and Hottub Error shows unavailable.

At this point I’m just using your code above, and then the code for HT Online and Error.

What am I missing? Bruce really appreciate the help.

I’d advise you to look at the latest code in my GitHub.
Rather than the code posted in this thread.

I have the exact same problem but with a cleverspa using the same API.

My binary_sensor.hottub_online shows as off and this is the same if I run a curl command outside HA

But my sensor.hottub_status shows

Heater: 0
Undercooling: 0
Current_temperature: 15
Filter: 1
Temperature_setup: 40
Superheat: 0
Bubble: 0
Overtime_filter: 1
Timing: 0
Check: 0
Time_filter: 10200
friendly_name: hottub_status
device_class: timestamp

And has a timestamp as the state

Could this be something to do with only able to register the tub on a single phone ?

If I run a curl command to switch on the bubbles for example

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Gizwits-User-token: TOKEN' --header 'X-Gizwits-Application-Id: 805cc6a3f41b48aeae471e2fcb6ebc73' -d '{"attrs": {"bubble": 1} } ' 'https://api.gizwits.com/app/control/DID'

I get

{
    "error_message": "device offline!",
    "error_code": 9042,
    "detail_message": null
}

But my phone is working fine which is where I got the DID, Token etc from.

EDIT - it was the tub, realised the app opens but couldn’t send any commands, maybe tub got confused whilst settings everything up don’t know.

Switched the tub off and back on and now all working.

Thank you Bruce for all the initial work and thank you musicman for the cleverspa specific bits.

Really appreciated

I am using the latest code in your GitHub. In my Sensor.yaml, I have this:

  • platform: rest
    name: hottub_status
    scan_interval: 30
    timeout: 20
    resource: https://usapi.gizwits.com/app/devdata/XXXYYYZZZ/latest
    device_class: timestamp
    headers:
    Content-Type: application/json
    X-Gizwits-Application-Id: 98754e684ec045528b073876c34c7348
    method: GET
    value_template: “{{ value_json.updated_at | timestamp_custom (’%Y-%m-%dT%H:%M:%S+00:00’) }}”
    json_attributes_path: “$.attr”
    json_attributes:
    • temp_now # Temperature of Water in Pump
    • temp_set # Target Temperature
    • temp_set_unit # Temperature displayed in C or F
    • power # Power - 1:On, 0:Off
    • filter_appm_min # Start Filter in x minutes
    • filter_power # Power - 1:On, 0:Off
    • filter_timer_min # Filter for x minutes
    • heat_appm_min # Start Heat in x minutes
    • heat_power # Heater - 1:On, 0:Off
    • heat_timer_min # Heat for x minutes
    • heat_temp_reach # Target Temperature Reached
    • wave_power # Bubbles - 1:On, 0:Off
    • wave_appm_min # Start Bubbles in x minutes
    • wave_timer_min # Bubbles for x minutes
    • locked # Pump Keypad Locked
    • earth # Earth Fault
    • system_err1 # Error 1 - Sensor Error / Water Flow Error / Debris Buildup
    • system_err2 # Error 2 - Water Flow Error
    • system_err3 # Error 3 - Temperature too low / Thermostat broken
    • system_err4 # Error 4 - Temperature too high / Thermostat broken / Thermostat needs reset
    • system_err5 # Error 5 - Temperature too high / Thermostat broken / Thermostat needs reset
    • system_err6 # Error 6 - Electrical Fault (Current Variation)
    • system_err7 # Error 7 - Electrical Fault
    • system_err8 # Error 8 - Thermostat needs reset
    • system_err9 # Error 9 - Internal Fuse Failure

In Configuration.yaml, this code:

binary_sensor:

  • platform: template
    sensors:
    hottub_online:
    friendly_name: “HotTub Online”
    value_template: “{{ (now() | as_timestamp - states(‘sensor.hottub_status’) | as_timestamp ) < 2000.0 }}”

123

Notice hotttub_status shows 5 hours ago. But I can click on it, and it will show an update a few minutes back. All of the individual items show correct (temp now, temp set, etc…see my pic in my last post).

The binary sensor never reads correct (HotTub Online) never shows on. I am assuming this is preventing the others from working correctly.

To keep things simple, just focused on the two pieces of code: hottub_status and HotTub online.

What am I missing or not doing correctly? Its killing me that I can figure this out.

I had the same thing, switched hottub off and on and it all came alive.

I’ve restarted it several times, no change.

This is great. Thanks for the work this made my life so much asier and managed to create some Automations from this,

Anyone located in the US, whom has this working? Would love to compare configs. I’m so close, and still stuck.,

How far have you got? What is working? what is not working?
Which app is your pump linked to?
Bestway app?