This article follows on from the previous article:
I wanted to replace a D1 Mini with a D1 MINI V4 new version.

The previous one was flashed with Esp Easy, the new one, I flashed it with ESP Home.
Here are the two tutorials:
Once the D1 MINI V4 flashed with Esp Home, I continue the configuration.
I take advantage of the change of D1Mini to replace the resistance of the divider bridge between A0 and GND, I put a 51K. The value of this resistance is set on the value of the thermoresistance at 25 ° C (to be measured manually before installation therefore).
For the record, here is the wiring diagram

The wiring of the D1 Mini is unchanged, so I reproduce the diagram with the D1 Mini V4.
Please refer to this page for further explanation:
I realize the configuration of the yaml file of the device ESP Home
Once the D1 mini flashed V4, I go to ESPHome Builder and find my equipment:

I click on EDIT and edit the yaml file, here is the configuration:
substitutions: name: esphome-web-e586c5 friendly_name: ESPHome Hot Water esphome: name: ${name} friendly_name: ${friendly_name} min_version: 2024.6.0 name_add_mac_suffix: false project: name: esphome.web version: dev esp8266: board: esp01_1m # Enable Logger Logging: # Enable the API Home Assistant api: # Allow Over-The-Air ota updates: - platform: esphome # Allow Wi-Fi configuration via the improv_serial serial port: wifi: # Set up a Wi-Fi AP: {} # In combination with the "ap" option, this allows the user to # configure Wi-Fi credentials via Wi-Fi AP. captive_portal: dashboard_import: _import _url package: github://esphome/example-configs/esphome-web/esp8266.yaml@main import_full _config: true # To have a "next URL" for improv_serial web_server: sensor: - platform: ntc sensor: temp_resistance name: "Temperature" unit _of _measurement: "°C" accuracy _decimals: 2 calibration: b_constant: 3950 reference_temperature: 25°C reference_resistance: 50kOhm - platform: resistance id: temp_resistance sensor: temp_voltage configuration: DOWNSTREAM resistor: 50kOhm reference_voltage: 3.3V - platform: adc pin: A0 id: temp_voltage # Turbine sensor for cold water - platform: pulse_meter pin: number: GPIO13 mode: input: true pullup: true name: 'Drinking Water Flow' unit _of _measurement: 'L/min' accuracy_decimals: 0 internal_filter: 100ms timeout: 5s filters: - multiply: 0.013 total: name: 'Total Drinking Water' unit _of _measurement: 'L' id: total_eau _potable accuracy_decimals: 2 filters: - multiply: 0.013 # Turbine sensor for hot water - platform: pulse_meter pin: number: GPIO14 mode: input: true pullup: true name: 'Hot Water Flow' unit _of _measurement: 'L/min' accuracy_decimals: 0 internal_filter: 100ms timeout: 5s filters: - multiply: 0.013 # 1/77 total: name: 'Total Conso Hot Water' unit _of _measurement: 'L' id: total_eau _chaude accuracy_decimals: 2 filters: - multiply: 0.013 # 1/77 # Calculated sensor for net cold water consumption - platform: template name: "Total Conso Cold Water" unit _of _measurement: "L" accuracy _decimals: 2 lambda: |- return id(total _eau _potable).state - id(total _eau _chaude).state;
In a few lines, here is the explanation:
Temperature sensor (NTC):
- Use an NTC thermistor
- Measurement in °C to 2 decimal places
- Cascading configuration: ADC (A0) → resistance → ntc
- Calibrated with a B constant of 3950 and a reference to 25°C/50kΩ
Two water flow sensors (pulse _meter):
- Drinking water on GPIO13, Hot water on GPIO14
- Setup in input mode with pullup enabled
- Conversion: multiplied by 0.013 (calibrated on 77 pulses/L)
- 5s timeout (return to 0 if no pulses)
- 100ms internal filter to stabilize playback
- Each has a total meter in litres
- Clarification: 0 decimal places for flow, 2 decimal places for total
A calculated sensor:
- Calculates cold water consumption
- Formula: Total drinking water – Total hot water
- Clarification: 2 decimal places
I select the sensors I keep

I will keep the last 4 sensors, deactivate the others and rename them by:
- sensor.temperature _eau _chaude
- sensor.esphome _total_eau _potable
- sensor.esphome_total_eau_chaude
- sensor.esphome_total_eau_froide


I add 3 offset sensors
When the D1 Mini V4 restarts (juice cutting for example), the count starts from zero.
So I organised the calculation of the total count via a sensor that gives me the sum of an offset and the value of the current count.
So I added 3 offset entities. For purists, only two are mandatory, the 3rd can be calculated.
So in Settings/Devices and Services/Inputs, I added an entry

Then Number


So this creates me input_number.eau _froide _offset.
I'm doing this for the other two offsets.

I chose to use ‘drinking water’ for everything that passes through the drinking water meter, i.e. total consumption: cold water + hot water.
I add 3 automations to manage the D1 Mini counter reset
The D1 Mini has a size defect, it loses the value of its meters in case of reset, so everything starts from scratch, so it does not suit me.
I noticed that at the time of the reset, the sensors switch to the ‘unavailable’ state before going back to zero.
I will therefore assign the previous state unavailable to an input _number which will serve as an offset, i.e. as a correction value for the overall count.
To make it simpler, I do 3 automations that do this:
alias: Offset Hot Water on Unavailable triggers: - entity_id: sensor.esphome _total_eau _chaude to: unavailable trigger: state actions: - target: entity_id: input_number.eau _chaude _offset data: value: >- {{ states('input_number.eau _chaude _offset') | float + trigger.from _state.state | float }} action: input_number.set_value mode: single alias: Offset Cold Water on Unavailable triggers: - entity_id: sensor.esphome _total_eau _froide to: unavailable trigger: state actions: - target: entity_id: input_number.eau_froide_offset data: value: >- {{ states('input _number.eau _froide _offset') | float + trigger.from _state.state | float }} action: input_number.set_value mode: single alias: Offset Drinking Water on Unavailable triggers: - entity_id: sensor.esphome _total_eau _potable to: unavailable trigger: state actions: - target: entity_id: input_number.eau _potable _offset data: value: >- {{ states('input_number.eau _potable _offset') | float + trigger.from _state.state | float }} action: input_number.set_value mode: single
For each type of water (hot, cold, drinking), when the sensor becomes unavailable:
- Automation captures the last known value before disconnection
- This value is added to an offset counter stored in an input_number
- This keeps the total cumulation even after a sensor restart

I declare the final consumption sensors
In my templates.yaml file, I add these three sensors:
- sensor: - name: Single _id Drinking Water Consumption: sensor.consumption _eau _potable unit _of _measurement: "L" device _class: water state_class: total_increasing state: > {% set measure_current = states('sensor.esphome_total_eau _potable') | float %} {% set total _offset = states('input_number.eau _potable _offset') | float %} {{ total _offset + measure _current if states('sensor.esphome _total_eau _potable')!= 'unavailable' else total _offset }} - name: Single _id "Cold Water Consumption": sensor.consumption _eau _froide unit _of _measurement: "L" device _class: water state_class: total_increasing state: > {% set measure_current = states('sensor.esphome_total_eau_froide') | float %} {% set total _offset = states('input_number.eau _froide _offset') | float %} {{ total _offset + measure _current if states('sensor.esphome _total_eau _froide')!= 'unavailable' else total _offset }} - name: Single _id "Hot Water Consumption": sensor.consumption _eau _chaude unit _of _measurement: "L" device _class: water state_class: total_increasing state: > {% set measure_current = states('sensor.esphome_total_eau _chaude') | float %} {% set total _offset = states('input_number.eau _chaude _offset') | float %} {{ total _offset + measure _current if states('sensor.esphome _total_eau _chaude')!= 'unavailable' else total _offset }}
These sensors are virtual sensors (templates) that allow water consumption to be tracked persistently. Let's analyse how they work:
Data sources:
- Physical sensors ESP Home (sensor.esphome_total_eau _*)
- Offset counters (input_number.eau _*_offset)
Types of measures:
- Drinking water: Total incoming water
- Hot water: Hot water consumption
- Cold water: Cold water consumption
Common characteristics:
- state_class: total_increasing: Indicates a meter that continuously increases
- _of _measurement unit: ‘L’: Measurement in litres
Operational logic:
{% set measure_current = states(‘sensor.esphome_total_eau _*’) | float %}
{% set total _offset = states(‘input_number.eau _*_offset’) | float %}
{{ total _offset + measure _current if states(…)!= ‘unavailable’ else total _offset }}
- If the ESP sensor is available: Total = Offset + Current measure
- If the ESP sensor is unavailable: Total = Offset only
I configure recorder
We must remember to keep what we need but also not to record what is not useful.
So in the Recorder configuration, I put in:
include: entity_globs: [...] - input_number.eau _* - sensor.consumption _eau _* exclude: entity_globs: [...] - sensor.esphome*
I'm testing the reset of the D1 Mini V4
So, in the end, I have
- 3 input_number which serve as offset,
- 3 sensors that give me the value of the current meters of the D1 Mini
- 3 calculated sensors that give me the value of permanent meters

At this stage, the D1 has never been relaunched, so the D1 counters and the permanent counters are identical.
I unplug and reconnect the D1 mini, the sensors will become unavailable for the reset time and then go back to 0.

At that time, the Offset take the last values of the counters. Then the D1 mini reconnects to Home assistant:

Permanent counters include the value of offset + value of the counter of D1 (0).
I run a little hot water and a little cold water, offsets do not move but the meters display the right values:

So I keep only the last 3 meters for my Dashboard and for conso statistics.
Result



If it had to be done again (ideas or feedback)
- I would use a turbine with a temperature sensor even for cold water, I am curious to know the temperature especially in winter.


