I wanted a reliable, local-only way to monitor water usage and water temperature for my water/irrigation. I currently have a Yo-Link shutoff/flow sensor, but it doesn’t report in real time and the temperature isn’t exposed to Home Assistant there is a feature request pending. But that’s not really what this post is about.
I also didn’t really find a clean or reliable approach that others were using for this, at least not one that stayed fully local and gave real-time data. Most solutions I came across were either overly complicated, cloud-dependent, or didn’t actually solve the real-time aspect for me with well water.
I looked at ultrasonic options as well, but I didn’t really trust the readings enough for something like this, especially long-term.
So I figured I’d put this together and share it, because it ended up being way simpler than I expected. Honestly, this ended up being one of those “why didn’t I do this sooner” setups.
I put together a really simple and easy ESPHome setup using a pulse-output water meter DAE MJ-100a and a DS18B20 Digital Temperature Sensor Thread Probe
The goal was:
- Real-time flow (not delayed like Yo-Link)
- Total usage tracking
- Water Temperature (used to calculate usable hot water minutes from my heat pump water heater based on incoming well temp)
- Fully local (no cloud dependency)
Real-Time Flow (big difference vs Yo-Link)
This is honestly the biggest improvement.
Using pulse_meter means ESPHome calculates flow based on pulses as they happen, instead of reporting averaged or delayed values like most sensors I’ve used. So instead of waiting for an update, you get:
- near instant response when water starts/stops
- visible ramp-up and ramp-down of flow
- short bursts actually show up instead of getting missed
It feels a lot more like watching a live sensor instead of something that reports every X seconds.
Hardware Used
- ESP32-S3 Dev Board (esp32-s3-devkitc-1 in ESPHome)
- DAE MJ-100a (pulse output, 0.1 gallon per pulse)
- DS18B20 Digital Temperature Sensor Thread Probe
- 4.7kΩ Resistor (for DS18B20 pull-up)
- Terminal block / screw connectors
Hardware / Wiring Notes
- The MJ-100a is just a dry contact pulse output, so it wires straight into a GPIO + GND
- I’m using GPIO5 with pullup + inverted in ESPHome on my ESP32
- The DS18B20 is on GPIO42, mainly because it’s physically close to 3V3/GND on my board for putting a 4.7k resistor between the GPIO and 3V3
ESPHome Side
Nothing too crazy in the config, but a couple things worth pointing out:
- The MJ-100a I’m using outputs 1 pulse per 0.1 gallon, so everything is multiplied by
0.1 - I exposed both:
- GPM (flow rate)
- Total gallons
- Raw pulse count (mainly for sanity checking / debugging)
Having the pulse count available actually helped a lot while testing as it made it easy to confirm I wasn’t missing anything.
Utility Meter (this made it way more useful)
Once I had total gallons working, I added a utility_meter in Home Assistant so I could break it down into something more useful:
- daily usage
- weekly usage
- monthly usage
Since this meter is installed after my main FloSmart, I’m using it specifically for irrigation. So now I effectively have:
- total house water (FloSmart)
- irrigation-only usage (this meter)
- and I can subtract the two if I want non-irrigation usage
That part ended up being more useful than I expected especially for understanding how much water irrigation is actually using over time.
What this actually gives you
From this setup I now have:
- Real-time flow rate (GPM)
- Total gallons used
- Raw pulse count (for verification/debugging)
- Water temperature at the pipe
- Daily / weekly / monthly irrigation usage (via
utility_meter)
Which is way more visibility than I had before, especially for irrigation.
esphome:
name: water
friendly_name: Water Meter
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
logger:
api:
encryption:
key: ""
ota:
- platform: esphome
password: "7b0a878b86e1562891756486028c7c43"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Water Fallback Hotspot"
password: !secret wifi_password
captive_portal:
one_wire:
- platform: gpio
pin: GPIO42
light:
- platform: esp32_rmt_led_strip
name: "Water RGB"
id: water_rgb
pin: GPIO48
num_leds: 1
chipset: ws2812
rgb_order: GRB
restore_mode: ALWAYS_OFF
sensor:
- platform: dallas_temp
name: "Water Temperature"
id: water_temperature
update_interval: 30s
- platform: pulse_meter
pin:
number: GPIO5
mode:
input: true
pullup: true
inverted: true
name: "Water Flow Rate GPM"
id: water_flow_gpm
unit_of_measurement: "gal/min"
device_class: water
state_class: measurement
accuracy_decimals: 2
timeout: 10s
internal_filter: 100ms
filters:
- multiply: 0.1
total:
name: "Water Total Gallons"
id: water_total_gallons
unit_of_measurement: "gal"
device_class: water
state_class: total_increasing
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: template
name: "Water Flow Rate PPM"
id: water_flow_ppm
unit_of_measurement: "pulses/min"
icon: mdi:counter
accuracy_decimals: 0
lambda: |-
if (isnan(id(water_flow_gpm).state)) return NAN;
return id(water_flow_gpm).state / 0.1;
update_interval: 10s
- platform: template
name: "Water Total Pulses"
id: water_total_pulses
unit_of_measurement: "pulses"
state_class: total_increasing
icon: mdi:counter
accuracy_decimals: 0
lambda: |-
if (isnan(id(water_total_gallons).state)) return NAN;
return id(water_total_gallons).state / 0.1;
update_interval: 30s
binary_sensor:
- platform: status
name: "Water ESP Status"


