Table of Contents

Water meter monitoring

Hardware

Using a ESP32 with QMC5883L compass module. TBD

Accuracy

Quite accurate, each interval (max to min to max) from the water meter appears to be 32.5ml, this was confirmed over the course of a few days and about 1.6 m³ of water (about 49 thousand intervals).

ESPhome yaml

This is still a work in progress, and will need some customization for your specific installation (such as defining which magnetic axis to monitor and thresholds).

watermeter.yaml
esphome:
  name: WaterMeter
  friendly_name: water
  project:
    name: "photonicsguy.Water Meter Sensor"
    version: "0.1.2"
  on_boot:
    priority: 300
    then:
      - lambda: |-
          id(sample)=id(total_pulses);

esp32:
  board: esp32dev
  framework:
    type: arduino
    version: recommended
 
# Don't use VERBOSE or DEBUG logging when Hall sensor has fast updates
logger:
  level: WARN
 
# Enable Home Assistant API
api:
  encryption:
    key: !secret ha_key

ota:
  password: !secret ota_passwd

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

i2c:
  - id: bus_a
    sda: 4
    scl: 15
    frequency: 800kHz
    scan: true

text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address
      entity_category: "diagnostic"

switch:
  - platform: restart
    name: "Reboot"
  - platform: template
    name: "Reset usage"
    id: resetusage
    optimistic: false
    icon: mdi:water-sync
    restore_mode: ALWAYS_OFF
    turn_on_action:
      then:
        - lambda: |-
            id(sample)=id(total_pulses);
            id(wateruse).publish_state((id(total_pulses)-id(sample)) * 0.01 * id(offset));
 

number:
  - platform: template
    name: "Threshold Minimum"
    id: minVal
    optimistic: true
    restore_value: true
    entity_category: config
    initial_value: 70
    min_value: -200
    max_value: 200
    step: 5
    unit_of_measurement: "µT"
    set_action:
      - lambda: id(pulse).set_lower_threshold(x);
  - platform: template
    name: "Threshold Maximum"
    id: maxVal
    optimistic: true
    restore_value: true
    entity_category: config
    initial_value: 90
    min_value: -200
    max_value: 200
    unit_of_measurement: "µT"
    step: 5
    set_action:
      - lambda: id(pulse).set_upper_threshold(x);
  - platform: template
    name: "Threshold Minimum alt"
    id: minVal2
    optimistic: true
    restore_value: true
    entity_category: config
    initial_value: 40
    min_value: -200
    max_value: 200
    step: 5
    unit_of_measurement: "µT"
    set_action:
      - lambda: id(pulse2).set_lower_threshold(x);
  - platform: template
    name: "Threshold Maximum alt"
    id: maxVal2
    optimistic: true
    restore_value: true
    entity_category: config
    initial_value: 60
    min_value: -200
    max_value: 200
    unit_of_measurement: "µT"
    step: 5
    set_action:
      - lambda: id(pulse2).set_upper_threshold(x);
 

globals:
  - id: total_pulses
    type: int
    restore_value: True
    initial_value: '0'
  - id: sample
    type: int
    restore_value: False
    initial_value: '0'
  - id: offset
    type: float
    restore_value: False
    initial_value: '3.25'

sensor:
  - platform: wifi_signal
    name: "RSSI"
    id: rssi_db
    filters:
      - delta: 2
      - throttle: 300s
    entity_category: "diagnostic"
  - platform: qmc5883l
    i2c_id: bus_a
    address: 0x0D
    field_strength_x:
      name: "Field Strength X"
      id: fsx
      internal: true
    range: 200uT
    oversampling: 64x
    update_interval: 0.005s
  - platform: template
    name: "Water Consumption new"
    id: waterconsumption3
    device_class: water
    unit_of_measurement: "m³"
    state_class: "total_increasing"
    icon: "mdi:water"
    accuracy_decimals: 4
    lambda: |-
        return id(total_pulses) * 0.00001 * id(offset);
  - platform: template
    name: "Water usage"
    id: wateruse
    device_class: water
    unit_of_measurement: "L"
    state_class: "measurement"
    icon: "mdi:hand-water"
    accuracy_decimals: 2
    lambda: |-
        return (id(total_pulses)-id(sample)) * 0.01 * id(offset);
  - platform: template
    name: "Counter"
    unit_of_measurement: pulses
    id: watercounter
    lambda: |-
        return id(total_pulses);

binary_sensor:
  - platform: analog_threshold
    name: "Pulse" # Cover closed
    id: pulse
    #internal: True
    sensor_id: fsx
    threshold:
      upper: 90
      lower: 80
    on_state:
      then:
        - lambda: |-
            id(total_pulses) += 1;
            id(waterconsumption3).publish_state(id(total_pulses) * 0.00001 * id(offset));
            id(wateruse).publish_state((id(total_pulses)-id(sample)) * 0.01 * id(offset));
            id(watercounter).publish_state(id(total_pulses));
  - platform: analog_threshold
    name: "Pulse2"  #Cover open
    id: pulse2
    sensor_id: fsx
    threshold:
      upper: 60
      lower: 40
    on_state:
      then:
        - lambda: |-
            id(total_pulses) += 1;
            id(waterconsumption3).publish_state(id(total_pulses) * 0.00001 * id(offset));
            id(wateruse).publish_state((id(total_pulses)-id(sample)) * 0.01 * id(offset));
            id(watercounter).publish_state(id(total_pulses));