Pendeln in deutschen Großstädten ist kein Spaß. Laut einem Artikel von Spiegel Online stehen deutsche Autofahrer im Jahr 475.000 Stunden im Stau.

Mit dem Traffic Plugin bietet SmartHomeNG die Möglichkeit, Fahrzeiten über die Google Directions API zu berechnen.

So lässt sich auf einfache Art und Weise ein Alarmsystem bauen, dass einem im Büro Bescheid gibt, wenn es Zeit wird zu fahren (oder man länger bleiben sollte, damit sich der Stau wieder auflösen kann).

Die Erkennung, wo man sich gerade befindet, wird dabei, wie in https://www.smarthomeng.de/geozonen-basierte-services-mit-der-egigeozone-app-und-dem-network-plugin beschrieben, über EgiGeoZone und das Network Plugin umgesetzt.

Das Traffic Plugin

Die automatisch generierte Doku zum Traffic Plugin findet sich unter https://www.smarthomeng.de/user/plugins_doc/config/traffic.html.

Die Readme liegt unter https://www.smarthomeng.de/user/plugins/traffic/README.html, der Quellcode auf Github unter https://github.com/smarthomeNG/plugins/tree/master/traffic.

Der Support-Thread im Forum findet sich unter https://knx-user-forum.de/forum/supportforen/smarthome-py/1048446-traffic-plugin-support-thread.

Das Plugin ist schnell in der etc/plugin.yaml konfiguriert:


traffic:
    class_name: Traffic
    class_path: plugins.traffic
    apikey: your own api key
    language: de (optional)

Der persönliche API Key für die Directions API von Google kann unter https://developers.google.com/maps/documentation/directions/intro?hl=de beantragt werden.

Die Items

Wichtig sind dabei folgende drei Items zur Erfassung des aktuellen Orts, an dem man sich aufhält. Die Items wurden bereits in dem oben genanntem Artikel zu EgiGeoZone eingeführt, und werden, wie im Artikel beschrieben, von EgiGeoZone bedatet:

location:

        lat:
            type: str
            visu_acl: ro
            cache: 'yes'

        lon:
            type: str
            visu_acl: ro
            cache: 'yes'

        zone:
            type: str
            visu_acl: ro
            cache: 'yes'

Wichtig dabei ist, dass der Zonenname in EgiGeoZone für die Arbeitsstätte (oder mehrere Arbeitsstätten) den String „Arbeit“ enthält und der Zonenname für daheim „Home“. Alternative Namen gehen auch, müssen dann aber in der Logik weiter unten angepasst werden.

Zusätzlich müssen noch die Home Koordinaten jeweils als Item definiert werden.
Tipp: Alternativ können auch die Attribute _lat und _lon direkt auf dem SmartHome Objekt (bin/smarthome.py) genutzt werden.

location:

    home:
        lat:
            type: str
            visu_acl: ro
            value: 48.xxxxxxx
            cache: 'yes'

        lon:
            type: str
            visu_acl: ro
            value: 11.xxxxxxx
            cache: 'yes'

Für die Speicherung der Reisedaten, die vom Traffic Plugin zurückgegeben werden, werden folgende Items benötigt:

travel_info:

    calculate_way_home:
       type: bool
       cache: 'yes'
    
    calculate_way_work:
        type: bool
        cache: 'yes'

    travel_time:
        type: num
        
        in_traffic:
            type: num

    travel_distance:
        type: num
        
    travel_summary:
        type: str

Die Logik traffic_info.py

Als nächstes muss die aktuelle Fahrzeit zyklisch jede Minute über eine Logik vom Plugin abgefragt werden.

TrafficInfo:
    filename: traffic_info.py
    crontab: '* * * *'

Der Code der Logik sieht wie folgt aus:


# Codeblock 1 - Fahrt von der Arbeit nach Hause
if sh.now().hour < 20 and sh.now().hour > 14 and sh.now().weekday() in [0, 1, 2, 3, 4] and ('Arbeit' in sh.location.zone()):
    sh.location.calculate_way_home(1)
    destination = sh.location.home.lat() + "," + sh.location.home.lon()
    origin = sh.location.lat() + "," + sh.location.lon()
else:
    sh.location.calculate_way_home(0)

# Codeblock 2 - Fahrt von zu Hause in die Arbeit
if sh.now().hour < 10 and sh.now().hour > 4 and sh.now().weekday() in [0, 1, 2, 3, 4] and sh.location.zone() == 'Home':
    sh.location.calculate_way_work(1)
    destination = "Musterstraße 5, München"
    origin = sh.location.home.lat() + "," + sh.location.home.lon()
else:
    sh.location.calculate_way_work(0)

# Codeblock 3 - Default: Fahrt nach Hause von "irgendwo".
if not sh.location.calculate_way_work() and sh.location.zone() != 'Home':
    destination = sh.location.home.lat() + "," + sh.location.home.lon()
    origin = sh.location.lat() + "," + sh.location.lon()
    sh.location.calculate_way_home(1)

# Codeblock 4 - Wege- und Zeitbestimmung, ggf. Alarmmeldung oder Reset der Items
if sh.location.calculate_way_work() or sh.location.calculate_way_home():
    route = sh.traffic.get_route_info(origin, destination, False)
    if route is not None:
        summary = route['summary'] + ": %.1f km in %.0f min" % (round(route['distance'] / 1000, 2), round(route['duration_in_traffic'] / 60, 2))
        sh.location.travel_time(route['duration'])
        if 'duration_in_traffic' in route:
            sh.location.travel_time.in_traffic(route['duration_in_traffic'])
        sh.location.travel_distance(route['distance'])
        sh.location.travel_summary(summary)
        if 'Arbeit' in sh.location.zone() and sh.location.travel_time.in_traffic() >= 2500 > sh.location.travel_time.in_traffic.prev_value() and sh.now().hour < 20 and sh.now().hour > 14:
            sh.pushbullet.note("Alarm: Verkehr", "%s Min. Fahrzeit nach Hause!" % round((sh.location.travel_time.in_traffic() / 60), 2))
else:
    sh.location.travel_time('')
    sh.location.travel_time.in_traffic('')
    sh.location.travel_distance('')
    sh.location.travel_summary('-')

Im ersten Block wird geprüft, ob die aktuelle Uhrzeit zwischen 14:00 und 20:00 liegt (Zeitraum für die Prüfung bzgl. „Weg von der Arbeit nach Hause“) und, ob der aktuelle Tag Montag – Freitag ist. Zusätzlich wird geprüft, ob man sich in einer Zone befindet, deren Name  die Zeichenkette „Arbeit“ enthält. Ist dies der Fall, so werden das boolsche Item calculate_way_home auf True und als destination die Home-Koordinate gesetzt.

Der zweite Codeblock macht das Gleiche, aber für die (bevorstehende) Anfahrt in die Arbeit von zu Hause aus (4:00-10:00 als Zeitintervall). Die aktuelle Zone enthält den String „Home“, als Destination wird die Adresse der Arbeitsstätte gesetzt. calculate_way_work wird auf True gesetzt.

Im dritten Codeblock wird nun geprüft, ob man auf dem Weg zur Arbeit und nicht zu Hause ist. Ist man dies nicht, so wird auf Basis der aktuellen Position (die nicht zwingend die Arbeitsstätte ist) immer die Fahrzeit nach Hause berechnet. Diese kann bspw. in der Visu angezeigt werden. calculate_way_home wird für die nachfolgende Berechnung ebenfalls auf True gesetzt.

Im vierten Codeblock wird nun das Traffic Plugin aufgerufen. Über get_route_info(origin, destination, False) werden die Daten zur Route von der aktuellen Position zum Ziel abgefragt. Das Item location.travel_time wird mit der reinen Fahrzeit ohne Berücksichtigung der aktuellen Verkehrlage, das Unteritem location.travel_time.in_traffic mit Berücksichtigung der aktuellen Verkehrslage befüllt.
In das Item location.travel_distance wird die aktuellen Distanz geschrieben.
In location.travel_summary landet ein kurzes Summary als String.
Zuletzt wird eine Alarmmeldung über das Pushbullet Plugin versendet: sh.pushbullet.note("Alarm: Verkehr", "%s Min. Fahrzeit nach Hause!" % round((sh.location.travel_time.in_traffic() / 60), 2))

Das Ergebnis ist hier zu sehen:

Neben der in diesem Artikel gezeigten Grundfunktionalität, lassen sich mit dem Plugin bzw. der Directions API auch noch deutlich mehr Informationen, wie bspw. die vollständigen Routing-Informationen zum Zielort, bestimmen.

Ich persönlich kombiniere einige dieser Daten mit der Anzeige meiner Position und der Wegstrecke nach Hause in Google Maps. Hierfür habe ich ein smartVISU Widget geschrieben. Dies ist Thema eines zukünftigen Artikels.

(Die in diesem Artikel verwendeten Screenshots wurden selber erstellt. Das Titelbild ist unter der Creative Commons Zero (CC0) Lizenz veröffentlicht und wurde von www.pexels.com bezogen.)


Schreibe einen Kommentar

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