Die lokale API ist dabei auf wenige Befehle beschränkt: An/Aus, Farbe, Farbtemperatur und Helligkeit. Dabei geht man wie folgt vor:
- virt. Ausgang (siehe hier) anlegen mit Adresse: /dev/udp/<<IP>>/4003
- virt. Ausgangs-Befehl anlegen (Methode: GET), z.B.
- On/Off: bei EIN: {"msg":{"cmd":"turn","data":{"value":1}}}; bei AUS: {"msg":{"cmd":"turn","data":{"value":0}}}
- Color: bei EIN: {"msg":{"cmd":"colorwc","data":{"color": <v>,"colorTemInKelvin":0}}}; v={"r":R,"g":G,"b":B}
- Brightness: "msg":{"cmd":"brightness","data":{"value":100} }}
-------------------
ALLGEMEINES (Teil 1) - ptReal / XOR-Prüfsumme / Base64-Kodierung
-------------------
Ein weiterer nicht dokumentierter Befehl ermöglicht es sämtliche Einstellungen die man per Govee-APP, also letztendlich per Bluetooth (BLE), auf dem Handy vornimmt auch per lokaler LAN API per UDP zu setzen:
- {"msg":{"cmd":"ptReal","data":{"command":[CMD]}}}
Der CMD-String ist dabei ein Base64-kodierter stets 20 Byte (jeweils in Hex) langer Befehl, oder aber eine Reihe von Base64-kodierten Befehlen (durch Kommas getrennt=Multi-Packet-Befehl, s.u.) die ursprünglich jeweils 20 Byte lang sind.
Folgende zwei einfache Befehle sollen als Beispiel dienen, wobei hier das zweite & dritte Bytes die eigentlichen Daten (erste Byte: 01: On/Off Daten; zweites Byte: 01=On/00=Off) enthält und das erste Byte (33) die Art des Pakets kodiert (beides fett). Das letzte Byte (fett, unterstrichen) ist eine Hex-XOR-Prüfsumme. Die vielen 00-Bytes müssen (siehe Beispiele später) ggf. ergänzt werden (=zero-padding, grau) bis es insgesamt 19 Bytes sind, sodass durch die Prüfsumme 20 bytes einen Befehl ergeben (vor der Base64-Konvertierung).
- On: 33 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 → CMD="MwEBAAAAAAAAAAAAAAAAAAAAADM="
- Off: 33 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 → CMD="MwEAAAAAAAAAAAAAAAAAAAAAADI="
Die Kodierung Hex>Base64 im letzten Schritt (jeweils der String nach dem Pfeil "→") kann man auch online durchführen, z.B. mit einem Tool wie hier.
Man kann nun entweder bekannte BLE Befehle benutzen (Liste), oder aber bestimmte Systematiken die durch Reverse-Engineering bereits bekannt wurden verwenden (oder selbst durch systematisches Vorgehen herausfinden). So ist bspw. bekannt, wie man einige Szenen mit ID < 255 per einfachem Befehl setzen kann, siehe Befehls-Berechnung hier bzw. Erläuterungen hier.
Weitere Befehle findet man hier.
Einiges scheint gut zu funktionieren und universell:
- Scenes: 33 05 04 ID 00 .. 00 ChecksumXOR (ID = hex Wert von dec-ID wie hier oder per curl "https://app2.govee.com/appsku/v1/lig...ries?sku=H6076" -H 'AppVersion: 6.3.11' -s | jq '[.data.categories[].scenes[] | {name: .sceneName, code: .lightEffects[0].sceneCode}]')
- On/Off: 33 01 ONOFF 00 .. 00 ChecksumXOR (ONOFF: 01=On; 00=Off)
- Brightness: 33 04 Br 00 .. 00 ChecksumXOR (Br: 0 .. 64 = 0...100%)
Music Modes für H6076
------------------------
Jedoch musste ich feststellen, dass Vieles was bereits bekannt ist bei meiner Govee-Lampe (H6076) nicht funktioniert bzw. scheinbar nicht universell funktioniert. D.h., z.B. für das setzen des Music-Modes scheint es diverse Befehle zu geben je nach Gerät (siehe hier, hier, hier bzw hier)
- 33 05 0c ... / 33 05 01 ... / 33 05 13 ...
- 33 05 0f ID Sens 00 ... 00 ChecksumXOR
Sens: 0..64 (Sensibilität: min ... max)
Andere Music-Modi erfordern wiederum, ähnlich wie schon bei vielen Szenen, Multi-Packet Befehle (s.u.).
Herausgefunden habe ich das wie folgt, und es erlaubt im Prinzip beliebige Befehle die man sonst nur mit der App geben kann:
-----------------------------------------------------
ALLGEMEINES (Teil 2): BLE Logging und Befehls-Extraktion
-----------------------------------------------------
Man benötigt dazu ein BLE-Log des Smartphones. Für ein iPhone geht es wie folgt:
- BLE-Aufzeichung vorbereiten: Mit einem Apple Developer Account (kostenfrei) geht man per Handy-Browser auf folgende Seite und klickt neben "Bluetooth for iOS/iPadOS" auf "Profile".https://developer.apple.com/bug-repo...ooth%20logging. Dieses installiert man im folgenden. Danach ist das Logging aktiv!
- BLE Befehle aufzeichnen: in der Govee-App dann die gewünschten Einstellungen vornehmen / Befehle absetzen. Am besten die Sequenz der Befehle aufschreiben und ggf. zwischendurch mit einfachen Befehlen ergänzen, z.B. On / Off
- Auslesen / Datei erstellen: gleichzeitig 1-2sek drücken: beide Vol-Tasten links & Taste rechts → vibriert kurz
- Übertragen: ca. 10min warten, dann: Einstellungen → Allgemein → Datenschutz & Sicherheit → Analyse & Verbesserungen → Analysedaten → sysdiagnose_YYYY.MM.DD_HH-MM-SS+0100_iPhone-OS_iPhone_TYPE → share → Airdrop to MacOS
- Analysieren: Download & install Wireshark für zB MacOS: https://www.wireshark.org/#downloadLink
- Wireshark starten:
- Open a capture file: .../Downloads/sysdiagnose_YYYY.MM.DD_HH-MM-SS+0100_iPhone-OS_iPhone_TYPE/logs/Bluetooth/bluetoothd-hci-latest.pklg
- in der Eingabezeile einen der folgenden Display-Filter oben setzen: (weitere Infos hier und hier)
- alle CMDs außer alive: frame.len == 0x20 && bluetooth.src == 00:00:00:00:00:00 && btatt.opcode == 0x52 && btatt.value[0:1] != aa
- Single-Packet-CMDs: frame.len == 0x20 && bluetooth.src == 00:00:00:00:00:00 && btatt.value contains 0x33 && btatt.opcode == 0x52 && btatt.value[0:1] != aa
- Multi-Packet-CMDs: frame.len == 0x20 && bluetooth.src == 00:00:00:00:00:00 && btatt.value contains 0xa3 && btatt.opcode == 0x52 && btatt.value[0:1] != aa
- BLE-Befehle erkennen/extrahieren: unten rechts bzw. im Feld btatt.value
- ggf. Profil auf dem iPhone wieder deinstallieren: Einstellungen → Allgemein → VPN und Geräteverwaltung → Profil auswählen und deinstallieren. (deinstalliert sich sonst nach 4 Tagen von selbst)
- aa .. .. → keep alive packets: btatt.value → uninteressant und daher oben rausgefiltert
- 33 .. .. → einfache Befehle mit nur einem Satz von 20 bytes. Z.B. Szenen mit ID<255 aktivieren bzw. Szene in App aktiv setzen, On/Off, Brightness, etc...
- a3 .. .. → Multi-Packet Befehle... Syntax wie hier. Z.B. Segmente steuern, Szenen mit ID>255, einige Music-Modes, ...
-----------------------------------------------------
ALLGEMEINES (Teil 3): Multi-Packet-Befehle
-----------------------------------------------------
Einige Befehle sind länger als 20 Byte. Die Govee-App scheint die Daten dann auf mehrere 20 Byte lange a3-Befehle aufzuteilen, d.h. in eine Sequenz von Multi-Packet-Befehlen. Diese sehen wie folgt aus:
- a3 00 01 #ofPackets Daten XORChecksum
- a3 01 Daten (weitergeführt) XORChecksum
- ...
- a3 ff Daten (letzter Datensatz) XORChecksum
Multi packet write (hier), Aurora:
- a3 00 01 05 02 02 20 00 00 00 01 02 01 ff 32 01 00 00 00 49 → owABBQICIAAAAAECAf8yAQAAAEk=
- a3 01 00 fa 32 03 00 ff 00 00 ff ff aa ff 00 03 00 80 00 40 → owEA+jIDAP8AAP//qv8AAwCAAEA=
- a3 02 00 00 00 23 00 00 00 03 02 01 ff 19 03 fa 00 00 02 9f → owIAAAAjAAAAAwIB/xkD+gAAAp8=
- a3 03 fa 00 04 7f ff 00 ff ff 00 a0 ff ff 00 ff ff 14 01 6b → owP6AAR//wD//wCg//8A//8UAWs=
- a3 ff ef 00 00 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 4c → o//vAAD/AAAAAAAAAAAAAAAAAEw=
Packet#: # of packet: 00, 01, 02, .., ff=End
Begin: 01 = value indicating Multi-Packet start (nur im ersten Packet)
#ofPackets: total # of packets in this Multi-Packet write CMD, z.B. oben: 05=5 nacheinander folgende BLE-Befehle bzw. Packets eines einzigen Multi-Packet-Befehls...
Ergänzt wird dieser Multi-Packet-Befehl in diesem Beispiel von einem einfachen Single-Packet-Befehl der in der Log folgte:
- 33 05 04 d0 2b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c9 → MwUE0CsAAAAAAAAAAAAAAAAAAMk=
- CMD="owABBQICIAAAAAECAf8yAQAAAEk=","owEA+jIDAP8AAP//qv8AAwCAAEA=","owIAAAAjAAAAAwIB/xkD+gAAAp8=","owP6AAR//wD//wCg//8A//8UAWs=","o//vAAD/AAAAAAAAAAAAAAAAAEw=","MwUE0CsAAAAAAAAAAAAAAAAAAMk ="
-----------------------------------------------------
Segment-Steuerung (H6076): Teil 1: Allgemeine Syntax
-----------------------------------------------------
Folgendes ist neu: man kann auch Segmente bzw. Pixel gezielt einzeln steuern. Im Internet fand ich folgende Syntax, die aber für meine Lampe nicht funktionierte, siehe hier. Stattdessen funktioniert bei meiner Govee H6076-Stehlampe das ganze wie folgt:
Es sind je 6 LEDs in einem kontrollierbarem Pixel im Grafitti Modus der App zusammengefasst und einzeln einstellbar. Dazu gibt es eine definierte Hintergrund-Farbe (& Helligkeit).
Durch systematisches Testen habe ich folgende allgemeine Syntax des Graffiti Modus erkannt:
- a3 Packet# Begin #ofPackets Grafitti BewModus Geschw HInt HColR HColG HColB total#Segments #Pixels1 Col1R Col1G Col1B Seg1ID1 Seg1ID2 ... #Pixels2 Col2R Col2G Col2B Seg2ID1 Seg2ID2 ... ... 00 .. 00 ChecksumXOR
- a3 P# ... (so viele Pakete wie notwendig für den Befehls-String) ChecksumXOR
- Packet#: # of packet: 00, 01, 02, .. ff=End
- Begin: 01 = value indicating multipacket start
- #ofPackets: total # of packets in this multi-packet write cmd
- Grafitti: value indicating Grafitti mode?
- BewModus: 0a: Runter; 09: Hoch; 02: Zyklus; 13: Verblassen; 0f: Funkeln; 14: Atmen
- Gewsch: 00 .. 64 (min=fix to max), hex values
- HInt: Intensity of Background color: 00 .. 64 (min .. max)
- HColR / HColG / HColB: RGB-Color values für den Hintergrund x: 00 .. ff (=0..255)
- total#Segments: Anzahl der nachfolgend definierten Segmente
- #Pixelsx: # of Pixels in Segment x
- ColxR / ColxG / ColxB: RGB-Color values for Segment x: 00 .. ff (=0..255)
- SegxIDy: ID des Pixels y des Segments x.
- ChecksumXOR: XOR byte checksum
Die 14 Pixel (1 .. 14) sind dabei wie folgt durchnummeriert bzw. haben folgende IDs:
- 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d
Folgender einfacher Befehl muss dabei zunächst zumindest am Anfang mitgesendet werden (=Grafitti-Modus?):
- 33 05 0a 20 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1f → CMD="MwUKIAMAAAAAAAAAAAAAAAAAAB8="
Segment-Steuerung (H6076): Teil 2: Beispiel 1
-----------------------------------------------------
Folgendes Beispiel, wobei ich die Farb-Byte-Triplets bold gesetzt habe um die Struktur der nacheinander folgenden Segmente (#Segments1 Col1R Col1G Col1B LEDxID1 LEDxID2 ...) zu verdeutlichen:
Pixel #1: rot, #5: grün, #10: blau, #11: gelb, Rest=Hintergrund: AUS
- a3 00 01 02 03 09 00 01 00 00 00 04 01 ff 00 00 00 01 00 50 → owABAgMJAAEAAAAEAf8AAAABAFA=
- a3 ff ff 00 04 01 00 00 ff 09 02 64 64 00 0a 0b 00 00 00 53 → o///AAQBAAD/CQJkZAAKCwAAAFM=
Testen kann man z.B. im Terminal via
echo -n '{"msg":{"cmd":"ptReal","data":{"command":["owABAgMJAAEAAAAEAf8AAAABAFA=","o///AAQBAAD/CQJkZAAKCwAAAFM=","MwUKIAMAAAAAAAAAAAAAAAAAAB8="]}}}' | nc -u -w1 <<IP>> 4003
So kann man also einzelne steuerbare Pixel = 6er-LED-Gruppen in beliebigen Farben und Kombinationen schalten, z.B. um Stati anzuzeigen (siehe das Projekt hier, oder hier).
---------------------------------------------------------
Segment-Steuerung (H6076): Teil 3: Beispiel 2: Prozent-Balken
---------------------------------------------------------
Folgende Sequenz gibt eine wachsende Anzahl der Pixel farbig aus (gelb: 64 64 0), d.h. ein einziges Segment (total#Segments=01=1) wachsender Länge (wachsende Anzahl an Pixel, bzw. mehr und mehr PixelIDs), z.B. nutzbar als Prozent-Balken (wie hier. Z.B. für eine Anzeige der PV-Produktion oder des Programmfortschritts der Kaffeemaschine):
[Man kann die Farbe leicht ändern, aber muss dafür die Prüfsumme neu berechnen und die Hex2Base64-Kodierung erneut vornehmen]
Die unterstrichenen Bytes im Folgenden sollen die Systematik verdeutlichen und zeigen die LED1IDs von 00=erstes 6er-LED-Pixel bis 0d=14. Pixel = letztes Pixel des einen Segments.
Alle Pixel AUS
a3 00 01 02 03 09 00 00 00 00 00 00 00 00 00 00 00 00 00 aa → owABAgMJAAAAAAAAAAAAAAAAAKo=
a3 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5c → o/8AAAAAAAAAAAAAAAAAAAAAAFw=
CMD="owABAgMJAAAAAAAAAAAAAAAAAKo=","o/8AAAAAAAAAAAAAAAAAAAAAAFw=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 01 64 64 00 00 00 00 aa → owABAgMJAAAAAAABAWRkAAAAAKo=
a3 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5c → o/8AAAAAAAAAAAAAAAAAAAAAAFw=
CMD="owABAgMJAAAAAAABAWRkAAAAAKo=","o/8AAAAAAAAAAAAAAAAAAAAAAFw=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-2. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 02 64 64 00 00 01 00 a8 → owABAgMJAAAAAAABAmRkAAABAKg=
CMD="owABAgMJAAAAAAABAmRkAAABAKg=","o/8AAAAAAAAAAAAAAAAAAAAAAFw=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-3. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 03 64 64 00 00 01 02 ab → owABAgMJAAAAAAABA2RkAAABAqs=
CMD="owABAgMJAAAAAAABA2RkAAABAqs=","o/8AAAAAAAAAAAAAAAAAAAAAAFw=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-4. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 04 64 64 00 00 01 02 ac → owABAgMJAAAAAAABBGRkAAABAqw=
a3 ff 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5f → o/8DAAAAAAAAAAAAAAAAAAAAAF8=
CMD="owABAgMJAAAAAAABBGRkAAABAqw=","o/8DAAAAAAAAAAAAAAAAAAAAAF8=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-5. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 05 64 64 00 00 01 02 ad → owABAgMJAAAAAAABBWRkAAABAq0=
a3 ff 03 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5b → o/8DBAAAAAAAAAAAAAAAAAAAAFs=
CMD="owABAgMJAAAAAAABBWRkAAABAq0=","o/8DBAAAAAAAAAAAAAAAAAAAAFs=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-6. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 06 64 64 00 00 01 02 ae → owABAgMJAAAAAAABBmRkAAABAq4=
a3 ff 03 04 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5e → o/8DBAUAAAAAAAAAAAAAAAAAAF4=
CMD=owABAgMJAAAAAAABBmRkAAABAq4=","o/8DBAUAAAAAAAAAAAAAAAAAAF4=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-7. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 07 64 64 00 00 01 02 af → owABAgMJAAAAAAABB2RkAAABAq8=
a3 ff 03 04 05 06 00 00 00 00 00 00 00 00 00 00 00 00 00 58 → o/8DBAUGAAAAAAAAAAAAAAAAAFg=
CMD="owABAgMJAAAAAAABB2RkAAABAq8=","o/8DBAUGAAAAAAAAAAAAAAAAAFg=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-8. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 08 64 64 00 00 01 02 a0 → owABAgMJAAAAAAABCGRkAAABAqA=
a3 ff 03 04 05 06 07 00 00 00 00 00 00 00 00 00 00 00 00 5f → o/8DBAUGBwAAAAAAAAAAAAAAAF8=
CMD="owABAgMJAAAAAAABCGRkAAABAqA=","o/8DBAUGBwAAAAAAAAAAAAAAAF8=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-9. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 09 64 64 00 00 01 02 a1 → owABAgMJAAAAAAABCWRkAAABAqE=
a3 ff 03 04 05 06 07 08 00 00 00 00 00 00 00 00 00 00 00 57 → o/8DBAUGBwgAAAAAAAAAAAAAAFc=
CMD="owABAgMJAAAAAAABCWRkAAABAqE=","o/8DBAUGBwgAAAAAAAAAAAAAAFc=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-10. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 0a 64 64 00 00 01 02 a2 → owABAgMJAAAAAAABCmRkAAABAqI=
a3 ff 03 04 05 06 07 08 09 00 00 00 00 00 00 00 00 00 00 5e → o/8DBAUGBwgJAAAAAAAAAAAAAF4=
CMD="owABAgMJAAAAAAABCmRkAAABAqI=","o/8DBAUGBwgJAAAAAAAAAAAAAF4=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-11. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 0b 64 64 00 00 01 02 a3 → owABAgMJAAAAAAABC2RkAAABAqM=
a3 ff 03 04 05 06 07 08 09 0a 00 00 00 00 00 00 00 00 00 54 → o/8DBAUGBwgJCgAAAAAAAAAAAFQ=
CMD="owABAgMJAAAAAAABC2RkAAABAqM=","o/8DBAUGBwgJCgAAAAAAAAAAAFQ=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-12. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 0c 64 64 00 00 01 02 a4 → owABAgMJAAAAAAABDGRkAAABAqQ=
a3 ff 03 04 05 06 07 08 09 0a 0b 00 00 00 00 00 00 00 00 5f → o/8DBAUGBwgJCgsAAAAAAAAAAF8=
CMD="owABAgMJAAAAAAABDGRkAAABAqQ=","o/8DBAUGBwgJCgsAAAAAAAAAAF8=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-13. Pixel gelb, Rest AUS
a3 00 01 02 03 09 00 00 00 00 00 01 0d 64 64 00 00 01 02 a5 → owABAgMJAAAAAAABDWRkAAABAqU=
a3 ff 03 04 05 06 07 08 09 0a 0b 0c 00 00 00 00 00 00 00 53 → o/8DBAUGBwgJCgsMAAAAAAAAAFM=
CMD="owABAgMJAAAAAAABDWRkAAABAqU=","o/8DBAUGBwgJCgsMAAAAAAAAAFM=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
1.-14. Pixel (=alle) gelb
a3 00 01 02 03 09 00 00 00 00 00 01 0e 64 64 00 00 01 02 a6 → owABAgMJAAAAAAABDmRkAAABAqY=
a3 ff 03 04 05 06 07 08 09 0a 0b 0c 0d 00 00 00 00 00 00 5e → o/8DBAUGBwgJCgsMDQAAAAAAAF4=
CMD="owABAgMJAAAAAAABDmRkAAABAqY=","o/8DBAUGBwgJCgsMDQAAAAAAAF4=","MwUKIAMAAAAAAAAAAAAAA AAAAB8="
Kommentar