Complete Smart Spa (whirlpool) control

I want to take over full control of my whirlpool spa, which has no smart possibilities. So I need to take over following components:

  • 100W circulation pump, lets call the entity “circulationpump”: I would realize this with ShellyPlus2PM
  • 3000W heater “poolheater”: Realize with the same ShellyPlus2PM
  • ozonator, but this is always on when circulation pump is on, so this is connected to the same life wire as circulation pump and is no additional entity.
  • 2x 1250W jetpumps, but they are independant, I could connect them with another ShellyPlus2PM.
  • Temperature (actualpooltemperature): The heater has 2x 30kOhm NTC (one at entrance of heater and one at exit), so they have 30kOhm at 25°C. I have a table with degree and corresponding resistance and I could connect those NTCs to analog input of shelly addons. But I am not sure, if those analog inputs are precise enough and how to calibrate and integrate this to HA. The other possibility would be 1wire digital temperatur sensors (DS18B20) but I don’t know where to mount them inside the pool so that they were able to measure correct.
  • I measure outdoor temperature with ShellyPlusAddon and 1wire digial DS18B20 “outdoortemp”

I want to do following:

  • filter cycle 1: this is a period with adjustable user inputs “startfiltertime1”, “durationfilter1” in hours and "nominaltemp1"in degrees C, in which the circulation pump (together with ozone) is running and temperature should be controlled to nominaltemp1.

  • filter cycle 2: this is a period with adjustable user inputs “startfiltertime2”, “durationfilter2” and “nominaltemp2”, in which the circulation pump (together with ozone) is running and temperature should be controlled to nominaltemp2. Nice would be an option to tick on for nominaltemp1=nominaltemp2.

  • nominaltemp1 and 2 should have a hysteresis of 0,5°C. Lets say nominaltemp1 is 36°C, heater should start when actualpooltemperature is below 35,5°C and should heat to 36,5°C.

  • outside filter cycles the circulation pump, ozone and heater are not running.

  • as actualpooltemperature is not correct outside filter cycles (water flow is missing), the display of the actualpooltemperature should indicate this in different color and values in (). To receive the correct data there should be an option to manually start the circulation pump for 2 minutes outside the filter cycles.

  • the jetpumps should run daily at a defined time “startjettime” for 5 minutes and also 12 hours later for 5 minutes. This is just to circulate the water inside the jet tubes in case nobody used the pool (and in winter necessary to prevent frozen jet tubes). The startjettime could also be the same as startfiltertime1 and startfiltertime2 if easier (then interval inbetween is maybe not 12 hours, but is ok).

  • whenever the jetpumps are on, also the circulation pump should be turned on.

Options (not a must have):

  • security option 1: if we can use the 2 existing NTCs of heater, there could be an option to check entrance temp vs. exit temp. If temp delta is too high this could be an indication that circulation pump is off or broken. In this case there should be a warning and heater stop. Maybe the circulation pump could also be checked via power consumption (ShellyPlus2PM).

  • security option 2: don’t know if necessary but in winter at e.g. outdoortemp < -5°C for more then e.g. 2 hours, circulation pump should turn on for e.g. 5 minutes every e.g. 4 hours to prevent frozen tubes which are close to the outside pools wall.

I can not open the pool now in winter to work on all those Shellys and temperature sensors but maybe someone can help me with the automation. I would do this then in spring. But in the meantime I could test the automation with helpers (switches, input_numbers…).
I know, this is quite tricky and as a HA starter I am not able to make this automations because I am still not familiar with the syntax. So is there anyone here who has already experiance with such a pool control?


After hard work and fights with the scripting syntax, here the following solution:
I did not realize this yet because of winter, therefore I used some helpers to simulate.

helpers and customized elemets which are needed also later for realization:

  • input datetime poolcycle1start
  • input datetime poolcycle1end
  • input datetime poolcycle2start
  • input datetime poolcycle2end
  • binary sensor poolfiltercycle (to calculate the filter cycle)
  • binary sensor poolneedheat (to calculate the need of heat, with hysteresis funktion)
  • input number poolsettemp

helpers and customized elements just for simulation, will be replaced later by real sensors during realisation:

  • input number poolactualtemp → from which I create a sensor poolactualtemp
  • input boolean pooljetpumps
  • input boolean poolheating
  • input boolean poolcirculationpump

here my templates.yaml

  - name: "Pool IST Temp"
    unique_id: "poolactualtemp"
    device_class: temperature
    unit_of_measurement: "°C"
    state: "{{ states('input_number.poolactualtemp') }}"

  - name: "Filter Zyklus"
    unique_id: "poolfiltercycle"
    state: >
      {{ states('input_datetime.poolcycle1start') <= now().strftime('%H:%M:%S') < states('input_datetime.poolcycle1end') or states('input_datetime.poolcycle2start') <= now().strftime('%H:%M:%S') < states('input_datetime.poolcycle2end') }}

  - name: "Pool Wärmebedarf"
    unique_id: "poolneedheat"
    state: |
      {% set hysteresis=0.3 %}
      {% set actualtemp=states('sensor.poolactualtemp')|float(0) %}
      {% set settemp=states('input_number.poolsettemp')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
      {{ actualtemp < settemp }}

here my automation:

alias: Pool
description: ""
  - platform: homeassistant
    event: start
  - platform: state
      - binary_sensor.poolfiltercycle
    from: "off"
    to: "on"
  - condition: state
    entity_id: binary_sensor.poolfiltercycle
    state: "on"
  - service: input_boolean.turn_on
      entity_id: input_boolean.poolcirculationpump
    data: {}
  - repeat:
        - if:
            - condition: state
              entity_id: binary_sensor.poolneedheat
              state: "on"
            - service: input_boolean.turn_on
                entity_id: input_boolean.poolheating
              data: {}
            - wait_template: >-
                {{ states('binary_sensor.poolneedheat') == 'off'  or
                states('binary_sensor.poolfiltercycle') == 'off' }}
              continue_on_timeout: true
            - service: input_boolean.turn_off
                entity_id: input_boolean.poolheating
              data: {}
            - wait_template: >-
                {{ states('binary_sensor.poolneedheat') == 'on' or
                states('binary_sensor.poolfiltercycle') == 'off' }}
              continue_on_timeout: true
        - condition: state
          entity_id: binary_sensor.poolfiltercycle
          state: "on"
  - service: input_boolean.turn_off
    data: {}
        - input_boolean.poolheating
        - input_boolean.poolcirculationpump
mode: single

The result:
Input are the 4 times for the 2 filtercycles and the set temp.
Circulation pump only during poolfiltercycle and heater only during this time but only if needed.
If filter cycle time will be changed, poolfiltercycle will be newly calculated.
If poolsettemp will be changed, poolneedheat will be newly calculated.

Jetpumps are controlled in a seperate automation. See here:

I’ve got a spa as well and I’ve considered something similar. For background, I’m an electrical engineer and I’ve spent almost two decades working with industrial control systems.

I’d really recommend not doing this control work in HA. You’d be stitching together a bunch of discreet components, without a lot of error coding and some tricky edge cases and debug.

Example: what happens if the pool is heating up, and your WiFi goes down? Or home assistant locks up? Would the heater relay stay on and never get an off command? How long till the hot tub would literally boil?

A better path forward would be to develop a safe control system on an ESP32 that behaves fine completely disconnected. Then worry about sending it things like schedules and such from HA.

In the case of my spa, it turns out others have reverse engineered the protocol to talk to the OEM controller. That still leaves me with some custom work, but I can have good confidence that I won’t write a bug and destroy my spa…

I fully understand what you mean and I know, there is a kind of risk. But, I did some security checks:

  • never run Heater without filter pump
  • automation that ckecks: filter pump on when heater on and when filter pump off → also heater off
  • turn off everythink at HA shutdown
  • ckecks at HA start
  • leave everythink off (on shelly devices) when power on
  • Do auto-off for heater after 3 hours in shelly directly which should work without wifi

I know, some local processing is much better but I am not able to read out the controllers protocol and no ESP32 skills…

So, finally everythink is running.
I installed a temperature sensor inside the pool at the filter area. Another temperature sensor on the heater. Additionally an autark thermal switch on the heater which shut off 230V at 53°C. And the most important: I cascaded the shelly for the heater with the shelly for the filter pump. That means, the heater only gets 230V when the filter pump is on. And in case of pump is on but not working, the thermal switch on heater will shut off the 230V. Additionally the shelly itself can turn off the pump and heater if heater temperature exceeds limit, also in case of Wifi breakdown.

So I think, the worst error scenarios are secured, even for the second error.

The only think which remains:
If pump and heater are on, then wifi breaks down, the pool is heated without control of HA. In this case, I advised shelly to stop at e.g. 38°C, independently from HA.

I also read out the button at the control panel to be able to turn on/off the jetpumps. I use them as input for a shelly Plus 2PM.

And the best: When I use aWattar, an Austrian electricity provider which offers hourly prices, I can automatically pick out the cheapest hours for heating :slight_smile: