Feed surplus energy produced by photovoltaics into the hot water

Good morning everybody.
I do have two components connected to my Openhab based on a raspberry 4. The Battery and photovoltaics on the roof and the modbus based water tank.
I am able to read and write everything and everything works like a charm.
So I wrote a Rule which does the following:

When the battery in my basement is >= 95% and the PV generates 2000 W more energy than my house needs for 15 minutes - tested every 30 Seconds - I want to rise the temperature of my water from 48°C (boost is 43°C) to 55°C (boost is 70°)

The default temp and boost temp is different but not neccesary for this scenario, as we have two states:

Default: 48°C Water + 43°C Boost
Rised temperature: 55°C Water + 70°C Boost

This values are provided by the manufacturer.

When my house uses more energy, than generated with my PV for 3 Minutes (tested 3x every minute) i want to bring the Default temperatures back to 48° and 43°

The code i have written is the following and I wanted to ask you guys if there are any improvements you could provide.
If you like my “template” then please use it, but i’d love to make it even better :slight_smile:

rule "PV-Ueberschuss ins Wasser"
    Item UG_Install_Batterie_EnergyProduction changed
        var Number Produktion = UG_Install_Batterie_EnergyProduction.state as Number
        var Number Verbrauch = UG_Install_Batterie_HouseConsumption.state as Number
        var Number Ueberschuss = Produktion - Verbrauch

        logInfo("Wassertemperatur", "Überschussregel beginnt")
        logInfo("Wassertemperatur", "Überschuss beträgt " + Ueberschuss + " W")
        //teste 15 Minuten lang und wenn das Ergebnis immer noch gleich ist, erhöhe die Wassertemperatur.
        if ((Ueberschuss >= 2000|"W") && (C_Installation_Wasser_Heizstab_Temp.state < 70|"°C"))
            logInfo("Wassertemperatur", "Überschuss der Stromproduktion ist größer 2000W, nämlich: " + Ueberschuss + " W")
            logInfo("Wassertemperatur", "Wassertemperatur ist unter 70°C eingestellt. " + C_Installation_Wasser_Heizstab_Temp.state)
            //Wenn Batteriespeicher voll
            if (UG_Install_Batterie_BatteryFuelCharge.state >= 90|"%")
                for (var i = 0; i < 30; i++) 
                // Istwerte von Verbrauch in Produktion überprüfen
                    var Number Produktion2 = UG_Install_Batterie_EnergyProduction.state as Number
                    var Number Verbrauch2 = UG_Install_Batterie_HouseConsumption.state as Number
                    var Number Ueberschuss2 = Produktion2 - Verbrauch2
                    var checkResult = (Ueberschuss2 >= 2000)
                    // If the check fails, break out of the loop
                    if (!checkResult) 
                        logInfo("Wassertemperatur", "Produktion ist keine 15 Minuten lang größer als 2500W")
                        logInfo("Wassertemperatur", "Schleife wird abgebrochen")

                    // Warte 30 Sekunden, bis die Schleife erneut startet
                    logInfo("Wassertemperatur", "30 Sekunden Warten beginnt jetzt: ")
                    logInfo("Wassertemperatur", "Durchgang " + (i+1))
                    logInfo("Wassertemperatur", "Überschuss der Stromproduktion ist immernoch größer 2500W: " + Ueberschuss2 + " W")

                    if (i == 29)
                        logInfo("Wassertemperatur", "Wassertemperatur wird auf 70°C eingestellt!")
                        sendBroadcastNotification("Wassertemperatur wird angehoben.", "water", "high")
                        //E-Heiz auf 70°C
                        //Wasser-Soll-Temp auf 55°C

        else if (((Ueberschuss < 0|"W")) && (C_Installation_Wasser_Heizstab_Temp.state != 43|"°C") && (C_Installation_Wasser_Temp_Soll != 48|"°C"))
            for (var f = 0; f < 3; f++) 
                logInfo("Wassertemperatur", "Durchgang " + (f+1))                 
                logInfo("Wassertemperatur", "Stromververbrauch ist zu gering.") 
                logInfo("Wassertemperatur", "Warte 1 Minute")     

                // Istwerte von Verbrauch in Produktion überprüfen
                var Number Produktion3 = UG_Install_Batterie_EnergyProduction.state as Number
                var Number Verbrauch3 = UG_Install_Batterie_HouseConsumption.state as Number
                var Number Ueberschuss3 = Produktion3 - Verbrauch3
                var checkResult2 = (Ueberschuss3 <= 0) 

                // If the check fails, break out of the loop
                if (!checkResult2) 
                    logInfo("Wassertemperatur", "Wasser bleibt heiß")
                    logInfo("Wassertemperatur", "Schleife wird abgebrochen")

                if (f == 2)
                logInfo("Wassertemperatur", "Voraussetzungen stimmen noch nicht.") 
                sendBroadcastNotification("Wassertemperatur wird gesenkt.", "water", "high")          
                //E-Heiz auf 43°C
                //Wasser-Soll-Temp auf 48°C

        logInfo("Wassertemperatur", "Es passiert quasi nix.")


I hope you enjoy this and I will be very happy to see if and how this code can be improved.

Thank you very much and take care
Marcel :slight_smile:


Obviously nobody? :sweat_smile:

Thank you @slax91, I will take a deeper look at it as soon as my heat pump has been delivered.

Thank you very much! :smiley:
I’m curious about how we can improve it.

One of projects I was involved in included electric heater from Askoma. That thing and its firmware is able to work with variable load (3 stages) and have an option to report surplus power, so it will automatically adjust its own work.
I haven’t got it working entirely, however I am still interested in such cases. After all, hot water tank is just another form of energy storage. :wink:

I’ve seen some solutions in the market, some saying that thyristors should be used to switch the power of the water tank’s resistance.

Operating 2000W heaters with mechanical switches subject to intense on/off cycles can damage the switch rapidly.

One interesting stand-alone solution, using dimmers instead of switches, is FreeDS. As it supports MQTT it can be integrated with OH.

Exactly. Thats why i dont want to waste my energy.
Heat water is very time and energy intensive. Better use it for my own

Hi Lukasz,

This stuff sounds like exactly what I´m looking for.
Do you have more details on it? Or can point me in the right direction? I’d a look on their website and for me as a computer-guy this is more than confusing :wink: .

I only interface with one product of Askoma which was “Askoheat”. This is a heater with modbus interface which you can use to report surplus energy, see: Askoheat-Modbus 1.15

Beyond modbus they do have a json/http api which I was trying to utilize (modbus is a mess). There are several implications such as legionella which could activate independently of the PV production, which should keep logic on your end a bit simpler (there is schedule to make legionella cycle).

I do not have access to it any more, but I have a message from their support when I asked about “feedin” value:

You recognized that correctly, the values that you feed into the grid will be entered as negative numbers.
Electricity that is drawn from the grid is worth as positive.
However, it is important to update the device software to version 4.3.2

Since I have no PV and still rely on gas I did not get this heater for myself. Maybe somewhere in future. :wink:

Sorry guys, but please stay at the topic.
If you want to talk about any heaters, products or anything else, open a new threat.

Thank you very much.

ok, back to topic :wink:

As said in the PM, I´m currently playing around with an adoption of your idea and script in a spare PI with OH4.

How do you avoid in your rule, that more (many?) instances of your rule are running parallel?? You trigger the rule, when the energy-production changed. This happens in my environment all the time. So the rule is fired very often (in my environment nearly every second).
The rule itself is running sometimes several minutes (depending on the conditions). So it happens at my installation, that I have a number of instances of the rule running parallel…

Did I miss something?

Just found out, that (at least in OH 4) the rule-engine itself prevent a rule from firing again, if it runs already.

Sorry I had a lot of things to do at weekend.
Yes exactly as you said. In the first version of this script i used to “lock” it. But since OH3 aint working with this lock or even better, has to locking option as default, its not necessary to use it.

And yes you’re right. The Trigger-Option is just the change of energy-production. In my case it is every 10 seconds. Dont know the exact time right now.
But even though… thats why I used the “try, catch, finally” and the first to If-checks. So that the pi hasn’t have to “calculate” all the things.

Best wishes and thank you very much for your input.


In my Country the consumption is measured every 15 minutes (for billing purposes) so I’ve parametrized OH to take decisions every 10 minutes. I use this not exactly to heat water, but to charge batteries in some devices (such as phones, vacuum cleaners, etc).

For example, this is today’s consumption:

Positive values mean that I’m sending electricity to the grid. So, if the average of the last 5 minutes is positive enough, I know I can switch on such battery chargers for 10 minute periods.

This way I do not overload neither OH nor mechanical switches.

Quick tip, just as I´m getting aware during the discussion: If you do the first “if”-Line

if ((Ueberschuss >= 2000|"W") && (C_Installation_Wasser_Heizstab_Temp.state < 70|"°C"))

into the “when”-conditions, the script is not even started :wink:

(looks a bit different in OH4, but that’s how I did):

configuration: {}
  - id: "4"
      itemName: MQTT_PV_EVULeistung
    type: core.ItemCommandTrigger
  - inputs: {}
    id: "3"
      itemName: MQTT_Ladestation_eAuto_ladt
      state: OFF
      operator: =
    type: core.ItemStateCondition
  - inputs: {}
    id: "2"
      itemName: MQTT_PV_BYD_Batterie_Ladestand
      state: "90"
      operator: ">"
    type: core.ItemStateCondition
  - inputs: {}
    id: "1"
      type: application/javascript
      script: >

I will check this :o.
Gimme a moment please :slight_smile: