Abbildung „Müllkalender“ in openHAB

Um regelmäßig seine Mülltonnen am Entsorgungstag am Straßenrand zu stellen, haben die meisten Personen einen Papierkalender im Haus. Viele Entsorger stellen aber auch die Abholdaten der Mülltonnen online oder per Kalender bereit

Für den Landkreis Dachau gibt es eine App (DAH-Müll) und eine Oberfläche zur Visualisierung. Leider kann man auf die Daten dort nicht direkt per ICS / iCalendar zugreifen  (da haben sich die Zuständigen etwas sehr gutes einfallen lassen).

Deswegen muss man jetzt manuell einmal die Kalenderdaten pro Jahr dort exportieren und sie in einen eigenen Kalender importieren (geht auch, aber Anwenderfreundlich ist etwas anderes).

Der Anwendungsfall ist also einen Kalender auszulesen und am Vortag eine Erinnerung an die digitalen Endgeräte, Sprachsteuerungen oder Kalender zu senden. Dazu wird in openHAB das CalDAV-Binding verwendet.

Beispiele für die Verwendung findet Ihr hier bei onesmarthome.de oder im openhabforum.de.

Kalendereinrichtung

Im ersten Schritt besorgt man sich die iCalendar-Daten aus seinem jeweiligen Landkreis / Ort. Bei mir waren die Daten hier.

Leider musste ich die Inhalte noch etwas „umformatieren“:

  • DTEND-Tag fehlt in den Kalenderdaten (ohne diese können die Daten nicht in der ownCloud verwendet werden)
  • DTEND-Tag muss einen Tag nach dem DTSTART-Tag gesetzt sein (sonst können die Daten nicht in der ownCloud verwendet werden)

Danach habe ich in meinem System (hier ownCloud) einen Kalender „Müll“ erstellt und die oben beschriebenen Daten importiert.

Danach kann ich folgenden Link für die weitere Integration verwenden:

https://URL/remote.php/dav/calendars/USER/CALENDAR/

Die URL, USER und CALENDAR müsst Ihr an eure Anforderungen anpassen.

Binding

Das CalDAV1-Binding wird wie folgt installiert:

Paper UI – Add-ons – Bindings – CalDAV Binding (personal) – INSTALL

ACHTUNG: Das CalDAV Binding (command) wird zur Steuerung und Präsenzmeldung verwendet und ist für diesen Beitrag nicht notwendig.

Services (caldavio.cfg)

Es wird die Datei services/caldavio mit folgendem Inhalt erstellt:

caldavio:Muell:url=https://URL/remote.php/dav/calendars/USER/CALENDAR/

caldavio:Muell:username=USER

caldavio:Muell:password=PASSWORD

caldavio:Muell:reloadInterval=60

caldavio:Muell:preloadTime=2880

caldavio:Muell:disableCertificateVerification=true

caldavio:Muell:charset=utf8

Die groß geschriebenen Wörter bitte mit euren Daten ergänzen. Wichtig war in meiner Konfiguration noch disableCertificateVerification (wahrscheinlich wegen meinen kostenlosen Zertifikaten) und charset auf UTF-8.

Den reloadInterval kann man zum Testen auch auf 1 Minute stellen.

Items (CalDAV.items)

Ich habe für zwei Kalendereinträge und Datumswerte die Items generiert (es können bei mir zwei Tonnen zum gleichen Tag abgeholt werden):

/* Müllkalender */
String      CalDAV_Muell_1       "Erste Tonne [%s]"                          <calendar>    { caldavPersonal="calendar:Muell type:EVENT eventNr:1 value:NAME" }
DateTime    CalDAV_Muell_1_Date  "Datum der Abholung [%1$td.%1$tm.%1$tY]"    <calendar>    { caldavPersonal="calendar:Muell type:EVENT eventNr:1 value:START" }
String      CalDAV_Muell_2       "Zweite Tonne [%s]"                         <calendar>    { caldavPersonal="calendar:Muell type:EVENT eventNr:2 value:NAME" }
DateTime    CalDAV_Muell_2_Date  "Datum der Abholung [%1$td.%1$tm.%1$tY]"    <calendar>    { caldavPersonal="calendar:Muell type:EVENT eventNr:2 value:START" }

Sitemap

In der Sitemap habe ich das recht einfach so dargestellt:

Frame label="Informationen" {
Frame label="Müllkalender"{
                           Text item=CalDAV_Muell_1
                           Text item=CalDAV_Muell_1_Date
                           Text item=CalDAV_Muell_2
                           Text item=CalDAV_Muell_2_Date
              }
}

Rules (CalDAV.rules)

Nun erfolgt die Automatisierung und die Benachrichtigung in der Regel (das kann man bestimmt noch etwas eleganter „scripten“):

rule "Müllkalender"
when
//Time cron "0 * * ? * *"    //every 1 Minute
    Time cron "0 15 17 * * ?"    //once per Day 17:15
    //Item date_today changed
then
    var String muelltonne_1
    muelltonne_1 = CalDAV_Muell_1.state.toString

    var String muelltonne_2
    muelltonne_2 = CalDAV_Muell_2.state.toString

    // Prüfung ob der Wert gefüllt ist (damit beim cast auf DateTimeType keine Fehlermeldung erscheint)
    if (muelltonne_1 != "UNDEF") {
        // Datum der Abholung wird mit dem aktuellen Datum verglichen
        // die erste Bedingung prüft, ob das aktuelle Datum vor dem Ablaufdatum + 24 Std. liegt
        // die zweite Bedingung prüft, ob das aktuelle Datum nach dem Datum der Abholung liegt
        if (now.isBefore(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getCalendar().getTime()).plusHours(24)) &&
            now.isAfter(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getCalendar().getTime()))) {

            // Benachrichtigung per Telegram an Bot senden
            if(muelltonne_2 == "UNDEF") {
                sendTelegram("xxx_bot", "Müllkalender: %s", muelltonne_1)
                logInfo("INFO","CalDAV.rules - Müllkalender: %s", muelltonne_1)           
            } else {
                sendTelegram("xxx_bot", "Müllkalender: %s & %s", muelltonne_1, muelltonne_2)
                logInfo("INFO","CalDAV.rules - Müllkalender: %s & %s", muelltonne_1, muelltonne_2)           
            }         
        }
    }
end

Fazit

Die Anbindung des Kalenders in openHAB war relativ einfach. Etwas schwieriger hat sich die Anpassung der ICS-Datei aus meinem Landkreis gestaltet. Danach hat man recht einfach (nach ein paar Tests) seine Kalenderdaten angebunden.

In der Regel waren dann noch ein paar Workarounds notwendig, aber dann waren die Daten auch richtig vorhanden. Die Konvertierung in UTF-8 / Unicode war nicht so einfach in der cfg zu finden. Auch den CRON-Job nur einmal am Tag zu einer bestimmten Zeit auszuführen war für einen „Nicht-UNIXer“ nicht das einfachste 😉

Nun bekomme ich aber am Vortag um 17:15 Uhr eine Information per Telegram, dass die Müllabfuhr am nächsten Tag kommt.

Habt Ihr noch Ideen für die weitere Anbindung von Kalenderdaten? Habt Ihr eine Präsenzmeldung oder Steuerung eures SmartHome damit realisiert?

18 Kommentare
  1. Kai
    Kai sagte:

    Hallo
    Erst einmal danke für die ausführliche Anleitung.
    Bekomme aber folgende Meldung
    2018-10-20 20:01:14.445 [INFO ] [el.core.internal.ModelRepositoryImpl] – Validation issues found in configuration model ‚Muell.rules‘, using it anyway:
    The method getCalendar() from the type DateTimeType is deprecated
    Gibt es eine Lösung ?
    Danke

  2. Kai
    Kai sagte:

    Ok habs gefunden

    rule „Müllkalender“

    when
    Time cron „0 00 19 * * ?“ or
    System started
    then
    var String Tonne
    Tonne = CalDav_Tonne .state.toString

    if (Tonne != „UNDEF“)
    {
    if
    (now.isBefore(new DateTime((CalDav_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).plusHours(24)) &&
    now.isAfter(new DateTime((CalDav_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli)))

    {sendCommand(Muell, ON)}
    { sendTelegram(„xxx_bot“, „Morgen %s rausstellen „, Tonne ) }
    }
    end

  3. Reinhard Eidelsburger
    Reinhard Eidelsburger sagte:

    Hallo Kai,

    vielen Dank für die Verbesserung. Jetzt sind auch die „Deprecated-Meldungen“ bei mir weg. Ich teste die Tage mal folgende Anpassung (den Artikel oben habe ich noch nicht aktualisiert):

    if (now.isBefore(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).plusHours(24)) &&
    now.isAfter(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli))) {

    und zum Beginn der Regel habe ich das „System started“ noch mit aufgenommen.

    Vielen Dank!

  4. Erwin
    Erwin sagte:

    Hallo Reinhard,

    Danke für die Anleitung. Ich habe aber noch Probleme mit dem if Term; Fehlermeldung bekomme ich nicht, aber auch keine Telegrams.
    Vielleicht findest du ob ein Fehler drin ist:

    Danke

    if (now.isBefore(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).plusHours(24)) && now.isAfter(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli))) {

  5. Reinhard
    Reinhard sagte:

    Hallo Erwin,

    ich habe gerade noch einmal meinen Stand kontrolliert. Der sieht wie folgt aus:

    if (now.isBefore(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).plusHours(24)) &&
    now.isAfter(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).minusHours(8))) {

    //logInfo(„INFO“,“CalDAV.rules – Müllkalender: TEST3″)

    // Benachrichtigung per Telegram an Bot senden
    if(muelltonne_2 == „UNDEF“) {
    sendTelegram(„xxx“, „Müllkalender: “ + muelltonne_1)
    logInfo(„INFO“,“CalDAV.rules – Müllkalender: “ + muelltonne_1)

    } else {
    sendTelegram(„xxx“, „Müllkalender: “ + muelltonne_1 + “ & “ + muelltonne_2)
    logInfo(„INFO“,“CalDAV.rules – Müllkalender: “ + muelltonne_1 + “ & “ + muelltonne_2)
    }

    }

    So funktioniert es bei mir in openHAB 2.4. Generell funktioniert Telegram bei dir, oder?

  6. David
    David sagte:

    Hallo Reinhard,

    danke für deine Mühe.
    Die rule geht so semi bei mir.
    Items werden geupdatet, aber die Benachrichtigung per pushover (bei dir per Telegramm) kommt immer erst am Tag der Abholung um 17:15 Uhr, wo es leider schon zu spät ist.
    Ist das bei dir auch so?

    Vielen Dank,
    David

  7. Reinhard
    Reinhard sagte:

    Hi David,

    das ist bei mir abhängig wie die Kalenderdaten im CalDAV-Binding gelesen werden. Ich lasse das am Abend davor und am Abholtag ausgeben. Damit die Tonne auch wieder geholt wird 🙂

  8. David
    David sagte:

    Ok, mein Termin im Item ist für den Abholtag um 00:00 Uhr gesetzt.
    Kannst du mir sagen, wie ich es schaffe, einen Tag vorher zu benachrichtigen? Muss ich bei .plusHours(24) oder .minusHours(?) was ändern? 🙂

  9. Reinhard
    Reinhard sagte:

    Ich habe es so drin:

    if (now.isBefore(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).plusHours(24)) &&
    now.isAfter(new DateTime((CalDAV_Muell_1_Date.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli).minusHours(8))) {

    Und die Regel wird um 18 Uhr so ausgeführt:

    rule „Müllkalender“
    when
    Time cron „0 0 18 * * ?“ //once per Day 18:00

    Damit kriege ich 18 Uhr vor dem Abholungstag eine Nachricht.

  10. Erwin
    Erwin sagte:

    Hallo,

    ich bin’s noch mal. Ich bekomme ein komisches Verhalten. Beim ersten start von rule und things wird alles gut ausgelesen und auch die Termine angezeigt. Aber schon am nächsten Tag bekommen die Variablen NULL als Wert und ich bekomme diesen Fehler:
    [ERROR] [ntime.internal.engine.ExecuteRuleJob] – Error during the execution of rule ‚Muellkalender‘: Could not cast NULL to org.eclipse.smarthome.core.library.types.DateTimeType; line 32, column 37, length 41

    Die Abfrage erfolgt immer um 18 Uhr, das passt also …
    Mittwoch wäre der Tag, an dem etwas angezeigt werden müsste, tut es aber leider nicht.

    Hast du noch eine Idee? Wir nutzen übrigens die gleiche Kalenderquelle 😉

  11. Erwin
    Erwin sagte:

    Ja, auch im Dachauer LK.

    Ist das Listing oben das aktuelle oder mit den Ergänzungen, Änderungen aus den Kommentaren?

    Danke

  12. Reinhard Eidelsburger
    Reinhard Eidelsburger sagte:

    Hi Erwin,

    ich sende dir gleich mal die kompletten Dateien per E-Mail zu.

    Schöne Grüße,
    Reinhard

  13. Jeroen Kokhuis
    Jeroen Kokhuis sagte:

    Hallo

    Ich habe ungefähr die gleiche Rule, erhalte jedoch jedes Mal die folgende Fehlermeldung:

    [ERROR] [ntime.internal.engine.RuleEngineImpl] – Rule ‚Abfalltermine sortieren‘: Could not cast UNDEF to org.eclipse.smarthome.core.library.types.DateTimeType; line 13, column 47, length 31

    Kannst du mir weiter helfen?
    Das ist meine Rule:

    rule „Abfalltermine sortieren“
    when
    Item Restmuell received update or
    Item Biomuell received update or
    Item Papiermuell received update or
    Item RestmuellTage changed or
    Item PapiermuellTage changed or
    Item BiomuellTage changed or
    Time cron „0 8 0 1/1 * ? *“
    then
    Thread::sleep(3000)
    var DateTime varTempDatum
    var DateTime varAbfall1Datum = new DateTime((Restmuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
    logInfo(„Afval.rules“, „varAbfall1Datum“ +varAbfall1Datum)
    var DateTime varAbfall2Datum = new DateTime((Biomuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
    logInfo(„Afval.rules“, „varAbfall2Datum“ +varAbfall2Datum)
    var DateTime varAbfall3Datum = new DateTime((Papiermuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
    logInfo(„Afval.rules“, „varAbfall3Datum“ +varAbfall3Datum)
    var String varTempArt
    var String varAbfall1Art = „Restmuell“
    var String varAbfall2Art = „Biomuell“
    var String varAbfall3Art = „Papiermuell“
    var String varTempTage
    var String varAbfall1Tage = RestmuellTage.state.toString
    var String varAbfall2Tage = BiomuellTage.state.toString
    var String varAbfall3Tage = PapiermuellTage.state.toString
    var i = 1
    while ((i=i+1) varAbfall2Datum) {
    varTempDatum = varAbfall1Datum
    varAbfall1Datum = varAbfall2Datum
    varAbfall2Datum = varTempDatum
    varTempArt = varAbfall1Art
    varAbfall1Art = varAbfall2Art
    varAbfall2Art = varTempArt
    varTempTage = varAbfall1Tage
    varAbfall1Tage = varAbfall2Tage
    varAbfall2Tage = varTempTage
    logInfo(„Afval.rules“, „varTempTage“ +varTempTage)
    if (varAbfall2Datum > varAbfall3Datum) {
    varTempDatum = varAbfall2Datum
    varAbfall2Datum = varAbfall3Datum
    varAbfall3Datum = varTempDatum
    varTempArt = varAbfall2Art
    varAbfall2Art = varAbfall3Art
    varAbfall3Art = varTempArt
    varTempTage = varAbfall2Tage
    varAbfall2Tage = varAbfall3Tage
    varAbfall3Tage = varTempTage
    logInfo(„Afval.rules“, „varTempTage“ +varTempTage)
    }
    }
    if (varAbfall2Datum > varAbfall3Datum) {
    varTempDatum = varAbfall2Datum
    varAbfall2Datum = varAbfall3Datum
    varAbfall3Datum = varTempDatum
    varTempArt = varAbfall2Art
    varAbfall2Art = varAbfall3Art
    varAbfall3Art = varTempArt
    varTempTage = varAbfall2Tage
    varAbfall2Tage = varAbfall3Tage
    varAbfall3Tage = varTempTage
    logInfo(„Afval.rules“, „varTempTage“ +varTempTage)
    }
    }
    Abfall_1_Datum.postUpdate(varAbfall1Datum.toString)
    logInfo(„Afval.rules“, „Abfall_1_Datum“ +Abfall_1_Datum)
    logInfo(„Afval.rules“, „varAbfall1Datum“ +varAbfall1Datum)
    Abfall_2_Datum.postUpdate(varAbfall2Datum.toString)
    Abfall_3_Datum.postUpdate(varAbfall3Datum.toString)
    Abfall_1_Art.postUpdate(varAbfall1Art)
    Abfall_2_Art.postUpdate(varAbfall2Art)
    Abfall_3_Art.postUpdate(varAbfall3Art)
    Abfall_1_Tage.postUpdate(varAbfall1Tage)
    Abfall_2_Tage.postUpdate(varAbfall2Tage)
    Abfall_3_Tage.postUpdate(varAbfall3Tage)

    end

    danke im voraus
    (Google Translate)

  14. Reinhard Eidelsburger
    Reinhard Eidelsburger sagte:

    Hallo Jeroen,

    ist denn die Zeile 13 der folgende Auszug:

    var DateTime varAbfall1Datum = new DateTime((Restmuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)

    Was kommt den hier bei dieser Log-Info bei dir an:

    logInfo(„Afval.rules“, „varAbfall1Datum“ +varAbfall1Datum)

    Es liest sich ja so, ob er da schon das Datum nicht entsprechend bekommt z.B. leer ist oder die „Konvertierung“ nicht klappt. Das mit dem „UNDEF“ kann in der Scripting-Welt leider sehr viel sein.

    Schöne Grüße,
    Reinhard

  15. marc
    marc sagte:

    Hallo,

    kann man den code vielleicht überarbeiten und posten mit den ganzen änderungen für mich als neuling ist das total unübersichtlich. danke

  16. Reinhard
    Reinhard sagte:

    Hi marc,

    schick mir bitte kurz deine E-Mail-Adresse, dann lasse ich dir vorab die kompletten Dateien von mir einmal zukommen.
    Ich versuche dann auch demnächst einmal einen aktualisierten Artikel zu schreiben.

    Schöne Grüße,
    Reinhard

  17. Jörg W.
    Jörg W. sagte:

    Hallo Reinhard,
    wäre auch sehr interessiert, musste meine ics-Datei auch ordentlich umschreiben (Umkreis Stuttgart), aber hab zumindest alles im Kalender. Als opeHAB Neuling wären deine Dateien Gold wert, dann kann ich mich dran setzen und verstehe danach hoffentlich auch die Zusammenhänge besser!
    Danke & Gruß,
    Jörg

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

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

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.