# Analytics snippet placeholder — HearthNodes (gatherer.top) # Paste the measurement snippet here (server.js inlines any content that contains # a markup tag into on every page). No GA4 property is assigned to this # site yet, so leave this file as comments only and the build emits no tracking # code.
HearthNodesgatherer.top live 11/28 EN 中文

Divert solar surplus to the hot water cylinder instead of exporting at 4 ¢

The immersion heater steps up in 400 W increments to absorb PV surplus, holding grid import between -50 W and +150 W for four hours. In a 4.2 kWp array this captured 6.8 kWh on a clear April day instead of exporting it at the feed-in rate.

protocol · matter latency · 60000 ms power · mains published · 2026-09-16 3 devices
Automation YAML energy-solar-surplus-water-heating.yaml
# HearthNodes recipe 16 - solar surplus to hot water
alias: "Energy - divert PV surplus to the cylinder"
id: hn_energy_solar_surplus_water
description: "Hold grid between -50 W and +150 W by modulating the immersion"
mode: single
max_exceeded: silent

trigger:
  - platform: numeric_state
    entity_id: sensor.grid_power
    below: -250
    for: "00:01:00"

variables:
  band_low_w: -50
  band_high_w: 150
  step_w: 400
  pwm_period_s: 10
  cylinder_c: "{{ states('sensor.cylinder_temperature') | float(0) }}"
  current_w: "{{ states('sensor.immersion_power') | float(0) }}"
  error_w: "{{ states('sensor.grid_power') | float(0) }}"
  next_w: "{{ [[current_w + (band_high_w - error_w), 0] | max, 3000] | min }}"
  duty: "{{ (next_w / 3000 * 100) | round(0) }}"

condition:
  - condition: numeric_state
    entity_id: sensor.cylinder_temperature
    below: 62
  - condition: numeric_state
    entity_id: sensor.battery_soc
    above: 55
  - condition: state
    entity_id: binary_sensor.high_power_appliance_active
    state: "off"
  - condition: template
    value_template: "{{ cylinder_c | float(0) < 62 }}"

action:
  - service: number.set_value
    target:
      entity_id: number.immersion_setpoint
    data:
      value: 62
  - service: script.pwm_switch
    data:
      switch: switch.immersion_element
      duty_pct: "{{ duty }}"
      period_s: "{{ pwm_period_s }}"
      step_w: "{{ step_w }}"
  - service: sensor.record
    data:
      entity_id: sensor.exported_energy_today
      note: "compare diversion against the feed-in rate"

# Grid meter loss -> irradiance-based forecast model, so the cylinder is never
# heated from grid import by accident. Cylinder probe failure stops diversion
# entirely: heating water to an unknown temperature is worse than exporting.
Trigger · numeric_state

sensor.grid_power

Grid power stays below -250 W (exporting) for 60 seconds.

Divert solar surplus to the hot water cylinder instead of exporting at 4 ¢ topology

Step-by-step

  1. switch.immersion_elementModulate the immersion in 400 W steps using a 10-second PWM period; longer periods make the meter read a bouncing value and the regulator hunts. PWM · 400 W steps · 10 s period
  2. number.immersion_setpointCap the target at 62 °C for the diverter and leave the cylinder's own mechanical thermostat at 70 °C as the independent safety limit. set 62 °C · hard limit 70 °C
  3. sensor.exported_energy_todayLog exported versus diverted energy daily so the diversion strategy can be judged against the actual feed-in tariff. log · compare against diversion
  4. input_boolean.cylinder_boostA wall button gives a 60-minute manual boost that ignores the surplus logic, for the evening a fourth guest arrives. manual 60 min boost overrides everything
Step-by-step
Hold the grid at a small import, never at zero

Targeting exactly 0 W makes the system oscillate between importing and exporting several times a minute. Holding between -50 W and +150 W means the meter sees a tiny import most of the time, which is stable and costs almost nothing. This is the single most important tuning value.

Let the battery win below 55 % state of charge

Diverting surplus to hot water while the battery is at 30 % means you buy grid power that evening. The 55 % threshold reflects a household that uses 4 kWh overnight; recalculate it from your own overnight consumption rather than copying the number.

Cap the diverter below the cylinder's own thermostat

62 °C target with a 70 °C mechanical limit gives two independent layers. A diverter that trusts its own sensor with no mechanical backup is one failed probe away from boiling the cylinder. Check the mechanical thermostat annually by hand.

Hardware

ModelProtocolLinkRole in this chain
Matter smart relay with power metering (e.g. Shelly or equivalent) matter wired Switches and measures the immersion load; the measurement is what closes the control loop.
Bidirectional grid meter (CT clamp or the inverter's own meter) matter wired Provides the signed grid power value; a one-directional meter cannot tell export from import.
Cylinder temperature probe on a 1 m immersion pocket zigbee wireless Strapped to the cylinder's pocket, not the pipe, so it reads stored water rather than flow temperature.

When it fails

If the grid meter drops out, the diverter falls back to a solar-forecast model that estimates surplus from irradiance, which is less accurate but keeps the cylinder from being heated from the grid. If the cylinder probe fails, diversion stops entirely and a notification says so — heating water to an unknown temperature is worse than exporting cheaply.

Recipes on the same trigger source

Terms Informational only. These recipes control mains electricity, water and physical access. You are responsible for local electrical and plumbing codes, for fusing and isolation, and for testing every fail-safe yourself. Nothing here is professional engineering, legal or safety advice.