Ferien und Feiertage via ICAL

Für welche Projekte verwendet Ihr OpenHAB? Was habt Ihr automatisiert? Stellt eure Projekte hier vor.

Moderatoren: Cyrelian, seppy

Benutzeravatar
seppy
Beiträge: 738
Registriert: 24. Sep 2015 20:25
Answers: 4
Wohnort: Bonn

Ferien und Feiertage via ICAL

Beitrag von seppy »

Hallo Zusammen,

mich hat die ganze Zeit geärgert, das ich Ferienzeiten und Feiertage nicht automatisch von einer zuverlässigen Quelle in mein openHab importiert bekomme. Ich konnte keinen kostenfreien und zuverlässigen Webservice finden, der diese Informationen in brauchbarer Form liefert. "Feiertag API's" gibt es eine Menge, aber Feiertage könnte ich auch berechnen. Schulferien sind nicht zu berechnen und die einzig zuverlässigen Quellen bieten keine API , sondern ICAL Downloads an.
Also habe ich mich für http://www.schulferien.org entschieden. Auf dieser sehr gut gepflegten Seite können diverse ICAL Kalender mit Ferienterminen und Feiertagen heruntergeladen werden.

Hier also meine Lösung:
1) Die Items die ich verwende:

Die beiden ICAL Items laden in meinem Fall alle zwei Stunden (Millisekunden: 7200000) via http binding den jeweils gewünschten ICAL Kalender.
Bei mir Ferien in NRW für das aktuelle Jahr ("%1$tY") und die Feiertage in NRW für das aktuelle Jahr ("%1$tY"). Die geladenen ICAL Informationen werden für die weitere Transformation an den Java Script transformation service (Bei mir Script: ical_holiday.js) von openHab übergeben.
Für mich war dies der einfachste Weg, da mit ein paar Zeilen Javascript die Aufgabe zu erledigen ist.

Der Schalter "GlobalHoliday" wird für weitere Regeln etc. genutzt und das String Item "GlobalHolidayName" enthält entweder den Namen des Feiertags oder der Ferien.

Code: Alles auswählen

String GlobalSpecialDayICAL
	{http="<[http://www.schulferien.org/iCal/Feiertage/icals/Feiertage_Nordrhein_Westfalen_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
String GlobalHolidayICAL
	{http="<[http://www.schulferien.org/iCal/Ferien/icals/Ferien_Nordrhein_Westfalen_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
Switch GlobalHoliday
	(gSystem)
String GlobalHolidayName
2) Das Transformation Script ("ical_holiday.js"):

Wie so oft habe ich auch hier das Rad nicht komplett neu erfunden. Ich habe den iCalendar javascript parser (https://code.google.com/p/ijp/) ijp als Basis verwendet. Auf dieser Basis liefert das Transformation Script ein false zurück, wenn das aktuelle Datum (heute) kein Event im übergebenen Kalender enthält. Wenn ein Event gefunden wird, entspricht der Rückgabewert dem Namen (summary) des entsprechenden Elements.

Im angefügten Textfile findet Ihr die vollständige Transformations Funktion.
ical_holiday.txt
3) Die passende Rule:
Die Regel übersetzt die Rückmeldung aus dem Transformations Script auf die Items GlobalHoliday und GlobalHolidayName.
Sobald sich der Wert der balden ICAL Items ändert, prüfe ich den Status und setze die entsprechenden Items.

Code: Alles auswählen

/**
 * Ferien und Feiertage prüfen
 */
rule "Event_Holiday"
when
	Item GlobalHolidayICAL changed or 
	Item GlobalSpecialDayICAL changed
then
	//Prüfen ob ein Feiertag ist
	if (GlobalSpecialDayICAL.state != "false" && GlobalSpecialDayICAL.state != "Uninitialized"){
		postUpdate(GlobalHoliday,ON)
		postUpdate(GlobalHolidayName,GlobalSpecialDayICAL.state)
		logInfo("HomeBox.SystemRules:Event_Holiday", "GlobalSpecialDayICAL changed: Updated Holiday Status: " + GlobalHoliday.state + " (" + GlobalHolidayName.state + ")")
	}
	// Prüfen ob Ferien sind
	else if (GlobalHolidayICAL.state != "false" && GlobalHolidayICAL.state != "Uninitialized"){
		postUpdate(GlobalHoliday,ON)
		postUpdate(GlobalHolidayName,GlobalHolidayICAL.state)
		logInfo("HomeBox.SystemRules:Event_Holiday", "GlobalHolidayICAL changed: Updated Holiday Status: " + GlobalHoliday.state + " (" + GlobalHolidayName.state + ")")
	} else {
		postUpdate(GlobalHoliday,OFF)
		postUpdate(GlobalHolidayName,"false")
		logInfo("HomeBox.SystemRules:Event_Holiday", "Updated Holiday Status: " + GlobalHoliday.state + " (false)")
	}
end
Bisher funktioniert alles reibungslos und ich muss mir um diesen Teil des automatisierten Hauses keine Gedanken mehr machen. Ich verwende die so gewonnenen Infos zum Beispiel für die Heizungssteuerung. Die Kinder sind in der Schulzeit den halben Tag aus dem Haus und ich kann die Kinderzimmer auf Absenktemperatur regeln. Oder die Rolläden fahren in den Ferien später hoch. Das sind nur zwei Anwendungsfälle bei mir.
Ich hoffe es nutzt noch jemand anderem und funktioniert zuverlässig. Wenn jemand Anmerkungen hat ... immer her damit!

Viele Grüße,

Seppy
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Homematic und HomematicIP über Raspberrymatic (RaspPi 4 4GB) mit 2x HMLAN. Steuerung und Visualisierung durch OpenHAB2 auf RaspPi in Hutschienengehäuse im Sicherungskasten. Rund 100 Aktoren/Sensoren

- Abgesichert durch APC USV
- Bewässerungssteuerung mit Hunter Magnetventilen (HM-LC-Sw4-DR)
- Beleuchtungssteuerung Innen und Aussen (HM-LC-Sw4-DR + HM-LC-SW1-FM + HMW-IO-12-SW7-DR)
- Rolladensteuerung mit Beschattungsautomatik über Temperaturdifferenzsensor (HM-LC-Bl1PBU-FM)
- Wetter und Unwetterinformationen von wunderground
- Benachrichtigung der Bewohner via Pushover
- Multimediawand und Dreambox Steuerung (HM-LC-SW1-FM)
- Heizungssteuerung mit Komfort und Energiesparfunktionen (HM-CC-RT-DN + HM-Sec-SC-2 + HMIP-eTRV-2)
- Werkstatt Kompressorsteuerung (HMW-IO-12-SW7-DR)
- Weihnachtsbeleuchtung außen
- Präsenzerkennung über Geolocation (iCloud Binding), iBeacon und WLAN (Unifi Binding)
- Philips HUE & Tasmota Devices (Tuya) Einbindung

Benutzeravatar
seppy
Beiträge: 738
Registriert: 24. Sep 2015 20:25
Answers: 4
Wohnort: Bonn

Re: Ferien und Feiertage via ICAL

Beitrag von seppy »

Nachtrag: Nach dem gleichen Prinzip werte ich auch den Abfallkalender meines Entsorgers aus. Wenn also morgen ein Abfuhrtermin im ICAL Kalender der Müllabfuhr steht, informiere ich den Haushalt per WhatsApp und zeige den Abfuhrtermin in der Sitemap:

Code: Alles auswählen

Frame label="Informationen" visibility=[GlobalGarbageCollection==ON, GlobalHoliday==ON] {
		Text item=GlobalGarbageName label="Müllabfuhrtermin [%s]" icon="recyclebin" labelcolor=[GlobalGarbageCollection==ON="red"]  visibility=[GlobalGarbageCollection==ON]
		Text item=GlobalHolidayName label="Ferien/Feiertag [%s]" icon="calendar" visibility=[GlobalHoliday==ON] 
	}
Wichtig ist nur dass der Kalender keine Serientermine enthält, sondern Einzeltermine. Bei meinem Versorger konnte ich zwei Varianten des ICAL Kalenders auswählen!
Homematic und HomematicIP über Raspberrymatic (RaspPi 4 4GB) mit 2x HMLAN. Steuerung und Visualisierung durch OpenHAB2 auf RaspPi in Hutschienengehäuse im Sicherungskasten. Rund 100 Aktoren/Sensoren

- Abgesichert durch APC USV
- Bewässerungssteuerung mit Hunter Magnetventilen (HM-LC-Sw4-DR)
- Beleuchtungssteuerung Innen und Aussen (HM-LC-Sw4-DR + HM-LC-SW1-FM + HMW-IO-12-SW7-DR)
- Rolladensteuerung mit Beschattungsautomatik über Temperaturdifferenzsensor (HM-LC-Bl1PBU-FM)
- Wetter und Unwetterinformationen von wunderground
- Benachrichtigung der Bewohner via Pushover
- Multimediawand und Dreambox Steuerung (HM-LC-SW1-FM)
- Heizungssteuerung mit Komfort und Energiesparfunktionen (HM-CC-RT-DN + HM-Sec-SC-2 + HMIP-eTRV-2)
- Werkstatt Kompressorsteuerung (HMW-IO-12-SW7-DR)
- Weihnachtsbeleuchtung außen
- Präsenzerkennung über Geolocation (iCloud Binding), iBeacon und WLAN (Unifi Binding)
- Philips HUE & Tasmota Devices (Tuya) Einbindung

msth
Beiträge: 1
Registriert: 5. Apr 2016 10:19

Re: Ferien und Feiertage via ICAL

Beitrag von msth »

Hallo Seppy,

vielen Dank für den Code. Habe bei bei mir integriert und sehr zufrieden.

Gruss Mario

Benutzeravatar
seppy
Beiträge: 738
Registriert: 24. Sep 2015 20:25
Answers: 4
Wohnort: Bonn

Re: Ferien und Feiertage via ICAL

Beitrag von seppy »

Freut mich, danke für das Feedback!


openhabforum.de
Homematic und HomematicIP über Raspberrymatic (RaspPi 4 4GB) mit 2x HMLAN. Steuerung und Visualisierung durch OpenHAB2 auf RaspPi in Hutschienengehäuse im Sicherungskasten. Rund 100 Aktoren/Sensoren

- Abgesichert durch APC USV
- Bewässerungssteuerung mit Hunter Magnetventilen (HM-LC-Sw4-DR)
- Beleuchtungssteuerung Innen und Aussen (HM-LC-Sw4-DR + HM-LC-SW1-FM + HMW-IO-12-SW7-DR)
- Rolladensteuerung mit Beschattungsautomatik über Temperaturdifferenzsensor (HM-LC-Bl1PBU-FM)
- Wetter und Unwetterinformationen von wunderground
- Benachrichtigung der Bewohner via Pushover
- Multimediawand und Dreambox Steuerung (HM-LC-SW1-FM)
- Heizungssteuerung mit Komfort und Energiesparfunktionen (HM-CC-RT-DN + HM-Sec-SC-2 + HMIP-eTRV-2)
- Werkstatt Kompressorsteuerung (HMW-IO-12-SW7-DR)
- Weihnachtsbeleuchtung außen
- Präsenzerkennung über Geolocation (iCloud Binding), iBeacon und WLAN (Unifi Binding)
- Philips HUE & Tasmota Devices (Tuya) Einbindung

Benutzeravatar
seppy
Beiträge: 738
Registriert: 24. Sep 2015 20:25
Answers: 4
Wohnort: Bonn

Re: Ferien und Feiertage via ICAL

Beitrag von seppy »

Hallo Zusammen,

zu beachten ist, dass sich die URL's geändert haben.

Viele Grüße,
Seppy
Homematic und HomematicIP über Raspberrymatic (RaspPi 4 4GB) mit 2x HMLAN. Steuerung und Visualisierung durch OpenHAB2 auf RaspPi in Hutschienengehäuse im Sicherungskasten. Rund 100 Aktoren/Sensoren

- Abgesichert durch APC USV
- Bewässerungssteuerung mit Hunter Magnetventilen (HM-LC-Sw4-DR)
- Beleuchtungssteuerung Innen und Aussen (HM-LC-Sw4-DR + HM-LC-SW1-FM + HMW-IO-12-SW7-DR)
- Rolladensteuerung mit Beschattungsautomatik über Temperaturdifferenzsensor (HM-LC-Bl1PBU-FM)
- Wetter und Unwetterinformationen von wunderground
- Benachrichtigung der Bewohner via Pushover
- Multimediawand und Dreambox Steuerung (HM-LC-SW1-FM)
- Heizungssteuerung mit Komfort und Energiesparfunktionen (HM-CC-RT-DN + HM-Sec-SC-2 + HMIP-eTRV-2)
- Werkstatt Kompressorsteuerung (HMW-IO-12-SW7-DR)
- Weihnachtsbeleuchtung außen
- Präsenzerkennung über Geolocation (iCloud Binding), iBeacon und WLAN (Unifi Binding)
- Philips HUE & Tasmota Devices (Tuya) Einbindung

favorit626
Beiträge: 35
Registriert: 15. Mai 2016 22:38

Re: Ferien und Feiertage via ICAL

Beitrag von favorit626 »

Hallo Seppy,
ich habe Deine Anleitung eins zu eins übernommen, nur die URL's auf meinem Standort geändert, aber es funktioniert nicht.

Da sieht das ganze dann so aus. S.Anhang
Muss noch irgendetwas ändern?
Wie nennst Du die ITEMS-Datei ?

Kannst Du mir irgendwie weiter helfen?

Gruß
Knut
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Benutzeravatar
Cyrelian
Beiträge: 601
Registriert: 24. Sep 2015 17:55
Answers: 4

Re: Ferien und Feiertage via ICAL

Beitrag von Cyrelian »

Hi Knut,

hast Du die URL angepasst? Die in der Anleitung von Seppy sind noch die alten.

Alte URL:

Code: Alles auswählen

{http="<[http://www.schulferien.org/iCal/Feiertage/icals/Feiertage_Nordrhein_Westfalen_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
Neue URL:

Code: Alles auswählen

{http="<[http://www.schulferien.org/media/ical/deutschland/feiertage_nordrhein-westfalen_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
CU
Cyrelian

favorit626
Beiträge: 35
Registriert: 15. Mai 2016 22:38

Re: Ferien und Feiertage via ICAL

Beitrag von favorit626 »

Hallo Cyrelian,

ja die habe ich angepasst oder besser gesagt direkt von der Seite Schulferien übernommen:

Code: Alles auswählen

{http="<[http://www.schulferien.org/media/ical/deutschland/feiertage_schleswig-holstein_2016.ics:7200000:JS(ical_holiday.js)]"}
Gruß
Knut

Benutzeravatar
Cyrelian
Beiträge: 601
Registriert: 24. Sep 2015 17:55
Answers: 4

Re: Ferien und Feiertage via ICAL

Beitrag von Cyrelian »

Hi Knut,

mir ist aufgefallen, das du direkt den "2016er" abfragst.
Seppy macht aber in seinem Script genau dafür eine Abfrage.

Von daher änder mal bitte folgendes:

Code: Alles auswählen

{http="<[http://www.schulferien.org/media/ical/deutschland/feiertage_schleswig-holstein_2016.ics:7200000:JS(ical_holiday.js)]"}
in:

Code: Alles auswählen

{http="<[http://www.schulferien.org/media/ical/deutschland/feiertage_schleswig-holstein_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
CU
Cyrelian

favorit626
Beiträge: 35
Registriert: 15. Mai 2016 22:38

Re: Ferien und Feiertage via ICAL

Beitrag von favorit626 »

Hallo Cyrelian,
Das hatte ich zuerst auch so, sah aber genauso aus.
Vielleicht habe ich ja grundsätzlich irgendwas falsch gemacht.

Als ITEMS-Datei habe ich

Code: Alles auswählen

String GlobalSpecialDayICAL
   {http="<[http://www.schulferien.org/media/ical/deutschland/feiertage_schleswig-holstein_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
String GlobalHolidayICAL
   {http="<[http://www.schulferien.org/ical/deutschland/ferien_schleswig-holstein_%1$tY.ics:7200000:JS(ical_holiday.js)]"}
Switch GlobalHoliday
   (gSystem)
String GlobalHolidayName
und das als "holiday.items" bezeichnet.

Als RULE-Datei das hier

Code: Alles auswählen

/**
 * Ferien und Feiertage prüfen
 */
rule "Event_Holiday"
when
   Item GlobalHolidayICAL changed or
   Item GlobalSpecialDayICAL changed
then
   //Prüfen ob ein Feiertag ist
   if (GlobalSpecialDayICAL.state != "false" && GlobalSpecialDayICAL.state != "Uninitialized"){
      postUpdate(GlobalHoliday,ON)
      postUpdate(GlobalHolidayName,GlobalSpecialDayICAL.state)
      logInfo("HomeBox.SystemRules:Event_Holiday", "GlobalSpecialDayICAL changed: Updated Holiday Status: " + GlobalHoliday.state + " (" + GlobalHolidayName.state + ")")
   }
   // Prüfen ob Ferien sind
   else if (GlobalHolidayICAL.state != "false" && GlobalHolidayICAL.state != "Uninitialized"){
      postUpdate(GlobalHoliday,ON)
      postUpdate(GlobalHolidayName,GlobalHolidayICAL.state)
      logInfo("HomeBox.SystemRules:Event_Holiday", "GlobalHolidayICAL changed: Updated Holiday Status: " + GlobalHoliday.state + " (" + GlobalHolidayName.state + ")")
   } else {
      postUpdate(GlobalHoliday,OFF)
      postUpdate(GlobalHolidayName,"false")
      logInfo("HomeBox.SystemRules:Event_Holiday", "Updated Holiday Status: " + GlobalHoliday.state + " (false)")
   }
end
als "Event_Holiday.rules" bezeichnet.

Und als Transform-Datei:
"ical_holiday.js"

Code: Alles auswählen

/**
* Javascript Transformator, der prüft ob das heutige Datum auf einen ICAL Eintrag fällt.
* Wenn ja wird die Kalenderbezeichnung des Tages zurückgegeben, wenn nicht ein false
*/
var holiday = false;
/**
* ijp - iCalendar javascript parser
* https://code.google.com/p/ijp/
*/
(function(i) {
	var Ical = function Ical(){
		this.version = '';
		this.prodid = '';
		this.events = [];
		this.todos = [];
		this.journals = [];
		this.freebusys = [];
	}
	var xprops = 'x-[^:;]+';
	var ianaprops = '[\\w]+[^:;]+'
	var icalParser = {
		icals : [],
		propsList : {
			'event':'(dtstamp|uid|dtstart|class|created|description|geo|last-mod|location|organizer|priority|seq|status|summary|transp|url|recurid|rrule|dtend|duration|attach|attendee|categories|comment|contact|exdate|rstatus|related|resources|rdate|'+xprops+'|'+ianaprops+')',
			'freebusy':'(dtstamp|uid|contact|dtstart|dtend|organizer|url|attendee|comment|freebusy|rstatus|'+xprops+'|'+ianaprops+')',
			'journal':'(dtstamp|uid|class|created|dtstart|last-mod|organizer|recurid|seq|status|summary|url|rrule|attach|attendee|categories|comment|contact|description|exdate|related|rdate|rstatus|'+xprops+'|'+ianaprops+')',
			'todo':'(dtstamp|uid|class|completed|created|description|dtstart|geo|last-mod|location|organizer|percent|priority|recurid|seq|status|summary|url|rrule|due|duration|attach|attendee|categories|comment|contact|exdate|rstatus|related|resources|rdate|'+xprops+'|'+ianaprops+')'
		},
		parseIcal : function(icsString){
			var cals = icsString.match(/BEGIN:VCALENDAR\r?\n(.*\r?\n)+?END:VCALENDAR/ig);
			for(var index in cals){
				var ical = new Ical(); 
				ical.version = this.getValue('VERSION',cals[index]);
				ical.prodid = this.getValue('PRODID',cals[index]);
				cals[index] = cals[index].replace(/\r\n /g,'');
				cals[index] = cals[index].replace(/BEGIN:VCALENDAR\r?\n/ig,'');
				var reg = /BEGIN:(V.*?)\r?\n(.*\r?\n)+?END:\1/gi;
				matches = cals[index].match(reg);
				if(matches){
					for(i=0;i<matches.length;i++){
						this.parseVComponent(matches[i],ical);
					}
				}
				this.icals[this.icals.length] = ical;
			}
		},
		parseVComponent : function(vComponent,ical){
			var nameComponent = vComponent.match(/BEGIN:V([^\s]+)/i)[1].toLowerCase();
			vComponent = vComponent.replace(/\r?\n[\s]+/igm,''); //unfolding
			vComponent = vComponent.replace(/(^begin|^end):.*/igm,'');
			var props = vComponent.match(new RegExp(this.propsList[nameComponent]+'[:;].*','gim'));
			if(props){
				var component=[];
				for(var index in props){
					var nom = props[index].replace(/[:;].*$/,'');
					var propKey = /*'prop_'+*/nom.toLowerCase();
					if(component[propKey]===undefined) component[propKey] = [];
					component[propKey][component[propKey].length] = this.getValue(nom,props[index]);
					component['raw'] = vComponent;
				}
				if(ical[nameComponent+'s'] !== undefined)
					ical[nameComponent+'s'][ical[nameComponent+'s'].length] = component;
			}
		},
		getValue: function(propName,line){
			var prop={};
			line = line.replace(/^\s+/g,'').replace(/\s+$/gi,'');
			reg = new RegExp('('+propName+')((?:;[^=]*=[^;:\n]*)*):([^\n\r]*)','gi');
			var matches = reg.exec(line);
			if(matches){
				var valeur = RegExp.$3;
				var tab_params=[];
				if(RegExp.$2.length>0){ 
					var params = RegExp.$2.substr(1).split(';');
					var pair;
					for(k=0;k<params.length;k++){
						pair = params[k].split('=');
						if(!pair[1]) pair[1] = pair[0];
						tab_params[pair[0]] = pair[1];
					}
				}
				prop = { value:valeur,name:propName };
				if(Object.keys(tab_params).length>0)
					prop.params = tab_params;
			}
			return prop;
		},
	}
	
	/**
	* Funktion um ical Datumsstring (YYYYMMDD) in Date Objekt zu wandeln
	*/
	function calenDate(icalStr)  {
		var strYear = icalStr.substr(0,4);
		var strMonth = parseInt(icalStr.substr(4,2),10)-1;
		var strDay = icalStr.substr(6,2);
		var strHour = 0;
		var strMin = 0;
		var strSec = 0;

		var oDate =  new Date(strYear,strMonth, strDay, strHour, strMin, strSec)

		return oDate;
	}
	
    icalParser.parseIcal(input);
	var event = icalParser.icals[0].events[0];
	icalParser.icals[0].events.forEach(function (currentValue, index, originalArray) {
		/**
		*	Prüfen ob das aktuelle Datum zwischen zwei Daten liegt, bzw. auf einen Tag fällt
		*/
		var dateFrom = calenDate(currentValue.dtstart[0].value);
		var dateTo = calenDate(currentValue.dtend[0].value);
		var dateCheck = new Date();
		//var dateCheck = calenDate("20151003");

		if (dateCheck >= dateFrom && dateCheck < dateTo){
			holiday = currentValue.summary[0].value;
		}
	});
    return holiday;
})(input);
In der SITEMAP habe ich folgendes eingetragen:

Code: Alles auswählen

}
	
	Frame label="Informationen" visibility=[GlobalGarbageCollection==ON, GlobalHoliday==ON] {
      Text item=GlobalGarbageName label="Müllabfuhrtermin [%s]" icon="recyclebin" labelcolor=[GlobalGarbageCollection==ON="red"]  visibility=[GlobalGarbageCollection==ON]
      Text item=GlobalHolidayName label="Ferien/Feiertag [%s]" icon="calendar" visibility=[GlobalHoliday==ON]
   }
	
Bei dem Müllkalender habe ich auch alles so übernommen, und funktioniert auch nicht. Vielleicht kriegen wir ja zwei Fliegen mit einer klappe. 8-)

Gruß
Knut

Antworten