When Your Shelly Overheats: Temperature-Driven Control for Hot Water

This is a follow-up to our earlier article on Solar Hot Water Diversion with Home Assistant and Shelly: A Debugging Story.

The Setup

We published our solar hot water diversion automation in June. The idea was straightforward: when the solar inverter is exporting more than 3.6 kW, turn on the hot water element. When the export drops, turn it off. We debugged three bugs — a sun gate, a kW-versus-W unit mismatch, and a missing hysteresis deadband — and got it working.

Then we switched to a simpler timer approach over winter. Just run the hot water from 08:45 to 14:00. Turn on, wait five hours, turn off. What could go wrong?

What Went Wrong

The hot water ran for about 48 minutes, then stopped. Not five hours. Forty-eight minutes.

We pulled the Shelly’s live status. The relay was off. The shutdown source wasn’t Home Assistant — it was the Shelly itself, reporting "source":"overtemp". The internal temperature sensor had hit roughly 90°C, and the Shelly Pro 1PM had tripped its own hardware thermal protection.

The math checks out. The hot water element draws 15.3 amps through a relay rated for 16 amps. Running at 95% of its maximum for 48 minutes in an Australian electrical cabinet pushes the internal temperature past the safety threshold. The Shelly did exactly what it was designed to do — it protected itself.

The problem was that our Home Assistant automation had no idea this had happened. The timer automation was sitting in mode: single with a five-hour delay. It issued a single ON command at 08:45 and then waited. When the Shelly silently shut itself off 48 minutes later, the automation didn’t notice. It had no trigger to re-check anything until 14:00.

Why the Original Automation Didn’t Catch It

We’d built an overheat guard into the original solar diversion automation — it monitored the Shelly’s temperature sensor and would cut power above 80°C. But we discovered that automation hadn’t fired since June 21st. The numeric_state triggers on the solar inverter sensor had gone silent during the winter when we weren’t exporting. The automation was technically enabled, but it wasn’t running.

So the only thing keeping the Shelly from melting itself was its own hardware protection, which kicks in roughly 10°C past where we should have caught it in software.

The Fix: Event-Driven Temperature Control

Instead of a blind timer or a duty-cycle loop, we rewrote the daytime run automation to use pure event triggers on the Shelly’s internal temperature sensor:

triggers:
  - trigger: numeric_state
    entity_id: sensor.shellypro1pm_80f3dac87e8c_temperature
    above: 78
  - trigger: numeric_state
    entity_id: sensor.shellypro1pm_80f3dac87e8c_temperature
    below: 70
  - trigger: time
    at: '08:45:00'
  - trigger: time
    at: '14:00:00'
  - trigger: homeassistant
    event: start

The numeric_state trigger is event-driven. It fires exactly when the sensor value crosses the threshold — not on a timer, not on a polling loop. When the relay heats to 78°C, the automation turns it off. When it cools to 70°C, it turns it back on. The 10-degree hysteresis prevents rapid toggling.

Each day, this generates roughly four to six triggers total. The rest of the time the automation sits completely dormant. It’s not checking every minute. It’s not running a loop. It’s waiting for the temperature to change.

The 08:45 time trigger provides the initial kickstart in the morning, and the 14:00 trigger ensures a hard off at the end of the window. A homeassistant start trigger handles recovery after reboots.

Full Automation

- id: hot_water_daytime_run_disabled
  alias: Hot water daytime run
  description: Temp-driven 08:45-14:00 with overheat protection.
  triggers:
  - trigger: numeric_state
    entity_id: sensor.shellypro1pm_80f3dac87e8c_temperature
    above: 78
  - trigger: numeric_state
    entity_id: sensor.shellypro1pm_80f3dac87e8c_temperature
    below: 70
  - trigger: time
    at: '08:45:00'
  - trigger: time
    at: '14:00:00'
  - trigger: homeassistant
    event: start
  actions:
  - variables:
      in_window: '{{ today_at(''08:45'') <= now() < today_at(''14:00'') }}'
      shelly_on: '{{ is_state(''switch.shellypro1pm_80f3dac87e8c'', ''on'') }}'
      temp: '{{ states(''sensor.shellypro1pm_80f3dac87e8c_temperature'') | float(0) }}'
  - choose:
    - conditions:
      - condition: template
        value_template: '{{ not in_window }}'
      sequence:
      - action: switch.turn_off
        target:
          entity_id: switch.shellypro1pm_80f3dac87e8c
    - conditions:
      - condition: template
        value_template: '{{ in_window and temp >= 78 and shelly_on }}'
      sequence:
      - action: switch.turn_off
        target:
          entity_id: switch.shellypro1pm_80f3dac87e8c
    - conditions:
      - condition: template
        value_template: '{{ in_window and temp <= 70 and not shelly_on }}'
      sequence:
      - action: switch.turn_on
        target:
          entity_id: switch.shellypro1pm_80f3dac87e8c
    - conditions:
      - condition: template
        value_template: '{{ in_window and temp < 78 and not shelly_on }}'
      - condition: template
        value_template: '{{ trigger.platform == ''time'' }}'
      sequence:
      - action: switch.turn_on
        target:
          entity_id: switch.shellypro1pm_80f3dac87e8c
  mode: single

The Result

On the first day after deploying this change, the hot water ran for 3.6 hours in a single uninterrupted cycle. The Shelly’s temperature never exceeded 78°C. The hardware overtemp protection never tripped. When the water tank’s thermostat reached temperature and cut the element, the relay stayed on but drew zero current — the system was satisfied and waiting.

On a cool cloudy day, the automation might cycle two or three times as the relay heats and cools. On a normal day, it runs once and stays on. The thermal mass of the relay and DIN rail sets the rhythm — we’re not imposing an arbitrary duty cycle on it.

Lessons Learned

Event-driven beats polling. A numeric_state trigger fires on sensor changes, not on a clock. The automation does nothing except when the temperature actually crosses a threshold. This generates roughly six triggers per day instead of hundreds of polling checks.

Hardware protection is a last resort, not a control mechanism. The Shelly’s built-in overtemp shutdown worked. It kept the device from being damaged. But it’s designed as a safety net, not as a thermostat. Catch the overheat in software at 78°C and you never reach 90°C.

A timer with a delay is not a control loop. mode: single with a five-hour delay meant the automation issued a single ON command and then disengaged. There was no mechanism to re-issue the command after a trip. Even a temperature sensor sitting right there on the Shelly couldn’t be acted on because nothing was watching it.

The Shelly Pro 1PM runs hot at 15A. The device is rated for 16A, but at 15.3A continuous it reaches 88°C in under 15 minutes. That’s within spec — the datasheet allows ambient temperatures up to 50°C and the internal relay is rated for the full current — but it leaves very little thermal headroom. If you’re switching a load this close to the rating, plan for thermal management or use a contactor.

Temperature-based hysteresis finds the natural rhythm. Instead of guessing a duty cycle (80% at 10 minutes? 60% at 5 minutes?), we let the Shelly’s own temperature sensor drive the cycle. The algorithm is simple: run until it’s hot, rest until it’s cool. The hardware defines its own limits.


Posted

in

by

Tags:

Comments

Leave a Reply

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