Fronius Ohmpilot + dual-element hot water + Home Assistant — proportional 0–6 kW solar diversion & Storm Mode

Fronius Ohmpilot + dual-element hot-water cylinder + Home Assistant

Effectively proportional 0–6 kW solar diversion using two ordinary 3 kW elements, with automatic poor-solar backup

Edit, Reference implementation yaml in a comment below

I’m posting this because I spent a surprising amount of time trying to work out how to make this combination behave the way I wanted, and found very little documentation covering this exact use case.

My system consists of:

  • Fronius GEN24 10.0 Plus inverter
  • Fronius Ohmpilot
  • Dual-element electric hot-water cylinder
  • 3 kW lower element
  • 3 kW upper element
  • External contactor controlling the upper element
  • Home Assistant
  • Independent physical thermostats on the hot-water cylinder

The main objective was to get effectively proportional solar diversion across the full 0–6 kW range, despite using two ordinary 3 kW resistive elements.

So if there is 1.5 kW of surplus solar available, I want roughly 1.5 kW going into the cylinder.

If there is 4 kW available, I want roughly 4 kW going into the cylinder.

And if there is 6 kW available, I want to be able to use the full 6 kW.

The second objective was to deal with extended periods of poor solar without making Home Assistant directly control a 3 kW heating element.

For that I use a second Ohmpilot configuration, which I call Storm Mode.

Home Assistant switches the Ohmpilot between Normal and Storm modes locally, while the Ohmpilot and the cylinder’s physical thermostats remain responsible for actually controlling the heating elements.

After quite a bit of experimentation, this is now working.


Physical arrangement

The lower 3 kW element is connected as Heater 1 on the Ohmpilot.

This is the normal proportionally controlled Ohmpilot output.

The upper 3 kW element works differently.

The Ohmpilot’s multifunction relay controls an external contactor, and that contactor supplies the upper element.

Conceptually:

PV → GEN24 → Ohmpilot

Ohmpilot Heater 1 → lower 3 kW element, proportional 0–3 kW

Ohmpilot multifunction relay → external contactor → upper 3 kW element, fixed 3 kW

The upper element retains its own physical thermostat.

At first glance, this looks like a 0–3 kW proportional heater plus another fixed 3 kW heater that would simply switch on as a big step.

That is not how the Ohmpilot behaves.

When the second element comes on, the Ohmpilot adjusts Heater 1 around that fixed 3 kW load.

That is what allows the combined system to behave like an effectively variable 0–6 kW load.


The key: Heater 2 is configured as an SG Ready heat pump

This was the part that wasn’t immediately obvious.

In the Ohmpilot configuration, Heater 2 is configured as an SG Ready heat pump.

There is no heat pump connected.

Instead, the Ohmpilot’s multifunction relay operates the external contactor supplying the upper 3 kW resistive element.

So from the Ohmpilot’s point of view:

SG Ready Heater 2 request → multifunction relay

In reality:

SG Ready Heater 2 request → multifunction relay → contactor → upper 3 kW resistive element

The upper element’s physical thermostat remains in circuit and ultimately determines whether the element actually heats.

This gives the Ohmpilot control over a second fixed 3 kW resistive load while retaining normal proportional control over Heater 1.

That combination is what makes the 0–6 kW behaviour possible.


Normal Mode: effectively proportional from 0–6 kW

My Normal Mode Heater 2 configuration is:

  • Mode: SG Ready heat pump
  • Start: Feed-in 3000 W
  • Stop: Consume 250 W

From 0–3 kW of available surplus, operation is straightforward.

The Ohmpilot proportionally controls Heater 1 to match the available surplus.

So below about 3 kW:

Available surplus ≈ Heater 1 power

The interesting behaviour starts once enough surplus is available for Heater 2.

When the Heater 2 start condition is satisfied, the Ohmpilot operates its multifunction relay.

The external contactor closes and connects the fixed 3 kW upper element.

Crucially, the Ohmpilot does not leave Heater 1 at 3 kW and simply add another 3 kW.

Instead, it reduces Heater 1 to compensate for the newly connected fixed 3 kW Heater 2 load.

For example, with approximately 4 kW of available surplus:

  • Upper element: 3 kW
  • Lower element: approximately 1 kW
  • Total water heating: approximately 4 kW

As the available surplus rises further, Heater 1 increases again around that fixed 3 kW upper-element load.

So the overall behaviour is approximately:

0–3 kW available: Heater 1 follows the available surplus.

3–6 kW available: Heater 2 supplies a fixed 3 kW and Heater 1 supplies the remaining 0–3 kW.

The result is effectively proportional solar diversion across the full 0–6 kW range using two ordinary 3 kW elements.

The system does not simply jump from 3 kW to 6 kW when the upper element is introduced.


Why this matters

If the second 3 kW element were switched independently, the behaviour would be much cruder.

Imagine 4 kW of surplus PV being available.

The lower element could absorb 3 kW.

If a separate controller then switched another 3 kW element on, total water-heating demand would suddenly become 6 kW.

That would create roughly 2 kW of grid import.

That is not what happens here.

The Ohmpilot brings in the fixed 3 kW Heater 2 load and simultaneously reduces Heater 1 to approximately 1 kW.

Total water-heating demand therefore remains close to the available 4 kW surplus.

As the available surplus increases, Heater 1 rises again.

This allows the system to make use of relatively small increases in solar generation above 3 kW without introducing a 3 kW step change in total load.


The lower element also causes significant mixing

Another behaviour that became apparent from monitoring the cylinder is that a 3 kW element operating near the bottom produces a surprisingly strong convective stirring effect.

The cylinder does not behave like two completely independent volumes of water separated neatly by the positions of the two elements.

When the lower element is operating hard, hot water rises and creates considerable circulation within the cylinder.

This matters when both elements are operating.

Although the upper element is physically positioned to heat the upper portion of the cylinder, the circulation produced by the lower 3 kW element means the upper element contributes to heating considerably more water than might initially be expected.

Under strong solar conditions, the two elements complement each other very well.

The lower element provides both substantial heating and circulation, while the upper element adds another 3 kW of heat into that circulating water.


Storm Mode

The second operating profile is what I call Storm Mode.

Its purpose is to give Heater 2 different behaviour after a poor-solar day, rather than relying indefinitely on surplus PV that may not arrive.

Heater 2 remains configured as:

SG Ready heat pump

but the thresholds change to:

  • Start: Consume 6000 W
  • Stop: Consume 10000 W

Those numbers look strange if you approach the configuration as conventional solar diversion.

They are deliberate.

Changing the start condition from Feed-in to Consume allows Heater 2 to be requested while the house is importing, rather than requiring a large solar export first.

The physical arrangement does not change:

Ohmpilot multifunction relay → contactor → upper 3 kW element

The difference between Normal and Storm modes is therefore an Ohmpilot configuration change, not a wiring change and not a separate Home Assistant-controlled heater.

The upper element’s physical thermostat remains responsible for its actual water-temperature limit.


Why Storm Mode uses a 10,000 W Consume stop threshold

The Consume 10000 W stop threshold is not just an arbitrarily large value intended to keep Heater 2 on.

It also provides an upper-import safeguard.

The upper element is a fixed 3 kW load.

If it is operating at the same time as other substantial household loads, total grid import can become significant.

The house supply ultimately has a finite capacity protected by the service or pole fuse.

The 10 kW Consume stop threshold gives the Ohmpilot a point at which it can release Heater 2 rather than blindly keeping another 3 kW load connected to an already heavily loaded house.

So Storm Mode deliberately permits grid consumption for the upper element, but not without an upper limit.

If household import rises sufficiently, the Ohmpilot can drop Heater 2 and remove that additional 3 kW load.

The exact threshold is obviously installation-specific.

In my system it was chosen as a safeguard to reduce the risk of excessive total household import and potentially blowing the service or pole fuse.


Home Assistant automatically selects Storm Mode

I did not want Storm Mode to depend on someone remembering to enable it after a poor-solar day.

Home Assistant therefore supervises the mode selection.

At 14:00, HA checks the amount of energy the Ohmpilot has received that day.

If daily Ohmpilot energy is below:

5 kWh

Home Assistant changes the Ohmpilot from Normal Mode to Storm Mode.

The basic logic is:

14:00

↓

Has the Ohmpilot received at least 5 kWh today?

Yes → remain in Normal Mode

No → change to Storm Mode

This uses the actual energy that has made it into the hot-water system rather than relying on a weather forecast.


Returning from Storm Mode to Normal Mode

Storm Mode is not intended to become the permanent configuration.

Home Assistant also watches actual Ohmpilot power.

When Ohmpilot power exceeds approximately:

2.5 kW

HA takes that as the condition to restore the normal solar-diversion configuration.

It then writes the Normal Mode Heater 2 settings back to the Ohmpilot:

  • Start: Feed-in 3000 W
  • Stop: Consume 250 W

The Ohmpilot then returns to its normal behaviour, including the effectively proportional 0–6 kW solar-diversion arrangement described above.

The important distinction is that Home Assistant is selecting the operating profile.

It is not trying to replace the Ohmpilot’s own fast power-control logic.


Home Assistant changes the Ohmpilot configuration locally

Another major objective was keeping this local.

I did not want switching between Normal and Storm modes to depend on a cloud service.

The Ohmpilot exposes a local web interface, and after some experimentation I found it possible to change the required configuration through its local endpoints.

The working method uses:

set.cgi

An important discovery was that trying to change only individual values was not the approach that ultimately worked reliably for this application.

Instead, Home Assistant sends the required full configuration payload.

I therefore have two known-good configurations:

  • Normal Mode
  • Storm Mode

Home Assistant sends the appropriate complete payload whenever a mode change is required.


Don’t just send the command — verify it

I did not want an automation that sends a request to the Ohmpilot and simply assumes it worked.

After writing the configuration, Home Assistant reads the Ohmpilot configuration back from:

settings.shtml

The returned values are checked against the profile HA requested.

A mode change therefore looks like:

Home Assistant selects Normal or Storm Mode

↓

Full configuration payload sent to set.cgi

↓

Ohmpilot applies configuration

↓

Home Assistant reads settings.shtml

↓

Requested configuration is verified

This makes the system much more trustworthy than simply firing an HTTP request and assuming the device accepted it.

It also makes troubleshooting considerably easier.


Verifying the actual Heater 2 relay state

There is another useful piece of local feedback available from the Ohmpilot.

The:

values.xml

endpoint exposes:

Relay1

which provides feedback about the Ohmpilot’s multifunction relay.

That means Home Assistant can distinguish between:

“The Ohmpilot is configured in Storm Mode.”

and:

“The Ohmpilot is actually requesting Heater 2 right now.”

Those are two different things.

The configuration tells me what the Ohmpilot should do when the appropriate conditions occur.

Relay1 tells me what it is actually doing with the multifunction relay.

Having both configuration readback and actual relay-state feedback has made the system much easier to understand and troubleshoot.


A note on the Ohmpilot day curve

I also experimented with using the Ohmpilot’s day curve/minimum-temperature function as part of the backup strategy.

I was not able to get it to behave consistently in the way I wanted, so it is not part of the final Normal/Storm configuration described here.


Why I did not simply use a smart relay to control the upper element

One option during this project was to have Home Assistant control the upper element directly through a smart relay and contactor arrangement.

That would certainly have been possible.

But I preferred not to make Home Assistant responsible for directly cycling a 3 kW water-heating element.

The arrangement I ended up with gives each part of the system a clearly defined job.

Home Assistant
selects the operating strategy.

The Ohmpilot
controls Heater 2 start/stop behaviour and proportionally controls Heater 1.

The contactor
handles the electrical switching of the upper 3 kW load.

The physical cylinder thermostats
remain responsible for limiting water temperature.

That separation was important to me.


Control and safety hierarchy

The finished arrangement effectively has several layers.

Physical temperature protection

The cylinder retains its independent physical thermostats and fail-safe temperature cut-offs.

These remain independent of Home Assistant and the network.

Electrical load switching

The external contactor switches the upper 3 kW resistive element.

The Ohmpilot’s multifunction relay only needs to control the contactor rather than carry the upper-element load itself.

Ohmpilot

The Ohmpilot controls:

  • proportional power to the lower element
  • Heater 2 start/stop logic
  • the multifunction relay
  • the interaction between the fixed 3 kW Heater 2 load and proportionally controlled Heater 1

That last point is what extends the effective proportional solar-diversion range from approximately 0–3 kW to 0–6 kW.

Home Assistant

Home Assistant provides the supervisory layer.

It:

  • determines when the system should use Normal or Storm Mode
  • writes the appropriate Ohmpilot configuration
  • verifies the configuration afterwards
  • monitors actual multifunction-relay state

It does not directly thermostat or rapidly cycle either 3 kW heating element.


Configuration summary

Normal Mode

Heater 2 type/mode: SG Ready heat pump

  • Start condition: Feed-in
  • Start threshold: 3000 W
  • Stop condition: Consume
  • Stop threshold: 250 W

Physical output:

Ohmpilot multifunction relay → contactor → upper 3 kW resistive element

Combined behaviour:

0–3 kW available: Heater 1 proportionally follows the available surplus.

Above approximately 3 kW: Heater 2 supplies its fixed 3 kW and Heater 1 proportionally supplies the balance.

Result:

Effectively proportional solar diversion from 0–6 kW.


Storm Mode

Heater 2 type/mode: SG Ready heat pump

  • Start condition: Consume
  • Start threshold: 6000 W
  • Stop condition: Consume
  • Stop threshold: 10000 W

Physical output remains:

Ohmpilot multifunction relay → contactor → upper 3 kW resistive element

The upper element’s physical thermostat remains in circuit.

The 10 kW Consume stop threshold provides an upper-import safeguard so the additional 3 kW load can be removed if total household consumption becomes excessive.


Automatic mode selection

At 14:00:

Daily Ohmpilot energy < 5 kWh → Storm Mode

For returning to normal operation:

Ohmpilot power > 2.5 kW → Normal Mode

Home Assistant changes the Ohmpilot configuration rather than directly controlling the heating element.


Local Home Assistant interface

The useful local Ohmpilot endpoints I ended up using are:

Configuration write

set.cgi

Used to send the complete configuration payload.

Configuration readback

settings.shtml

Used to confirm that the requested Normal or Storm settings have actually been applied.

Operational feedback

values.xml

Used to obtain live Ohmpilot information, including:

Relay1

which provides feedback on the multifunction-relay state.

This gives Home Assistant both command and feedback, rather than command alone.


What is useful about this arrangement

None of the individual pieces are especially unusual.

Dual-element hot-water cylinders exist.

The Ohmpilot supports Heater 2.

SG Ready heat pumps exist.

Contactors obviously exist.

Home Assistant can make local HTTP requests.

The useful part was combining them in a slightly unconventional way:

Configure Ohmpilot Heater 2 as an SG Ready heat pump, but use its multifunction relay to control a contactor supplying an ordinary 3 kW resistive upper element.

The important consequence is that this does not merely provide two stages of water heating.

It effectively extends the Ohmpilot’s proportional solar-diversion range from 0–3 kW to 0–6 kW.

Heater 1 provides the continuously variable component.

Heater 2 provides an additional fixed 3 kW load.

When Heater 2 is introduced, the Ohmpilot adjusts Heater 1 around that fixed load so the combined demand continues to follow the available surplus.

Storm Mode then provides a second operating profile for poor-solar conditions without requiring Home Assistant to take over direct control of the heating element.

HA changes the rules under which the Ohmpilot operates, verifies that those rules were successfully loaded, and monitors what the Ohmpilot is actually doing.

Throughout all of this, the physical thermostats remain independent of Home Assistant.


Why I’m posting this

I’m not suggesting that my exact thresholds are appropriate for every installation.

PV size, cylinder capacity, element positions, household hot-water demand, service capacity, electricity tariffs and local electrical requirements all need to be considered.

Anything involving mains-voltage water-heating equipment, contactors and changes to fixed wiring obviously needs to be designed and installed appropriately for the applicable electrical regulations.

What I wanted to document is the architecture, because I spent a long time looking for examples of somebody doing essentially this and could not find much.

The main takeaways are:

  1. Heater 1 is the normal proportionally controlled 0–3 kW Ohmpilot load.
  2. Heater 2 is configured as an SG Ready heat pump.
  3. The SG Ready/multifunction relay actually controls a contactor feeding a second 3 kW resistive element.
  4. The second element is fixed at 3 kW, but the Ohmpilot adjusts Heater 1 around it.
  5. The result is effectively proportional solar diversion across approximately 0–6 kW, not merely two 3 kW stages.
  6. The lower element produces substantial convective mixing, so the upper element contributes to heating considerably more than a small isolated volume at the top.
  7. Storm Mode changes the Heater 2 thresholds to Consume 6000 W / Consume 10000 W for poor-solar operation.
  8. The 10 kW Consume stop threshold allows the additional 3 kW load to be removed under excessive household import, helping protect the house supply or pole fuse.
  9. Home Assistant automatically selects Storm Mode after the poor-solar criterion is met and restores Normal Mode when the return criterion is reached.
  10. HA changes the Ohmpilot configuration locally through set.cgi.
  11. HA reads settings.shtml afterwards to verify that the requested configuration was accepted.
  12. values.xml / Relay1 provides feedback about the actual multifunction-relay state.
  13. The physical cylinder thermostats remain independent temperature controls and fail-safes.

If someone else eventually finds this while staring at an Ohmpilot, a dual-element cylinder and a contactor and wondering whether the combination can be made to work — yes, it can.

The most useful thing to know is that you do not have to sacrifice proportional diversion above 3 kW.

With the second element configured this way, the Ohmpilot can effectively turn two ordinary 3 kW resistive elements into a 0–6 kW variable solar-diversion load, while Home Assistant adds a supervisory layer for poor-solar operation without taking over the Ohmpilot’s core control job.

Hopefully this saves somebody else some of the experimentation it took to get here.

Reference implementation — adapt this to your own installation.

The example below is the Home Assistant configuration I use. I have sanitised my local IP address and installation-specific sensor entity IDs.

Do not blindly copy the set.cgi URLs. They contain other Ohmpilot configuration values in addition to the Heater 2 settings. First inspect and record the configuration of your own Ohmpilot, preserve the settings appropriate to your installation, and only change the Heater 2 parameters you intend to control.

The two installation-specific HA entities shown as sensor.YOUR_OHMPILOT_DAILY_ENERGY and sensor.YOUR_OHMPILOT_POWER must also be replaced with your own sensors. The example assumes the daily-energy sensor reports Wh.

rest_command:
  ohmpilot_storm_mode:
    url: >-
      http://OHMPILOT_IP_ADDRESS/set.cgi?name=Ohmpilot&H1Auto=manually&H1Ph=1+phasig&H1Power=3000&tempInst=on&legProt=on&legCyc=168&maxTempUsed=on&maxTempCyc=75&chbxtime1=on&ontime1=00%3A00&offtime1=00%3A00&temp1=20&H2Ph=SG+Ready+Waermepumpe&H2Power=3000&H2ThModeOn=Bezug&H2ThOn=6000&H2ThModeOff=Bezug&H2ThOff=10000
    method: GET

  ohmpilot_normal_mode:
    url: >-
      http://OHMPILOT_IP_ADDRESS/set.cgi?name=Ohmpilot&H1Auto=manually&H1Ph=1+phasig&H1Power=3000&tempInst=on&legProt=on&legCyc=168&maxTempUsed=on&maxTempCyc=75&chbxtime1=on&ontime1=00%3A00&offtime1=00%3A00&temp1=20&H2Ph=SG+Ready+Waermepumpe&H2Power=3000&H2ThModeOn=Einspeisung&H2ThOn=3000&H2ThModeOff=Bezug&H2ThOff=250
    method: GET


rest:
  - resource: http://OHMPILOT_IP_ADDRESS/settings.shtml
    scan_interval: 30

    sensor:
      - name: Ohmpilot Heater 2 Mode
        unique_id: ohmpilot_heater_2_mode

        value_template: >-
          {% set h2_type = value
            | regex_findall_index('loadH2Ph\\(\\).*?selectedIndex\\s*=\\s*([0-9]+)')
            | int(-1) %}

          {% set on_mode = value
            | regex_findall_index('loadH2ThModeOn\\(\\).*?selectedIndex\\s*=\\s*([0-9]+)')
            | int(-1) %}

          {% set on_threshold = value
            | regex_findall_index('loadH2ThOn\\(\\).*?H2ThOn\\.value\\s*=\\s*"([0-9]+)"')
            | int(-1) %}

          {% set off_mode = value
            | regex_findall_index('loadH2ThModeOff\\(\\).*?selectedIndex\\s*=\\s*([0-9]+)')
            | int(-1) %}

          {% set off_threshold = value
            | regex_findall_index('loadH2ThOff\\(\\).*?H2ThOff\\.value\\s*=\\s*"([0-9]+)"')
            | int(-1) %}

          {% if h2_type == 4
                and on_mode == 0
                and on_threshold == 3000
                and off_mode == 1
                and off_threshold == 250 %}
            Normal

          {% elif h2_type == 4
                  and on_mode == 1
                  and on_threshold == 6000
                  and off_mode == 1
                  and off_threshold == 10000 %}
            Storm

          {% elif h2_type == -1
                  or on_mode == -1
                  or on_threshold == -1
                  or off_mode == -1
                  or off_threshold == -1 %}
            Unavailable

          {% else %}
            Other
          {% endif %}


binary_sensor:
  - platform: rest
    name: Ohmpilot Heater 2 Active
    unique_id: ohmpilot_heater_2_active
    resource: http://OHMPILOT_IP_ADDRESS/values.xml
    scan_interval: 10

    value_template: >-
      {% set items = value_json['values']['value'] %}
      {% set ns = namespace(relay='0') %}

      {% for item in items %}
        {% if item['@id'] == 'Relay1' %}
          {% set ns.relay = item['#text'] %}
        {% endif %}
      {% endfor %}

      {{ ns.relay == '1' }}


input_datetime:
  ohmpilot_last_mode_command:
    name: Ohmpilot Last Mode Command
    has_date: true
    has_time: true

  ohmpilot_last_mode_verified:
    name: Ohmpilot Last Mode Verified
    has_date: true
    has_time: true


input_text:
  ohmpilot_last_trigger_reason:
    name: Ohmpilot Last Trigger Reason
    max: 255

  ohmpilot_last_action_result:
    name: Ohmpilot Last Action Result
    max: 255

  ohmpilot_last_requested_mode:
    name: Ohmpilot Last Requested Mode
    max: 32


script:
  ohmpilot_set_storm_verified:
    alias: Ohmpilot - Set Storm Mode Verified
    mode: single

    fields:
      trigger_reason:
        name: Trigger reason
        description: Why Storm Mode was requested
        example: Manual dashboard

    sequence:
      - variables:
          reason: >-
            {{ trigger_reason | default('Manual dashboard', true) }}

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_requested_mode
        data:
          value: Storm

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_trigger_reason
        data:
          value: "{{ reason }}"

      - action: input_datetime.set_datetime
        target:
          entity_id: input_datetime.ohmpilot_last_mode_command
        data:
          datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_action_result
        data:
          value: Storm requested - awaiting verification

      - action: logbook.log
        data:
          name: Ohmpilot Storm Mode
          message: >-
            Storm Mode requested. Reason: {{ reason }}.
          entity_id: sensor.ohmpilot_heater_2_mode

      - action: rest_command.ohmpilot_storm_mode

      - delay: "00:00:02"

      - action: homeassistant.update_entity
        target:
          entity_id: sensor.ohmpilot_heater_2_mode

      - wait_template: >-
          {{ is_state('sensor.ohmpilot_heater_2_mode', 'Storm') }}
        timeout: "00:01:00"
        continue_on_timeout: true

      - if:
          - condition: state
            entity_id: sensor.ohmpilot_heater_2_mode
            state: Storm

        then:
          - action: input_datetime.set_datetime
            target:
              entity_id: input_datetime.ohmpilot_last_mode_verified
            data:
              datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

          - action: input_text.set_value
            target:
              entity_id: input_text.ohmpilot_last_action_result
            data:
              value: Storm verified

          - action: logbook.log
            data:
              name: Ohmpilot Storm Mode
              message: >-
                Storm Mode VERIFIED by Ohmpilot configuration readback.
                Heater 2 relay is currently
                {{ 'ON' if is_state('binary_sensor.ohmpilot_heater_2_active', 'on') else 'OFF' }}.
              entity_id: sensor.ohmpilot_heater_2_mode

        else:
          - action: input_text.set_value
            target:
              entity_id: input_text.ohmpilot_last_action_result
            data:
              value: >-
                Storm verification FAILED - readback is
                {{ states('sensor.ohmpilot_heater_2_mode') }}

          - action: logbook.log
            data:
              name: Ohmpilot Storm Mode
              message: >-
                Storm Mode verification FAILED after 60 seconds.
                Current readback:
                {{ states('sensor.ohmpilot_heater_2_mode') }}.
              entity_id: sensor.ohmpilot_heater_2_mode

          - action: persistent_notification.create
            data:
              title: Ohmpilot Storm Mode verification failed
              message: >-
                Home Assistant requested Storm Mode at
                {{ states('input_datetime.ohmpilot_last_mode_command') }}.

                Reason:
                {{ states('input_text.ohmpilot_last_trigger_reason') }}

                The Ohmpilot did not verify Storm Mode within 60 seconds.

                Current mode readback:
                {{ states('sensor.ohmpilot_heater_2_mode') }}.


  ohmpilot_set_normal_verified:
    alias: Ohmpilot - Set Normal Mode Verified
    mode: single

    fields:
      trigger_reason:
        name: Trigger reason
        description: Why Normal Mode was requested
        example: Manual dashboard

    sequence:
      - variables:
          reason: >-
            {{ trigger_reason | default('Manual dashboard', true) }}

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_requested_mode
        data:
          value: Normal

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_trigger_reason
        data:
          value: "{{ reason }}"

      - action: input_datetime.set_datetime
        target:
          entity_id: input_datetime.ohmpilot_last_mode_command
        data:
          datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

      - action: input_text.set_value
        target:
          entity_id: input_text.ohmpilot_last_action_result
        data:
          value: Normal requested - awaiting verification

      - action: logbook.log
        data:
          name: Ohmpilot Normal Mode
          message: >-
            Normal Mode requested. Reason: {{ reason }}.
          entity_id: sensor.ohmpilot_heater_2_mode

      - action: rest_command.ohmpilot_normal_mode

      - delay: "00:00:02"

      - action: homeassistant.update_entity
        target:
          entity_id: sensor.ohmpilot_heater_2_mode

      - wait_template: >-
          {{ is_state('sensor.ohmpilot_heater_2_mode', 'Normal') }}
        timeout: "00:01:00"
        continue_on_timeout: true

      - if:
          - condition: state
            entity_id: sensor.ohmpilot_heater_2_mode
            state: Normal

        then:
          - action: input_datetime.set_datetime
            target:
              entity_id: input_datetime.ohmpilot_last_mode_verified
            data:
              datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

          - action: input_text.set_value
            target:
              entity_id: input_text.ohmpilot_last_action_result
            data:
              value: Normal verified

          - action: logbook.log
            data:
              name: Ohmpilot Normal Mode
              message: >-
                Normal Mode VERIFIED by Ohmpilot configuration readback.
                Heater 2 relay is currently
                {{ 'ON' if is_state('binary_sensor.ohmpilot_heater_2_active', 'on') else 'OFF' }}.
              entity_id: sensor.ohmpilot_heater_2_mode

        else:
          - action: input_text.set_value
            target:
              entity_id: input_text.ohmpilot_last_action_result
            data:
              value: >-
                Normal verification FAILED - readback is
                {{ states('sensor.ohmpilot_heater_2_mode') }}

          - action: logbook.log
            data:
              name: Ohmpilot Normal Mode
              message: >-
                Normal Mode verification FAILED after 60 seconds.
                Current readback:
                {{ states('sensor.ohmpilot_heater_2_mode') }}.
              entity_id: sensor.ohmpilot_heater_2_mode

          - action: persistent_notification.create
            data:
              title: Ohmpilot Normal Mode verification failed
              message: >-
                Home Assistant requested Normal Mode at
                {{ states('input_datetime.ohmpilot_last_mode_command') }}.

                Reason:
                {{ states('input_text.ohmpilot_last_trigger_reason') }}

                The Ohmpilot did not verify Normal Mode within 60 seconds.

                Current mode readback:
                {{ states('sensor.ohmpilot_heater_2_mode') }}.


automation:
  - id: ohmpilot_heater_2_relay_log
    alias: Ohmpilot - Log Heater 2 Relay Changes
    mode: queued

    triggers:
      - trigger: state
        entity_id: binary_sensor.ohmpilot_heater_2_active
        from: "off"
        to: "on"

      - trigger: state
        entity_id: binary_sensor.ohmpilot_heater_2_active
        from: "on"
        to: "off"

    actions:
      - action: logbook.log
        data:
          name: Ohmpilot Heater 2
          message: >-
            Heater 2 relay changed to
            {{ 'ON' if is_state('binary_sensor.ohmpilot_heater_2_active', 'on') else 'OFF' }}.
            Operating mode:
            {{ states('sensor.ohmpilot_heater_2_mode') }}.
          entity_id: binary_sensor.ohmpilot_heater_2_active


  - id: ohmpilot_auto_storm_low_solar
    alias: Ohmpilot - Automatic Storm Mode
    mode: single

    triggers:
      - trigger: time
        at: "14:00:00"

      - trigger: homeassistant
        event: start

    conditions:
      - condition: time
        after: "13:59:59"
        before: "16:00:00"

      - condition: state
        entity_id: sensor.ohmpilot_heater_2_mode
        state: Normal

      - condition: template
        value_template: >-
          {% set e = states('sensor.YOUR_OHMPILOT_DAILY_ENERGY') %}
          {{ is_number(e) and (e | float(0)) < 5000 }}

    actions:
      - action: script.ohmpilot_set_storm_verified
        data:
          trigger_reason: >-
            Automatic storm protection - only
            {{ (states('sensor.YOUR_OHMPILOT_DAILY_ENERGY')
                | float(0) / 1000) | round(2) }}
            kWh delivered by Ohmpilot by 2 pm


  - id: ohmpilot_auto_restore_normal
    alias: Ohmpilot - Restore Normal On Strong Solar
    mode: single

    triggers:
      - trigger: numeric_state
        entity_id: sensor.YOUR_OHMPILOT_POWER
        above: 2500

    conditions:
      - condition: state
        entity_id: sensor.ohmpilot_heater_2_mode
        state: Storm

    actions:
      - action: script.ohmpilot_set_normal_verified
        data:
          trigger_reason: >-
            Automatic recovery - Ohmpilot power exceeded 2500 W,
            indicating strong solar has returned