Indego4shNG

Table of Content

  1. Generell

  2. Credits

  3. Change Log Neu

  4. Konfiguration Update

  5. Web-Interface

  6. Logik-Trigger

  7. öffentlich Funktionen (API)

  8. Gartenkarte „pimpen“

  9. Nutzung der Original Bosch-Mäher-Symbole

  10. Die Bosch-Api 4.0.1 - behind the scenes

Generell

Das Indego-Plugin wurde durch ein Reverse-Engineering der aktuellen (Version 3.0) App von Bosch entwickelt. Als Basis diente das ursprüngliche Plugin von Marcov. Es werden alle Funktionen der App für den Betrieb sowie einige zusätzliche bereitgestellt. Für die Ersteinrichtung wird weiterhin die Bosch-App benötigt. Das Plugin erhält die Version der aktuellen Bosch-API. (4.0.1)

Credits

Vielen Dank an schuma für die tolle Unterstützung während der Entwicklungsphase, die Umsetzung vieler Teile in der Visu sowie den vielen unzähligen Tests und sehr viel Geduld.

Vielen Dank an bmx für das Umstellen des Plugins auf Smart-Plugin. Vielen Dank an psilo für die Erlaubnis zur Verwendung der LED-Grafiken im Web-Interface. Vielen Dank an Marcov für die Entwicklung des ursprünglichen Plugins. Vielen Dank an das Core-Team für die Einführung der STRUCTS, das hat die Arbeit deutlich vereinfacht. Vielen Dank an Jan Odvarko für die Entwicklung des Color-Pickers (http://jscolor.com) unter Freigabe für Opensource mit GPLv3

Change Log

2023-05-06 V4.0.1

  • Login via Single-Key-ID eingebaut

  • Endpoit der Bosch-API wurde geändert (siehe Konfiguration)

2023-03-08 V4.0.0

  • Login via Bosch-ID eingebaut

2023-02-05 V3.0.2

  • Anpassungen für die geänderten Daten für das Wetter (es werden nun 7 Tage statt 5 übermittelt, die Sonnenstunden je Tag wurden entfern)

2021-05-16 V3.0.1

  • rücksetzen des Messerzählers eingebaut

  • besseres Handling beim automatischen AUS/EIN-Loggen

  • Einstellen des Mäher-Standorts über das Web-Interface (die Location wird durch die Bosch-App nicht richtig gesetzt, die Wetterdaten werden dann nicht mehr korrekt übermittelt.)

2019-10-28 V3.0.0

  • Kommunikation auf requests geändert

  • Verwendung von vordefinierten STRUCTS für alle benötigten Items

  • verbessertes Login/Session-Handling

  • Umstellung auf Code64 verschlüsselte Credentials

  • Integration eines Wintermodus wenn der Mäher stillgelegt ist

  • Integration der Mähkalenderverwaltung

  • Integration der SmartMow-Einstellungen

  • Integration „Mähen nach UZSU“

  • verbesserte Darstellung der Icons für das Wetter

  • Gartenkarte als Item in Visu integriert

  • „pimpen“ der Gartenkarte mit eigenen Vektoren

  • Mähspurdarstellung für die IndegoConnect 350/400

  • Aktualisierung der Mäherposition beim Mähen alle 7 Sekunden

  • Darstellung der Informationen zum genutzten GSM-Netz sowie zum verwendeten Standort

  • Updatefunktionen für Firmware integriert

  • Integration der Sensorempfindlichkeit

  • Integration von unterschiedlichen Bilder für Große/Kleine Mäher

  • Alarme / Meldungen werden in einem Popup dargestellt und können gelesen/gelöscht werden.

  • VISU um Batterie-Informationen erweitert

  • diverse Charts für Batterie, Temperatur, Mäheffizienz, Mäh-/Ladezeiten

  • Protokoll für Mäher STATI und Bosch-Kommunikation im Web-Interface

  • Unterstützung für base64 codierte Credentials im Web-Interface

  • Trigger für Alarme und STATI des Mähers im Web-Interface

  • Mäherfarbe für die Darstellung der Kartenkarte im Web-Interface wählbar

Requirements

Das UZSU-Plugin wird genutzt. Das UZSU-Plugin sollte vor dem Indego4shNG-Plugin geladen sein (Reihenfolge in der smarthome/etc/plugin.yaml)

benötigte Software

  • SmartVISU 2.9 oder höher (es werden Dropins verwendet)

  • smarthomeNg 1.6 oder höher (es werden vordefinierte STRUCTS verwendet)

  • für die Darstellung der Charts muss eine Database-plugin aktiviert sein

Supported Hardware

  • Indego Connect 350/S+350/400/S+400, im folgenden die „Kleinen“ genannt

  • Indego Connect 800/1000/1200/1300, im folgenden die „Großen“ genannt

Die Firmware der „Kleinen“ und der „Großen“ liefern unterschiedliche Informationen und stellen unterschiedliche Funktionen zur Verfügung. Hier werden kurz die Unterschiede erläutert:

Bei den „Großen“ gibt es folgende Einschränkungen:

  • der Ladezustand des Akkus wird auf Grund der abfallenden Spannung berechnet (35 Volt = 100 %, 28 Volt = 0%) Langzeitbeobachtungen haben gezeigt, dass die Mähe bei 31 Volt zurück in die Ladestation fahren. Es wird unterstellt, dass 31 Volt noch 20 % Akkuladestand sind.

  • Es werden aktuell keine Informationen zur Netznutzung bereitgestellt.

  • Die Aktualisierung der Mäherposition erfolgt nur ca. alle 30 Minuten während des Mähens

  • Die Sensor-Empfindlichkeit kann nicht eingestellt werden

Bei den „Kleinen“ gibt es folgende Einschränkungen:

  • Es wird von Bosch keine gemähte Fläche übermittelt. Diese kann mittels des „MowTracks“ aber angezeigt werden.

Konfiguration

plugin.yaml

folgende Einträge werden in der „./etc/plugin.yaml“ benötigt.

  • plugin_name: Indego4shNG: fix „Indego4shNG“

  • class_path: plugins.indego4shng: fix „plugins.indego4shng“

  • path_2_weather_pics: XXXXXXX: ist der Pfad zu den Bilder des Wetter-Widgets. (default =“/smartvisu/lib/weather/pics/“)

  • img_pfad: XXXXXXX: ist der Pfad unter dem die Gartenkarte gespeichert wird. (default = „/tmp/garden.svg“) Die Datei wird nicht für die VISU benötigt. Man kann die Datei als Vorlage zum „pimpen“ der Gartenkarte verwenden

  • indego_credentials : XXXXXXX: sind die Zugangsdaten für den Bosch-Server im Format base64 encoded.

  • parent_item : indego: name des übergeordneten items für alle Child-Items

  • cycle : 30: Intervall in Sekunden für das Abrufen des Mäher-Status (default = 30 Sekunden)

  • url: https://api.indego-cloud.iot.bosch-si.com/api/v1/ : Url des Bosch-Endpoints

Die Zugangsdaten (indego_credentials) können nach dem Erststart des Plugins im Web-Interface erfasst und gespeichert werden

!! Das parent-Item kann umbenannt werden ,es müssen dann aber alle items in der indego.html angepasst werden !!

Beispiel:

Indego4shNG:
    plugin_name: Indego4shNG
    class_path: plugins.indego4shng
    path_2_weather_pics: /smartvisu/lib/weather/pics/
    img_pfad: /tmp/garden.svg
    indego_credentials:
    parent_item: indego
    cycle: '30'
    url: https://api.indego-cloud.iot.bosch-si.com/api/v1/

items.yaml

Es wird ledigliche folgender Eintrag für die Items benötigt. Die restlichen Informationen werden aus der mitgelieferten Struct-Definition gelesen. Eine entsprechende Config-Datei ist im Ordner „items“ des Plugins bereits vorhanden und muss nur in den Ordner „./smarthome/items“ kopiert werden.

%YAML 1.1
---

indego:
    struct: indego4shng.child

SmartVisu

Die Inhalte des Ordners „./sv_widgets“ müssen in den entsprechenden Ordner der VISU. In der Regel „/var/www/html/smartvisu/dropins“ kopiert werden. Wenn das smartvisu-Plugin verwendet wird und das kopieren der Widget nicht abgeschalten ist, werden die Dateien beim Start von shNG automatisch in den Dropin-Ordner kopiert. Ansonsten müssen die Daten manuell in das Verzeichnigs „./dropins“ kopiert werden.

Die Icons aus „indego4shng/pages/icons/“ müssen in das visu-dir „dropins/icons/ws/“ kopiert werden.

Im Ordner „/pages“ des plugins ist eine vorgefertigte Raumseite für die SmartVISU. (indego.html) Diese muss in den Ordner „/pages/DeinName/“ kopiert werden und die Raumnavigation entsprechend ergänzt werden.

!!! Immer auf die Rechte achten !!!

Web-Interface

Kurze Erläuterung zum Web-Interface

erster Tab - Übersicht Indego-Items

Webif-Tab1

zweiter Tab - Originalgartenkarte / Settings

Hier wird die Original-Gartenkarte wie sie von Bosch übertragen wird angezeigt. Es kann mit dem Colour-Picker die Farbe des Mähers in der Visu angepasst werden. Die Originalkarte bleibt unverändert. Im ersten Tab wird unter dem Item indego.visu.map_2_show die modifizierte Karte angzeigt. Es können auf dieser Seite zusätzlich Vektoren eingefügt werden welche die Gartenkarte erweitern bzw.“aufhübschen“ Hier kann die Location auf den Bosch-Servern gespeichert werden. Es müssen Längen/Breitengrad angegeben werden. Wenn noch keine Koordinaten in den Items gespeichert sind werden die Long/Lat von shNG vorgeschlagen. Sieh auch hier: gardenmap

Es können hier bis zu 4 Trigger für Stati gewählt werden. 999999 - kein Status gewählt. Immer wenn der Status des Mähers auf den gewählten Status wechselt wird das Trigger-item „indego.trigger.state_trigger_X:“ (X = 1-4 ) gesetzt. Die Trigger können in einer Logik verarbeitet werden. Beispiel siehe bei Logiken. Es können bis zu 4 Texte für Meldungen erfasst werden. Wenn der Text in der Überschrift oder im Inhalt der Meldung ist wird der Trigger „indego.trigger.alarm_trigger_X:“ (X = 1-4 ) beim eintreffen der Meldung gesetzt.

Webif-Tab1

dritter Tab - State-Protokoll

Hier können die einzelnen Statuswechsel des Mähers eingesehen werden. Es erfolgt bei jedem Statuswechsel ein Eintrag, das Protokoll ist selbst rotierend und hat maximal 500 Einträge

Webif-Tab1

vierter Tab - Kommunikationsprotokoll

Hier können Protokoll-Einträge zu den einzelnen Kommunikationsanfragen mit dem Bosch-Server eingesehen werden. Es erfolgt bei jedem Statuswechsel ein Eintrag, das Protokoll ist selbst rotierend und hat maximal 500 Einträge Webif-Tab1

Logik-Trigger

Über die Items :

indego.trigger_state_trigger_1(2)(3)(4)

und

indego.trigger_alarm_trigger_1(2)(3)(4)

können Events auf state-Wechsel und Meldungen in Logiken ausgeführt werden. Die Trigger werden über das Web-Interface definiert. Bei den Alarmen wird ein Teil des Textes der Alarm-Meldung oder der Überschrift angegeben. Groß- Kleinschreibung spielt keine Rolle Wenn der Text in der Meldung bzw. der Überschrift enthalten ist wird der Trigger ausgelöst.

Beispiel :

#!/usr/bin/env python3
# indego2alexa.py

text=''
try:
    triggeredItem=trigger['source']
    triggerValue = trigger['value']
    
    # Check the State-Items 
    if triggeredItem == 'indego.trigger.state_trigger_1':
        if triggerValue == True:
            text = 'Achtung der Indego nimmt seine Arbeit auf'
    
    elif triggeredItem == 'indego.trigger.state_trigger_2':
        if triggerValue == True:
            text = 'Der Indego hat seine Arbeit getan Danke Indego'        
            
    elif triggeredItem == 'indego.trigger.state_trigger_3':
        if triggerValue == True:
            text = ''        
            
    elif triggeredItem == 'indego.trigger.state_trigger_4':
        if triggerValue == True:
            text = ''        
    
    # Now the Alarm-Items
    if triggeredItem == 'indego.trigger.alarm_trigger_1':
        if triggerValue == True:
            text = 'Achtung der Indego benötigt Wartung'
    
    elif triggeredItem == 'indego.trigger.alarm_trigger_2':
        if triggerValue == True:
            text = 'Achtung der Indego benötigt neue Messer'        
            
    elif triggeredItem == 'indego.trigger.alarm_trigger_3':
        if triggerValue == True:
            text = ''        
            
    elif triggeredItem == 'indego.trigger.alarm_trigger_4':
        if triggerValue == True:
            text = ''
            
    if text != '':
        sh.alexarc4shng.send_cmd('Kueche', 'Text2Speech', text);
except:
    pass

öffentliche Funkionen

Es gibt eine Funktion die z.B. über Logiken aufgerufen werden kann.

send_command(Payload as String)

Man kann so z.B. den Mäher bei einsetzendem Regen der durch die Wetterstation erkannt wird zurück in die Ladestation schicken. Anderes Beispiel wäre beim Verlassen des Hauses den Mäher starten.

#!/usr/bin/env python3
# indego_rc.py

sh.Indego4shNG.send_command('{"state":"returnToDock"}','Logic')
#sh.Indego4shNG.send_command('{"state":"mow"}','Logic')
#sh.Indego4shNG.send_command('{"state":"pause"}','Logic')



Gardenkarte „pimpen“

Die Gartenkarte wird vom Bosch-Server heruntergeladen und als Item für die Visu verwendet. Die Datei wird als Vorlage zum Erweitern unter dem angegebenen Pfad gespeichert ( vgl. img_pfad im Konfig-Teil).

Man kann die Karte als Vorlage in einem Online-Tool (#https://editor.method.ac/) als Vorlage laden. Es werden dann einfach die zusätzlichen Vektoren eingezeichnet oder via „File / Import Image“ hinzugeladen.

Man kann die veränderte Karte auch lokal zwischenspeichern.

Am Ende wählt man im Menü die Ansicht „View“ den Eintrag „Source“. Hier kann man die erweiterten Vektoren einfach in die Zwischenablage kopieren und im Web-Interface unter Tab-2 einfügen. Der letzte Original-Eintrag der Bosch-Karte ist die Zeile mit

<circle id="svg_8" r="15" cy="792" cx="768" fill="#FFF601" stroke-width="0.5" stroke="#888888"/>

Die Werte können abweichen, da hier auch die Position des Mähers sowie die ID enthalten ist. Am besten auf „circle“ und den Farbwert „#FFF601“ achten.

Diese Zeile ist der gelbe Punkt (Mäher) in der Originalkarte. Beim Verlassen der Textarea werden die neuen Vektoren sofort in ein Item gespeichert und die Gartenkarte neu gerendert. Das Ergebnis ist in der VISU sofort sichtbar.

Beispiel :

pimp_my_map

Nutzung der Original Bosch-Mäher-Symbole

Es werden die Bilder der Bosch 2.2.8 App verwenden. Man kann sich die Bilder aus der „Legacy Bosch Smart Gardening“-App extrahieren. Die APK-Datei ist im Internet zu finden.

Die apk-Datei mit einer Archiv-Verwaltung öffnen und dort im Pfad „/assets/www/assets“ die Bilder extrahieren und in den Dropins-Ordner kopieren. Die Bilder haben folgende Dateinamen :

Für die „Großen“

indego.png
indego-docked.png
indego-mowing.png

Für die „Kleinen“

indego-s.png
indego-docked-s.png
indego-mowing-s.png

Sobald die Dateien mit den Bildern vorhanden sind findet das Widget diese und verwendet sie automatisch. Die entsprechenden Bilder für die „Großen“/“Kleinen“ werden auf Grund des Mähertyps automatisch gewählt und dargestellt.

Die Bosch-Api 4.0.1 - behind the scenes

Hier ist die Schnittstelle der Bosch-API kurz beschrieben und die Implementierung im Plugin dokumentiert. Der Header ist in den meisten Fällen mit der Session-ID zu füllen :

headers = {'accept' : '*/*',
                   'authorization' : 'Bearer '+ self._bearer,
                   'connection' : 'Keep-Alive',
                   'host'  : 'api.indego-cloud.iot.bosch-si.com',
                   'user-agent' : 'Indego-Connect_4.0.0.12253',
                   'content-type' : 'application/json'
           }

@Get - steht für einen get-request in Python. Die URL lautet : „https://api.indego-cloud.iot.bosch-si.com/api/v1/“ gefolgt vom entsprechenden Zugriffspunkt

url = "https://api.indego-cloud.iot.bosch-si.com/api/v1/" +"alms/{}/automaticUpdate".format(alm_sn) 
response = requests.get(url, headers=headers)

Über die Items :

plugin-Supp. API-URL Payload
ja "@DELETE("alerts/{alert_id}")" -
nein @POST("users") {}
nein @DELETE("alerts") -
nein @DELETE("alms/{alm_serial}/map") -
nein @DELETE("users/{user_id}") -
ja @GET("alms/{alm_serial}/config") -
ja @GET("alms/{alm_serial}") {"needs_service": false, "alm_firmware_version": "00647.01043", "service_counter": 159551, "bareToolnumber": "3600HA2300", "alm_name": "Indego", "alm_sn": "XXXXXXXXXX", "alm_mode": "manual"}
nein @GET("alms/{alm_serial}") -
nein @GET("pub/accessories") -
ja @GET("alerts") -
ja @GET("alms/{alm_serial}/automaticUpdate") -
ja @GET("alms/{alm_serial}/updates") -
ja @GET("alms/{alm_serial}/operatingData") -
ja @GET("alms/{alm_serial}/calendar") {}
ja @GET("alms/{alm_serial}/predictive/location") {}
ja @GET("alms/{alm_serial}/predictive/lastcutting") -
ja @GET("alms/{alm_serial}/map") -
nein @GET("alms/{alm_serial}/info") {"bareToolnumber": "3600HA2300"}
ja @GET("alms/{alm_serial}/location") {"longitude": x.xxxx, "latitude": xx.xxxxx}
ja @GET("alms/{alm_serial}/network") -
ja @GET("alms/{alm_serial}/predictive/nextcutting") -
nein @GET("alms/{alm_serial}/info") -
nein @GET("pub/accessories/{accessory_code}") -
nein @GET("alms/{alm_serial}/security") {"enabled": true, "autolock": false}
ja @GET("alms/{alm_serial}/predictive") {"enabled": false}
ja @GET("alms/{alm_serial}/predictive/schedule") {'exclusion_days': [{'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 14, 'En': True, 'Attr': 'tTD', 'StMin': 0}, {'StHr': 18, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 't', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 0}, {'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 12, 'En': True, 'Attr': 'tT', 'StMin': 0}, {'StHr': 18, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 'tT', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 1}, {'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 14, 'En': True, 'Attr': 'tTD', 'StMin': 0}, {'StHr': 16, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 'tT', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 2}, {'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 'tTD', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 3}, {'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 'tTD', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 4}, {'slots': [{'StHr': 0, 'EnMin': 0, 'EnHr': 8, 'En': True, 'Attr': 'C', 'StMin': 0}, {'StHr': 8, 'EnMin': 0, 'EnHr': 14, 'En': True, 'Attr': 'tTD', 'StMin': 0}, {'StHr': 16, 'EnMin': 0, 'EnHr': 22, 'En': True, 'Attr': 'tT', 'StMin': 0}, {'StHr': 22, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 5}, {'slots': [{'StHr': 0, 'EnMin': 59, 'EnHr': 23, 'En': True, 'Attr': 'C', 'StMin': 0}], 'day': 6}], 'schedule_days': [{'slots': [{'En': True, 'StHr': 14, 'EnMin': 0, 'StMin': 0, 'EnHr': 16}], 'day': 0}]}
ja @GET("alms/{alm_serial}/predictive/setup") -
ja @GET("alms/{alm_serial}/state") {"svg_xPos": 768, "runtime": {"session": {"charge": 0, "operate": 4}, "total": {"charge": 34352, "operate": 193907}}, "mowed": 89, "mowmode": 0, "xPos": 14, "yPos": 96, "svg_yPos": 792, "mapsvgcache_ts": 1573585675296, "state": 64513, "map_update_available": false}
nein @GET("pub/static/{resource_id}") -
nein @GET("pub/support/DE") {'email': 'indego.support@de.bosch.com', 'phone': '+49 711 400 40 470'}
nein @GET("users/{user_id}") {'optInApp': False, 'display_name': 'XXXXXXXXX', 'email': 'xxxxx.xxxx@xxxxxxxx.xxx', 'country': 'DE', 'language': 'de', 'optIn': False}
nein @GET("pub/video&country_code=DE&language=de&mowerType=XXXX") -
ja @GET("alms/{alm_serial}/predictive/weather") -
ja @POST("authenticate") -
nein @POST("authenticate?facebook") -
ja @DELETE("authenticate") -
nein @POST("alms/{alm_serial}/pair") -
nein @POST("alms/{alm_serial}/map") -
ja @POST("alms/{alm_serial}/requestPosition") -
ja @PUT("alms/{alm_serial}") -
ja @PUT("alerts/{alert_id}") -
nein @PUT("alerts") -
ja* @PUT("alms/{alm_serial}/config") -
ja @PUT("alms/{alm_serial}/automaticUpdate") {}
nein @PUT("alms/{alm_serial}/updates/notification/{process_id}") -
ja @PUT("alms/{alm_serial}/calendar") -
nein @PUT("alms/{alm_serial}/dateAndTime") -
ja @PUT("alms/{alm_serial}/predictive/location") -
ja @PUT("alms/{alm_serial}/updates") -
nein @PUT("alms/{alm_serial}/predictive/reset") @Query("reinitialize")
nein @PUT("alms/{alm_serial}/security") -
ja @PUT("alms/{alm_serial}/predictive") -
ja @PUT("alms/{alm_serial}/predictive/setup") -
ja @PUT("alms/{alm_serial}/state") -
nein @PUT("users/{user_id}") -
ja @POST("authenticate/check") -
nein @POST("pub/resetpassword") -
nein @DELETE("alms/{alm_serial}/pair") -

‚* nur bei 350/350+/400/400+