MVG (ÖVPN München) Abfahrtsmonitor über HTTPMOD abfragen

Begonnen von Nogga, 12 Dezember 2017, 22:12:38

Vorheriges Thema - Nächstes Thema

Nogga

Ich bin relativ neu in den "fortgeschrittenen" Themen bei FHEM.
Jetzt möchte ich gerne statt in einem iFrame den Abfahrtsmonitor der Münchner MVG in einem Device darstellen.

Es gibt eine Mobil-Seite, die die Abfahrtszeiten relativ einfach strukturiert darstellt (als Mobil-Seite) - im Beispiel mit einer konkreten Haltestelle:
http://www.mvg-live.de/ims/dfiStaticAnzeige.svc?haltestelle=Westkreuz+Bf.&ubahn=&bus=&tram=&sbahn=checked

Nun möchte ich die nächsten 5 Abfahrten gerne in den Readings haben.
Folgende Regex spuckt mir schonmal die Werte in verschiedenen Groups aus:
/<td class="lineColumn">(.*)<\/td>(.*)<td class="stationColumn">(.*)<span(.*)<td class="inMinColumn">(.*)<\/td>/sgU
(siehe: https://regex101.com/r/o37aem/1 )

Wie bekomme ich das jetzt in die HTTPMOD Readings rein (regex Gruppe 1, 3 und 5)  rein?

Zusätzliche Fragestellungen:
- wie bekomme ich den überflüssgen "Whitespace" vor und nach dem Stationsnamen raus (Leerzeichen und Absätze)
- wer die regex optimiert hinsichtlich überflüssiger Gruppen ignorieren - gerne zu!
- wie kann ich bestimmte Ziele (über den Haltestellen-Namen) ignorieren, sodass einfach die nächste Station zurückgegeben wird (Hintergrund: die App unterscheidet nicht die Richtungen)
- wie kann ich das einfach in einer Readingsgroup darstellen

Herzlichen Dank!

amenomade

reading10Name Line01
reading10Regex (?s)row.*?lineColumn">(.*?)<
reading11Name Station01
reading11Regex (?s)row.*?stationColumn">[\s]*(.*?)[\s]*<
reading12Name Minutes01
reading12Regex (?s)row.*?inMinColumn">(.*?)<

reading20Name Line02
reading20Regex (?s)row.*?row.*?lineColumn">(.*?)<
reading21Name Station02
reading21Regex (?s)row.*?row.*?stationColumn">[\s]*(.*?)[\s]*<
reading22Name Minutes02
reading22Regex (?s)row.*?row.*?inMinColumn">(.*?)<

reading30Name Line03
reading30Regex (?s)row.*?row.*?row.*?lineColumn">(.*?)<
reading31Name Station03
reading31Regex (?s)row.*?row.*?row.*?stationColumn">[\s]*(.*?)[\s]*<
reading32Name Minutes03
reading32Regex (?s)row.*?row.*?row.*?inMinColumn">(.*?)<

usw.

Bestimmte Ziele ignorieren ist somit nicht möglich, da die Readings grundsätzlich unabhängig voneinander sind. Die einzige Möglichkeit wäre m.A. mit userReadings und Code
Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

Nogga

Das probiere ich gleich mal heute Abend aus!
Vielen Dank.

Könnte man das im Zweifel nicht per hard-coding ausschließen?
Es gibt doch einen Negier-Operator bei Regex: z.B. https://stackoverflow.com/questions/1240275/how-to-negate-specific-word-in-regex/1240365
So in der (Pseudo-) Art:

!(Herrsching|Wessling)

amenomade

ZitatEs gibt doch einen Negier-Operator bei Regex:

Ja, aber, wenn Du "nicht negierst" (sprich, das ist eine Zeile, die dich interessiert), müsstest Du dann Rückwährts die Linie holen. Es sei denn, die Linie ist immer die gleiche für eine bestimmte Station. Dann musst Du einfach auf "nicht Herrsching oder Wessling" matchen, und dann Vorwährts die Minuten matchen. Die Linie kannst Du dann kalkulieren.
Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

JoWiemann

Hallo,

es gibt auch noch das Modul 98_DBPlan. Vielleicht deckt es ja Deine Bedürfnisse ab.

PS: ist nicht im Standard Fhem enthalten. Einfach im Forum oder Wiki suchen.


Gesendet von iPhone mit Tapatalk

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

amenomade

Alternativ: du extrahierst auf einmal in einem einzigen Reading "Linie Station Minutes"
Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

Nogga

@Jo: Richtig, aber die zeigt meines Wissens den Fahrplan an und wenn ich mich recht erinnere hat das bei mir nicht funktioniert.
@amenomade: Das ginge auch - ich versuche das mal heute abend. Es sei denn Du hast auf Anhieb auch die Variante um das in einem Reading mit Negieren abzulegen?!

JoWiemann

Hm, was hat denn nicht funktioniert?


Gesendet von iPhone mit Tapatalk

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

Nogga

Das hab ich vor langer Zeit mal probiert.

Ich will eigentlich nur die Abfahrtszeiten von einer bestimmten Haltestelle. Und bei dem Modul muss man auch das Ziel angeben und noch ein paar andere Dinge... Und es sind auch nicht die Minuten bis zur Abfahrt sondern die Abfahrts-Uhrzeiten.

Alles in allem ist es glaube ich per HTTPMOD + Regex bedeutend einfacher und schneller...

Nogga

Das funktioniert tatsächlich ganz gut:


defmod mvg_abfahrten HTTPMOD http://www.mvg-live.de/ims/dfiStaticAnzeige.svc?haltestelle=Westkreuz+Bf.&ubahn=&bus=&tram=&sbahn=checked 1800
attr mvg_abfahrten userattr reading10Name reading10Regex reading11Name reading11Regex reading12Name reading12Regex reading20Name reading20Regex reading21Name reading21Regex reading22Name reading22Regex
attr mvg_abfahrten enableControlSet 1
attr mvg_abfahrten reading10Name Line01
attr mvg_abfahrten reading10Regex (?s)row.*?lineColumn">(.*?)<
attr mvg_abfahrten reading11Name Station01
attr mvg_abfahrten reading11Regex (?s)row.*?stationColumn">[\s]*(.*?)[\s]*<
attr mvg_abfahrten reading12Name Minutes01
attr mvg_abfahrten reading12Regex (?s)row.*?inMinColumn">(.*?)<
attr mvg_abfahrten reading20Name Line02
attr mvg_abfahrten reading20Regex (?s)row.*?row.*?lineColumn">(.*?)<
attr mvg_abfahrten reading21Name Station02
attr mvg_abfahrten reading21Regex (?s)row.*?row.*?stationColumn">[\s]*(.*?)[\s]*<
attr mvg_abfahrten reading22Name Minutes02
attr mvg_abfahrten reading22Regex (?s)row.*?row.*?inMinColumn">(.*?)<


und noch tabellarisch in einer readingsgroup:


defmod mvg_abfahrtsmonitor readingsGroup <Linie>,<Ziel>,<min> mvg_abfahrten:Line01,Station01,Minutes01 mvg_abfahrten:Line02,Station02,Minutes02
attr mvg_abfahrtsmonitor nonames 1


Schön wäre jetzt tatsächlich noch das Filtern von bestimmten Einträgen (=Destinationen)

amenomade

Auf Destination könntest Du die Readings so holen (ich will hier nur München oder Ebersberg):
reading01Name Row1
reading01Regex (?s)((München|Ebersberg).*?\/tr)

reading02Name Row2
reading02Regex (?s)(?:München|Ebersberg).*?((München|Ebersberg).*?\/tr)

reading03Name Row3
reading03Regex (?s)(?:München|Ebersberg).*?(?:München|Ebersberg).*?((München|Ebersberg).*?\/tr)

usw

Und dann auf jedem "Row", userReadings bauen, um jeweils Station und Minutes extrahieren, und Line kalkulieren.
Pi 3B, Alexa, CUL868+Selbstbau 1/2λ-Dipol-Antenne, USB Optolink / Vitotronic, Debmatic und HM / HmIP Komponenten, Rademacher Duofern Jalousien, Fritz!Dect Thermostaten, Proteus

Nogga

Ich hab jetzt nochmal Deine zweite Variante ausprobiert - leider erfolglos. Ich bekomme immer nur die Abfahrten von Ebersberg :-(

Ich habe die gleiche Übung mal in Javascript gebaut, nur von Perl habe ich keine Ahnung:


    // Make request to MVG
    ajax(
        {
            url: e.item.dataUrl
        },
        function(response) {
            // remove line breaks
            response = response.replace(/(\r\n|\n|\r)/gm, "");
            // regex to identify rows
            var responseArray = response.match(/<tr class="row(even|odd)">(.+?)<\/tr>/gi);
            var resultBody = '';
            console.log(e.item.dataStationDrop);
           
            // loop through array and build departure text lines
            responseArray.forEach(function (element, index, array) {
                var dropDeparture = false;
               
            var destination = element.match(/<td class="stationColumn">(.*?)<span/);
                e.item.dataStationDrop.forEach(function(dropElement, dropIndex, dropArray) {
                    if (dropElement.toLowerCase() == destination[1].trim().toLowerCase()) {
                        dropDeparture = true;
                    }
                });

                if (dropDeparture === false) {
               
                    var line = element.match(/<td class="lineColumn">(.*?)<\/td>/);
                    if (line !== null) { line = line[1].trim(); }
       
                    if (destination !== null) { destination = destination[1].trim().truncate(9); }
                   
                var departure = element.match(/<td class="inMinColumn">(.*?)<\/td>/);
                    if (departure !== null) { departure = departure[1].trim(); }
       
                    // add destination to output
                    if (line !== null && destination !== null && departure !== null) {
                        // padding: http://stackoverflow.com/a/32016635/3712979
                        var departureLine = ('  ' + departure).substr(-2) + 'm: ' + line + ' ' + destination;
                        console.log(departureLine);
                        resultBody = resultBody + "\n" + departureLine;
                    }
                }
            });

(Das war Teil einer anderen kleinen App, deshalb nur unvollständig)

Kann ich eventuell nicht sogar die gesamte Extraktion von einem Wert in ein User-Reading eines Dummies legen?! ODer ggf. über eine Routine in MyUtils?