Hi guys,
i just wanted to share a script that combines my photovoltaik system (power inverter: sma) with the heatpump (waterkotte eco air a1 touch) to maximize the usage of the power excess. It is based on an already posted script here in the community (but i wasn’t able to find the original post again. (this is only for demostration purpose, use it on your own risk)
I started last week with openhab2 by moving from zwave.me to the openhab2 system.
I’m still working on the improvement of the scripts, so feel free to give me some hints.
binding = ntp,astro,smaenergymeter,ecotouch1
Rules
import java.util.concurrent.locks.ReentrantLock
/*
based on a script of the openhab2 community
*/
var Timer execHeatingTimer = null
var Timer execWarmWaterTimer = null
var ReentrantLock ruleIsRunning = new ReentrantLock
rule "PV Optimisation - Initialize Values"
when
System started
then
PV_PowerExcessThreshold.postUpdate(1500) //exceed threshold in kWh
PV_PowerExcessDuration.postUpdate(10) //average exceed must be above threshold for this duration (minutes)
PV_Warmwater_TargetTemperature.postUpdate(60)
PV_Heating_TargetAdaption.postUpdate("2.0")
PV_Optimisation_MinRunTime.postUpdate(60)
/* Do set default values only on start up (Rule is also trigged on changes of the rule itselfs on code changes) */
PV_Warmwater_DefaultTemperature.postUpdate("45")
PV_Heating_DefaultAdaption.postUpdate("-2.0")
if (PV_Optimisation.state == NULL) PV_Optimisation.postUpdate(ON) //activate optimatisation
if (PV_Optimisation_WarmwaterStatus.state == NULL) PV_Optimisation_WarmwaterStatus.postUpdate(OFF) //indicates if warm water is running
if (PV_Optimisation_HeatingStatus.state == NULL) PV_Optimisation_HeatingStatus.postUpdate(OFF) //indicates if heating is running
//force to execute rules to end heating even if rule was reloaded due code changes
if (PV_Optimisation_WarmwaterStatus.state == ON) {
PV_Optimisation_WarmwaterStatus.postUpdate(OFF)
PV_Optimisation_WarmwaterStatus.postUpdate(ON) // rule below should start
}
//force to execute rules to end heating even if rule was reloaded due code changes
if (PV_Optimisation_HeatingStatus.state == ON) {
PV_Optimisation_HeatingStatus.postUpdate(OFF)
PV_Optimisation_HeatingStatus.postUpdate(ON) // rule below should start
}
end
/* Stop heating on System Shut Down or Switch for STOP Optimisation or Sunset
*/
rule "Set Default Values On System Shut Down"
when
System shuts down
or
Channel 'astro:sun:home:set#event' triggered START
or
Item PV_Optimisation changed to OFF
then
if (PV_Optimisation_HeatingStatus.state == ON) {
val normAdaption = (PV_Heating_DefaultAdaption.state as DecimalType).toString;
HeatPump_Adapt_Heating.sendCommand(normAdaption)
PV_Optimisation_HeatingStatus.sendCommand(OFF)
sendMail(EMails.state.toString, "Smart-Home-Nachricht [PV] Heizen beendet.", "Eigenverbrauchsoptimierung beendet! (Heizen)")
logInfo("[PV]","Eigenverbrauchsoptimierung (Heizkurve) wurde ausgeschalten!")
}
if (PV_Optimisation_WarmwaterStatus.state == ON) {
val normTemperature = (PV_Warmwater_DefaultTemperature.state as DecimalType).toString;
HeatPump_Warmwater_Temperature.sendCommand(normTemperature)
PV_Optimisation_WarmwaterStatus.sendCommand(OFF)
sendMail(EMails.state.toString, "Smart-Home-Nachricht [PV] Warmwasser beendet.", "Eigenverbrauchsoptimierung beendet! (Warmwasser)")
logInfo("[PV]","Eigenverbrauchsoptimierung (Warmwasser) wurde ausgeschalten!")
}
end
/*
This Rule is trigged as soon as the Heating of Enery Usage Optimization is started.
It ensures that the heating is running a specific time and after that it ensures that the heating stops.
*/
rule "End Heating Time"
when
Item PV_Optimisation_HeatingStatus changed from OFF to ON
then
var Boolean isNotAlreadyRunning = (execHeatingTimer === null || execHeatingTimer.hasTerminated);
if (isNotAlreadyRunning) {
val sunsetTime = new DateTime(Sunset_Time.state.toString)
// create timer to allow the dumping to run for unitl 1h before sunset
execHeatingTimer = createTimer(sunsetTime.minusMinutes(60), [|
try {
val normAdaption = (PV_Heating_DefaultAdaption.state as DecimalType).toString;
HeatPump_Adapt_Heating.sendCommand(normAdaption)
PV_Optimisation_HeatingStatus.sendCommand(OFF)
logInfo("[PV]","Rücksetzen der Heizparameter auf " + normAdaption)
var float currentEnergyProduction = (SMAEnergyMeter_BezogeneLeistung.state as Number).floatValue
val energythreshhold = (PV_PowerExcessThreshold.state as Number).floatValue
if ( currentEnergyProduction < energythreshhold ) {
logInfo("[PV]", "Laufzeit Eigenverbrauchsoptimierung beendet, Produktion < Schwelle")
} else {
logInfo("[PV]", "Laufzeit Eigenverbrauchsoptimierung beendet, Produktion > Schwelle")
}
} finally {
execHeatingTimer = null;
}
])
}
end
/*
This Rule is trigged as soon as the warm water heating of Enery Usage Optimization is started.
It ensures that the warm water heating is running a specific time and after that it ensures that the heating stops.
*/
rule "End Water Heating Time"
when
Item PV_Optimisation_WarmwaterStatus changed from OFF to ON
then
var Boolean isNotAlreadyRunning = (execWarmWaterTimer === null || execWarmWaterTimer.hasTerminated);
if (isNotAlreadyRunning) {
// create timer to allow the dumping to run for at least the minimum run time
execWarmWaterTimer = createTimer(now.plusMinutes((PV_Optimisation_MinRunTime.state as Number).intValue), [|
try {
val normTemperature = (PV_Warmwater_DefaultTemperature.state as DecimalType).toString;
HeatPump_Warmwater_Temperature.sendCommand(normTemperature)
PV_Optimisation_WarmwaterStatus.sendCommand(OFF)
logInfo("[PV]","Rücksetzen der Warmwasserparameter auf " + normTemperature)
var float currentEnergyProduction = (SMAEnergyMeter_BezogeneLeistung.state as Number).floatValue
val energythreshhold = (PV_PowerExcessThreshold.state as Number).floatValue
if ( currentEnergyProduction < energythreshhold ) {
logInfo("[PV]", "Laufzeit Eigenverbrauchsoptimierung beendet, Produktion < Schwelle")
} else {
logInfo("[PV]", "Laufzeit Eigenverbrauchsoptimierung beendet, Produktion > Schwelle")
}
}
catch(Throwable t) {
logError("[PV]", "Error End Heating Time" + t.getMessage())
}
finally {
execWarmWaterTimer = null;
}
])
}
end
/*
This Rule is trigged as soon as the sma meter device reports an change on the output of power.
Requires:
Persistence-Binding; E-Mail-Binding; Heating-Binding; PV-Binding
*/
rule "Energy Usage Optimatisation"
when Item SMAEnergyMeter_BezogeneLeistung changed
then
if (ruleIsRunning.isLocked) {
logDebug("[PV]", "Rule is locked")
return;
}
try {
ruleIsRunning.lock()
val sunsetTime = new DateTime(Sunset_Time.state.toString)
//check if its already to late to start heating
if (now.plusMinutes((PV_Optimisation_MinRunTime.state as Number).intValue + 30).isAfter(sunsetTime)) {
logDebug("[PV]", "Sunset is to close to start optimisation " + Sunset_Time)
ruleIsRunning.unlock()
return;
}
// check if the logic to dump excess power production into the heating of "hot" warmwater is switched on
// and heating is not already startet
if ((PV_Optimisation.state == OFF) || ((PV_Optimisation_WarmwaterStatus.state == ON) && (PV_Optimisation_HeatingStatus.state == ON))) {
logDebug("[PV]", "Nothing to do... Optimisation is:" + PV_Optimisation.state.toString + "/WarmWater:" + PV_Optimisation_WarmwaterStatus.state.toString + "/Heating:" + PV_Optimisation_HeatingStatus.state.toString)
ruleIsRunning.unlock()
return;
}
logDebug("[PV]", "Single Instance of Rule is Executed")
val float avgEnergyExcess = SMAEnergyMeter_BezogeneLeistung.averageSince(now.minusMinutes((PV_PowerExcessDuration.state as Number).intValue)).floatValue //need persistance
val energythreshhold = (PV_PowerExcessThreshold.state as Number).floatValue
val energythreshhold2 = 2 * energythreshhold
// check if the photovoltaics plant is currently generating enough energy to that the net export to the grid is higher than
// the excess threshold
if ( avgEnergyExcess < energythreshhold ) {
ruleIsRunning.unlock()
return;
}
logDebug("[PV]", "Produktion der letzten 10 Minuten: " + avgEnergyExcess + " > Schwelle "+ energythreshhold)
// check current excess energy
val currentEnergyProduction= (SMAEnergyMeter_BezogeneLeistung.state as Number).floatValue
if ( currentEnergyProduction < energythreshhold ) {
logDebug("[PV]","Aktuelle Produktion "+ currentEnergyProduction +" unterhalb der Schwelle, Vorgang abgebrochen.")
ruleIsRunning.unlock()
return;
}
logDebug("[PV]","Produktion " + currentEnergyProduction + "> Schwelle "+ energythreshhold + ", Starte Prüfung für Eigenverbrauchsoptimierung")
//-- All requirements are met so we can start using energy output
val currentTemperature = (HeatPump_Warmwater_CurrentTemperature.state as DecimalType).floatValue
val targetTemperature = (PV_Warmwater_TargetTemperature.state as DecimalType).floatValue
val Boolean startWarmWater = (currentTemperature < (targetTemperature-15)); //careful, if running a time based heating on the heat pump itself (on my device this means a decrease of target temperature so that the temperatur could never be reached)
if (startWarmWater && (PV_Optimisation_WarmwaterStatus.state != ON)) {
//begin dumping excess porwer into warmwater heating
logInfo("[PV]","Produktion > Schwelle, Starte Warmwasser " + currentTemperature + "/" + targetTemperature)
HeatPump_Warmwater_Temperature.sendCommand((PV_Warmwater_TargetTemperature.state as DecimalType).toString)
PV_Optimisation_WarmwaterStatus.postUpdate(ON) //triggers additional rule
Thread::sleep(100)
sendMail(EMails.state.toString, "Smart-Home-Nachricht [PV] Warmwasser aktiviert.", "Produktion " + currentEnergyProduction + " > Schwelle "+ energythreshhold + " => Starte Warmwasser.")
}
var float temperatureOutside = (HeatPump_Temperature_Outside24h.state as DecimalType).floatValue
val boolean itsColdOutside = temperatureOutside < 16;
val Boolean startHeating = (!startWarmWater || ((currentEnergyProduction > energythreshhold2) && (avgEnergyExcess > energythreshhold2) ))
if (startHeating && (PV_Optimisation_HeatingStatus.state != ON) && itsColdOutside) {
logInfo("[PV]","Produktion > Schwelle, Starte Heizung")
HeatPump_Adapt_Heating.sendCommand((PV_Heating_TargetAdaption.state as DecimalType).toString)
PV_Optimisation_HeatingStatus.postUpdate(ON)
Thread::sleep(100)
sendMail(EMails.state.toString, "Smart-Home-Nachricht [PV] Heizung aktiviert.", "Produktion " + currentEnergyProduction + " > Schwelle "+ energythreshhold + " => Erhöhe Heizkurve.")
}
}
finally {
ruleIsRunning.unlock()
}
end
Items
/*Global*/
Number Chart_Period "Chart Period"
String EMails "Für Informationen genutzte E-Mail [%s]"
/* Astro
Requires: Astro Binding / Gerhard Riegler
*/
DateTime Sunrise_Time "Sunrise [%1$tH:%1$tM]" { channel="astro:sun:home:rise#start" }
DateTime Sunset_Time "Sunset [%1$tH:%1$tM]" { channel="astro:sun:home:set#start" }
/* Photovoltaik
Requires: SMA Energy Meter Binding / Binding for SMA Energy Meter. /Author: Osman Basha
*/
Group Chart_Photovoltaik
Group Photovoltaik (Home)
Group Photovoltaik_power (Home)
Group Photovoltaik_energy (Home)
Switch PV_Optimisation "Optimierung Eigenverbrauch [%i]" <settings> (Photovoltaik)
Switch PV_Optimisation_WarmwaterStatus "Optimierung Warmwasser [%i]" <switch> (Heatpump,Photovoltaik) //ReadOnly
Switch PV_Optimisation_HeatingStatus "Optimierung Heizung [%i]" <switch> (Heatpump,Photovoltaik) //ReadOnly
Number PV_PowerExcessThreshold "Grenze für Eigenverbrauchsoptimierung [%i kWh]" <energy> (Photovoltaik)
Number PV_PowerExcessDuration "Minimale Dauer des Überschusses [%i Min]" <temperature> (Photovoltaik)
Number PV_Warmwater_DefaultTemperature "Normal-Vorgabe für Warmwassertemperatur [%.1f °C]" <temperature> (Photovoltaik, Heatpump)
Number PV_Warmwater_TargetTemperature "Ziel-Vorgabe für Warmwassertemperatur [%.1f °C]" <temperature> (Photovoltaik, Heatpump)
Number PV_Optimisation_MinRunTime "Dauer der Aufheizperiode [%i Min]" <settings> (Photovoltaik, Heatpump)
Number PV_Heating_TargetAdaption "Vorgabe für Heizkurve-Niveau [%.1f °C]" <number> (Photovoltaik, Heatpump)
Number PV_Heating_DefaultAdaption "Normal-Vorgabe für Heizkurve-Niveau [%.1f °C]" <number> (Photovoltaik, Heatpump)
/* Waterkotte EcoTouch heat pump
Requires Waterkotte EcoTouch Binding / This is the binding for the Waterkotte EcoTouch heatpump. /Author: Sebastian Held
*/
Group Heatpump (Home)
Group Heatpump_outside "Heizung-Außen" (Outside)
Group Heatpump_source (Home)
Group Heatpump_control (Home)
Group Heatpump_water "Heizung-Wasser" (Home)
Group Heatpump_heating "Heizung-Heizen" (Home)
Group Heatpump_power "Heizung-Leistung" (Home)
Group Heatpump_state "Heizung-Status" (Home)
Number HeatPump_Temperature_Outside "Wärmepumpe Außentemperatur [%.1f °C]" <temperature> (Heatpump,Heatpump_outside,Outside) { ecotouch="temperature_outside" }
Number HeatPump_Temperature_Outside1h "Wärmepumpe Außentemperatur 1h [%.1f °C]" <temperature> (Heatpump,Heatpump_outside,Outside) { ecotouch="temperature_outside_1h" }
Number HeatPump_Temperature_Outside24h "Wärmepumpe Außentemperatur 24h [%.1f °C]" <temperature> (Heatpump,Heatpump_outside,Outside) { ecotouch="temperature_outside_24h" }
Number HeatPump_Temperature_8 "Wärmepumpe Vorlauf [%.1f °C]" <temperature> (Heatpump,Heatpump_control,Home) { ecotouch="temperature_flow" }
Number HeatPump_Temperature_9 "Wärmepumpe Rücklauf [%.1f °C]" <temperature> (Heatpump,Heatpump_control,Home) { ecotouch="temperature_return" }
Number HeatPump_Temperature_10 "Wärmepumpe Rücklauf Soll [%.1f °C]" <temperature> (Heatpump,Heatpump_control,Home) { ecotouch="temperature_return_set" }
Number HeatPump_Warmwater_CurrentTemperature "Wärmepumpe Warmwasser Ist [%.1f °C]" <temperature> (Heatpump,Heatpump_water,Home) { ecotouch="temperature_water" }
Number HeatPump_Temperature_12 "Wärmepumpe Warmwasser Soll [%.1f °C]" <temperature> (Heatpump,Heatpump_water,Home) { ecotouch="temperature_water_set" }
Number HeatPump_Warmwater_Temperature "Wärmepumpe Warmwasser Soll2 [%.1f °C]" <temperature> (Heatpump,Heatpump_water,Home) { ecotouch="temperature_water_set2" }
Number HeatPump_Temperature_14 "Wärmepumpe Heizung Ist [%.1f °C]" <temperature> (Heatpump,Heatpump_heating,Home) { ecotouch="temperature_heating_return" }
Number HeatPump_Temperature_15 "Wärmepumpe Heizung Soll [%.1f °C]" <temperature> (Heatpump,Heatpump_heating,Home) { ecotouch="temperature_heating_set" }
Number HeatPump_Temperature_16 "Wärmepumpe Heizung Soll-Wert [%.1f °C]" <temperature> (Heatpump,Heatpump_heating,Home) { ecotouch="temperature_heating_set2" }
Number HeatPump_Adapt_Heating "Wärmepumpe Temperaturanpassung -2,0/+2,0 [%.1f °C]" <number> (Heatpump,Heatpump_heating,Home) { ecotouch="adapt_heating" }
Number HeatPump_state "Wärmepumpe Status [%i]" <settings> (Heatpump,Home) { ecotouch="state" }
Switch HeatPump_state_sourcepump "Wärmepumpe Status Quellenpumpe [%i]" <settings> (Heatpump,Heatpump_state,Home) { ecotouch="state_sourcepump" }
Switch HeatPump_state_heatingpump "Wärmepumpe Status Heizungsumwälzpumpe [%i]" <settings> (Heatpump,Heatpump_state,Home) { ecotouch="state_heatingpump" }
Switch HeatPump_state_compressor1 "Wärmepumpe Status Kompressor [%i]" <settings> (Heatpump,Heatpump_state,Home) { ecotouch="state_compressor1" }
Switch HeatPump_state_water "Wärmepumpe Status Motorventil Warmwasser[%i]" <settings> (Heatpump,Heatpump_state,Home) { ecotouch="state_water" }
/* <not used> */
Number HeatPump_Temperature_6 "Wärmepumpe Quelleneintrittstemperatur [%.1f °C]" <temperature> (Heatpump,Heatpump_source,Home) { ecotouch="temperature_source_in" }
Number HeatPump_Temperature_7 "Wärmepumpe Quellenaustrittstemperatur [%.1f °C]" <temperature> (Heatpump,Heatpump_source,Home) { ecotouch="temperature_source_out" }
Number HeatPump_power_1 "Wärmepumpe elektrische Leistung [%.1f kW]" <energy> (Heatpump,Heatpump_power,Home) { ecotouch="power_compressor" }
Number HeatPump_power_2 "Wärmepumpe thermische Leistung [%.1f kW]" <energy> (Heatpump,Heatpump_power,Home) { ecotouch="power_heating" }
Things I don’t like about the framework:
- To much different types for same semantics: null versus NULL, Number versus int etc. (remembers to visual and borland c++ variable type chaos)
- I know thread programming is a pain … but createTimer is not one of my favorite solutions (that semaphore handling is real strange)
- No defined order of rule initialization. (even rules with System started trigger can be executed after others)
- No possibility to define an own order for execution of .rules .items files etc.
- Break of good object oriented programming practice in rules due to missing of language element for reusable code (i know lambda / inline functions but those are no improvement in coding language evolution)