Startseite » automatische Heizregeln mit OpenHAB und semantischen Gruppen

automatische Heizregeln mit OpenHAB und semantischen Gruppen

Wenn dein Tagesablauf menschlich ist und sich häufig Änderungen ergeben wie unterschiedliche Anwesenheitszeiten, Urlaub, Ausflüge, Schichtarbeit, … dann willst du dein Heizsystem dem vielleicht auch ebenso dynamisch anpassen können. Vielleicht hast du auch ab und zu Änderungen in der Hardware und baust dein Smarthome gerade erst auf. Dann passieren in den Regeln schnell mal Fehler.

Im Folgenden will ich dir ein Beispiel vorstellen, wie eine Regel deine Heizungen ganz im Griff hat, abhängig von semantischen Gruppen.

notwendige Gruppen

Für das Konzept sind zwei Typen von Gruppen notwendig.

  1. semantische Gruppen: Du hast dein Zuhause bereits mit semantischen Gruppen definiert
  2. Triggergruppen: Deine Items müssen wissen, dass sie die Regel auslösen müssen. Daher fassen wir sie in Triggergruppen zusammen
  3. Item-Gruppen: Items, die zu einem Gegenstand als Gruppe zugeordnet werden

Gruppen sind technisch immer die gleichen Arten Gruppen. Die unterschiedliche Bezeichnung ist lediglich aufgrund der Funktion, die sie erfüllen sollen, gewählt. Das hilft beim Verstehen und Nachvollziehen.

/etc/openhab/items/semantic-model.items

//functional Groups
Group 						fgPersist							// for Items to preserve their history
Group:Number:COUNT(OPEN)	fgIntrusion		"Intrusion [%s]"	// All window- and doorsensors

Group						fgRadiatorBoost						// Radiator Boost
Group:DateTime:EARLIEST		fgFaultDetection					// Fault or disturbance detection for wireless connections (as "last seen")

//trigger groups
Group		tgHeatingEvents			// events, that will change temperature to a custom setpoint like a calendar evenent
Group		tgWindow				// Window- or door contact sensors
Group		tgMotionDetection		// PIR Sensors
Group		tgHausAus				// a special button to tell my home, that I will go to sleep now
Group		tgWatchdogEvents		// alarm system events
Group		tgLightshow				// dynamic scene picker
Group		tgLSchange				// dynamic scene influencer
Group		tgLSinterrupt			// dynamic scene interruptor
Group		tgWarnOnLimit			// Gas- and Rad-Sensors for warning
Group		tgCalc					// Item triggers a calculations for another Item (like Temperature or Humidity for Dewpoint)



// Semantic Groups
Group sgWacholderweg "Zuhause" 													["Location"]
	Group sgIndoor "Indoor" 															["Indoor"]
		Group sgHouse "Haus" 						<group> 		(sgIndoor) 			["House"] 		{ alexa="Other" }
			Group sgGroundFloor "EG" 				<groundfloor> 	(sgHouse) 			["GroundFloor"]
				Group sgBedroom "Schlafzimmer" 		<bedroom> 		(sgGroundFloor) 	["Bedroom"] 	{ alexa="Other" }
				Group sgCorridorEG "Flur"	 		<corridor> 		(sgGroundFloor) 	["Corridor"]	{ alexa="Other" }
				Group sgKitchen "Küche" 			<kitchen> 		(sgGroundFloor) 	["Kitchen"] 	{ alexa="Other" }
				Group sgBathroom "Bad" 				<bath> 			(sgGroundFloor) 	["Bathroom"] 	{ alexa="Other" }
				Group sgLivingRoom "Wohnzimmer" 					(sgGroundFloor) 	["LivingRoom"] 	{ alexa="Other" }
			Group sgCellar "Keller" 				<cellar> 		(sgHouse) 			["Cellar"] 		{ alexa="Other" }
				Group sgCorridorCellar "Flur Keller" <corridor> 	(sgCellar) 			["Corridor"]
				Group sgGarage "Garage" 			<garagedoor> 	(sgCellar) 			["Garage"] 		
	Group sgOutdoor "Outdoor" ["Outdoor"]
				Group sgEnvironment "Draussen" 		<flow> 			(sgOutdoor) 		["Garden"] 		{ alexa="Other" }
				Group sgGarden "Garten" 			<garden> 		(sgOutdoor) 		["Garden"]
				Group sgTerrace "Terrasse" 			<terrace> 		(sgOutdoor) 		["Terrace"] 	{ alexa="Other" }

mögliche beeinflussende Items

tgHeatingEvents

Die Kalender-Items basieren auf dem OpenHAB ical-Modul. So kann aus einem Google-Kalender die Beschreibung als Zahl ausgelesen werden und als Setpoint an ein Thermostat übermittelt werden. Die items werden zu einer itemGroup pro Kalender zusammengefasst. diese itemGroup wird zu einem Raum mittels semantischer Gruppe zugewiesen. Die semantischen Tags („Webservice“) geben dem System Aufschluss darüber, worum es sich bei der Gruppe handelt.

Wenn sich der Start-Zeitpunkt oder der Titel ändert, soll damit die Heizregel ausgelöst werden. Das wird mit der Triggergruppe „tgHeatingEvents“ deklariert.

/etc/openhab/items/calendars.items

Group:Switch:COUNT(ON)		igCalHeatingWohnzimmer							"Heizprofil Wohnzimmer"				(sgLivingRoom)["WebService"]
Switch				KalenderHeizprofilWohnzimmer_CurrentEventPresence		"Current Event Presence"			(igCalHeatingWohnzimmer)											{ channel="icalendar:calendar:Wohnzimmer:current_presence" }
Number:Temperature	KalenderHeizprofilWohnzimmer_CurrentEventTitle			"Current Event Title [%.1f °C]"		(igCalHeatingWohnzimmer,tgHeatingEvents)["Setpoint","Temperature"]	{ channel="icalendar:calendar:Wohnzimmer:current_title" }
DateTime			KalenderHeizprofilWohnzimmer_CurrentEventStart			"Current Event Start"				(igCalHeatingWohnzimmer,tgHeatingEvents)["Status","Timestamp"]		{ channel="icalendar:calendar:Wohnzimmer:current_start" }
DateTime			KalenderHeizprofilWohnzimmer_CurrentEventEnd			"Current Event End"					(igCalHeatingWohnzimmer)											{ channel="icalendar:calendar:Wohnzimmer:current_end" }

Group:Switch:COUNT(ON)		igCalHeatingBedroom								"Heizprofil Schlafzimmer"			(sgBedroom)["WebService"]
Switch				KalenderHeizprofilSchlafzimmer_CurrentEventPresence		"Current Event Presence"			(igCalHeatingBedroom)												{ channel="icalendar:calendar:Schlafzimmer:current_presence" }
Number:Temperature	KalenderHeizprofilSchlafzimmer_CurrentEventTitle		"Current Event Title [%.1f °C]"		(igCalHeatingBedroom,tgHeatingEvents)["Setpoint","Temperature"]		{ channel="icalendar:calendar:Schlafzimmer:current_title" }
DateTime			KalenderHeizprofilSchlafzimmer_CurrentEventStart		"Current Event Start"				(igCalHeatingBedroom,tgHeatingEvents)["Status","Timestamp"]			{ channel="icalendar:calendar:Schlafzimmer:current_start" }
DateTime			KalenderHeizprofilSchlafzimmer_CurrentEventEnd			"Current Event End"					(igCalHeatingBedroom)												{ channel="icalendar:calendar:Schlafzimmer:current_end" }

// ... eventually more calendars like these ...

tgWindow

Dabei handelt es sich um simple Items, die den Status von Fenstern und Türen aufzeigen. Ich hab mich für Sensoren auf Zigbee-Basis und MQTT entschieden. Die Batterien halten ewig.

Der Kontakt- und Batterie-Status reichen für die Zwecke vollkommen aus. Auch diese beiden Zustände werden zu einer itemGroup zusammengefasst. Diese itemGroup wird als „Window“ getaggt und einem Raum zugeordnet. die fg-Gruppen sind an anderer Stelle interessant, jedoch nicht für die Heizregel. Auch diese Items müssen sauber getaggt werden.

Wenn sich ein Fenster öffnet, soll die Heizung in dem Raum abgeschaltet werden.

/etc/openhab/items/zigbee-sensors.items

Group:Number:COUNT(OPEN)	igWindowSensor_LivingRoom			"Fenster Wohnzimmer [%s]"	<Window>		(sgLivingRoom)["Window"]
Contact						WindowSensor_LivingRoom_contact		"Kontakt [%s]" 								(fgPersist,igWindowSensor_LivingRoom,fgIntrusion,tgWindow)["OpenState","Opening"]	{ channel="mqtt:topic:myMosquitto:WindowSensor_LivingRoom:contact" }
Number:Dimensionless		WindowSensor_LivingRoom_battery		"Batterie [%.1f %%]" 		<Battery>		(igWindowSensor_LivingRoom)["Battery","Measurement","Energy"]						{ channel="mqtt:topic:myMosquitto:WindowSensor_LivingRoom:battery" }

// ... eventually more sensors like these ...

tgHausAus (optional)

Das ist mein Knopf am Bett. Oder die Software-Variante als Switch-Item. Der soll das Haus abschalten, wenn ich ins Bett gehe und darauf drücke.

Tritt ein neues Ereignis ein, wie etwa eine Kalendergesteuerte Temperaturänderung, wird der Modus abgelöst.

/etc/openhab/items/hue.items

Group					igSmartButton1				"Haus-Aus-Knopf"								(sgBedroom)["RemoteControl"]
String					SmartButton1_DimmerSwitch													(igSmartButton1,tgHausAus)["Status"]				{ channel="mqtt:topic:myMosquitto:SmartButton1:action" } //on, off, skip_backward, skip_forward, press, hold, release
Number:Dimensionless	SmartButton1_Battery		"Batterie [%.1f %%]"	<Battery>				(igSmartButton1)["Battery","Measurement","Energy"]	{ channel="mqtt:topic:myMosquitto:SmartButton1:battery" }
Switch					SmartButton1_manual			"Haus"											(igSmartButton1,tgHausAus)["Switch"]				{ expire="2s,state=ON", alexa="Switch" }

tgWatchdogEvents (optional)

Das sind Events, die durch das Alarmsystem hervorgerufen werden. Hier bietet es sich an, das manuelle Einschalten der Alarmanlage das Heizsystem des Hauses in einen Urlaubsmodus zu schicken.

Heizungs-Items

Group					igThermostatWohnzimmer						"Wohnzimmer Heizung"				<radiator>		(sgLivingRoom)["RadiatorControl"]										{ synonyms="Wohnzimmer Thermostat", alexa="Thermostat" }
Number:Temperature		ThermostatWohnzimmer1_Temperature			"Aktuelle Temp. (l) [%.1f °C]"		<Temperature>	(fgPersist,igThermostatWohnzimmer)["Measurement","Temperature"]			{ channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:local_temperature" }
Number:Temperature		ThermostatWohnzimmer1_SetTemperature		"Soll-Temp. (l) [%.1f °C]"			<Temperature>	(fgPersist,igThermostatWohnzimmer,sgLivingRoom)["Setpoint","Temperature"]{ alexa="TargetTemperature", channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:current_heating_setpoint", channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer2:current_heating_setpoint" }
Number:Temperature		ThermostatWohnzimmer1_SetTemperatureRO		"Soll-Temperatur (RO) [%.1f °C]"	<Temperature>	(igThermostatWohnzimmer)["Measurement","SetpointRO"]					{ channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:current_heating_setpoint" }
String					ThermostatWohnzimmer1_RadiatorMode			"Radiator mode [%s]"								(igThermostatWohnzimmer)												{ expire="1s,manual", channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:preset" }
Number:Dimensionless	ThermostatWohnzimmer1_Battery				"Battery level [%.1f %%]"			<Battery>		(igThermostatWohnzimmer)["Battery","Measurement","Energy"]				{ alexa="BatteryLevel", channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:battery" }
Number:Dimensionless	ThermostatWohnzimmer1_ValvePosition			"Ventilposition [%.1f %%]"							(fgPersist,igThermostatWohnzimmer)["OpenLevel","Opening"]				{ channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:valve_position" }
Switch					ThermostatWohnzimmer1_Boost					"Boost"												(igThermostatWohnzimmer,fgRadiatorBoost)["Switch","Opening"]			{ channel="mqtt:topic:myMosquitto:ThermostatWohnzimmer1:boost" }

OpenHAB Rule DSL

Sind die Gruppen und Items sauber definiert, kann die Regel wie folgt (oder auf deine Bedürfnisse angepasst) verwendet werden. Zunächst werden die Bedingungen eines Ereignisses ermittelt. Erst im letzten Bereich werden diese dann zu einer Änderung des Sollwertes zusammengesetzt.

import org.openhab.core.model.script.ScriptServiceUtil
import java.util.List
import java.util.Map

var Map<String, QuantityType<Temperature>> setpoints = newHashMap

//it is possible, that the signal gets lost. So try it again, till the not commanded item reacts to a change
//yes, i could set the "noautoupdate"-option. But alexa then will answer wrong temperature, when i ask... so: duplicate item
val setRadiator = [ NumberItem radiator, QuantityType<Temperature> setpoint, Map<String, Timer> setpoints |
	//when setpoint is changed, send it multiple times because sometimes the zigbee-signal gets lost
	val String igName = radiator.getGroupNames.findFirst[ String groupName | groupName.startsWith("ig") ]
	val GroupItem igItem = ScriptServiceUtil.getItemRegistry.getItem(igName) as GroupItem
	
	val NumberItem setpointROitem = igItem.members.findFirst[ item | item.hasTag("Measurement") && item.hasTag("SetpointRO") ]
	
	setpoints.put(radiator.name,setpoint)
	
	//2s, 4s, 8s, 16s ... make the timespan bigger between the tryouts
	createTimer(now, [ |
		var long sleep = 2000
		for(var i=0; i<10 && (setpointROitem.state == NULL || (setpointROitem.state as QuantityType<Temperature>) != setpoints.get(radiator.name)); i++) {
			radiator.sendCommand(setpoint)
			sleep *= 2
			Thread::sleep(sleep)
		}
	])
	
]

rule "heating"
when
	Member of tgHeatingEvents changed
	or Member of tgWindow changed
	or Member of tgHausAus changed
	or Member of tgWatchdogEvents changed
then {

	//find the affected rooms
	var List<String> roomList = newArrayList()
	
	//which rooms shall be affected due to the trigger?
	if(triggeringItem.name == "SmartButton1_DimmerSwitch" || triggeringItem.name == "SmartButton1_manual"){
		//which rooms shall be shut down, when I go to sleep and press my home-off-button
		roomList = newArrayList("sgOfficeJulia","sgKitchen","sgLivingRoom","sgOfficeAlex")
	}
	else if(triggeringItem.name == "WatchdogManual"){
		//which rooms shall be shut down, when I activate the alarm system manually?
		roomList = newArrayList("sgBedroom","sgOfficeJulia","sgKitchen","sgLivingRoom","sgOfficeAlex")
	}
	else{
		//which room is affected due to the triggering item room?
		
		//detect itemGroup (ig) of the item
		val igName = triggeringItem.getGroupNames.findFirst[ String groupName | groupName.startsWith("ig") ]
		val GroupItem igItem = ScriptServiceUtil.getItemRegistry.getItem(igName) as GroupItem
		
		//detect semanticGroup of the itemGroup
		val sgName = igItem.getGroupNames.findFirst[ String groupName | groupName.startsWith("sg") ]
		roomList = newArrayList(sgName)
	}
	
	
	//do something with the rooms:
	roomList.forEach[ String sgName |
		val GroupItem room = ScriptServiceUtil.getItemRegistry.getItem(sgName) as GroupItem
	
		//what states has the room?
		
		//window open?
		var window = CLOSED
		room.members.forEach[ NumberItem thing |
			if( thing.tags.contains("Window") || thing.tags.contains("FrontDoor") || thing.tags.contains("BackDoor") ){
				if(thing.state != NULL){
					if( (thing.state as Number).intValue > 0){
						window = OPEN
					}
				}
			}
		]
		
		//holiday or manual alarm is on?
		var watchdog = if(WatchdogManual.state == ON) true else false
		
		//home-off-button?
		var hausAusKnopf = if(triggeringItem.name == "SmartButton1_DimmerSwitch" || triggeringItem.name == "SmartButton1_manual") true else false
		
		//is there a heating event in the calendar for that room?
		var QuantityType<Temperature> calendarSetpoint = 0|°C
		room.members.forEach[ GroupItem gItem |
			if(gItem.tags.contains("WebService")){
				gItem.members.forEach[ NumberItem item |
					if( item.tags.contains("Setpoint") && item.tags.contains("Temperature") ){
						calendarSetpoint = if(item.state != UNDEF && item.state >= 6|°C) (item.state as QuantityType<Temperature>) else 17|°C
					}
				]
			}
		]
		
		
		//set the temperature
		room.members.forEach[ GroupItem gItem |
			if(gItem.tags.contains("RadiatorControl")){
				gItem.members.forEach[ NumberItem radiator |
					
					if( radiator.tags.contains("Setpoint") && radiator.tags.contains("Temperature") ){
						
						if(window == OPEN){
							setRadiator.apply(radiator,6|°C,setpoints)
							//logInfo("Test", "Set window OPEN: {}", triggeringItem)
						}
						else if(watchdog){
							setRadiator.apply(radiator,6|°C,setpoints)
							//logInfo("Test", "Set watchdog: {}", triggeringItem)
						}
						else if(hausAusKnopf && 17|°C < (radiator.state as QuantityType<Temperature>)){
							setRadiator.apply(radiator,17|°C,setpoints)
							//logInfo("Test", "Set Haus-Aus: {}", triggeringItem)
						}
						else if(calendarSetpoint > 0|°C){
							setRadiator.apply(radiator,calendarSetpoint,setpoints)
							//logInfo("Test", "Set calendar: {}", triggeringItem)
						}
						else if(17|°C < (radiator.state as QuantityType<Temperature>)){
							setRadiator.apply(radiator,17|°C,setpoints)
							//logInfo("Test", "Set default: {}", triggeringItem)
						}
						
					}
				]
			}
		]
	]
	
}
end

1 Kommentar zu „automatische Heizregeln mit OpenHAB und semantischen Gruppen“

  1. Pingback: OpenHAB: Wenn man vergisst das Fenster zu schließen - Smarthome DIY - Heimautomatisierung selbst gemacht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..

Translate »