Skip to content

Home Assistant Setup Instructions

Tyler Adam edited this page Jun 9, 2024 · 5 revisions

Home Assistant Setup Instructions

These instructions are provided courtesy of @sonapsent

Summary

The following shows how nodejs-poolController can be integrated into Home Assistant. This allows everything smart home related to be controlled from a central platform and provide additional local automation for a swimming pool. After setup it is possiable to create notifcations, view historical data/graphs, or even connect to voice services like Google or Alexa within Home Assistant.

Disclaimer: Myself and others involved in nodejs-poolController are not responsiable for damage to equipment, thermonuclear war, or other harm to personnel. Controlling industrial equipment is serious so please monitor the system and thoroughly test your setup.

Example Equipment

  • Controller: [e.g. Nixie Single Body (nodejs-PC v7.2)]
  • 30,000 Gallon In-Ground Pool
  • Pump manufacturer and model: [e.g. V-Green 165 VS Motor / Stealth Pump, relay control]
  • Filter: [Pressure Gauge]
  • Temperature Sensors: [ 10k thermistors for water and air]
  • Chemical controller: [e.g. REMChem, Atlas EZO ORP and PH]
  • Heater(s): [e.g. Heatpump, relay control]
  • Chlorinator: [e.g. Aquarite 900, RS485]

Requirements / Recommendations

The instructions below assume that both nodejs-PoolController and Home Assistant are setup and running independently. Please ensure that pool devices are working within the web based dashPanel before continuing.

nodejs-poolController:

  • Enable MQTT on dashPanel
  • Optional: Enable MQTT on REM (for pressure sensor)

Home Assistant Add-Ons:

  • MQTT Broker
  • NodeRed
  • Visual Studio Code (or other various text editors)
  • Optional: HACS

Home Assistant - configuration.yaml

The following sections can be added to the appropriate sections in your HA configuration.yaml file depending on what equipment you have. Pay attention to the rootTopic text for your setup as the default will be different depending on controller setup. In the example below this has been simplified to "pool" for state_topic. The use of an application like MQTT Explorer is valuable for troubleshooting and monitoring messages in real time.

After the configuration file is saved it is nessary to restart HA for new entities to be created or changes to take place.

configuration>server controls>check configuration>restart server

input_number:
  pool_tempurature_set_point:
    name: Pool Tempurature Set Point
    min: 70
    max: 90
    step: 1
    icon: mdi:target
    unit_of_measurement: "°F"
  pool_chlorine_set_point:
    name: Pool Chlorine Set Point
    min: 0
    max: 100
    step: 10
    icon: mdi:target
    unit_of_measurement: "%"
sensor:

#Pool Controller Sensors
  - platform: mqtt
    name: Pool Water Temp
    state_topic: "pool/state/temps/bodies/1/pool/temp"
    unit_of_measurement: '°F'
    value_template: "{{ value_json.temp | round(1) }}"
    expire_after: 6000

  - platform: mqtt
    name: Pool Air Temp
    state_topic: "pool/state/temps/air"
    unit_of_measurement: '°F'
    value_template: "{{ value_json.temp | round(1) }}"
    expire_after: 6000

  - platform: mqtt
    name: Pool Pump Flow
    icon: mdi:pump
    state_topic: "pool/state/pumps/50/v-green165/flow"
    value_template: "{{ value_json.flow }}"

  - platform: mqtt
    name: Pool Heater Status
    icon: mdi:fire
    state_topic: "pool/state/temps/bodies/1/pool/heatStatus"
    value_template: "{{ value_json.heatStatus.desc }}"

  - platform: mqtt
    name: Pool Chlorinator Status
    icon: mdi:check-circle-outline
    state_topic: "pool/state/chlorinators/1/aquarite/status"
    value_template: "{{ value_json.status.desc }}"

  - platform: mqtt
    name: Pool Chlorinator Target Output
    icon: mdi:chemical-weapon
    state_topic: "pool/state/chlorinators/1/aquarite/targetOutput"
    value_template: "{{ value_json.targetOutput }}"
    unit_of_measurement: '%'

  - platform: mqtt
    name: Pool Salt Level
    icon: mdi:shaker-outline
    state_topic: "pool/state/chlorinators/1/aquarite/saltLevel"
    value_template: "{{ value_json.saltLevel }}"
    unit_of_measurement: ppm

  - platform: mqtt
    name: Pool PH Level
    icon: mdi:beaker-question
    state_topic: "pool/state/chemControllers/50/remchem/pHLevel"
    value_template: "{{ value_json.pHLevel | round(2) }}"
    unit_of_measurement: ' '
    expire_after: 6000

  - platform: mqtt
    name: Pool ORP Level
    icon: mdi:beaker-question-outline
    state_topic: "pool/state/chemControllers/50/remchem/orpLevel"
    value_template: "{{ value_json.orpLevel | round(0) }}"
    unit_of_measurement: ' '
    expire_after: 6000

  - platform: mqtt
    name: Pool Saturation Index
    icon: mdi:water-check-outline
    state_topic: "pool/state/chemControllers/50/remchem/saturationIndex"
    value_template: "{{ value_json.saturationIndex | round(2)}}"
    unit_of_measurement: ' '

  - platform: mqtt
    name: Pool Flow Alarm
    icon: mdi:pipe
    state_topic: "pool/state/chemControllers/50/remchem/alarms/flow"
    value_template: "{{ value_json.alarms.flow }}"

  - platform: mqtt
    name: Pool Filter Pressure
    icon: mdi:air-filter
    state_topic: "pool/state/pressures/filter"
    value_template: "{{ value_json | round(1) }}"
    unit_of_measurement: psi
    expire_after: 6000

switch:

#Pool Controller Sensors
  - platform: mqtt
    name: Pool
    icon: mdi:pool
    state_topic: "pool/state/circuits/6/pool"
    value_template: "{{ value_json.isOn }}"
    command_topic: "pool/state/circuits/toggleState"
    payload_on: '{"id": 6}'
    payload_off: '{"id": 6}'
    state_on: "on"
    state_off: "off"
    optimistic: false
    retain: false
    qos: 0

  - platform: mqtt
    name: Pool Pump Speed 1
    icon: mdi:pump
    state_topic: "pool/state/circuits/2/pumpsp1"
    value_template: "{{ value_json.isOn }}"
    command_topic: "pool/state/circuits/toggleState"
    payload_on: '{"id": 2}'
    payload_off: '{"id": 2}'
    state_on: "on"
    state_off: "off"
    optimistic: false
    retain: false
    qos: 0

  - platform: mqtt
    name: Pool Pump Speed 2
    icon: mdi:pump
    state_topic: "pool/state/circuits/3/pumpsp2"
    value_template: "{{ value_json.isOn }}"
    command_topic: "pool/state/circuits/toggleState"
    payload_on: '{"id": 3}'
    payload_off: '{"id": 3}'
    state_on: "on"
    state_off: "off"
    optimistic: false
    retain: false
    qos: 0

  - platform: mqtt
    name: Pool Pump Speed 3
    icon: mdi:pump
    state_topic: "pool/state/circuits/4/pumpsp3"
    value_template: "{{ value_json.isOn }}"
    command_topic: "pool/state/circuits/toggleState"
    payload_on: '{"id": 4}'
    payload_off: '{"id": 4}'
    state_on: "on"
    state_off: "off"
    optimistic: false
    retain: false
    qos: 0

  - platform: mqtt
    name: Pool Pump Speed 4
    icon: mdi:pump
    state_topic: "pool/state/circuits/5/pumpsp4"
    value_template: "{{ value_json.isOn }}"
    command_topic: "pool/state/circuits/toggleState"
    payload_on: '{"id": 5}'
    payload_off: '{"id": 5}'
    state_on: "on"
    state_off: "off"
    optimistic: false
    retain: false
    qos: 0

  - platform: mqtt
    name: Pool Heater Mode
    icon: mdi:fire
    state_topic: "pool/state/temps/bodies/1/pool/heatMode"
    value_template: "{{ value_json.heatMode.desc }}"
    state_on: "Heater"
    state_off: "Off"
    command_topic: "pool/state/body/heatMode"
    payload_on: '{"id":1,"mode":"heater"}'
    payload_off: '{"id":1,"mode":"off"}'
    optimistic: false
    retain: false
    qos: 0
climate:
  - platform: mqtt
    name: "Pool Heater 2"
    min_temp: 40
    max_temp: 104
    modes:
      - "off"
      - "heat"
    current_temperature_topic: "pool/state/temps/bodies/1/pool/temp"
    value_template: "{{ value_json.temp }}"
    mode_state_topic: "pool/state/temps/bodies/1/pool/heatMode"
    mode_state_template: >-
      {% if value_json.heatMode.val == 0 %}
        off
      {% elif value_json.heatMode.val == 1 %}
        off
      {% elif value_json.heatMode.val == 2 %}
        heat
      {% endif %}
    mode_command_topic: "pool/state/body/heatMode"
    mode_command_template: >-
      {% set values = { 'heat':'{"heatMode":2, "id":1}', 'off':'{"heatMode":1, "id":1}'} %}    
      {{ values[value] if value in values.keys() else '{"heatMode":1, "id":1}' }}
    action_topic: "pool/state/temps/bodies/1/pool/heatStatus"
    action_template: >-
      {% if value_json.heatStatus.val == 0 %}
        off
      {% elif value_json.heatStatus.val == 1 %}
        heating
      {% endif %}
    temperature_command_topic: pool/setpoint #REQUIRES AN AUTOMATION - SEE BELOW
    temperature_state_topic: "pool/state/temps/bodies/1/pool/setPoint"
    temperature_state_template: "{{ value_json.setPoint }}"
Automation in HA Config for syncing setpoint temperature:
- alias: "pool setpoint converter"
  trigger:
    platform: mqtt
    topic: pool/setpoint
  action:
    service: mqtt.publish
    data_template:
      topic: pool/state/body/setPoint # <-- from https://github.com/tagyoureit/nodejs-poolController/wiki/Bindings-Integrations-in-2.0#mqtt
      payload: '{"id":1,"setPoint":{{trigger.payload | int}}}' # <-- id sets body/circuit

Automation Example 1 - Node-Red

You will need the following or similar automations to keep the temperature and/or chlorinator set points synchronized between nodejs-poolController and Home Assistant. You will need to remap references to your specific HA and MQTT servers in node-red after importing the code below.

Temperature Set Point Alt text

[{"id":"4319aa0f.2cbff4","type":"server-state-changed","z":"f34355e5.bd58d8","name":"Tempurature Set Point","server":"38c4ea68.0b9556","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_number.pool_tempurature_set_point","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":180,"y":2160,"wires":[["57f184f1.f9c97c"]]},{"id":"57f184f1.f9c97c","type":"function","z":"f34355e5.bd58d8","name":"Data","func":"msg.payload = {\"id\": 1, \"setPoint\": msg.payload}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":2160,"wires":[["9dfef7f5.e73638"]]},{"id":"9dfef7f5.e73638","type":"mqtt out","z":"f34355e5.bd58d8","name":"","topic":"pool/state/body/setPoint","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"f5279b01.b328d8","x":570,"y":2160,"wires":[]},{"id":"846063cc.a4605","type":"mqtt in","z":"f34355e5.bd58d8","name":"","topic":"pool/state/temps/bodies/1/pool/setPoint","qos":"0","datatype":"auto","broker":"f5279b01.b328d8","nl":false,"rap":true,"rh":0,"x":230,"y":2220,"wires":[["3741aa31.3d9186"]]},{"id":"ab1bb7f0.961018","type":"api-call-service","z":"f34355e5.bd58d8","name":"Set Tempurature Target","server":"38c4ea68.0b9556","version":3,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.pool_tempurature_set_point","data":"{\"value\":\"{{payload}}\"}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":890,"y":2220,"wires":[[]]},{"id":"3741aa31.3d9186","type":"json","z":"f34355e5.bd58d8","name":"","property":"payload","action":"","pretty":false,"x":510,"y":2220,"wires":[["ed09e2e2.ba582"]]},{"id":"ed09e2e2.ba582","type":"split","z":"f34355e5.bd58d8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":670,"y":2220,"wires":[["ab1bb7f0.961018"]]},{"id":"38c4ea68.0b9556","type":"server","name":"Home Assistant","version":1,"legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"f5279b01.b328d8","type":"mqtt-broker","name":"HASSIO - MQTT","broker":"localhost","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]

Chlorinator Set Point Alt text

[{"id":"1c3c85e8.30330a","type":"server-state-changed","z":"f34355e5.bd58d8","name":"Chlorine Set Point","server":"38c4ea68.0b9556","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_number.pool_chlorine_set_point","entityidfiltertype":"exact","outputinitially":false,"state_type":"num","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":170,"y":2280,"wires":[["670c0c22.2c1e94"]]},{"id":"670c0c22.2c1e94","type":"function","z":"f34355e5.bd58d8","name":"Data","func":"msg.payload = {\"id\": 1, \"poolSetpoint\": msg.payload}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":2280,"wires":[["3e2b068a.f93a8a"]]},{"id":"3e2b068a.f93a8a","type":"mqtt out","z":"f34355e5.bd58d8","name":"","topic":"pool/state/chlorinator","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"f5279b01.b328d8","x":560,"y":2280,"wires":[]},{"id":"49f2a840.fdddb8","type":"mqtt in","z":"f34355e5.bd58d8","name":"","topic":"pool/state/chlorinators/1/aquarite/poolSetpoint","qos":"0","datatype":"auto","broker":"f5279b01.b328d8","nl":false,"rap":true,"rh":0,"x":250,"y":2340,"wires":[["dfa3118d.6dceb"]]},{"id":"dda4d920.bb2138","type":"api-call-service","z":"f34355e5.bd58d8","name":"Set Chlorine Target","server":"38c4ea68.0b9556","version":3,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.pool_chlorine_set_point","data":"{\"value\":\"{{payload}}\"}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":870,"y":2340,"wires":[[]]},{"id":"dfa3118d.6dceb","type":"json","z":"f34355e5.bd58d8","name":"","property":"payload","action":"","pretty":false,"x":510,"y":2340,"wires":[["f0cde6ae.d4e118"]]},{"id":"f0cde6ae.d4e118","type":"split","z":"f34355e5.bd58d8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":670,"y":2340,"wires":[["dda4d920.bb2138"]]},{"id":"38c4ea68.0b9556","type":"server","name":"Home Assistant","version":1,"legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"f5279b01.b328d8","type":"mqtt-broker","name":"HASSIO - MQTT","broker":"localhost","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]

At this point you can make any automation you can dream up. If you need any ideas to start with, here is a list of some of mine.

  • Logic to toggle pool "on" state and select pump speeds while keeping them sync'd with nodejs-poolController
  • Schedule for the pool pump
  • Avoid using electricity during peak hours
  • Turn the heat pump on only for days that pool is likely to be used
  • Notification if communication drops from the nodejs-poolController (ex: monitor the pressure value for "unavailable", as the MQTT value is setup to expire after 10 minutes)

Automation Example 2 - Home Assistant YAML

Of course another way of doing automations in HA is to simply using yaml. The following two automations should keep the chorinator set point synchronized in both directions.

Credits to @truvec

automation:
  - id: '808456'
    alias: "Set SWG slider"
    trigger:
      platform: mqtt
      topic: "pool/state/chlorinators/1/aquarite/poolSetpoint"
    action:
      service: input_number.set_value
      target:
        entity_id: input_number.pool_chlorine_set_point
      data:
        value: "{{ trigger.payload_json.poolSetpoint }}"

  - id: '808457'
    alias: "SWG slider moved"
    trigger:
      platform: state
      entity_id: input_number.pool_chlorine_set_point
    action:
      service: mqtt.publish
      data:
        topic: "pool/state/chlorinator"
        retain: true
        payload: "{\"id\":1,\"poolSetpoint\":{{ states('input_number.pool_chlorine_set_point')|round(0)}}}"

Lovelace Dashboard

Here is a card that I use to display relevant pool information. This will need to be edited for your use case as it contains sensors not created or explained in this writeup. There are also some custom lovelace plugins that I use below that can be installed manually or by using HACS.

  • custom:mini-graph-card
  • custom:multiple-entity-row

Alt text

type: vertical-stack
cards:
  - type: custom:mini-graph-card
    entities:
      - entity: sensor.pool_water_temp
        name: Pool
      - entity: sensor.pool_air_temp
        name: Enclosure
      - aggregate_func: min
        color: gray
        entity: binary_sensor.night
        show_legend: false
        show_line: false
        show_points: false
        y_axis: secondary
    hours_to_show: 24
    points_per_hour: 2
    name: Pool Tempuratures
    show:
      labels: true
    decimals: 1
    state_map:
      - label: Night
        value: 'off'
      - label: Day
        value: 'on'
        type: ''
  - type: entities
    title: Pool Control
    show_header_toggle: false
    entities:
      - entity: switch.pool
      - entity: input_select.pool_pump_speed
      - type: custom:multiple-entity-row
        entity: sensor.pool_filter_pressure
        name: Pump/Filter
        secondary_info: last-changed
        show_state: false
        entities:
          - entity: sensor.pool_filter_pressure
            name: Pressure
          - entity: sensor.iotawatt_input_9
            name: Power
      - entity: input_number.pool_tempurature_set_point
      - type: custom:multiple-entity-row
        entity: sensor.pool_heater_status
        name: Pool Heater
        secondary_info: last-changed
        show_state: false
        entities:
          - entity: sensor.pool_heater_status
            name: Status
          - entity: sensor.iotawatt_input_8
            name: Power
          - entity: switch.pool_heater_mode
            name: Enable
            toggle: true
      - entity: input_number.pool_chlorine_set_point
      - type: custom:multiple-entity-row
        entity: sensor.pool_salt_level
        name: Chlorine
        secondary_info: last-changed
        show_state: false
        entities:
          - entity: sensor.pool_salt_level
            name: Salt
          - entity: sensor.pool_chlorinator_target_output
            name: Duty
          - entity: input_boolean.make_chlorine
            name: Enable
            toggle: true
      - type: custom:multiple-entity-row
        entity: sensor.pool_orp_level
        name: Chemical Levels
        secondary_info: last-changed
        show_state: false
        entities:
          - entity: sensor.pool_orp_level
            name: ORP
          - entity: sensor.pool_ph_level
            name: PH
          - entity: sensor.pool_saturation_index
            name: Sat-Index