When I bought my house I also inherited a nice outdoor swimming pool. Little did I know about pool maintenance back then and so during the first couple of months everything that could go wrong did go wrong. Anyway, I am now in a position to go beyond the static pool maintenance and am smartening up the whole thing. This post is the first one in a short series describing various small improvements I have made – starting with making the pool pump a bit smarter.
Pool Pump – Before
The pool pump needs to run several hours per day – a little less in winter and a bit more in summer during the swimming season. Mine was hooked up to a simple timer switch which turned on the pool pump and the liquid chlorine pump at the same time. And over the course of the year I had to fiddle around with the timer switch to increase and decrease the duration as necessary.
The timer switch is a simple approach, but comes with a few disadvantages:
- To make most of my self-produced solar electricity, I had to continuously modify the switch’s settings.
- During a power outage the timer just stops, and continuous afterwards wherever it left, i.e. I had to realign the timer after each outage.
- There is no feedback, i.e. if for example the fuse blows for whatever reason the pump stops working and it just takes a few days until the water turns green and a lot of chemicals are required to fix this.
Pool Pump – After
Remote control the pump
To be able to control the pool pump through Home Assistant, I first of all had to have the ability to remote control the electricity circuit. My first choice was a Z-Wave switch, but unfortunately the power outlet is in a very inconvenient position: behind and under the pool, quite far away from the house and far from any location where a repeater would have helped. The outlet and the switch are not water-proof, so both had to remain inside the pool pump cupboard. And I could not find a Z-Wave switch with an external antenna.
Next, I looked at Wifi switches, and settled on a WeMo Insight. There are a few tear-down reports for that switch which taught me how to replace the built-in antenna with a pigtail cable, so that I could connect a 3 metre antenna cable (SMA male to female jack, coax RG58) with the external antenna mounted on the pool fence, well above the water level so that it can reach my Wifi.
The integration of the WeMo Insight switch into Home Assistant was working as expected – well at least after a small fix to the underlying library, since WeMo now apparently uses more network ports where the switches can be listening on.
Once that was working I could focus on the actual automation.
Of course you can use other types of switches depending your individual circumstances or preferences. In the following configuration snippets you will have to replace the entity id switch.wemo_insight_pool
with your switch’s entity id; it just needs to support states on
and off
as well as services turn_on
and turn_off
.
Automating the pump
With a pool pump switched by Home Assistant I still wanted some level of control and introduced several levers and switches, and also implemented a level of monitoring and reporting.
Controls – Auto, On and Off
Anything that you would normally just turn on or off and that should be automated in the future would probably require a 3-way-switch with the options: Auto, On and Off. While On and Off simply turn the attached Wifi switch on or off and the automation is ignored, in Auto mode the automation component is controlling the switch state.
input_select:
pool_pump:
name: Pool Pump mode
options:
- 'Auto'
- 'On'
- 'Off'
initial: 'Auto'
icon: mdi:engine
Controls – Swimming Season or not
I am distinguishing between Swimming Season and Off-peak Season. With a simple switch (input_boolean
) I can change the current mode.
input_boolean:
swimming_season:
name: Swimming Season
icon: mdi:swim
Controls – Duration
For each mode – swimming or off-peak – a separate duration can be configured defining how many hours per day the pool pump should run. For now I kept it simple only allowing to select a full number of hours with reasonable minimums and maximums.
input_number:
run_pool_pump_hours_swimming_season:
name: Run Pool Pump in Swimming Season
min: 1
max: 8
step: 1
mode: slider
run_pool_pump_hours_off_season:
name: Run Pool Pump in Off Season
min: 1
max: 6
step: 1
mode: slider
Automating the Wifi switch
The actual automation of the pool pump is triggered every 5 minutes with a check that compares the current state of the system with how it should be. Since the pool pump should run at daytime to make use of my solar electricity, and cannot run during the night due to local council restrictions, I added a condition to only run the check from one hour before sunrise to one hour after sunset. The actual check is happening inside a custom component. I ruled out the other options – a script would not be powerful enough, and an app based on AppDaemon would introduce another level of complexity without a real benefit compared to Home Assistant’s custom component.
The reason for the regular checks instead of just calculating static on/off times is that this approach can survive outages, and will just pick up when Home Assistant is restarted.
automation:
- alias: 'Pool Pump On'
trigger:
- platform: state
entity_id: input_select.pool_pump
to: 'On'
action:
service: homeassistant.turn_on
entity_id: switch.wemo_insight_pool
- alias: 'Pool Pump Off'
trigger:
- platform: state
entity_id: input_select.pool_pump
to: 'Off'
action:
service: homeassistant.turn_off
entity_id: switch.wemo_insight_pool
- alias: 'Check Pool Pump'
trigger:
- platform: time
minutes: '/5'
seconds: 00
condition:
condition: and
conditions:
- condition: sun
after: sunrise
after_offset: '-1:00:00'
- condition: sun
before: sunset
before_offset: '1:00:00'
action:
service: pool_pump_service.check
data:
switch_entity_id: switch.wemo_insight_pool
Custom component
The code below follows this high-level structure:
- If the pool pump mode is set to ‘Auto’, continue with the check.
- If the sun is above the horizon, continue with the check, otherwise switch the pump off.
- If set to swimming season, split the total time into 3 runs (1st and 3rd for 25% of the configured duration, and 2nd run for 50%), if in off season split the total time into 2 runs (1st and 2nd run for 50% of the configured duration each).
Start the first run at the hard-coded number of minutes after sunrise and leave on for the calculated amount of time. Start the second run after the first one-hour break. And in swimming season schedule a third run, again after a break after the second run.
All the selected and hard-coded timings can be fine-tuned to match the individual circumstances. In my case, I am observing a steep increase in solar electricity production early in the morning and hence selected the start of the first run accordingly. In off season the second run is then happening around noon when the solar electricity production is at its peak. During swimming season the first run starts even earlier and a longer break happens in the afternoon before the third run finishes shortly before sunset – and shortly before solar electricity production decreases.
What is currently missing in this setup is displaying the next start or stop time.
# Check if pool pump is supposed to run, and turn it on or off accordingly
import logging
from datetime import timedelta
from homeassistant.helpers.sun import get_astral_event_date
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'pool_pump_service'
ATTRIBUTE_SWITCH_ENTITY_ID = 'switch_entity_id'
OFF_SEASON_MAX_DURATION = 6.0
OFF_SEASON_RUN_1_AFTER_SUNRISE_OFFSET_MINS = 120
OFF_SEASON_1ST_BREAK_MINUTES = 60
SWIMMING_SEASON_MAX_DURATION = 8.25
SWIMMING_SEASON_RUN_1_AFTER_SUNRISE_OFFSET_MINS = 75
SWIMMING_SEASON_BREAK_1_MINUTES = 60
SWIMMING_SEASON_BREAK_2_MINUTES = 165
def setup(hass, config):
"""Set up is called when Home Assistant is loading our component."""
def switch_pool_pump(switch_entity_id, target_state):
switch = hass.states.get(switch_entity_id)
if switch:
if switch.state == target_state:
# Already in the correct state
_LOGGER.info("Switch is in correct state: %s", target_state)
else:
# Not in the correct state
data = { "entity_id": switch_entity_id }
if target_state == 'on':
hass.services.call('homeassistant', 'turn_on', data)
else:
hass.services.call('homeassistant', 'turn_off', data)
_LOGGER.info("Switched from '%s' to '%s'", switch.state,
target_state)
else:
_LOGGER.warning("Switch unavailable: %s", switch_entity_id)
def handle_check(call):
_LOGGER.info("Starting pool pump check")
switch_entity_id = call.data.get(ATTRIBUTE_SWITCH_ENTITY_ID)
# Read the pool pump configuration
mode = hass.states.get('input_select.pool_pump')
swimming_season = hass.states.get('input_boolean.swimming_season')
run_hours_swimming_season = min(SWIMMING_SEASON_MAX_DURATION,
float(hass.states.get(
'input_number.run_pool_pump_hours_swimming_season').state))
run_hours_off_season = min(OFF_SEASON_MAX_DURATION,
float(hass.states.get(
'input_number.run_pool_pump_hours_off_season').state))
_LOGGER.info("* Pool pump mode: %s", mode.state)
_LOGGER.info("* Swimming season: %s", swimming_season.state)
_LOGGER.info("* Pool pump run hours swimming season: %s",
run_hours_swimming_season)
_LOGGER.info("* Pool pump run hours off season: %s",
run_hours_off_season)
# Only check if pool pump is set to 'Auto'
if mode.state == 'Auto':
_LOGGER.info("Pool pump set to 'Auto'")
# Get sun details for today
now = dt_util.now()
_LOGGER.info("* Time is now %s", now)
sun = hass.states.get('sun.sun')
if sun.state == 'above_horizon':
_LOGGER.info("* Sun above horizon")
date = now.date()
sunrise = get_astral_event_date(hass, 'sunrise', date)
sunset = get_astral_event_date(hass, 'sunset', date)
_LOGGER.info("* Sunrise: %s",
sunrise.astimezone(hass.config.time_zone))
_LOGGER.info("* Sunset: %s",
sunset.astimezone(hass.config.time_zone))
if swimming_season.state == 'on':
# Swimming Season (Summer)
_LOGGER.info("* Swimming season")
duration1 = run_hours_swimming_season * 60.0 * 0.25
duration2 = run_hours_swimming_season * 60.0 * 0.5
duration3 = run_hours_swimming_season * 60.0 * 0.25
_LOGGER.info(
"* Run pool pump 3 times for %s/%s/%s minutes",
duration1, duration2, duration3)
# Check for 1st run
run_1_start = sunrise + timedelta(
minutes=SWIMMING_SEASON_RUN_1_AFTER_SUNRISE_OFFSET_MINS)
run_1_stop = run_1_start + timedelta(minutes=duration1)
_LOGGER.info("* Run 1/3: %s - %s",
run_1_start.astimezone(hass.config.time_zone),
run_1_stop.astimezone(hass.config.time_zone))
if run_1_start <= now <= run_1_stop:
# Turn on pool pump
_LOGGER.info("* Pool pump should be on (Run 1/3)")
switch_pool_pump(switch_entity_id, 'on')
else:
# Check for 2nd run
run_2_start = run_1_stop + timedelta(
minutes=SWIMMING_SEASON_BREAK_1_MINUTES)
run_2_stop = run_2_start + timedelta(minutes=duration2)
_LOGGER.info("* Run 2/3: %s - %s",
run_2_start.astimezone(
hass.config.time_zone),
run_2_stop.astimezone(
hass.config.time_zone))
if run_2_start <= now <= run_2_stop:
# Turn on pool pump
_LOGGER.info("* Pool pump should be on (Run 2/3)")
switch_pool_pump(switch_entity_id, 'on')
else:
# Check for 3rd run
run_3_start = run_2_stop + timedelta(
minutes=SWIMMING_SEASON_BREAK_2_MINUTES)
run_3_stop = run_3_start + timedelta(
minutes=duration3)
_LOGGER.info("* Run 3/3: %s - %s",
run_3_start.astimezone(
hass.config.time_zone),
run_3_stop.astimezone(
hass.config.time_zone))
if run_3_start <= now <= run_3_stop:
# Turn on pool pump
_LOGGER.info(
"* Pool pump should be on (Run 3/3)")
switch_pool_pump(switch_entity_id, 'on')
else:
# Turn off pool pump
_LOGGER.info("* Pool pump should be off")
switch_pool_pump(switch_entity_id, 'off')
else:
# Off Season (Winter)
_LOGGER.info("* Off season")
duration = run_hours_off_season * 60.0 * 0.5
_LOGGER.info("* Run pool pump 2 times for %s/%s minutes",
duration, duration)
# Check for 1st run
run_1_start = sunrise + timedelta(
minutes=OFF_SEASON_RUN_1_AFTER_SUNRISE_OFFSET_MINS)
run_1_stop = run_1_start + timedelta(minutes=duration)
_LOGGER.info("* Run 1/2: %s - %s",
run_1_start.astimezone(hass.config.time_zone),
run_1_stop.astimezone(hass.config.time_zone))
if run_1_start <= now <= run_1_stop:
# Turn on pool pump
_LOGGER.info("* Pool pump should be on (Run 1/2)")
switch_pool_pump(switch_entity_id, 'on')
else:
# Check for 2nd run
run_2_start = run_1_stop + timedelta(
minutes=OFF_SEASON_1ST_BREAK_MINUTES)
run_2_stop = run_2_start + timedelta(minutes=duration)
_LOGGER.info("* Run 2/2: %s - %s",
run_2_start.astimezone(
hass.config.time_zone),
run_2_stop.astimezone(
hass.config.time_zone))
if run_2_start <= now <= run_2_stop:
# Turn on pool pump
_LOGGER.info("* Pool pump should be on (Run 2/2)")
switch_pool_pump(switch_entity_id, 'on')
else:
# Turn off pool pump
_LOGGER.info("* Pool pump should be off")
switch_pool_pump(switch_entity_id, 'off')
else:
_LOGGER.info("* Sun below horizon")
# Turn pool pump if it's still running
_LOGGER.info("* Pool pump should be off")
switch_pool_pump(switch_entity_id, 'off')
else:
_LOGGER.info("Pool pump set to '%s'", mode.state)
hass.services.register(DOMAIN, 'check', handle_check)
# Return boolean to indicate that initialisation was successfully.
return True
The code is registered as a service so that it can easily be used in an automation action.
The custom component is stored in a file [config_dir]/custom_components/pool_pump_service.py
. And to make the custom component known to Home Assistant a simple configuration entry is required:
# Enable custom component
pool_pump_service:
Future improvements of the automation could include actual current power production and the weather forecast, so that for example the pool pump could be deferred if it’s cloudy in the morning while sunshine is predicted for the afternoon. Another improvement could be a better coordination of the pool pump with other appliances running during the day.
Monitoring – How much energy is used?
Knowing how much energy is used is not just a nice byproduct that the WeMo Insight switch provides. The current power usage is a very useful indicator if the pool pump is running – as opposed to just knowing if the switch is turned on. I also had a couple of occasions where Home Assistant claimed to have turned on the switch and showed it as on, but had actually lost the connection and neither switch nor pump were actually on.
sensor:
- platform: template
sensors:
wemo_insight_pool_energy_today:
value_template: '{% if (states.switch.wemo_insight_pool.attributes.today_energy_kwh) %}{{ states.switch.wemo_insight_pool.attributes.today_energy_kwh | float(2) }}{% else %}0.0{% endif %}'
friendly_name: "WeMo Insight Pool Energy Today"
unit_of_measurement: "kWh"
wemo_insight_pool_current_power:
value_template: '{% if is_state("switch.wemo_insight_pool", "on") %}{{ states.switch.wemo_insight_pool.attributes.current_power_w | float(0) }}{% else %}0{% endif %}'
friendly_name: "WeMo Insight Pool Current Power"
unit_of_measurement: "W"
The reason for the if-statements in the above configuration is that the WeMo Insight switch does not always report a value or does not report the attribute at all.
Monitoring – Pump running or not?
To know if the pool pump is actually running or not I am using the previous sensor that reports the current power usage of the switch. In the following binary sensor I am assuming that the pool pump is on if the switch reports more than 8W of power usage.
binary_sensor:
- platform: template
sensors:
pool_pump_running:
value_template: "{{ states.sensor.wemo_insight_pool_current_power.state | float > 8 }}"
friendly_name: "Pool Pump running"
device_class: moving
Monitoring – How long is the pump running?
Based on the binary template sensor that determines whether or not the pump is running, I can now use the history_stats
sensor to tell me how long the pump has been running each day.
sensor:
- platform: history_stats
name: Pool Pump running today
entity_id: binary_sensor.pool_pump_running
state: 'on'
type: time
start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
end: '{{ now() }}'
Reporting – The daily message
Showing the details on the Home Assistant dashboard is one thing, but I thought it would be useful to send myself a daily message confirming how many hours the pool pump had been running, compared to how long it was supposed to run. Since I already integrated Pushover, it was just a matter of formatting the message and send it out each day, 30 minutes after sunset.
automation:
- alias: 'Report Pool Pump'
trigger:
- platform: sun
event: sunset
offset: '00:30:00'
action:
service: notify.pushover
data_template:
message: "Pool Pump was on for {{states.sensor.pool_pump_running_today.attributes.value}} today.
({%- if is_state('input_boolean.swimming_season', 'on') -%}Swimming Season / {{ states.input_number.run_pool_pump_hours_swimming_season.state | round(0) }}h{%- else -%}Off Season / {{ states.input_number.run_pool_pump_hours_off_season.state | round(0) }}h{%- endif -%})."
title: "Pool Pump Report"
data:
html: 1
Outlook
Now that the pool pump is fixed up, I will look at monitoring the water temperature in the next post.
Update 18 Apr 2019
Home Assistant 0.86 introduced time_pattern
for triggering an automation on a regular basis. If you are using version 0.86 or later, please change the above automation for checking the pool pump to:
automation:
- alias: 'Check Pool Pump'
trigger:
- platform: time_pattern
minutes: '/5'
seconds: 00
condition:
condition: and
conditions:
- condition: sun
after: sunrise
after_offset: '-1:00:00'
- condition: sun
before: sunset
before_offset: '1:00:00'
action:
service: pool_pump_service.check
data:
switch_entity_id: switch.wemo_insight_pool
Leave a Reply