FHEM > Codeschnipsel

myMüll Abfuhrtermine in FHEM und als ics-Datei

(1/1)

yersinia:
Dies ist eine Zusammenfassung des Threads MyMüll.de: Einstieg in die Modul-Programmierung in FHEM von LuckyLuis und ich möchte meine Lösung hier etwas strukturierter teilen. Inhaltlich ist das eine Kopie des Threads.
Meine Gemeinde ist letztes Jahr auf MyMüll umgestiegen, was ansich ganz nett ist (zumindest die App) - allerdings ist die ical-Verfügbarkeit ein Graus: man kann die Kalenderdatei nicht einfach in ein Kalenderprogramm oder FHEM einbinden. Zurzeit importiere ich händisch einmal im Jahr die zur Verfügung gestellte ical-Datei in einen privaten Kalender, welche dann FHEM zur Verfügung gestellt wird. Das nervt mich trotzdem, auch weil die Daten im Internet vorliegen und man von Aktualisierungen -außer in der App selbst- nichts mitbekommt. Zumal ist das so eine once-a-year-Aufgabe, die man gern vergisst. Es lässt sich (bisher) auch nicht automatisieren - oder mir ist nicht bekannt, wie man das mit FHEM-Boardmitteln (HTTPMOD) lösen könnte.

Aus dem ioBroker und HomeKit Bereich habe ich dann etwas gefunden, wie man eine JSON Struktur erhält - und die kann man mit dem JsonMod-Modul auslesen und dann in FHEM weiter verarbeiten.

Nach ein bisschen herumprobieren hab ich eine -für mich- recht passable Lösung gefunden. Ist zwar irgendwie durch den Rücken übers Knie ins Auge, aber es funktioniert. Da man dies aber eher selten aktualisiert (bzw mMn muss), sollte das machbar sein.
1. wir brauchen zwei JsonMod Devices: eines um Abfallarten zu erhalten und eines für die Termine
2. ein bisschen perl-code in der myUtils kippt das in eine ical-Datei und legt diese unter www ab
Optional:
3. ein Calendar-Device um diese ical-Datei einzulesen
4. ein ABFALL-Device um das ganze aufzubereiten > siehe post #3
5. ein DOIF um die Daten regelmässig abzurufen
6. ein HTTPSRV Device um den kalender im internen Netz zur Verfügung stellen zu können

Hinweis: ein regelmäßiges Update der Daten macht imho wenig Sinn - ein bis zwei mal im Monat reicht vollkommen, wenn nicht sogar seltener. Deswegen sind alle JsonMod Devices per Standard disabled und können trotzdem manuell aktualisiert werden (via set reread). Wer möchte kann dies alles über ein at oder DOIF automatisieren (siehe auch Vorschlag weiter unten). Ein seltener Datenabruf kann auch dazu führen, dass man beim Anbieter unterm Radar fliegt und weniger auffällt (zum Bsp dann durch häufige/viele Abfragen von der gleiche IP und als nicht-MyMüll-App-Anfragen).

Ich hab das bei mir auch erfolgreich getestet - mein FHEM ist Stand Revision 25573 und läuft auf einem RasPi4 mit RaspiOS 11 (bullseye) und perl v5.32.1.

Ggf. müssen weitere Pakete (JsonMod) oder Module (ABFALL) installiert werden bevor ihr anfangen könnt.

Da ich kein perl-Programmierer bin, geht das Ganze sicher auch eleganter/besser/performanter. Vlt kann einer der perl-Gurus hier ein prüfenden Blick drüber werfen und optimieren. Ich wäre dankbar. :)

Vorgehen:
1. Als Erstes werden die Abfallarten benötigt, die in der Gemeinde zur Verfügung stehen. Dazu benötigt man zunächst die city_id der Gemeinde - die sucht man sich in diesem JSON-String raus:

--- Code: ---https://mymuell.jumomind.com/mmapp/api.php?r=cities
--- Ende Code ---
Wegberg hat die city_id 66005 (wichtig ist auch auf das attribut has_streets = true zu achten; nur dann gibt es imho noch Straßen die in Abfuhrbezirke unterteilt sind; ansonsten ist der Abfuhrbezirk hier unter area_id hinterlegt; man benötigt beide ids).
Mit der city_id erstellt man das erste JsonMod Device für die Abfallarten:

--- Code: ---defmod mymuell_trashtypes JsonMod https://mymuell.jumomind.com/mmapp/api.php?r=trash&city_id=66005
attr mymuell_trashtypes disable 1
attr mymuell_trashtypes group MyMüll
attr mymuell_trashtypes interval 0 10 15 * *
attr mymuell_trashtypes readingList multi(jsonPath("\$[*]"), property('name'), property('title'));;
attr mymuell_trashtypes room MyMüll
attr mymuell_trashtypes stateFormat { my $ret = "last Update: ";;\
  $ret .= POSIX::strftime("%d.%m. %H:%M",localtime(time_str2num(ReadingsTimestamp($name,".computedReadings","2000-01-01 00:00:00"))));;\
  return $ret;;\
  }
attr mymuell_trashtypes update-on-start 0
attr mymuell_trashtypes webCmd reread
--- Ende Code ---
Wichtig: einmalig ein reread durchführen!

2. Für den Abfuhrbezirk benötigt man die city_id und ruft den zweiten Link mit dem Straßenverzeichnis auf

--- Code: ---https://mymuell.jumomind.com/mmapp/api.php?r=streets&city_id=66005
--- Ende Code ---
und sucht nach der gewünschten Straße und notiert sich die area_id. Die Straße Am Gaskessel hat die area_id 2734.
Wenn der Abfuhrbezirk schon unter Punkt 1 gelistet war, kann man diesen direkt übernehmen.

Mit der city_id aus Punkt 1 und dem gefundenen Abfuhrbezirk (area_id) erstellt man das zweite JsonMod Device für die Abfuhrtermine:

--- Code: ---defmod mymuell_trash JsonMod https://mymuell.jumomind.com/webservice.php?idx=termins&city_id=66005&area_id=2734
attr mymuell_trash userattr trashTypeDevice
attr mymuell_trash disable 1
attr mymuell_trash group MyMüll
attr mymuell_trash interval 0 11 15 * *
attr mymuell_trash readingList multi(jsonPath("\$.._data[?(\@.cal_garbage_type in ['WEG_PAP', 'WEG_BIOTON', 'WEG_REST', 'WEG_LEICHT'])]"), concat(property('cal_id'), "_date"), property('cal_date_normal'));;\
multi(jsonPath("\$.._data[?(\@.cal_garbage_type in ['WEG_PAP', 'WEG_BIOTON', 'WEG_REST', 'WEG_LEICHT'])]"), concat(property('cal_id'), "_name"), ReadingsVal("mymuell_trashtypes",property('cal_garbage_type'),property('cal_garbage_type')));;
attr mymuell_trash room MyMüll
attr mymuell_trash stateFormat { my $ret = "last Update: ";;\
  $ret .= POSIX::strftime("%d.%m. %H:%M",localtime(time_str2num(ReadingsTimestamp($name,".computedReadings","2000-01-01 00:00:00"))));;\
  return $ret;;\
}
attr mymuell_trash trashTypeDevice mymuell_trashtypes
attr mymuell_trash update-on-start 0
attr mymuell_trash webCmd reread
--- Ende Code ---
Das Device mit den Abfallarten (erstellt unter Punkt 1) wird für das zweite JsonMod Device benötigt - siehe hier das userAttribut trashTypeDevice: dies muss den Devicenamen aus Punkt 1 enthalten, da readingList darauf zurückgreift und die Beschreibung entsprechend ersetzt.

Adaptiert das readingList ('WEG_PAP', 'WEG_BIOTON', 'WEG_REST', 'WEG_LEICHT') nach euren Wünschen bzw. den Abfallarten, die euch interessieren. Die zur Verfügung stehenden Arten findet ihr im ersten JsonMod Device aus Punkt 1 (mymuell_trashtypes):

--- Code: ---   READINGS:
     2022-01-26 16:53:28   WEG_BIO         pflanzliche Gartenabfälle
     2022-01-26 16:53:28   WEG_BIOTON      Biomüll
     2022-01-26 16:53:28   WEG_GLAS        Glasverpackungen
     2022-01-26 16:53:28   WEG_LEICHT      Leichtverpackungen
     2022-01-26 16:53:28   WEG_PAP         Altpapier
     2022-01-26 16:53:28   WEG_REST        Restabfall
     2022-01-26 16:53:28   WEG_SCHAD       Schadstoffe
     2022-01-26 16:53:28   WEG_SCHADMO     Schadstoffmobil
     2022-01-26 16:53:28   WEG_WEIH        Weihnachtsbaum
--- Ende Code ---
Wichtig: einmalig ein reread durchführen!

3. Öffnet das Terminal eurer FHEM Installation und installiert die Pakete libdata-ical-perl und libdatetime-format-ical-perl (und notiert euch die ggfs für eure recovery Dokumentation)

--- Code: ---sudo apt update && sudo apt install libdatetime-format-ical-perl libdata-ical-perl
--- Ende Code ---

4. in FHEM navigiert ihr zu der 99_myUtils.pm und fügt folgenden perl code hinzu:

--- Code: ---sub mymuell_trash {
  # created by yersinia - version 2023-01-17
  # https://forum.fhem.de/index.php/topic,125789.0.html
  # https://forum.fhem.de/index.php/topic,114898.0.html
  #
  use Data::ICal    qw( ); #requires libdata-ical-perl
  use Data::ICal::Entry::Event qw( );
  use Data::ICal::Entry::TimeZone qw( );
  use DateTime                 qw( );
  use DateTime::Format::ICal   qw( ); #requires libdatetime-format-ical-perl

  my $mymdev = shift // return "mymuell device not defined - call is mymueltrash(\"<DEVICE>\")";
 
  my $todayYMDHMS = DateTime
      ->now(time_zone => 'local')
      ->set_time_zone('floating')
      ->strftime('%Y-%m-%d %H:%M:%S');

  if(defined($mymdev)) {
  if(ReadingsVal($mymdev,".computedReadings","none") eq "none") {
#reading is not available - either device wrong or not yet reloaded
return "Reading .computedReadings is not available. Have you reloaded JsonMod device ".$mymdev."?";
}
  # get readings from device
my @mymreadings = split(',',ReadingsVal($mymdev,".computedReadings",""));

# sort by date ascending
# first build hash with dates
my %mymdatehash;
foreach my $mymr (@mymreadings) {
if($mymr =~ m/_date/) {
my $mymrId = substr($mymr,0,(length($mymr)-length("_date")));  #extract id by cutting off last characters
$mymdatehash{$mymrId} = [ ReadingsVal($mymdev,$mymr,"01.01.2000") ]; # reading will be "%d.%m.%Y"
}
}

# second sort by date
# https://stackoverflow.com/questions/50119939/sorting-hashof-array-of-datetime-string-in-perl
my @mymIdSorted = map { $_->[1] }
    sort { $a->[0] <=> $b->[0] }
    map {
        [ Time::Piece->strptime( $mymdatehash{$_}->[0], '%d.%m.%Y' )->epoch, $_ ]
    }
    keys %mymdatehash;

@mymreadings = (); #empty array and rebuild
foreach my $element (@mymIdSorted) {
push (@mymreadings, $element);
}

# https://perlmaven.com/generate-calendar-in-ical-format
my $trashcalendar = Data::ICal->new();

# add timezone to Calendar https://manpages.debian.org/bullseye/libdata-ical-perl/Data::ICal::Entry::TimeZone.3pm.en.html
my $tz = "Europe/Berlin";
my $trashtimezone = Data::ICal::Entry::TimeZone->new();
$trashtimezone->add_properties(tzid => $tz);
$trashcalendar->add_entry($trashtimezone);

my $trashEvLastModified = POSIX::strftime("%Y%m%dT%H%M%S",localtime(time_str2num(ReadingsTimestamp($mymdev,".computedReadings",$todayYMDHMS))));
my $trashEvLastUpdated = POSIX::strftime("%Y-%m-%d %H:%M:%S",localtime(time_str2num(ReadingsTimestamp($mymdev,".computedReadings",$todayYMDHMS))));

foreach my $mymr (@mymreadings) {
my $trashevent = Data::ICal::Entry::Event->new;
my $trashEvId = $mymr;
my $trashEvDesc = ReadingsVal($mymdev,($trashEvId."_name"),""); #get description of event
my ($d,$m,$y) = split('\.',ReadingsVal($mymdev,($trashEvId."_date"),"")); #get date of event by appending _date to id
my $trashEvBegin = DateTime->new(
    year => $y,
    month => $m,
    day => $d,
);
$trashEvBegin = $trashEvBegin->ymd('');

$trashevent->add_properties(
        summary     => $trashEvDesc,
description => $trashEvDesc."\nlast update of device \"".$mymdev."\" was on ".$trashEvLastUpdated."\ngenerated by FHEM script from MyMuell Device \"".$mymdev."\" on ".$todayYMDHMS,
comment => "generated by FHEM script from MyMuell Device \"".$mymdev."\" on ".$todayYMDHMS,
dtstart => [$trashEvBegin, {'VALUE' => 'DATE', TZID => $tz}],
'last-modified' => [$trashEvLastModified, {TZID => $tz}],
uid => "fhem-mymuell-".$mymdev."-".$trashEvId,
    );
    $trashcalendar->add_entry($trashevent);
}
my $trashICalPath = "www";
my $trashICalFilename = $trashICalPath."/fhem_mymuell_".$mymdev.".ics";
my $trasherror = FileWrite($trashICalFilename, $trashcalendar->as_string);
if(defined($trasherror)) {
return $trasherror;
} else {
return "mymuell ics file written to ".$trashICalPath."/fhem_mymuell_".$mymdev.".ics";
}
  } else {
  return "mymuell device not defined - call is mymueltrash(\"<DEVICE>\")";
  }
}
--- Ende Code ---
Speichern nicht vergessen.

5. Wenn noch nicht geschehen, ladet die JsonMod Devices einmal neu damit die readings erstellt werden. Dazu entweder reread in den Devices ausführen oder in der FHEM-Kommandozeile in der angegebenen Reihenfolge ausführen:

--- Code: ---set mymuell_trashtypes reread
--- Ende Code ---

--- Code: ---set mymuell_trash reread
--- Ende Code ---

6. führt die sub aus indem ihr in der FHEM Kommandozeile

--- Code: ---{main::mymuell_trash("mymuell_trash")}
--- Ende Code ---
eingebt. Die Funktion benötigt als Paramater den Devicenamen des zweiten JsonMod Devices (Punkt 2). Es gibt keine aktive Rückmeldung ob das erzeugen der Kalenderdatei funktioniert hat.

Ihr solltet aber im Ordner [/opt/fhem/]www eine ical Datei finden. Im Terminal findet man dies durch

--- Code: ---ls -lah /opt/fhem/www
--- Ende Code ---
raus:

--- Code: ----rw-r--r--  1 fhem dialout 12459 26. Jan 17:39 fhem_mymuell_mymuell_trash.ics
--- Ende Code ---
Der Dateiname setzt sich zusammen aus fhem_mymuell_<devicename>.ics.
(Passt den Pfad in der sub an, wenn ihr die ical-Datei woanders haben wollt/braucht.)
Die Termine sind als ganztägig und die Zeitzone als "Europe/Berlin" definiert.

Optional:
7. erstellt ein Calendar Device, welches auf die Datei zugreift - achtet auf den korrekten Pfad im File-Parameter (siehe Punkt 6):

--- Code: ---defmod mymuell_calendar Calendar ical file www/fhem_mymuell_mymuell_trash.ics 43200
attr mymuell_calendar group MyMüll
attr mymuell_calendar hideLaterThan 43d
attr mymuell_calendar hideOlderThan 1d
attr mymuell_calendar room MyMüll
--- Ende Code ---
Das Interval ist mit 43200s (12h) angegeben - das sollte eigtl reichen. Am Besten wäre ein manuelles Update nachdem die ical-Datei neu geschrieben worden ist.

8. erstellt ein ABFALL-Device - auch hier muss das korrekte Calender-Device (Punkt 7) angeben werden. > siehe post #3

9. Vorschlag für ein DOIF, welches die Quellen zweimal im Monat aktualisiert (am 2. und 16. gegen 15:00 Uhr):

--- Code: ---defmod mymuell_doif DOIF ([([15:00]+int(rand(900)))] and\
(($mday == 2) or ($mday == 16)))\
({Log3 "mymuell_doif",3,"mymuell: update trashtype"}, set mymuell_trashtypes reread)\
({Log3 "mymuell_doif",3,"mymuell: update trash events"}, set mymuell_trash reread)\
({Log3 "mymuell_doif",3,"mymuell: call ical function"}, {main::mymuell_trash("mymuell_trash")})\
({Log3 "mymuell_doif",3,"mymuell: update calendar"}, set mymuell_calendar update)
attr mymuell_doif do always
attr mymuell_doif group MyMüll
attr mymuell_doif room MyMüll
attr mymuell_Lgf_doif wait 0,30,60,60
--- Ende Code ---
Vielleicht kann es ja jemand gebrauchen. ;)

Updates:
2022-02-06 - die ics Datei wird in dem Ordner [/opt/fhem/]www abgelegt. Dies hat zum Vorteil, dass diese a) im Backup inkludiert ist und b) über HTTPSRV im lokalen Netzwerk Kalenderprogrammen wie zB Thunderbird zur Verfügung gestellt werden kann (getestet mit TB 91.5.1).
2022-02-15 - die Event-Description enthällt nun die Update-Zeitpunkte vom MyMüll-Device (Punkt 2) und der ICal-Datei
2022-08-23 - Anpassen der Definition eines Ganztagsevent (basierend auf stackoverlow-Beitrag und den dort verlinkten RFC 5545)
2022-12-08 - Hinweis auf ABFALL-Modul (siehe post #3) hinzugefügt; ABFALL Modul durch CALVIEW und zustätzlichen myUtils Code ersetzt (Punkt 8 )
2022-12-11 - Code (Punkt 4) erweitert (Array wird sortiert) und notify (Punkt 8 ) hinzugefügt
2022-12-23 - Punkt 8 bereinigt (calview Vorschlag nicht Zielführend und notify daher nicht mehr benötigt)
2023-01-17 - kleine Code-Aktualisierung: die Termine werden aufsteigend nach Datum sortiert bevor diese in die Kalenderdatei geschrieben werden; bei erfolgreicher Erstellung der Kalenderdatei gibt es nun auch Rückmeldung.

yersinia:
Kleines Update im ersten Post für den Perl-Code:

--- Zitat ---2022-08-23 - Anpassen der Definition eines Ganztagsevent (basierend auf stackoverlow-Beitrag und den dort verlinkten RFC 5545)
--- Ende Zitat ---

Eine weitere Möglichkeit sich die Abfuhrtermine anzeigen zu lassen ist die Kombination von CALVIEW und readingsGroup (für all jene, die das ABFALL-Modul nicht nutzen wollen).

CALVIEW-Device (benötigt das Calendar-Device aus dem vorigen Post (Punkt 7)):

--- Code: ---defmod mymuell_calview CALVIEW mymuell_calendar
attr mymuell_calview group MyMüll
attr mymuell_calview modes next
attr mymuell_calview room MyMüll
--- Ende Code ---

readingsGroup basierend auf diesem CALVIEW Device (zeigt die nächsten 5 Termine an):

--- Code: ---defmod mymuell_rg readingsGroup mymuell_calview:t_001_daysleftLong,t_001_weekdayname,t_001_summary,t_001_bdate\
mymuell_calview:t_002_daysleftLong,t_002_weekdayname,t_002_summary,t_002_bdate\
mymuell_calview:t_003_daysleftLong,t_003_weekdayname,t_003_summary,t_003_bdate\
mymuell_calview:t_004_daysleftLong,t_004_weekdayname,t_004_summary,t_004_bdate\
mymuell_calview:t_005_daysleftLong,t_005_weekdayname,t_005_summary,t_005_bdate
attr mymuell_rg alias MyMüll Abfuhrtermine
attr mymuell_rg group MyMüll
attr mymuell_rg mapping %READING
attr mymuell_rg room MyMüll
--- Ende Code ---

Es gibt auch einen alternativen Vorschlag aus dem Wiki zu readingsGroup.

yersinia:
Seit vorgestern habe ich nun auch die Abfuhrdaten fürs nächste Jahr (2023) drin. Ich schließe daraus, dass es bisher gut funktioniert. 8)

Wenn man nur die Daten benötigt, reicht es im Übrigen aus, nur Punkt 2 aus dem ersten Post durchzuführen (nachdem man die city_id, wie in Punkt 1 beschrieben, ermittelt hat). Punkt 1 sorgt nur für eine Ersetzung der Codes in Textform (zB WEG_PAP => Altpapier).

yersinia:
Stand jetzt zeichnet es sich ab, dass das in #1 erwähnte ABFALL-Modul nicht weiterentwickelt wird bzw verwaist ist. Daher hier zwei gute Vorschläge.


--- Zitat von: OdfFhem am 15 November 2022, 20:29:36 ---Ich benutze folgende FHEM-Devices und erhalte alle Informationen, die ich pro Abfallart für eine (readingGroup, FTUI, ...)-Darstellung benötige:


--- Code: ---defmod Abfallkalender Calendar ical file FHEM/abfall.ics.cfg 86400
attr Abfallkalender hideLaterThan 40d
attr Abfallkalender hideOlderThan 1s

--- Ende Code ---


--- Code: ---defmod AbfallView4Bio CALVIEW Abfallkalender 1
attr AbfallView4Bio event-on-change-reading .*
attr AbfallView4Bio filterSummary Abfallkalender:.*Bio.*
attr AbfallView4Bio maxreadings 1
attr AbfallView4Bio modes next
attr AbfallView4Bio weekdayformat de-short

--- Ende Code ---
...

--- Code: ---defmod AbfallView4Papier CALVIEW Abfallkalender 1
attr AbfallView4Papier event-on-change-reading .*
attr AbfallView4Papier filterSummary Abfallkalender:.*Papier.*
attr AbfallView4Papier maxreadings 1
attr AbfallView4Papier modes next
attr AbfallView4Papier weekdayformat de-short

--- Ende Code ---


--- Code: ---defmod rg_Abfallkalender readingsGroup <>,<Datum>,<Tag>,<Termin>\
NAME=AbfallView.*:t_001_bdate,t_001_weekdayname,t_001_summary

--- Ende Code ---

--- Ende Zitat ---

--- Zitat von: KyleK am 16 November 2022, 14:00:19 ---Ich würde noch einen 3. Hut in den Ring werfen: DOIF + uiTable  8)
https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg#Anzahl_der_Tage_bis_zur_Abfall-Entsorgung

Ich bin vom ABFALL-Modul auf diese Variante umgestiegen, und bin zufrieden.

--- Ende Zitat ---

Damian:
Ich habe den Wiki-Beitrag zu dem Thema erweitert. Jetzt werden auch die Tage für Termine im nächsten Jahr korrekt bestimmt.

https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg#Anzahl_der_Tage_bis_zur_Abfall-Entsorgung

Navigation

[0] Themen-Index

Zur normalen Ansicht wechseln