Pool control with a picture entities card and aqualinkd

@johntdyer,

It’s a sensor, so it doesn’t change the pool mode. It just shows the current pool mode based on the state of 3 other entities:

    #a tri-state pump mode for off/pool/spa to simplify my control panel
    aqualink_pump_mode:
      value_template: >-
        {%- if is_state('binary_sensor.filter_pump_delay', 'on') -%}
          delay
        {%- elif is_state('switch.spa_mode', 'on') -%}
          spa
        {%- elif is_state('switch.filter_pump', 'on') -%}
          pool
        {%- else -%}
          off
        {%- endif -%}
      icon_template: >-
        {%- if is_state('binary_sensor.filter_pump_delay', 'on') -%}
          mdi:timer-sand
        {%- elif is_state('switch.spa_mode', 'on') -%}
          mdi:hot-tub
        {%- elif is_state('switch.filter_pump', 'on') -%}
          mdi:pool
        {%- else -%}
          mdi:power-sleep
        {%- endif -%}

The poolmode template used by the button is what calls python_script.set_pool_mode in the tap_action:

template: toggle
variables:
  mode: null
styles:
  icon:
    - padding-left: 3px
    - padding-right: 3px
state:
  - value: delay
    icon: 'mdi:timer-sand'
    styles:
      card:
        - background-color: yellow
      name:
        - color: black
      icon:
        - color: black
tap_action:
  action: call-service
  service: python_script.set_pool_mode
  service_data:
    mode: '[[[ return variables.mode ]]]'

The poolmode template makes use of another template called toggle, but that’s just some common styling that I use for my toggle buttons.

yea, I was asking if I chuld check out the python_script.set_pool_mode file, curious on the logic you’re using to toggle between the modes

@johntdyer,

Sure, I misunderstood what you were looking for.

#
# Created by Keith Townsend, 8/8/2020
# Set pool mode from a simple button that represents one of the modes.
# The use case is to have a pair of pool & spa buttons handling 3 states: pool/spa/off.
# Pressing one will switch to that mode if it wasn't in that mode, or it will turn the system off if it was.
#

# # The following may be added to your python_scripts/services.yaml if you like.
# set_pool_mode:
#   description: Set pool mode. Switches to given mode if not current mode, or turns system off if it was in that mode.
#   fields:
#     mode:
#       description: spa, pool or off

# dependencies:
mode_desire = data.get('mode').lower()
mode_sensor = 'sensor.aqualink_pump_mode'
pump_switch = 'switch.filter_pump'
spa_switch = 'switch.spa_mode'
cleaner_switch = 'switch.cleaner'
blower_switch = 'switch.spa_blower'
spa_heater_switch = 'switch.spa_heater'
pool_heater_switch = 'switch.pool_heater'

if mode_desire is None:
    logger.warning("===== mode argument is required")
elif hass.states.get(mode_sensor) is None:
    logger.warning("===== entity %s wasn't found", mode_sensor)
elif hass.states.get(pump_switch) is None:
    logger.warning("===== entity %s wasn't found", pump_switch)
elif hass.states.get(spa_switch) is None:
    logger.warning("===== entity %s wasn't found", spa_switch)
elif hass.states.get(cleaner_switch) is None:
    logger.warning("===== entity %s wasn't found", cleaner_switch)
elif hass.states.get(blower_switch) is None:
    logger.warning("===== entity %s wasn't found", blower_switch)
elif hass.states.get(spa_heater_switch) is None:
    logger.warning("===== entity %s wasn't found", spa_heater_switch)
elif hass.states.get(pool_heater_switch) is None:
    logger.warning("===== entity %s wasn't found", pool_heater_switch)
else:
    current_mode = hass.states.get(mode_sensor).state.lower()
    if current_mode == 'delay':
        logger.info("===== command for mode [%s] ignored; waiting for pump delay", mode_desire)
    elif mode_desire == 'off' or current_mode == mode_desire:
        hass.services.call('homeassistant', 'turn_off', {"entity_id":spa_heater_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":pool_heater_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":cleaner_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":blower_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":spa_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":pump_switch})
    elif mode_desire == 'pool':
        hass.services.call('homeassistant', 'turn_off', {"entity_id":spa_heater_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":blower_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":spa_switch})
        hass.services.call('homeassistant', 'turn_on', {"entity_id":pump_switch})
    elif mode_desire == 'spa':
        hass.services.call('homeassistant', 'turn_off', {"entity_id":pool_heater_switch})
        hass.services.call('homeassistant', 'turn_off', {"entity_id":cleaner_switch})
        hass.services.call('homeassistant', 'turn_on', {"entity_id":pump_switch})
        hass.services.call('homeassistant', 'turn_on', {"entity_id":spa_switch})
    else:
        logger.warning("===== mode %s wasn't expected", mode_desire)

Hello, great project ! Although I do not have iAqulink, I have implemented these ideas to both my pool and spa system components.
I have a question, as all my systems a custom and separated, I would like the abiility to have in a single button card, the text of two entities.

For example on entity reports whether the heatpump is on or off, another separate entity reports whihc mode it is on (Heating, cooling, Freeze protect, water ciculation etc…):
I would likie to display in your “Status display” the combination of the states as “On (Heating)” Or “On (Conservation Heating)” or “Off (Freeze protect)”.

The “State_text” option does not appear to accept templates where I could have combined various texts and attributes…

@Nordicfastware, the custom button card does allow javascript templating for the state_display property. See here for the specific part of the documentation. I don’t see the state_text property you referred to, which could be why it didn’t work for you.

I have managed to get Aqualinkd running and verified the sensor data is being published to the mosquitto broker residing on my Home Assistant. I tried copying HASSIO.Implementation.txt into configuration.yaml, however that method seems obsolete now in Home Assistant. Can anyone offer up the preferred way of having the Aqualinkd sensors and switches show up in Home Assistant?

@mattorola7 how did you set up Aqualinkd and HA mosquitto broker to get them talking? I can’t seem to get the HA MQTT Broker to see my aqualinkd, though the AqualinkD web app is working fine. But the MQTT information at github.com/sfeakes/AqualinkD/wiki#AqualinkD seems to be a bit out of date as well. And, yes, I also found the HASSIO.Implementation.txt content to cause the current HA (23.8.1) to throw a bunch of errors. So I’m assuming there has been some syntax changes since that was created a couple years ago. Any suggestions welcome.

MQTT is definitely configured differently now than it used to be due to a significant change in how Home Assistant expects the MQTT entities configured.

My current entries in configuration.yaml (you may need to adjust for your situation):

mqtt:
  climate: !include_dir_merge_list entities/mqtt/climate/
  light: !include_dir_merge_list entities/mqtt/lights/
  sensor: !include_dir_merge_list entities/mqtt/sensors/
  binary_sensor: !include_dir_merge_list entities/mqtt/sensors_binary/
  switch: !include_dir_merge_list entities/mqtt/switches/

and expand these to see the individual yaml files the above is referencing:

config/entities/mqtt/climate/aqualinkd.yaml
- unique_id: aqualink_pool_heater
  name: Pool Heater
  modes:
    - "off"
    - "heat"
  # send_if_off: true #deprecated
  initial: 70
  power_command_topic: "aqualinkd/Pool_Heater/set"
  payload_on: "1"
  payload_off: "0"
  current_temperature_topic: "aqualinkd/Temperature/Pool"
  min_temp: 70
  max_temp: 90
  #Disabled mode_command_topic because it sends mode string; use power_command_topic instead because it will send 1,0
  #mode_command_topic: "aqualinkd/Pool_Heater/set"
  mode_state_topic: "aqualinkd/Pool_Heater/enabled"
  mode_state_template: >-
    {% set values1 = { '0':'off', '1':'heat'} %}
    {{ values1[value] if value in values1.keys() else 'off' }}
  temperature_command_topic: "aqualinkd/Pool_Heater/setpoint/set"
  temperature_state_topic: "aqualinkd/Pool_Heater/setpoint"
  temperature_state_template: "{{ value_json }}"
  action_topic: "aqualinkd/Pool_Heater"
  action_template: >-
    {% set values1 = { '0':'off', '1':'heating'} %}
    {{ values1[value] if value in values1.keys() else 'off' }}

- unique_id: aqualink_spa_heater
  name: Spa Heater
  modes:
    - "off"
    - "heat"
  # send_if_off: true #deprecated
  initial: 100
  power_command_topic: "aqualinkd/Spa_Heater/set"
  payload_on: "1"
  payload_off: "0"
  current_temperature_topic: "aqualinkd/Temperature/Spa"
  min_temp: 80
  max_temp: 104
  #Disabled mode_command_topic because it sends mode string; use power_command_topic instead because it will send 1,0
  #mode_command_topic: "aqualinkd/Spa_Heater/set"
  mode_state_topic: "aqualinkd/Spa_Heater/enabled"
  mode_state_template: >-
    {% set values2 = { '0':'off', '1':'heat'} %}
    {{ values2[value] if value in values2.keys() else 'off' }}
  temperature_command_topic: "aqualinkd/Spa_Heater/setpoint/set"
  temperature_state_topic: "aqualinkd/Spa_Heater/setpoint"
  temperature_state_template: "{{ value_json }}"
  action_topic: "aqualinkd/Spa_Heater"
  action_template: >-
    {% set values1 = { '0':'off', '1':'heating'} %}
    {{ values1[value] if value in values1.keys() else 'off' }}
  
- unique_id: aqualink_freeze_protect
  name: Freeze Protect
  modes:
    - "off"
    - "cool"
  # send_if_off: true #deprecated
  initial: 36
  power_command_topic: "aqualinkd/Freeze_Protect/set"
  payload_on: "1"
  payload_off: "0"
  current_temperature_topic: "aqualinkd/Temperature/Air"
  min_temp: 34
  max_temp: 42
  #Disabled mode_command_topic because it sends mode string; use power_command_topic instead because it will send 1,0
  #mode_command_topic: "aqualinkd/Freeze_Protect/set"
  mode_state_topic: "aqualinkd/Freeze_Protect/enabled"
  mode_state_template: >-
    {% set values = { '0':'off', '1':'cool'} %}
    {{ values[value] if value in values.keys() else 'off' }}
  temperature_command_topic: "aqualinkd/Freeze_Protect/setpoint/set"
  temperature_state_topic: "aqualinkd/Freeze_Protect/setpoint"
  temperature_state_template: "{{ value_json }}"
  action_topic: "aqualinkd/Freeze_Protect"
  action_template: >-
    {% set values1 = { '0':'off', '1':'cooling'} %}
    {{ values1[value] if value in values1.keys() else 'off' }}
config/entities/mqtt/lights/aqualinkd.yaml
- unique_id: aqualink_spa_light
  name: "Spa Light"
  state_topic: "aqualinkd/Aux_3"
  command_topic: "aqualinkd/Aux_3/set"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false

- unique_id: aqualink_pool_light
  name: "Pool Light"
  state_topic: "aqualinkd/Solar_Heater"
  command_topic: "aqualinkd/Solar_Heater/set"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
config/entities/mqtt/sensors/aqualinkd.yaml
# AqualinkD
- unique_id: aqualink_pool_state
  state_topic: "aqualinkd/Service_Mode"
  name: "Aqualink Mode"
  value_template: "{{ value }}"    
  icon: mdi:wrench

- unique_id: aqualink_message
  state_topic: "aqualinkd/Display_Message"
  name: "Aqualink Message"
  value_template: "{{ value if not value|lower == 'service mode' else '' }}"    
  icon: mdi:information

- unique_id: aqualink_air_temp
  state_topic: "aqualinkd/Temperature/Air"
  name: "Air Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"
  device_class: temperature

- unique_id: aqualink_spa_temp
  state_topic: "aqualinkd/Temperature/Spa"
  name: "Spa Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"  
  device_class: temperature

- unique_id: aqualink_pool_temp
  state_topic: "aqualinkd/Temperature/Pool"
  name: "Pool Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"     
  device_class: temperature

- unique_id: aqualink_pool_heater_temp
  state_topic: "aqualinkd/Pool_Heater/setpoint"
  name: "Pool Heater Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"     
  device_class: temperature

- unique_id: aqualink_spa_heater_temp
  state_topic: "aqualinkd/Spa_Heater/setpoint"
  name: "Spa Heater Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"        
  device_class: temperature

- unique_id: aqualink_freeze_protect_temp
  state_topic: "aqualinkd/Freeze_Protect/setpoint"
  name: "Freeze Protect Temp"
  value_template: "{{ value_json }}"    
  unit_of_measurement: "°F"        
  device_class: temperature
config/entities/mqtt/sensors_binary/aqualinkd.yaml
- unique_id: aqualink_has_message
  state_topic: "aqualinkd/Display_Message"
  name: "Aqualink Has Message"
  qos: 0
  payload_on: "1"
  payload_off: "0"
  value_template: "{{ '1' if value|string|trim|length > 0 and value|lower != 'service mode' else '0' }}"

- unique_id: aqualink_pool_heater
  state_topic: "aqualinkd/Pool_Heater"
  name: "Pool Heating"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: aqualink_spa_heater
  state_topic: "aqualinkd/Spa_Heater"
  name: "Spa Heating"
  qos: 0
  payload_on: "1"
  payload_off: "0"    

- unique_id: aqualink_freeze_protection
  state_topic: "aqualinkd/Freeze_Protect"
  name: "Freeze Protecting"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: aqualink_alive
  state_topic: "aqualinkd/Alive"
  name: "AqualinkD Alive"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: aqualink_battery
  state_topic: "aqualinkd/Battery"
  name: "Aqualink Battery"
  qos: 0
  payload_on: "0"
  payload_off: "1"
  device_class: battery

- unique_id: filter_pump_delay
  state_topic: "aqualinkd/Filter_Pump/delay"
  name: "Filter Pump Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: spa_mode_delay
  state_topic: "aqualinkd/Spa_Mode/delay"
  name: "Spa Mode Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: pool_light_delay
  state_topic: "aqualinkd/Solar_Heater/delay"
  name: "Pool Light Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: cleaner_delay
  state_topic: "aqualinkd/Aux_1/delay"
  name: "Cleaner Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: spa_blower_delay
  state_topic: "aqualinkd/Aux_2/delay"
  name: "Spa Blower Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"

- unique_id: spa_light_delay
  state_topic: "aqualinkd/Aux_3/delay"
  name: "Spa Light Delay"
  qos: 0
  payload_on: "1"
  payload_off: "0"
config/entities/mqtt/switches/aqualinkd.yaml
- unique_id: aqualink_freeze_protect
  name: "Freeze Protection"
  state_topic: "aqualinkd/Freeze_Protect/enabled"
  command_topic: "aqualinkd/Freeze_Protect/set" #NOTE: aqualinkd does not support toggling Freeze Protect due to being a menu option rather than a simple command; use RS4 controller
  json_attributes_topic: "aqualinkd/Freeze_Protect"
  json_attributes_template: "{{ {'active': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:snowflake

- unique_id: aqualink_filter_pump
  name: "Filter Pump"
  state_topic: "aqualinkd/Filter_Pump"
  command_topic: "aqualinkd/Filter_Pump/set"
  json_attributes_topic: "aqualinkd/Filter_Pump/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false

- unique_id: aqualink_pool_light
  name: "Pool Light"
  state_topic: "aqualinkd/Solar_Heater"
  command_topic: "aqualinkd/Solar_Heater/set"
  json_attributes_topic: "aqualinkd/Solar_Heater/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:lightbulb

- unique_id: aqualink_spa_mode
  name: "Spa Mode"
  state_topic: "aqualinkd/Spa_Mode"
  command_topic: "aqualinkd/Spa_Mode/set"
  json_attributes_topic: "aqualinkd/Spa_Mode/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:hot-tub

- unique_id: aqualink_cleaner
  name: "Cleaner"
  state_topic: "aqualinkd/Aux_1"
  command_topic: "aqualinkd/Aux_1/set"
  json_attributes_topic: "aqualinkd/Aux_1/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:broom

- unique_id: aqualink_spa_blower
  name: "Spa Blower"
  state_topic: "aqualinkd/Aux_2"
  command_topic: "aqualinkd/Aux_2/set"
  json_attributes_topic: "aqualinkd/Aux_2/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false  
  icon: mdi:chart-bubble

- unique_id: aqualink_spa_light
  name: "Spa Light"
  state_topic: "aqualinkd/Aux_3"
  command_topic: "aqualinkd/Aux_3/set"
  json_attributes_topic: "aqualinkd/Aux_3/delay"
  json_attributes_template: "{{ {'delay': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:lightbulb

- unique_id: aqualink_pool_heater
  name: "Pool Heater"
  state_topic: "aqualinkd/Pool_Heater/enabled"
  command_topic: "aqualinkd/Pool_Heater/set"
  json_attributes_topic: "aqualinkd/Pool_Heater"
  json_attributes_template: "{{ {'active': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:fire

- unique_id: aqualink_spa_heater
  name: "Spa Heater"
  state_topic: "aqualinkd/Spa_Heater/enabled"
  command_topic: "aqualinkd/Spa_Heater/set"
  json_attributes_topic: "aqualinkd/Spa_Heater"
  json_attributes_template: "{{ {'active': value|int} | tojson }}"
  qos: 1
  payload_on: "1"
  payload_off: "0"
  retain: false
  icon: mdi:fire```
1 Like

Thanks very much. I had expected the current best practice would be to split the configuration.yaml file, but wasn’t sure quite how to do so. Appreciate your posting your files here.

One other, possibly naive, question: Do I need to explicitly install mosquitto on the aqualinkd RPi to get this working? I have the HA mqtt integration installed for communications with solar assistant, and that “just worked”. But I can’t seem to get it to recognize aqualinkd. Can’t see it on mqtt explorer either.

I’m using MQTT add on in Home Assistant.

I am new to MQTT and I’m still having problems getting the aqualinkd MQTT connected to Home Assistant. Not sure what I’m doing wrong as the MQTT addon is working for another IoT client on my same network using similar setup as described below. I’m setting the MQTT customize/active: to true and the customize/folder: to /share/mosquitto as described in the MQTT docs.

The mqtt stuff on my /etc/aqualinkd.conf file on the aqualinkd RPi is:

# mqtt stuff
mqtt_address = localhost:1883
mqtt_user = <un>
mqtt_passwd = <pw>
mqtt_aq_topic = aqualinkd

My /share/mosquitto/aqualinkd.conf file on HA is:

connection AqualinkD
remote_username <same <un> as mqtt_user above>
remote_password <same <pw> as mqtt_passwd above>
address <aqualinkd_ip>
topic # in
topic aqualinkd/# out 

Also per MQTT docs I have added an acl.conf file in this directory containing:

acl_file /share/mosquitto/accesscontrollist

and an accesscontrollist file containing:

user addons
topic readwrite #

user homeassistant
topic readwrite #

user <HA user name>
topic readwrite #

But the MQTT Broker log is repeating the following:

2023-08-12 15:48:04: Connecting bridge AqualinkD (<aqualinkd_ip>:1883)
2023-08-12 15:48:04: Client local.core-mosquitto.AqualinkD disconnected: Connection refused.
2023-08-12 15:48:35: Connecting bridge AqualinkD (<aqualinkd_ip>:1883)
2023-08-12 15:48:35: Client local.core-mosquitto.AqualinkD disconnected: Broken pipe.
2023-08-12 15:48:54: Connecting bridge AqualinkD (<aqualinkd_ip>:1883)
2023-08-12 15:48:54: Client local.core-mosquitto.AqualinkD disconnected: Connection refused.
2023-08-12 15:49:25: Connecting bridge AqualinkD (<aqualinkd_ip>:1883)
2023-08-12 15:49:25: Client local.core-mosquitto.AqualinkD disconnected: Connection refused.
2023-08-12 15:49:50: New connection from <HA_container_ip>:44936 on port 1883.
2023-08-12 15:49:50: Client <unknown> closed its connection.

Seems I’m not getting the authentication right. But not sure how to fix it. MQTT Explorer shows no aqualinkd messages when pointed at either the aqualinkd or HA broker. And the aqualinkd related entities on HA using @ktownsend-personal’s latest configuration.yaml files are showing unknown in the dashboard. What am I doing wrong here?