Steinel Lampen & Steinel BT/IP Gateway

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 349

    #1

    Steinel Lampen & Steinel BT/IP Gateway

    Steinel Lampen (BT Mesh) in Loxone einbinden

    ----------------------------------
    Ausgangslage

    Der Hersteller Steinel bietet ein breites Spektrum an Lampen für den Außenbereich (IP44 und IP54) die per BT-Mesh Technologie netzartig alle miteinander verbunden sind (bzw. sein können) und miteinander sprechen können (und als Knoten im Netzwerk Daten weiterleiten können). Die Lampen können bzw. müssen in ihrem mitunter kollektiven Verhalten per "Steinel Connect" App angelernt und konfiguriert werden.

    Mit folgenden Schritten erreicht man einen aus Sicht der Steinel App manuellen Betriebsmodus, der die Lampen in Loxone integrierbar und steuerbar bzw. automatisierbar macht (samt Bewegungs-Meldungen & Licht-Intensitäten). Hierfür wird der Loxberry benötigt (nutzt php-Skripte (insb. für Dim-Befehle und Lux-Wert Interpretation)und das vorinstallierte MQTT-Plugin), sowie notwendigerweise das Steinel BT/IP Gateway. In der Loxone Config verwendet das Ganze dann hauptsächlich Status-Bausteine, eine Lichtsteuerung und virt. Aus- und Eingänge. Für eine einfache An-/Aus-Steuerung von Lampen (ohne Lux-Input der Lampen) reicht ggf. auch ein beliebiger MQTT-Broker + das native Loxone MQTT-Plugin als Schnittstelle. Außerdem ist ein MQTT-Analyse Tool wie z.B. der Incoming View vom Loxberry MQTT-Plugin, oder aber besser der MQTT Explorer von Vorteil.

    Die Zielstellung dieser Anleitung ist eine Signalkette wie folgt:
    Lampe - Gateway - ? - (Miniserver/Smart-Home Zentrale) - ? - Gateway - Lampe,

    Die Lösung sieht genauso aus:
    Bewegung / Lichtverhältnisse → Lampe (mit PIR- & Lux-Sensor + BT, ggf. Stati) → IP/BT-Gateway → MQTT-Broker (=Loxberry, subscribe) → Miniserver (Loxone Logik, Lichtsteuerungs-Baustein) → MQTT-Broker (publish) → IP/BT-Gateway → Lampe → AN/AUS / Dim-Level / Settings / Status-Abfragen

    Getestet wurde alles mit einigen Lampen des Typs "Steinel L 840 SC" (S: mit Bewegungsmelder & Bluetooth). Die Lösung + Hintergründe sind unten.

    ----------------------------------
    Die Hardware: Steinel BT/IP Gateway

    Aus den öffentlich verfügbaren Dokumenten von Steinel entnimmt man: "Das Bluetooth IP Gateway vernetzt Bluetooth Mesh-Systeme mit IP-Netzwerken via MQTT, erlaubt die Ausleitung von Statusinformationen zu Leuchten und Sensoren sowie Remote-Einstellungen und Übersteuerungen." Außerdem die Produktnummer: EAN 4007841 091309. IP20 (für den Innenbereich).

    Aktuell ist das Gerät laut Steinel Hotline nur für Fachpartner bzw. über den Fachhandel erhältlich. Es ist ein Produkten aus der "Professional Line". Typischerweise läuft es dann beim Kunden in Verbindung mit der Steinel "Capstone"-Software (https://www.steinel.de/en/group/sens...ions/capstone/).

    Für den hier beschriebenen Use-Case hatte mir Steinel jedoch freundlicherweise auch als Privatperson ausnahmsweise das Gateway angeboten, und mich bei einigen Fragen freundlichst unterstützt (ich bekam Beispiel-Befehle, sowie eine Dokumentation des Steinel Sensor Mesh-Models)! Es ist ein kleines, weißes Gerät mit Ethernet-Schnittstelle und Netzstecker (PoE ist wohl geplant, aber noch nicht implementiert). Wifi wird nicht unterstützt.

    Das Steinel BT/IP-Gateway trägt auf dem Gerät die Bezeichnung HÄFELE (SE & Co KG)) Connect Gateway / Gateway BT/IP 850.00.074, Model GL-S10-C (scheinbar ähnlich diesem hier), Item no. 850 00 074, Input: 5V DC 1A. Es hat ferner eine aufgedruckte BLE MAC und eine S/N. Unifi zeigt ferner als Application im Traffic: "Lets Encrypt.". Außerdem findet man in der Steinel App: FW 1.1.40; Hardware 10; GW10C....;

    UVP = ca. 170 €/Stk.

    ----------------------------------
    Basics

    Zur Funktionsweise des Gateway & BT-Messages: Syntax

    Die Lampen im BT-Mesh kommunizieren fortlaufend miteinander. Jede Lampe kann als Knoten im BT-Netzwerk fungieren und Daten werden weitergeleitet, geteilt etc. Die Inhalte der BT-Messages (payloads, opcodes) sind laut Steinel im BT-Mesh Standard beschrieben und nutzen die regulären Mesh-Models.

    Im BT-Mesh sind Gruppen vorgesehen. Diese konfiguriert man in der Steinel Connect App um eine Teilmenge von Lampen gemeinsam bzw. im Verbund auf beispielsweise Bewegung reagieren zu lassen. Aber auch für die übergreifende, gruppenweise Steuerung, Stati-Abfragen und das Setzen von Einstellungen sind die Gruppen relevant und praktisch.

    Das Gateway wiederum ist kein Teil einer Gruppe. Es hält aber die Kommunikation mit allen Gruppen. Als Teil der (verschlüsselten) BT-Meshs tunnelt es fortlaufend, und leider recht hochfrequent, alle (bzw. auf die Teilmenge auf die es selbst im BT-Mesh "subscribed" hat. Dies ist wohl eine Teilmenge der BT-Mesh-Knoten Informationen) BT-Mesh Messages aus dem BT-Mesh (bzw. den Meshes?) der vernetzten Lampen per MQTT als (z.T. umformatierte?) MQTT-Message (bzw. Telegram) ins lokale Netzwerk and den MQTT-Broker. Auch umgekehrt funktioniert das Gateway, indem es MQTT-Messages als BT-Mesh Message in das (entsprechende?) BT-Mesh forwarded. Hierbei sind bei dem vom Gateway kommenden Messages etliche redundant, und eine Message alle paar Sekunden ist typisch (bei 4 Lampen waren es im Schnitt alle 5s eine MQTT-Nachricht)... Bei Bewegung, Schalten der Lampen, Konfiguration per App (s.u.) etc. natürlich mehr. Es erfolgt bewusst seitens Steinel keine weitere Verarbeitung oder Filterung der Messages auf dem Gateway selbst, da es für Szenarien mit vielen Lampen gedacht ist und die Rechenleistung für aufwendige Prozessierungen nicht ausgelegt ist.

    Hilfreich waren die BT Mesh Spezifikationsdokumente, sowie die Additional Supplemental Documents, wenngleich all diese recht schwer verständlich scheinen: https://www.bluetooth.com/specifications/specs/ (Mesh Model, Mesh Profile, ...)

    Hilfreich ist ferner ein Hex-2-Dezimal Converter wie beispielsweise: https://www.rapidtables.com/convert/...o-decimal.html.

    ----------------------------------
    Setup des Gateways
    Das Gateway muss zu allererst über die Steinel Connect App eingebunden werden, damit es Teil des BT-Meshs wird. Das geht recht einfach und ist in der App eigentlich selbsterklärend. Danach erscheinen die Messages in einem Topic, z.B. "Steinel", welches in der Steinel Connect App bei der Einbindung zu definieren ist. Dort war auch der MQTT-Broker zu definieren (z.B. das Loxberry MQTT Gateway Plugin, Username & password, IP, ...).

    ----------------------------------
    Die eingehenden (MQTT-)Messages
    Die Messages stehen danach im MQTT-Broker in einem json-format unter dem Topic "Steinel/rawMessage" als QoS=0 zur Verfügung. Per MQTT kann man die Lampen nun auslesen und Werte setzen. Die eingehenden jsons sind stets wie folgt, {"opcode": "XXYYZZ", "source": "HHhh", "destination": "AABB", "payload": "...."}, wobei der "opcode" mit 3 bytes in hex angibt, um welche Art von payload es sich handelt, "source" mit 2 byte in hex von wem die Message kommt, "destination" in hex an wen sie ging, und "payload" in hex die eigentlichen Daten enthält. Die payloads haben unterschiedliche Längen, und beinhalten auch manchmal Präfixe die genauer spezifizieren um was für Daten es sich handelt.

    Die üblichen Messages sehen in etwa so aus:
    • {"opcode":"000052","source":"0005","destination ":" c001","payload":"c409e8d802"} → hier Luminance-Info enthalten. "source" ist ein Lux-Sensor einer Lampe(n-Gruppe)
    • {"opcode":"000052","source":"0004","destination ":" c001","payload":"4008c8"} → hier Bewegungs-Info enthalten. "source" ist ein PIR-Sensor einer Lampe. (payload "400800" folgt zeitlich stets auf "4008c8").
    Der "opcode" von Sensor-Werten ist immer "000052". Siehe hierzu auch S. 302 in Kapitel 7.1. des Dokuments "Mesh Model - Bluetooth® Specification": "Server Model: Sensor, Message Name: Sensor Status, Opcode: 0x52".

    Zur payload bei Bewegungen:
    • "4008c8" → 0x40, 0x08, 0xc8; c8 → 200 in Dezimal. = max = Bewegung: Start
    • "400800" → 0x40, 0x08, 0x00; 00 → 200 in Dezimal. = min = Bewegung: Ende
    Zur payload bei Lichtverhältnissen / Lux-Werten:
    • z.B. "c409e8d802" → 0xc4 0x09; dann XXYYZZ mit einem uint24-Wert (sättigend bei 200.000, bzw. "40 0d 03") der als 3 hex bytes kodiert ist. Abweichend davon ist jedoch der Lux-Wert 0xff 0xff 0xff = 16.777.215 wenn die Lampe an ist! (also außerhalb des Wertebereichs)
    Zu den "source" und "destination" IDs:
    Durch den Export der Projekt-json Datei in der Smartphone App kommt man am einfachsten an die IDs dran: Im Reiter "Projekt" auf die drei Punkte neben dem eigenen Projekt klicken → Sicherung erstellen → per Airdrop oder Mail das json exportieren.

    Aus der Anzeige in einem json-Tree viewer (z.B.: https://jsonformatter.org/json-viewer) kann man folgendes entnehmen:
    • Unter "groups/#/" finden sich die Gruppen-IDs ("address") in Hex-Kodierung in Verbindung mit den Namen ("name").
    • Unter "nodes/#/" finden sich die einzelnen Lampen-IDs ("unicastAddress") in Hex-Kodierung in Verbindung mit den Namen der Lampen ("name"). Die Gruppen-Zugehörigkeit jeder Lampe findet man z.B. unter /nodes/#/elements/2/models/0/publish unter "address" (in Hex), oder unter /steinel/nodeInfos/#/ bei "groupAddress" (in Dezimal)
    • Unter "steinel/occupancySensors/#/" finden sich die einzelnen PIR-Sensor-IDs ("elementAddress") in Verbindung mit den dazugehörigen Lampen-IDs ("nodeAddress") in Dezimal-Kodierung.
    • Unter "steinel/presentAmbientLightLevelSensors/#" finden sich die einzelnen Lux-Sensor-IDs ("elementAddress") in Verbindung mit den dazugehörigen Lampen-IDs ("nodeAddress") in Dezimal-Kodierung.
    Alle IDs sind eindeutig / einmalig, egal ob Sensor / Gruppe / Lampe / Controller / Hub / App. Es gibt zumindest vier IDs je Lampe, angefangen bei "0002" (2), da "0001" (1) die Smartphone-App ist. D.h. die IDs sind:
      • [App: "0001" (1)]
      • Lampen ID (typisch: "0002" (2), "0006" (6), ... )
      • Light Controller ID (= Lampen-ID+1) (typ.: "0003" (3), "0007" (7), ...)
      • PIR-ID (=Lampen-ID+2) (typ: "0004" (4), "0008" (8), "000c" (12), ...)
      • Lux-ID (=Lampen-ID+3) (typ: "0005" (5), "0009" (9), "000d" (13), ...)
      • [Gateway: (letzte ID+1: bei 4 Lampen z.B. "0012" (18))]
      • Gruppen-ID: (typisch: "c000" (49152), "c001" (49153), ...)
    Die Werte-Bereiche finden sich im json-file unter /provisioners/0/allocatedGroupRange/0/highAddress (CC9A) und /lowAddress (C000), sowie /provisioners/0/allocatedUnicastRange/0/highAddress (199A) und /lowAddress (0001).

    Alternativ: Durch eine systematische Analyse (ggf. Lampen einzeln physisch Aus- und Einschalten / abklemmen; gezielte PIR Auslösungen einzelner Lampen; ggf. gezieltes Verdunkeln, ..) solcher fortlaufend auflaufender MQTT-Telegramme in einem Programm wie dem MQTT Explorer kann man das auch ohne den json-Export herausfinden:
    • Lampen-Gruppen-IDs: die Sensor-Werte (PIR und Lux) gehen jeweils an eine Gruppen-spezifische ID, "destination": "c0##". Ein Gruppen-granulares Steuern ist mit diesen IDs möglich. Das müsste die "Publish Group" des BT Mesh Standards sein.]
    • Lampen-IDs (scheinbar: "00##"): In der Steinel Connect App sind genau diese Lampen-IDs zu finden: Produkte → Lampe auswählen → drei Punkte oben rechts → Produktdetails → Adresse im Netzwerk. Zum Steuern (MQTT publish) müssen diese umgewandelt werden von hex in dezimal. Ggf. sind die Lampen-IDs die "Server" im BT Mesh Standard? Auch das Gateway hat eine solche Geräte-ID (ebenfalls über die App ablesbar).
    • PIR-Sensor-spezifische IDs (scheinbar: "00##"): kommen als "source" zusammen mit den Bewegungs-Triggern (d.h. "payload": "4008XX"). Eine ID je Lampe = 1 PIR-Sensor je Lampe!
    • Lux-Sensor IDs (scheinbar: "000#"): kommen als "source" zusammen mit den Helligkeitswerten (d.h. "payload": "c409XXYYZZ"). Nur eine ID je Gruppe publiziert automatisch! Welche Lampe? → Steinel Connect App → Produkte → Lampen einzeln auswählen (durchprobieren) → drei Punkte oben rechts → Produktdetails → Helligkeitssensor der Gruppe (Ja / Nein). Ggf. kann man die anderen Lichtsensoren einer Gruppe freischalten in der App: Profil = Vollautomatik wählen → Lichtsensor antippen → Als Netzwerksensor freigeben. => Habe ich nicht probiert. Man kann aber jeden Lux-Sensor abfragen (s.u.)!
    Eine solche zielgerichtete Analyse (oder der json-Export wie oben) ist also notwendig um das alles zum Laufen zu bringen und die Sensor-Daten richtig zuzuordnen, bzw. die Lampen zielgerichtet anzusprechen!

    Virtuelle Ausgangsbefehle für das Loxberry MQTT-Gateway erlauben dann die Lampen(-Gruppen) zu steuern. Das kann man z.B. auch über den MQTT Explorer zunächst testen.

    Die ausgehenden (MQTT-)Messages / (publish)
    Lampen steuern und konfigurieren kann man über MQTT publishing in das Topic "Steinel/rawMessage/send" (das Gateway liest diese mit und forwarded diese ins BT-Mesh). Dabei sind ein paar Dinge zu beachten:
    • es wird keine "source" im json benötigt
    • das Ziel = "destination" kann typischerweise sowohl eine einzelne Lampe, als auch eine Gruppe sein
    • die zwei Felder "destination" und "opcode" sind von hex in Dezimal zu kodieren und ohne Anführungszeichen!
    • TIDs tauchen manchmal in payloads auf. Dies sind transaction identifier. Die diesbezügliche policy, wiederholte Befehle innerhalb einer Zeitfensters von ca. 6 Sekunden zu ignorieren bei Dopplung, wird scheinbar bei fast allen Befehlen nicht durchgesetzt. d.h. die TIDs sind meistens egal.
    • Typischerweise kommt auf Befehle eine Antwort zurück mit einem entsprechenden opcode für den jeweilig geänderten Status. Tauchen viele "ff" bytes auf, hat die Lampe den Befehl nicht verstanden.
    • die payload kann auch leer sein ("") bei Status-Anfragen (bei GET-Befehlen)
    Z.B. kann man die einzelne Lampe (unabhängig von ihrer Gruppen-Zugehörigkeit) mit der ID 2 ein- und ausschalten via:

    {"destination":2,"opcode":33282,"payload":"0178 000 0"} bzw. "payload": "00580000"; die letzten zwei byte (hier: 0x00 0x00) sind TID und Delay

    Zum opcode 33282: Diese Dezimalzahl, sofern man sie als Hexadezimalzahl darstellt (0x8202), steht für generische An/Aus-Befehle, siehe S. 301 in Kapitel 7.1. des Dokuments "Mesh Model - Bluetooth® Specification": "Server Model: Generic OnOff, Message Name: Generic OnOff Set, Opcode: 0x82 0x02). Das erste byte der payload (0x01 oder 0x00) ist der value der gesetzt wird, als An und Aus, siehe S. 42 im selben Dokument ("target value").

    Die Lampe quittiert einen solchen Befehl mit einer Response im Mesh mit opcode "008204" (Generic OnOff Status), payload ist ein bytes (00 oder 01).

    Man kann die On/Off-Befehle auch mit einer Transition Time modifizieren. Dazu ist das dritte (vorletzte) byte da! Auch hier: es scheint, dass die TID (zweites byte, obig 0x58 und 0x78, =transaction identifier) policy nicht enforced wird. d.h. folgendes funktioniert (mit TID=0x00) und bewirkt ein fade-in / fade-out bei den Befehlen von ca. 1sek, egal wie schnell ich die Befehle ohne TID-Inkrementierung hintereinander ausführe:

    {"destination":2,"opcode":33282,"payload":"0100 100 0"} bzw. "payload": "00001000".

    Lampen-Gruppen kann man auch gemeinsam steuern. Beim Ein/Aus-Befehl für Gruppen ist jedoch ausnahmsweise der TID wichtig und ein Inkrementieren ist erforderlich!

    Gruppe 1: "c000" 49152 → {"destination":49152,"opcode":33282,"payload":" 010 10000"} (An) oder "00010000" (Aus), wieder an: "01020000", ...
    Gruppe 2: "c001" → 49153 → {"destination":49153,"opcode":33282,"payload":" 010 10000"} (An) ...

    Befehle zum Dimmen sehen so aus (mehr zum encoding, also der payload je nach Dim-Level, weiter unten):
    {"destination":2,"opcode":33286,"payload":"0080 000 000"} → Lampe 2 dimmen
    {"destination":49153,"opcode":33286,"payload":" FF0 0000000"} → Gruppe 2 dimmen.

    Zum opcode 33286: Diese Dezimalzahl, sofern man sie als Hexadezimalzahl darstellt (0x8206), steht wohl für generische Level-Set-Befehle (in der "acknowledged"-Variante), siehe S. 301 in Kapitel 7.1. des Dokuments "Mesh Model - Bluetooth® Specification": "Server Model: Generic Level, Message Name: Generic Level Set, Opcode: 0x82 0x06). Die Lampe quittiert einen solchen Befehl mit einer Response im Mesh mit opcode "008208" (Generic Level Status) an "destination": "0012" (egal welche Lampe das reported, i.e. = 18 = Gateway), payload davon sind zwei bytes (dim level).

    Analog zu den An/Aus-Befehlen kann man auch beim Dimmen mit dem vorletzten byte die Transition Time einstellen. z.B. folgendes macht einen weicheren aber immer noch schnellen Übergang:
    {"destination":2,"opcode":33286,"payload":"FF7F FF0 200"}

    Die PIR-Sensor Reichweite lässt sich in der App (im Automatik-Modus → Erweiterte Einstellungen → Bewegungssensor → Lampe auswählen → Globale Sensoreinstellungen → Drag & Drop der zwei Reichweiten RW1 und RW2) über Befehle setzen sie im MQTT Explorer wie folgt ankommen:
    z.B. {"opcode":"d50563","source":"0001","destination ":" 0004","payload":"4200f5ffc8000064"}, gefolgt von einem opcode "d7..."-Status. Der Opcode deutet auf Vendor-spezifische Messages hin. "0xD5" des in diesem Fall Vendor-spezifischen opcodes steht für einen "Sensor Ext Setting Set" Befehl. "0xD7" darauf folgend für eine response ("Ext Setting Status") vom Sensor.
    Dabei ist die Syntax der payload für die Reichweite offenbar: 42 00 f5 ff 00-c8(RW1) 00 00 00-c8(RW2)
    f5 ff steht für "[Range 4](#range-4)"

    Per MQTT-Befehl wie folgt kann man die Reichweiten also wahrscheinlich einstellen:
    {"opcode":13960547,"destination":4,"payload":"4 200 f5ffc80000c8"} wobei ich den opcode in dezimal umwandeln musste. Es kommt eine Bestätigung (Status) vom Sensor zurück: {"opcode":"d70563","source":"0004","destination ":" 0012","payload":"4200f5ff03c80000c8"}. In der App wird jedoch kein aktualisierter Wert angezeigt/visualisiert. Das muss aber nichts heißen, da die App keine get Anfrage beim Aufrufen des entsprechenden Menüs startet! Vermutlich speichert die App das lokal und geht davon aus, dass alle Einstellungen von der App kommen.

    Die PIR-Sensitivität lässt sich in der App auch einstellen (im Automatik-Modus → Erweiterte Einstellungen → Bewegungssensor → Lampe auswählen → Globale Sensoreinstellungen → Sensitivität (Niedrigste, Niedrigere, Standard, Höhere, Höchste)). Der entsprechende Befehl den man mitlesen kann ist:
    {"opcode":"d50563","source":"0001","destination ":" 0004","payload":"4200f6ff00"}
    Es kommt als Response zurück: {"opcode":"d70563","source":"0004","destination ":" 0001","payload":"4200f6ff0300"}.
    Die Syntax der payload ist: 42 00 f6 ff 00-04(Niedrigste bis Höchste)
    f6 ff steht für "[Sensitivity Mode](#sensitivity-mode)"
    Per MQTT-Befehl wie folgt kann man die Sensitivität also wahrscheinlich einstellen:

    {"opcode":13960547,"destination":4,"payload":"4 200 f6ff40"}, wobei ich den opcode in dezimal umwandeln musste. Es kommt eine Bestätigung vom Sensor zurück: {"opcode":"d70563","source":"0004","destination ":" 0012","payload":"4200f6ff0340"}. In der App wird jedoch kein aktualisierter Wert angezeigt. Das muss aber nichts heißen, da die App keine get Anfrage beim Aufrufen des entsprechenden Menüs startet! Vermutlich speichert die App das lokal und geht davon aus, dass alle Einstellungen von der App kommen. Der Befehl kann auch an eine Gruppe von Lampen gehen.

    Profile Einstellen:
    Etwas Hintergrund: Stellt man in der App ein Profil bzw. einen Modus für eine Gruppe ein (Schaltermodus, Nightmatic, Vollautomatik, ..), dann sieht man einen Befehl mit opcode 0x82 x42 (scene recall) von der App an die Lampen-ID, und von dieser wiederum (manchmal??) erfolgen Messages zur Gruppe ("c00#") bzw. vom dazugehörigen Light Controller (Server?) der Gruppe (ID: "0003") bzgl. Light LC Mode Status ("008294"), Light LC Light OnOff Status ("00829c") und Light LC OM Status ("008298", OM=Occupancy Mode), sowie eine Bestätigung von der Lampe an die App zur eingestellten Szene (Scene Status, "0x5e"). Außerdem noch zwei weitere Telegramme: von der App an die Lampe mit opcode: "c50563" (also c5 + Steinel Company ID), sowie eine von der Lampe zurück an die App mit "c70563" (also c7 + Steinel Company ID, 0x0563 → Steinel Vertrieb GmbH, siehe hier, S. 252)...

    Über folgende MQTT-Messages lassen sich daher vermutlich auch die Profile setzen:
    Message 1: {"opcode":33346,"destination":2,"payload":"0500 09" }, mit XX=TID; → 0x82 0x42 Scene → Scene Recall ("008242",→ 33346)
    payloads für die Profile: Schaltermodus:"0500XX"; Vollautomatik:"0300XX"; Halbautomatik (bei Gruppen mit >1 Lampe): "0400XX"; Nightmatic: "0600XX"
    die XX stehen für TIDs die zu inkrementieren sind.
    Response: {"opcode":"00005e","source":"0002","destination ":" 0012","payload":"000300"}, opcode 5e: Scene → Scene Status. Es werden keine weiteren Status-Reports für die Gruppe getriggert.

    Message 2: {"opcode":12911971,"destination":3,"payload":"1 f00 0300"} → "0563" = Steinel; "c50563" → 12911971 (0xC5: wie 0xD5 ein Ext Setting Set?)
    Response: {"opcode":"000052","source":"000d","destination ":" c000","payload":"c409400d03"} → scheint also zu klappen. Jedoch: Keine Anzeige in der App.


    Status-Abfragen
    Man kann per MQTT nicht nur Steuern, und passiv die automatisch regelmäßig eingehenden Messages mitlesen. Man kann auch Stati (Sensor/Lampen-Daten sowie auch deren Einstellungen) gezielt abfragen: So kann man per MQTT einen Befehl (publish, Get) an eine Lampe oder einen Sensor schicken um einen Status-Report anzufordern. Diese zurückgelieferten MQTT-Messages (Response: Status) kann man dann lesen und spiegeln den aktuellen Zustand der Lampe wieder (Profil, An/Aus, Dim-Level), bzw. geben den aktuellen Sensor-Werte (Bewegung? Lux?) zurück. Dazu muss die ID (Lampen-ID oder Sensor-ID, oder Gruppen-ID) bekannt sein. Auch für diese Anfragen (=ausgehende MQTT-Messages) muss man die opcodes und destination-IDs von Hex in Dezimal umwandeln. Die Antworten gehen zurück an das Gateway (bei mir: Adresse im Netzwerk = 18 → "0012" in hex).

    Status(An/Aus)-Abfragen einer Lampe (Beispiel: Lampen-ID 2):
    {"opcode":33281,"destination":2,"payload":""}: Generic OnOff Get: 0x82 0x01 33281
    z.B. zurück: {"opcode":"008204","source":"0003","destination ":" 0012","payload":"01"} → An ("01") / Aus ("00") (Generic OnOff Status 0x82 0x04)

    Die entsprechende Anfrage an eine Lampen-Gruppe gibt die An/Aus-Stati aller Lampen-IDs und Controller/Server-IDs (=Lampen-ID + 1) zu den jeweiligen Lampen zurück:

    Beispiel mit 3 Lampen in Gruppe 1 ("c000"): {"opcode":33281,"destination":49152,"payload":" "}:
    • {"opcode":"008204","source":"000e","destination ":" 0012","payload":"00"} → 14
    • {"opcode":"008204","source":"000a","destination ":" 0012","payload":"00"} → 10
    • {"opcode":"008204","source":"0006","destination ":" 0012","payload":"00"} → 6
    • {"opcode":"008204","source":"000b","destination ":" 0012","payload":"00"} → 11
    • {"opcode":"008204","source":"000f","destination ":" 0012","payload":"00"} → 15
    • {"opcode":"008204","source":"0007","destination ":" 0012","payload":"00"} → 7
    Zur Zuordnung:
    Die Controller/Server-IDs sind auch dediziert über den folgenden Befehl bestimmbar:

    Light LC Mode Get (0x82 0x91 → 33425) an Gruppe 1 ("c000") mit 3 Lampen, response ausschließlich der Controller/ Server-IDs jeweils mit Light LC Mode Status (0x82 0x94)
    • {"opcode":33425,"destination":49152,"payload":" "}:
    • {"opcode":"008294","source":"000f","destination ":" 0012","payload":"00"} → 15
    • {"opcode":"008294","source":"000b","destination ":" 0012","payload":"00"} → 11
    • {"opcode":"008294","source":"0007","destination ":" 0012","payload":"00"} → 7
    Die Lampen-IDs sind auch dediziert über folgenden Befehl bestimmbar: Light Lightness Get (0x82 0x4B → 33355) an Gruppe 1 ("c000") mit 3 Lampen, response nur der Lampen-IDs jeweils mit Light Lightness Status (0x82 0x4E):
    • {"opcode":33355,"destination":49152,"payload":" "}:
    • {"opcode":"00824e","source":"000a","destination ":" 0012","payload":"0000"} → 10
    • {"opcode":"00824e","source":"0006","destination ":" 0012","payload":"0000"} → 6
    • {"opcode":"00824e","source":"000e","destination ":" 0012","payload":"0000"} → 14
    Status(Dim-Level)-Abfragen einer Lampe:
    Dim-Level Anfragen per Generic Level Get: 0x82 0x05 → 33285:
    {"opcode":33285,"destination":2,"payload":""}
    Response z.B. wie folgt:
    {"opcode":"008208","source":"0002","destination ":" 0012","payload":"ff7f"} → ff7f → 65407 = 100 (Generic Level Status: 0x82 0x08)

    Die Anfrage an eine Lampen-Gruppe gibt die Dim-Level aller Lampen-IDs zurück.
    Beispiel mit 3 Lampen in Gruppe 1 ("c000"):
    • {"opcode":33285,"destination":49152,"payload":" "}:
    • {"opcode":"008208","source":"000e","destination ":" 0012","payload":"0080"} → 14
    • {"opcode":"008208","source":"0006","destination ":" 0012","payload":"0080"} → 6
    • {"opcode":"008208","source":"000a","destination ":" 0012","payload":"0080"} → 10

    Status-Abfrage: PIR und Lux-Sensoren:
    Sensoren-Abfragen per Sensor Get: 0x82 0x31 → 33329.
    Folgendes Beispiel zunächst für einen PIR-Sensor mit der dezimal ID 4: hex2dec("0004") ist ein PIR-Sensor
    {"opcode":33329,"destination":4,"payload":""}:
    Response z.B. wie folgt:
    {"opcode":"000052","source":"0004","destination ":" 0012","payload":"400800"}→ keine Bewegung (Sensor Status 0x52)

    Folgendes Beispiel nun für einen Lux-Sensor mit der dezimal ID 5: hex2dec("0005") ist hier der Gruppen-Lux-Sensor:
    {"opcode":33329,"destination":5,"payload":""}:
    Response z.B. wie folgt:
    {"opcode":"000052","source":"0005","destination ":" 0012","payload":"c409ffffff"} → Lux=16777215 = max (auf diese Status-Abfrage ist der Wertebereich nicht bis 200.000!) (Sensor Status 0x52)

    Die folgende Anfrage an eine Lampen-Gruppe gibt die Werte aller Sensoren (PIR und Lux) aller Lampen zurück
    Beispiel mit 3 Lampen in Gruppe 1 ("c000"): {"opcode":33229,"destination":49152,"payload":" "}:
    • {"opcode":"000052","source":"000c","destination ":" 0012","payload":"400800"} → 12 → PIR-ID
    • {"opcode":"000052","source":"0010","destination ":" 0012","payload":"400800"} → 16 → PIR-ID
    • {"opcode":"000052","source":"0008","destination ":" 0012","payload":"400800"} → 8 → PIR-ID
    • {"opcode":"000052","source":"000d","destination ":" 0012","payload":"c40974e901"} → 13 → Lux-ID
    • {"opcode":"000052","source":"0009","destination ":" 0012","payload":"c409ecee01"} → 9 → Lux-ID
    • {"opcode":"000052","source":"0011","destination ":" 0012","payload":"c409e03b02"} → 17 → Lux-ID
    Die PIR-IDs sind also an den Payloads (erstes byte = 0x40), bzw. die Lux-IDs an den ersten zwei byte = 0xc4 0x09 zu erkennen.
    Beispiel mit 1 Lampe in Gruppe 2 ("c001"): {"opcode":33329,"destination":49153,"payload":" "}:
    • {"opcode":"0081cd","source":"0012","destination ":" c001","payload":""} → ?
    • {"opcode":"000052","source":"0004","destination ":" 0012","payload":"400800"} → 4 → PIR-ID
    • {"opcode":"000052","source":"0005","destination ":" 0012","payload":"c409cc2900"} → 5 → Lux-ID
    Um welche Art von Sensoren es sich bei den jeweiligen IDs handelt kann man auch wie folgt herausfinden: Befehl Sensor Descriptor Get (0x82 0x30) an Gruppe 1 ("c000"):
    {"opcode":33328,"destination":49152,"payload":" "}:
    liefert als response Sensor Descriptor Status (0x51) zurück, wobei laut der Steinel-Spezifikationen die bytes 0x0042 für PIR stehen ("Motion Sensed", "This property represents the activity level, as, for example, detected by a motion sensor.", "#motion-sensed"), und 0x004e für Lux-Sensoren ("Present Ambient Light Level", "This property represents the light level as measured by a light sensor measuring illuminance (Lux).", "#present-ambient-light-level"):
    • {"opcode":"000051","source":"0008","destination ":" 0012","payload":"4200000000010038"} → 8
    • {"opcode":"000051","source":"0010","destination ":" 0012","payload":"4200000000010038"} → 16
    • {"opcode":"000051","source":"000c","destination ":" 0012","payload":"4200000000010038"} → 12
    • {"opcode":"000051","source":"0011","destination ":" 0012","payload":"4e00999119010038"} → 17
    • {"opcode":"000051","source":"000d","destination ":" 0012","payload":"4e00999119010038"} → 13
    • {"opcode":"000051","source":"0009","destination ":" 0012","payload":"4e00999119010038"} → 9
    Die Sensor-Daten-Abfrage funktioniert auch mit folgenden Vendor-spezifischen Befehlen:
    {"opcode":13632867,"destination":4,"payload":"" } → "0563" = Steinel; "d00563" → 13632867, wobei 0xD0 für "Sensor Ext Data Get" steht → man bekommt, je nach "destination", die payload für Bewegung (An/Aus) oder den Lux-Wert (letzteren auch bei Angabe der Lampen-ID).

    Abfragen zu Einstellungen:
    Abfrage einer PIR-Sensitivität:
    0xD4 → Sensor Settings Get (Vendor-specific prefix), d.h. "d40563" → 13895011, d.h.:
    {"opcode":13895011,"destination":4,"payload":"4 200 f6ff"}
    Es kommt eine Status-Report vom Sensor mit dem aktuellen Wert der Sensitivität zurück:
    {"opcode":"d70563","source":"0004","destination ":" 0012","payload":"4200f6ff0303"}
    Das ganze funktioniert auch mit Gruppen-Anfragen und liefert entsprechend viele Rückmeldungen (z.B. für Gruppe 1: "destination":49152).

    Abfrage einer PIR-Reichweite:
    {"opcode":13895011,"destination":4,"payload":"4 200 f5ff"}
    Es kommt eine Status-Report vom Sensor mit dem aktuellen Werten der beiden Reichweiten 1 und 2 zurück:
    {"opcode":"d70563","source":"0004","destination ":" 0012","payload":"4200f5ff03c80000c8"}

    Abfrage aktuelles Profil:
    wie Message 2, d.h. an den Controller einer Lampe, jedoch mit c4: "c40563" → 12846435
    {"opcode": 12846435,"destination":3,"payload":"1f00"}
    Antwort an das Gateway mit der aktuellen Szene ("5e"), hier im Beispiel 05 = Schaltermodus = Loxone:
    {"opcode":"c70563","source":"0003","destination ":" 0012","payload":"1f000500"}

    Die Anfrage kann auch an eine Gruppe gerichtet werden (z.B. für Gruppe 1: "destination":49152 statt 3). Es kommen entsprechend viele Antworten der einzelnen Controller (identisch) zurück, da Szenen nur Gruppenweise eingestellt werden.


    -------------------------------------------------------------
    Integration in Loxone

    Nach diesen vielen Beispielen und Erläuterungen nun also zur Umsetzung!

    Um die Helligkeitswerte der Lampensensoren einzulesen, um ferner die Bewegungs-Sensor-Auslösungen in Loxone einzubinden, und um weiter damit eine komplette Lichtsteuerung nachzubauen und so die vollständige Kontrolle über die Steinel-Lampen zu erhalten, kann man wie folgt vorgehen:

    1. Steinel Connect App Einstellungen & MQTT-Analyse ("source"s und "destination"s)
    • die Gruppen von Lampen einrichten (jede Gruppe wird eine ID haben, s.o.)
    • In Profil "Automatik" die PIR-Sensoren der Lampen auf die gewünschten Sensitivitäten und Reichweiten einstellen. Diese Einstellungen bleiben beim Wechsel zum Profil "Schaltermodus" erhalten.
    • danach jeweils auf "Schaltermodus" stellen + Lampen mit Strom versorgen. → damit wird effektiv die Steuerung an Loxone ausgelagert. Normalerweise bewirkt der Schaltermodus, dass die Lampen wie Dauerstrom-Lampen funktionieren: d.h., Strom da: Lampe An, Strom aus: Lampe Aus.
    • IP/BT Gateway anschließen und in der App einbinden (keiner Gruppe zuordnen)
    • IDs der relevanten "source"s und "destination"s herausfinden:
      • Am einfachsten per Analyse des exportierten Projekt-json-Files (s.o.)
      • Oder: Analyse der "sources" im MQTT-Stream des Gateways (z.B. im MQTT Explorer) unter "Steinel/rawMessages" wie oben beschrieben. Z.B. zielgerichtet mit je vier Status-Anfragen an jede Gruppe (d.h. "opcode": 33285, 33425 und 33229, jeweils für "c000" (in Dezimal!, i.e.49152!), "c001" (49153), ..., je nachdem wieviele Gruppen man hat):
        • Lampen-IDs ## (Dezimal-Zahlen, für Schreiben/publish), [per 33285, oder 33355]
        • Lampen-PIR IDs: "XXXX" (Hex-Strings, für lesen), [per 33229, oder 33328, oder ggf. per Lampen-ID+2]
        • Lampen-Gruppen-Lux IDs: "YYYY" (Hex-Strings, für lesen)). [per 33229, oder 33328, oder ggf. per Lampen-ID+3]
        • Lampen Controller IDs (braucht man eigentlich nicht...) [per 33425, oder ggf. per Lampen-ID+1]
      • Zuordnung der IDs zu den physischen Lampen und Gruppen.
        • Im json-file ist dies über den "name" möglich.
        • Oder einfach nacheinander per Lampen-ID oder Gruppen-spezifischer Ein/Aus-Befehle (s.o.) per MQTT publish in "Steinel/rawMessages/send" durchprobieren. Die PIR- (Lampen-ID+2) und Lux-Sensoren (Lampen-ID+3) sollten sich ergeben, s.o.
    2. Luminance- bzw. Helligkeits-Werte per XL-php-Skript (Loxberry, timeout z.B. 30s) & virt. Ausgang regelmäßig abfragen: (im Skript werden die Gruppen als "source" abgefragt). Es handelt sich um uint24 Wert im hex little endian Format. Die Werte liegen zwischen 0 und 200.000. Das php-Skript wandelt das in Prozent bzw. Werte 0-100 um:

    Loxberry: (nutzt XL-Funktion: link)
    • Ein php-Skript wird benötigt um in einem while-loop die eingehenden kodierten Luminance-Werte abzuwarten. Da diese als QoS=0 reinkommen, so mein Verständnis, braucht es diese Vorgehensweise. Ein einfaches, einmaliges Abfragen führte bei mir nicht zum Ziel.
    • php-Skript (steinel.php) anpassen:
      • IP des Gateway → sorgt für Lux-Wert 101 bei Ausfall des Gateways → siehe Validierung später...
      • Lux-Sensor IDs der jeweiligen Lampen-Gruppe anpassen. Ggf. weitere boolsche Variablen & MQTT topics einführen für weitere Lampen-Gruppen: if ($source == "0005" and $GotData0005==false) ..)
      • Lux-Sensor IDs von hex in dec umwandeln → für Get Befehle anpassen ($Get000d = "{"opcode":33329,"destination":13,"...). Kann man weglassen / auskommentieren und auf die Sensor-Push-MQTT-Nachrichten stattdessen warten.. kommen ca. alle 10-20 sek.
      • Gateway MQTT Topic anpassen wie in Steinel Connect App definiert (aktuell: "Steinel/")
      • ggf. publish MQTT Topic anpassen (s.u.): $mqtt->set("Steinel/custom/L0005 ...
    • Filemanager: Skript-Datei hochladen: Upload steinel.php in den Ordner /opt/loxberry/webfrontend/html/XL/user
    Loxone Config:
    • Virt. Ausgang in Loxone: http://LoxberryUsername:LoxberryPW@loxberry → virt. Ausgangsbefehl: /XL/user/steinel.php →
    • Taster-Baustein zum Triggern
    • Impulsgeber-Baustein triggert Taster regelmäßig, z.B. On: 1s; Off: 900s → alle 15 min.
    • Virt. Eingang (Digitaleingang: NEIN) in Loxone mit Bezeichnung: Steinel_custom_L0005 (Bezeichnung exakt gemäß php-Skript. "_" ersetzt "/") → bekommt Luminance Wert des MQTT publish Befehls vom php-Skript! Validierung Überwachen: 0...100; bei 101 (un-ping-able) → Standardwert z.B. 100 (bei Fehler/Gateway inaktiv: keine Bewegungsmelder-getriggerten Lichter an...)
    3. Bewegungsmeldungen der Steinel-Lampen in Loxone: hier als "source" die einzelnen PIR-IDs unterscheiden / abfangen.

    Loxberry: MQTT Gateway:
    • subscribe: Steinel/custom/# und Steinel/rawMessage/# → gemäß angelegtem Topic in der Steinel-App beim Gateway!
    • json parsing enabled. → dennoch bleiben für die php-Skripte die ursprünglichen jsons erhalten. Es geht hier nur ums forwarding zum Miniserver.
    • Incoming view: "Show details and advanced settings" auswählen, dann: Steinel_rawMessage_payload & Steinel_rawMessage_source → "Reset after send" auswählen
    • die anderen Steinel_rawMessage: "Do not forward" auswählen → Miniserver schonen soweit es geht.
    Loxone Config:
    • 1x Status-Baustein: Steinel_rawMessage_source & Steinel_rawMessage_payload als I1 und I2: check sources for IDs (PIR-Sensor-ID ist Lampen-spezifisch!) & check payload == 4008c8. Ausgabe: Val=1, 2, 3, 4, ... (die Lampen als Integer) bzw. sonst Val= 0.
    • 1x Baustein: Radiotasten. Output "Val" vom Statusbaustein → an Eingang "Sel"
    • je Lampe:
      • Ausschaltverzögerung: z.B. 5 sek., macht Auslösungen im nachfolgenden (s.u.) Status-Baustein sichtbar trotz "Reset after send"; Radiotasten-Ausgänge O1, O2, .. gehen je an Tr-Eingang der einzelnen Ausschaltverzögerungen.
      • Status-Baustein; O der Ausschaltverzögerung geht jeweils an I1; Statusbaustein z.B. in Kategorie "Präsenzerkennung": dann bei I1=1 → Bewegung durch Lampe erkannt (Val=1), sonst nichts erkannt (Val=0).
    4. Licht-Steuerung: per MQTT (Loxberry) → "destination" = Lampe ist anzupassen!

    Loxone Config:
    • Lichtsteuerungsbaustein (z.B. je Lampen-Gruppe)
      • Br-Eingang der Lichtsteuerung: hier Virt. Eingang der jeweiligen Lampen-Gruppe mit den Luminance-Werten in % (siehe oben Punkt 1) anschließen.
      • Mo-Eingang der Lichtsteuerung: hier die Val-Ausgänge aller Status-Bausteine (Präsenzerkennung) der Lampen (siehe oben Punkt 2) anschließen.
      • Threshold < 100 einstellen wie gewohnt.
      • Im Lichtsteuerungbaustein (Doppelklick) die Steinel-Lampe für Präsenz wählen.
    Option 1: An/Aus
    • Loxone Config:
      • Lichtkreise je Lampe anlegen als Typ: Schalter
      • virt. Ausgangsbefehl (Digital → Ja! check!) beim virt. Ausgang des MQTT Gateways anlegen (Anführungszeichen mit/ohne beachten!):
        • Befehl bei EIN: {"destination":2,"opcode":33282,"payload":"0178 020 0"}
        • Befehl bei AUS: {"destination":2,"opcode":33282,"payload":"0058 020 0"}
      • Lc-Ausgänge des Lichtsteuerungbausteins jeweils an die virt. Ausgang VQs.
    Option 2: Dimmbar
    • Loxone Config:
      • Lichtkreise je Lampe anlegen als Typ: Dimmer 0-100%
      • virt. Ausgang (Digital? → Nein! uncheck!) in Loxone: http://LoxberryUsername:LoxberryPW@loxberry → virt. Ausgangsbefehl: /XL/user/steinelDim_X.php?Dim=<v.1>&Lamp=LampID
      • Lc-Ausgänge des Lichtsteuerungbausteins jeweils an die virt. Ausgang VQs.
    • Loxberry: (nutzt XL-funktion: link)
      • php-Skript (steinelDim_X.php) ggf. anpassen, i.e. publish topic: $mqtt->set("Steinel/rawMessage/send", $CMDMessage);
      • Filemanager: Skript-Datei hochladen: Upload steinelDim_X.php in den Ordner /opt/loxberry/webfrontend/html/XL/user


    -------------------------------------------------

    5. (OPTIONAL): Manuelles Testen

    Optional kann man die php-Skripte vorab im Terminal am Loxberry testen, und ggf. Berechtigungen ändern (sollte nicht nötig sein):
    • chmod +x steinelDim_X.php
    • Test des Skripts im Terminal:
      • login with loxberry Username & password
      • cd /opt/loxberry/webfrontend/html/XL/user
      • php -f steinel.php
      • php-cgi -f steinelDim_X.php Dim=50 Lamp=2
    Oder im Browser:
    http://<<IPLoxberry>>/admin/system/tools/filemanager/system/webfrontend/html/XL/user/steinelDim_X.php?Dim=50&Lamp=2


    6. (OPTIONAL) Beispiel Lauflicht

    Mit einem Ablaufsteuerung-Baustein kann man auch Lauflichter realisieren. Hierzu zunächst einen Auswahltasten-Baustein um die Sleep-Time einzustellen (z.B. zwischen 0,1 und 2; value auf v.1 stellen). Ausgang O geht an AI1 der Ablaufsteuerung. Der Ausgang AQ1 der Ablaufsteuerung wiederum geht an den Eingang "Mood" des Lichtsteuerungsbausteins. Dieser sollte Stimmungen (mit Mix: an) für jede Lampe einzeln haben (Stimmung 1: Lampe 1 = 100 & Lampe 2 = 0 & Lampe 3 = 0; Stimmung 2: Lampe 1 = 0 & Lampe 2 = 100 & Lampe 3 = 0; Stimmung 3 = ... etc.). Das Lauflicht dann aktivieren durch z.B. Taster an Eingang S1 der Ablaufsteuerung. Achtung: Bei den digitalen Ausgängen zur Ausführung des php-Skripts ggf. auf die eingestellten Minimalen Zeitabstände achten, sowie ggf. in den MQTT-Befehlen auf die Transtion-Times. Ansonsten begrenzen diese die Geschwindigkeit der Lauflicht-Animation.

    Die Sequenz 1 der Ablaufsteuerung dann z.B. wie folgt anlegen (Doppelklick auf Baustein):
    Code:
    set value 1 = AI1
    set AQ1 = 1
    sleep value 1 s
    set AQ1 = 2
    sleep value 1 s
    set AQ1 = 3
    sleep value 1 s
    Ergänzende Hinweise

    Das alles wäre vermutlich schöner in einem Steinel-Plugin für den Loxberry umgesetzt. Leider übersteigt das meine Programmierfähigkeiten. Falls das jemand angehen will, könnte ich gerne Testen. Dabei wäre es noch schön die Transition Time einstellen / übergeben zu können.

    Schön wäre noch den Status der Lampen abzufragen und in Loxone nachzuhalten, für die Fälle wo mal ein Befehl nicht ausgeführt wird / verloren geht oder ähnliches. Wie das mit einem Lichtsteuerungbaustein zusammengehen kann, das weiß ich nicht. vllt. per php-Skript ab und an de Lampenstatus abfragen und per "$ms1→ " entsprechend setzen?

    Zur App:

    Die App selbst vom Smartphone (BT muss aktiviert sein) kommuniziert ihre Konfigurationen ebenfalls per BT-Telegrammen an die Lampen bzw. Gruppen. Das kann man bei eingeschaltetem Gateway im MQTT Explorer verfolgen. Dabei scheint die App die "source":"0001" zu sein. Die Lampengruppen haben wieder eine andere Kennung.

    Als erstes erfolgt bei Verbindung ein time set und ein response zum time status. Das alles im Austausch mit einer ID "0002", also der Lampen-ID.

    Schaut man sich in der App eine Gruppe dann an und verbindet sich mit dieser, dann taucht eine ID sowohl der Sensoren auf (zusammen mit Messages zum Einholen der Werte, i.e. "008231" → sensor get), als auch eine ID vermutlich des Light Controllers (ID: "0003")

    PHP-Programme

    Die folgenden Programmbeispiele laufen auf einem Loxberry und werden regelmäßig aufgerufen um die Luminance-Werte zu dekodieren und als MQTT zu publizieren (wird in Loxone eingelesen), bzw. um einen DIM-Wert zu kodieren und als Dimming-Befehl per MQTT an eine spezifische Lampe / Gruppe zu senden.

    Die Programme sind an einigen Stellen auf die konkreten Lampen-IDs, IPs (Gateway) bzw. die gewählten MQTT-topics anzupassen!

    steinel_Dim_X.php
    PHP-Code:
    <?php
    // ===== Sets dim level for a specific Lamp based on arguments provided in call =====
    // call function like http://loxberry/legacy/steinelDim_X.php?Dim=<<DimLevel>>&Lamp=<<LampID>>
    
    require_once "loxberry_XL.php";
    header('Content-Type: application/json; charset=utf-8');
    set_time_limit(5); //timeout of php-program
    
    //get desired Dim-Level from function call
    $DimLevel = $_GET['Dim']; //get <<DimLevel>>
    //var_dump($DimLevel); //show what got input as desired Dim-Level
    
    //get desired Lamp-ID from function call
    $LampID = $_GET['Lamp']; //get <<LampID>>
    //var_dump($LampID); //show what got input as Lamp-ID
    
    //generate payload part of MQTT Dim command
    $raw = ($DimLevel / 100) * 65535 - 32768;
    $signedLevel = (int) floor($raw + 0.5); // Equivalent to round() but avoids -1 at 50%
    if ($signedLevel < -32768) $signedLevel = -32768; //clamp
    if ($signedLevel > 32767) $signedLevel = 32767;
    $unsignedLevel = $signedLevel & 0xFFFF;
    $levelBytes = pack('v', $unsignedLevel); // 'v' = little-endian unsigned short
    $byteLow = ord($levelBytes[0]);
    $byteHigh = ord($levelBytes[1]);
    $prefix = ($DimLevel == 0) ? 0x00 : 0xFF;
    $suffix1 = 0x02; //Transition time; 0x00 for immediate set. 02 makes it smooth but fast. 10 makes it ca. 1sec
    $suffix2 = 0x00; //Delay
    $bytes = [$prefix, $byteHigh, $byteLow, $suffix1, $suffix2];
    $hex = '';
    
    foreach ($bytes as $b) { $hex .= strtoupper(str_pad(dechex($b), 2, '0', STR_PAD_LEFT)); }
    //var_dump($hex); //show the payload we compiled
    
    //compile the MQTT Message.
    $CMDMessage = "{"destination":" . $LampID . ","opcode":33286,"payload":"" . $hex . ""}";
    var_dump($CMDMessage); //show the command which gets published
    
    //now set the Lamp's dimming value by MQTT-publishing the command
    //the command will be read by the Gateway & tunneld into the BT-Mesh
    $mqtt->set("Steinel/rawMessage/send", $CMDMessage);
    ?>

    steinel.php
    PHP-Code:
    <?php
    // ===== Reads in Luminance values of all Lamps =====
    
    require_once "loxberry_XL.php";
    echo "\n";
    set_time_limit(10); //timeout of php-program
    
    // Ping IP of the Steinel BT Gateway, max 2 tries for each 1s -> 2s max
    $ip = "192.168.X.XX";
    exec("ping -c 2 -W 1 " . $ip, $output, $result);
    
    //check ping result
    if ($result == 0) { //ping successfull. Continue with MQTT-Analysis of Gateway's messages
    $GotData000d = false; //Luminance for source 000d not yet analyse & published
    $GotData0005 = false; //Luminance for source 0005 not yet analyse & published
    
    //(Optional) reduce wait time by forcing / triggering report (Get Message -> Status Response)
    $Get000d = "{"opcode":33329,"destination":13,"payload": ""}"; //for Lux-ID: "000d"(hex)
    $Get0005 = "{"opcode":33329,"destination":5,"payload":\ ""}"; //for Lux-ID: "0005"(hex)
    
    $mqtt->set("Steinel/rawMessage/send", $Get000d);
    $mqtt->set("Steinel/rawMessage/send", $Get0005);
    
    while (($GotData000d == false) or ($GotData0005 == false)) {  //loop until both data got analysed & published
    $GotData = false;
    
    while ($GotData == false) {  //loop until some data gets read
    $data = $mqtt->get ( "Steinel/rawMessage" );
    
    if (is_null($data)==false) {  $GotData = true;
    var_dump($data); //show what was read  } else {  sleep(0.2); // wait and retry to read topic  }  }
    
    // parse json
    $jsonArray = json_decode($data, true);
    $payload = $jsonArray['payload'];
    $opcode = $jsonArray['opcode'];
    $source = $jsonArray['source'];
    
    //check if data contains Luminance value. If so: convert to percent (0-100) and MQTT-publish
    if ($opcode == "000052") {  $LumHeader = substr($payload, 0, 4);
    $LumV = substr($payload, 4);
    
    if ($LumHeader == "c409") {  //convert Luminance to uint24 from hex little endian, then to percent
    $value = hexdec(unpack("H*", strrev(pack("H*", $LumV)))[1]);
    $valueSc = round(($value/200000)*100,2);
    
    //publish value to source-specific custom MQTT-topic (to be read by Loxone as virtual Input)
    if ($source == "0005" and $GotData0005==false) {  if ($payload == "c409ffffff") {  echo "Lamp " . $source . " is on: leave Lux value as is" . "\n";  } else {  echo "publishing converted value: " . $valueSc . " (from: " . $payload . ") for source: " . $source . "\n";
    $mqtt->set("Steinel/custom/L0005", strval($valueSc));  }
    $GotData0005 = true; //data got analysed and published for source 0005 (Lampe 1)
    
    } elseif ($source == "000d" and $GotData000d==false) {  if ($payload == "c409ffffff") {
    echo "Lamp " . $source . " is on: leave Lux value as is" . "\n";  } else {  echo "publishing converted value: " . $valueSc . " (from: " . $payload . ") for source: " . $source . "\n";
    $mqtt->set("Steinel/custom/L000d", strval($valueSc));  }  $GotData000d = true; //data got analysed and published for source 000d (Lampe 2)  }  }  }  } } else {  //publish out-of-range value
    echo "Gateway un-ping-able: Publishing value 101 for all sources\n";
    
    $mqtt->set("Steinel/custom/L0005", strval(101));
    $mqtt->set("Steinel/custom/L000d", strval(101)); }
    ?>
    Zuletzt geändert von MarkusCosi; vor 4 Tagen.
Lädt...