Tracking water use per switch

Hello,

I’m looking for an approach to track water used per switch.

I have 27 switch entities that turn on sprinkler valves. I want to track total water usage for each switch. I also have a water meter sensor that tracks total water volume (in gallons) just for the irrigation system.

The switches (valves) are mutually exclusive (only one can be on at a time) and the water meter only tracks the water used by the entire irrigation system (run by the 27 switches).

I can think of two approaches.

  1. Record/save the current water meter reading when a given switch turns on (e.g. start total_gallons) and then when the switch turns off store the difference.
  2. Update a switch’s total gallons when the water meter value changes. That requires looking up which switch is currently running (or last run) and update its total. Seems more error prone, for example if the update happens after a switch is turned off.

It would be nice to avoid creating 54 (or 81) helpers and instead store the last start/end gallons and total running gallons in attributes on each of the 27 switch entities, but I don’t see a way to create/update attributes. (There was this abandoned script that was suppose to provide that feature.)

So, it seems I need 27 input_number helpers for the start value, and another 27 to track the total gallons.

Is there another approach I’m not considering or know about?

By the way, before I could measure the actual water meter reading I used an automation to guess gallons used for each switch based on the time it was running. That automation is below.

Sure would be handy to be able to trigger on a label instead of listing all 27 switches.

Automation based on time and gallons/minute
alias: "Event: Trace sprinkler water usage"
description: >-
  When a sprinkler valve moves from state on to anything else record total
  gallons used by multiplying the time the switch was on by the gallons/min
  value stored in the switch's attribute as defined in customize.yaml.

  Yes, there's a bit of a race condition here due to the read-then-update
trigger:
  - platform: state
    entity_id:
      - switch.sprinkler_valve_01
      - switch.sprinkler_valve_02
      - switch.sprinkler_valve_03
      - switch.sprinkler_valve_04
      - switch.sprinkler_valve_05
      - switch.sprinkler_valve_06
      - switch.sprinkler_valve_07
      - switch.sprinkler_valve_08
      - switch.sprinkler_valve_09
      - switch.sprinkler_valve_10
      - switch.sprinkler_valve_11
      - switch.sprinkler_valve_12
      - switch.sprinkler_valve_13
      - switch.sprinkler_valve_14
      - switch.sprinkler_valve_15
      - switch.sprinkler_valve_16
      - switch.sprinkler_valve_17
      - switch.sprinkler_valve_18
      - switch.sprinkler_valve_19
      - switch.sprinkler_valve_20
      - switch.sprinkler_valve_21
      - switch.sprinkler_valve_22
      - switch.sprinkler_valve_23
      - switch.sprinkler_valve_24
      - switch.sprinkler_valve_25
      - switch.sprinkler_valve_26
      - switch.sprinkler_valve_27
    from: "on"
condition: []
action:
  - alias: Get entity to update and calulate new total
    variables:
      seconds_on: "{{ (now() - trigger.from_state.last_changed).total_seconds() }}"
      gallons_per_sec: "{{ state_attr(trigger.entity_id , 'gallons_per_minute')|float / 60 }}"
      gallons_entity: >-
        {{ trigger.entity_id.replace( 'switch.sprinkler_valve_',
        'input_number.sprinkler_gallons_' ) }}
      cur_gallons: "{{ states(gallons_entity)|float }}"
      new_total: "{{ ((gallons_per_sec * seconds_on) + cur_gallons)|round(1) }}"
  - alias: Update the total gallons entity
    target:
      entity_id: "{{ gallons_entity }}"
    data:
      value: "{{ new_total }}"
    action: input_number.set_value
  - alias: Finally, write a log entry of water used.
    metadata: {}
    data:
      message: >
        Entity: '{{ trigger.entity_id }}' States: '{{ trigger.from_state.state
        }}' => '{{ trigger.to_state.state }}' Changed: {{ (now() -
        trigger.from_state.last_changed).total_seconds() }} {{
        trigger.from_state.last_changed }} > {{ trigger.to_state.last_changed }}
        *gps='{{ gallons_per_sec }}' entity='{{ gallons_entity }}' cur-new='{{
        cur_gallons }}'>'{{ new_total }}'
    target:
      entity_id: notify.sprinkler_log
    action: notify.send_message
mode: parallel

Thanks

The utility meter has a tariff feature. It will create a utility meter per tariff. You can use it to separate out sensors for each of the switches by naming the tariffs after the switches. You’ll need very few extra sensors for it.

I think if you leave out the reset chcle, it will just keep the total for each.

So create a tariff around the water meter and set the tariff on the active switch. I do the same for gas going to heating, shower or other.

Thanks. I had used the utility meter helper previously but didn’t consider using the tariff feature like that.

I also have a hose connected to the irrigation system, which isn’t controlled by HA (but still measured). So, I created a “hose” tariff and when a valve turns off I reset the tariff to “hose” – i.e. I’m assuming that water used when all valves are off should be counted as “hose” usage.

But, since my water meter sensor has an update of every 15 seconds I delay my automation for 15 seconds when a valve is turned off.

automation example (suggestions welcome...)
alias: "Utility: Set tariff valve for irrigation"
description: >-
  This automation works in conjunction with the utility meter helper
  integration. See: https://www.home-assistant.io/integrations/utility_meter/

  This sets the current "tariff" when a switch/valve is turned on to track water
  by valve, and then resets it to "hose" when a switch turns off.

  This depends on the events coming in in the right order. e.g. valve_05 turns
  on and sets the tariff to "valve_05", then it turns off setting back to
  default "hose", then the next valve turns on and sets the new tariff to its
  valve number.

  Therefore, the script mode is set to "queued"

  Not quite sure if there's any behavior needed for state Unavailable. For now,
  just ignore, but still logs the event.

  There's a delay of 15s when a valve is turned off to catch the last update
  from the meter sensor (also updates ever 15s). (Note: the valves are
  configured to have a 30s delay between them)
trigger:
  - platform: state
    entity_id:
      - switch.sprinkler_valve_01
      - switch.sprinkler_valve_02
      - switch.sprinkler_valve_03
      - switch.sprinkler_valve_04
      - switch.sprinkler_valve_05
      - switch.sprinkler_valve_06
      - switch.sprinkler_valve_07
      - switch.sprinkler_valve_08
      - switch.sprinkler_valve_09
      - switch.sprinkler_valve_10
      - switch.sprinkler_valve_11
      - switch.sprinkler_valve_12
      - switch.sprinkler_valve_13
      - switch.sprinkler_valve_14
      - switch.sprinkler_valve_15
      - switch.sprinkler_valve_16
      - switch.sprinkler_valve_17
      - switch.sprinkler_valve_18
      - switch.sprinkler_valve_19
      - switch.sprinkler_valve_20
      - switch.sprinkler_valve_21
      - switch.sprinkler_valve_22
      - switch.sprinkler_valve_23
      - switch.sprinkler_valve_24
      - switch.sprinkler_valve_25
      - switch.sprinkler_valve_26
      - switch.sprinkler_valve_27
action:
  - alias: Determine which tariff to set
    variables:
      new_tariff: |-
        {% if is_state( trigger.entity_id, 'off' ) %}
          hose
        {% elif is_state( trigger.entity_id, 'on' ) %}
          {{ trigger.entity_id.replace( 'switch.sprinkler_', '' ) }}
        {% else %}
          none
        {% endif %}
  - if:
      - condition: template
        value_template: "{{ is_state( trigger.entity_id, 'off' ) }}"
    then:
      - delay:
          hours: 0
          minutes: 0
          seconds: 15
          milliseconds: 0
  - alias: Set the tariff on the meters.
    if:
      - condition: template
        value_template: "{{ is_state( trigger.entity_id, ['on','off'] ) }}"
    then:
      - action: select.select_option
        target:
          entity_id:
            - select.monthly_irrigation
            - select.daily_irrigation
        data:
          option: "{{ new_tariff }}"
  - alias: Finally, write a log entry of water used.
    data:
      message: >
        {{ trigger.entity_id }}: '{{ trigger.from_state.state }}' => '{{
        trigger.to_state.state }}' Tariff='{{ new_tariff }}'
    target:
      entity_id: notify.sprinkler_log
    action: notify.send_message
mode: queued
1 Like