TekOnline

Solar Hot Water Diversion with Home Assistant and Shelly: A Debugging Story

We set up a Home Assistant automation to divert excess solar power to a hot water system using a Shelly Pro 1PM relay. The concept was simple: when the solar system is exporting more than a threshold, turn the hot water on. When there is not enough export, turn it off.

The automation ran every five minutes. It monitored net power. It checked the relay state. But for days, nothing happened. The hot water never turned on, even on sunny days with plenty of export.

This article covers what went wrong and how we fixed it.

The Hardware

  • Shelly Pro 1PM (switch.shellypro1pm_80f3dac87e8c): a DIN-rail relay with power monitoring, rated for up to 15A. The hot water element draws roughly 3.85 kW.
  • Solar inverter: monitored through the SEMS integration, providing sensor.net_power_in_out_power_2.
  • Home Assistant running on a QEMU VM (HAOS).

The Automation (First Attempt)

The automation was designed to check every five minutes whether solar export exceeded a configurable threshold, and turn the relay on or off accordingly.

- id: hot_water_solar_control
  alias: Solar hot water control
  triggers:
    - minutes: /5
      trigger: time_pattern
    - trigger: state
      entity_id:
        - sensor.net_power_in_out_power_2
        - input_number.hot_water_export_threshold
  conditions:
    - condition: state
      entity_id: sun.sun
      state: above_horizon
  actions:
    - variables:
        net_power: '{{ states("sensor.net_power_in_out_power_2") | float(0) }}'
        export_threshold: '{{ states("input_number.hot_water_export_threshold") | float(300) }}'
    - if: net_power < -export_threshold and not shelly_on
      then: turn on
    - if: net_power >= -export_threshold and shelly_on
      then: turn off

It looked correct. The sun was up. The solar was exporting 4.8 kW. The threshold was 300 W. But the relay stayed off.

Problem 1: The Sun Gate

The automation had a top-level condition:

conditions:
  - condition: state
    entity_id: sun.sun
    state: above_horizon

This is a gate. When the sun is down, the automation does not run at all. None of the rules evaluate. The relay keeps its last state until the next sunrise.

If the relay was turned on during the day — say, by an earlier version of the automation — it would stay on all night. There was no rule to turn it off at sunset.

The fix: remove the sun gate and add a dedicated sunset cleanup rule.

- if: not sun_up and shelly_on
  then: turn off

The sunset event triggers the automation, this rule catches it and turns the relay off.

Problem 2: The Unit Mismatch

This was the real head-scratcher.

The automation compared two values:

  • net_power from sensor.net_power_in_out_power_2
  • export_threshold from input_number.hot_water_export_threshold

The threshold was set to 3600 W. The solar was exporting 4.8 kW. The automation checked:

net_power < -export_threshold

Which evaluated as:

-4.8 < -3600

That is false. The sensor reports in kW. The threshold is in W. The automation was comparing kilowatts against watts.

The values never matched.

The fix: multiply the net power reading by 1000 so both values are in watts.

net_power: '{{ states("sensor.net_power_in_out_power_2") | float(0) * 1000 }}'

After this fix, the comparison became:

-4800 < -3600

True. The relay turned on immediately.

Problem 3: The Oscillation Risk

Once the hot water turns on, it draws about 3.85 kW. This drops the net export. If the original threshold was used as both the on and off threshold, a cycle would occur:

  1. Export > 3600 W → relay on
  2. Heater draws 3850 W → net export drops to ~950 W
  3. Export < 3600 W → relay off
  4. Export returns to 4800 W → relay on
  5. Repeat every five minutes

This is a classic hysteresis problem.

The fix: use two different thresholds with a wide hysteresis band.

  • Turn on when exporting more than 3600 W (net power < -3600 W)
  • Turn off only when importing from the grid (net power >= 0 W)

The 3600 W gap means the relay stays on as long as there is any export, even after the heater loads it down.

- if: sun_up and net_power < -export_threshold and not shelly_on
  then: turn on
- if: sun_up and net_power >= 0 and shelly_on
  then: turn off

After this change, the relay ran continuously for the rest of the day without cycling.

The Final Automation

The finished automation has three rules, evaluated in order, every five minutes:

RuleConditionAction
0Sun down + relay onTurn off
1Sun up + exporting > 3600 W + relay offTurn on
2Sun up + importing (net ≥ 0 W) + relay onTurn off
- id: hot_water_solar_control
  alias: Solar hot water control
  description: Divert excess solar to hot water.
  triggers:
    - minutes: /5
      trigger: time_pattern
    - trigger: state
      entity_id:
        - sensor.net_power_in_out_power_2
        - input_number.hot_water_export_threshold
    - event: sunrise
      trigger: sun
    - event: sunset
      trigger: sun
  actions:
    - variables:
        net_power: '{{ states("sensor.net_power_in_out_power_2") | float(0) * 1000 }}'
        export_threshold: '{{ states("input_number.hot_water_export_threshold") | float(300) }}'
        shelly_on: '{{ is_state("switch.shellypro1pm_80f3dac87e8c", "on") }}'
        sun_up: '{{ is_state("sun.sun", "above_horizon") }}'
    - if: not sun_up and shelly_on
      then: switch.turn_off
    - if: sun_up and net_power < -export_threshold and not shelly_on
      then: switch.turn_on
    - if: sun_up and net_power >= 0 and shelly_on
      then: switch.turn_off
  mode: single

Debugging Sensors

While troubleshooting, we added template sensors to expose each piece of the automation logic on a dashboard card. They made it much easier to see what the automation was evaluating in real time.

SensorPurpose
sensor.hot_water_debug_net_powerNet power in watts
sensor.hot_water_debug_export_thresholdCurrent threshold
sensor.hot_water_debug_exportingYES if exporting above threshold
sensor.hot_water_debug_sunSun status
sensor.hot_water_debug_verdictPlain-text explanation of the current state

What We Learned

  1. Check your units. kW and W look similar on paper but produce wildly different comparisons. The sensor may report in a different unit than you expect.
  2. Sun conditions should not be gates. If you need sun-dependent logic, build it into individual rules rather than blocking the entire automation. Otherwise there is nothing to turn things off at sunset.
  3. Hysteresis prevents toggling. Without a deadband between turn-on and turn-off thresholds, any automation that controls a significant load will oscillate.
  4. Debug sensors save time. Exposing the internal variables of an automation as template sensors on a dashboard makes it obvious when something is wrong.

Sources


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *