Use excess Photovoltaics Production intelligently (Building a time based "hysteresis")

Hi folks,

I have a question around timers in rules. Let me describe my use case first:

I’m getting a photovoltaics plant soon and I want build a few solutions to optimize my self consumption, i.e. switch on dump loads when there is enough power production instead of feeding the power to the grid.

One dump load that I plan to use is the hot water generation of my heatpump. The heatpump being one that uses a Luxtronic controller, I can use the novelanheatpump binding to switch the hot water target temperature to a higher temperature and dump the excess power from the photovoltaics into the hot water tank.

I order not to generate excess compressor starts on the heatpump, what I had in mind was to put on a few delays before actually switching the temperature higher.

SO:

  • When I detect that the excess power production is higher than a certain threshhold, I want to start a timer “minimum wait time”, say 15 minutes
  • If the timer expires, I want to check if the power generation is still above the threshhold.
  • If yes, set the hot water target temperature on the heatpump to, say 55°C.
  • Now start another timer (again configurable) “minimum run time”, during which the hot water temp shouldn’t be modified again, regardless of power production
  • Once the timer expires, again check power production. If it dropped below the threshhold, switch the hot water temperature back down to it’s standard value of 43°C.

I haven’t played around with any of the timing rules yet, so it would be great to get some pointers on how to do this.

Thanks!

Thomas

This is what i came up with, go ahead and shoot it down :grinning:

var Timer tEV_ww_minrun = null
var Timer tEV_ww_waittime = null

rule "default values"

when System started 

then
  sendCommand(EV_warmwater,OFF) 
  sendCommand(EV_warmwater_exc,55)
  sendCommand(EV_warmwater_norm,43)
  sendCommand(EV_warmwater_waittime,15)
  sendCommand(EV_warmwater_minrun,15)
  sendCommand(EV_warmwater_state,OFF)
end


rule "EV_warmwater"

when Item se9km_Watt received update
then
// check if the logic to dump excess power production into the heating of "hot" warmwater is switched on
	if (EV_warmwater.state == ON) {
		// check if the photovoltaics plant is currently generating enough energy to that the net export to the grid is higher than
		// the excess threshold of 1500 Watts
		if ( (se9km_Watt.state as DecimalType).floatValue < -1500) {
			// check if there isn't any timer already running
			if ((tEV_ww_minrun == null) && (tEV_ww_waittime == null)) {
				// create timer to wait for the minimum waittime and check excess energy again
				tEV_ww_waittime = createTimer(now.plusMinutes((EV_warmwater_waittime.state as DecimalType).intValue)) [|
					if ( (se9km_Watt.state as DecimalType).floatValue < -1500) {
						 //minimum waittime ran out, and excess power is still there, begin dumping excess porwer into warmwater heating
						 sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_exc.state as DecimalType).toString)
						 // create timer to allow the dumping to run for at least the minimum run time
						 tEV_ww_minrun = createTimer(now.plusMinutes((EV_warmwater_minrun.state as DecimalType).intValue)) [|
						 	//minrun time is done, switch warmwater load tempeature back to normal
						 	sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_norm.state as DecimalType).toString)
						 	tEV_ww_minrun = null
						 ] 
					}
					tEV_ww_waittime = null
				]
			}
		}
	} 
end
1 Like

after doing some testing, it seems that this setup will mostly work. here’s a version that carries some additional optimisations, for instance this one will keep the target temperature high as long as production of above the threshold. If it drop below, it will switch the temp back to normal.

import org.openhab.core.library.types.*
import java.lang.Math

var Timer tEV_ww_minrun = null
var Timer tEV_ww_waittime = null

var Timer testtimer1 = null
var Timer testtimer2 = null


/* rule "default values"

when System started 

then
  sendCommand(EV_warmwater,ON) 
  sendCommand(EV_warmwater_threshhold,1500)
  sendCommand(EV_warmwater_exc,55)
  sendCommand(EV_warmwater_norm,43)
  sendCommand(EV_warmwater_waittime,5)
  sendCommand(EV_warmwater_waittime_run,OFF)
  sendCommand(EV_warmwater_minrun,30)
  sendCommand(EV_warmwater_state,OFF)
  sendCommand(se9km_Watt_int,2)
  sendCommand(se9km_WattSF_int,3)
  testtimer1 = createTimer(now.plusSeconds(90)) [|
  	sendCommand(se9km_Watt_int,3)
  	testtimer1 = null
  ]
  testtimer2 = createTimer(now.plusSeconds(180)) [|
  	sendCommand(se9km_Watt_int,0)
  	testtimer2 = null
  ]

end
*/

rule "EV_warmwater"

when Item se9km_Watt received update
then
// check if the logic to dump excess power production into the heating of "hot" warmwater is switched on
	if (EV_warmwater.state == ON) {
		// check if the photovoltaics plant is currently generating enough energy to that the net export to the grid is higher than
		// the excess threshold of 1500 Watts
		if ( (se9km_Watt.state as DecimalType).floatValue > (EV_warmwater_threshhold.state as DecimalType).floatValue ) {
			// check if there isn't any timer already running
			if ((tEV_ww_minrun == null) && (tEV_ww_waittime == null)) {
				// create timer to wait for the minimum waittime and check excess energy again
				sendCommand(EV_warmwater_waittime_run,ON)
				logInfo("[EV]", "Produktion >Schwelle, Starte min-Wait Timer fuer Eigenverbrauchsoptimierung Warmwasser")
				tEV_ww_waittime = createTimer(now.plusMinutes((EV_warmwater_waittime.state as DecimalType).intValue)) [|
					if ( (se9km_Watt.state as DecimalType).floatValue > (EV_warmwater_threshhold.state as DecimalType).floatValue ) {
						 //minimum waittime ran out, and excess power is still there, begin dumping excess porwer into warmwater heating
						 logInfo("[EV]","Produktion >Schwelle, Starte Eigenverbrauchsoptimierung Warmwasser")
						 sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_exc.state as DecimalType).toString)
						 sendCommand(EV_warmwater_state,ON)
						 // create timer to allow the dumping to run for at least the minimum run time
						 tEV_ww_minrun = createTimer(now.plusMinutes((EV_warmwater_minrun.state as DecimalType).intValue)) [|
						 	//minrun time is done, if production is less than threshhold switch temp back to normal,
						 	//otherwise leave it on high, and we'll check production otside of the timer
						 	if ( (se9km_Watt.state as DecimalType).floatValue < (EV_warmwater_threshhold.state as DecimalType).floatValue ) {
						 		sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_norm.state as DecimalType).toString)
						 		sendCommand(EV_warmwater_state,OFF)
						 		tEV_ww_minrun = null
						 		logInfo("[EV]", "minimale Laufzeit fuer Aufladen WW-Speicher beendet, Produktion < Schwelle")
							} else {
						 		logInfo("[EV]", "minimale Laufzeit fuer Aufladen WW-Speicher beendet, Produktion > Schwelle")
						 		tEV_ww_minrun = null
						 	}
						] 
					}
					else 
					// minwait has expired, but the production in now less than threshhold, destroy the minwait timer, to be safe, also
					// switch back target temperature to normal
					{
						if (tEV_ww_waittime != null) {
							tEV_ww_waittime.cancel
							tEV_ww_waittime = null
							sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_norm.state as DecimalType).toString)
						 	sendCommand(EV_warmwater_state,OFF)
							logInfo("[EV]","Produktion unter die Schwelle gefallen, min-Wait Timer gecancelt")
							sendCommand(EV_warmwater_waittime_run,OFF)
						}
					}
					// normal exit of the minrun timer, destroy it
					tEV_ww_waittime = null
					sendCommand(EV_warmwater_waittime_run,OFF)
				]
			}
		}
		else
		// plan is generating less than 1500W
		{
			// check if we need to cancel the minimal wait timer
			if (tEV_ww_waittime != null) {
				tEV_ww_waittime.cancel
				tEV_ww_waittime = null
				logInfo("[EV]","Produktion unter die Schwelle gefallen, min-Wait Timer gecancelt")
				sendCommand(EV_warmwater_waittime_run,OFF)
			} else {
				// minimum wait wasn't running, maybe we're in the minimal running time
				if (tEV_ww_minrun == null) {
					// we're outside of minimal running time, so we can switch back the target temperature back to normal
					logInfo("[EV]","Produktion unter Schwelle gefallen, Brauchwasser-Soll zurueck auf normal")
					sendCommand(HeatPump_warmwater_temperature,(EV_warmwater_norm.state as DecimalType).toString)
					sendCommand(EV_warmwater_state,OFF)
				}
			}
		}
	} 
end
1 Like

It looks like you are a OH professional already. :slight_smile:

How often this gets an update?
when Item se9km_Watt received update

You could prevent some rule executions with
when Item se9km_Watt changed
maybe. This will only trigger the rule if the value is another then last time.

Thanks, that is high praise indeed!

This items will be fed by a Smartmeter devices that will most likely update once a second or so. Good idea to switch the trigger to “item changed”, but it will probably not make too much of a difference.

BR,

Thomas

Every second? Wow. This means your rule will be fired every second also!? This will eat up a lot of cpu time I think. Nothing you want in a responsive home automation environment. Especially if you want to switch light bulbs also. Or you are on a Raspberry PI. Maybe you could tweak it with a rule that looks only every 5 mins or so onto the values.

Limiting the rule execution might be a good idea.
I run OH in a VM, so there is a bit of CPU available, but this is definitely worth looking at. I might set up a rule that copies the meter value to a different item once every 5 minutes and check that item for changes. I am checking the timers for ‘== null’ at the start of the rule, so it will usually abort execution pretty early on.

I’m not absolutely sure about this. But I noticed (in my RaspiPi) that if a rule is currently processed, all other rules have to wait until this is finished. Maybe this is valid only on the same rules file. I can’t say how good OH is on multithreading. But it relies on an event system and each event is normally processed one by one.

Since I noticed this, a optimize everything and try to schedule (timer) as much as I can and completely prevent hughe calculations if not necessary. It looks like timers are running in it’s own thread and don’t block anything.

Please (real) OH pros. Can you bring light into the darkness? :slight_smile: