These are BLE-only RGB string lights with 15 discrete individually addressable bulbs/segments. They are controlled by a Govee app and have no IR or RF remote. Since I'm not a masochist and don't want to call a REST API to control a local bluetooth device, I reverse engineered the protocol and wrote a python library to control them.
The bulk of the insights I've gathered are in research.
To look at the raw notes and snoop logs, see raw. This is not intended to be particularly readable, mostly just documentation of my process.
In poking the various registers, I've managed to softlock my device multiple times which required removing the power to get out. Nothing so far has bricked it, but I can't make any guarantees. You use raw commands at your own risk.
python mqtt.py
Starts an MQTT client that listens for commands to control the lights. I've made no effort to make this configurable so you'll need to change the host yourself. The command API is modeled after tasmota and is as follows:
cmnd/govee/...
- Run a given command with the arguments in the message. Usually space-separated values.stat/govee/RESULT
- The result of the last command. Always JSON.- On error, returns
{"ERROR": "error message"}
. Some commands return additional context or incomplete results. - On success, returns
{Command: result}
- On error, returns
power
- Get or set the power state (register01
).- Accepts
1|0|on|off|true|false|toggle
, case insensitive.
- Accepts
dimmer
- Get or set the global dimmer level (register04
).- Accepts floats or percentages.
mode
- Readonly the current mode (register05
+ some others).{"name": "color", "brightness": percent, "color": hex}
- All segments are the same color and brightness.{"mode": "segment", "segments": [{"color": hex, "brightness": percent}]}
- Each segment has its own color and brightness.{"mode": "scene", "name": scene, "code": int}
- The device is in a scene mode.
version
- Readonly device version information.version[0]
- Get all device information as a tuple.version1
- Get the device version (HW version, register06
).version2
- Get the device HW version (register0703
).version3
- Get the device FW version (register0704
).
mac
- Get the device MAC address (register0705
).restart
- Get the restart reason or restart the device (register0e
).status
- Get all device information.scene
- Get or set the current scene (a3
command with param data +w0504xxxx
).- This can be
Category - Scene
,Scene
, or a (possibly hex with 0x) id. Case and whitespace insensitive.
- This can be
scenes
- Readonly list of scenes and their ids,{Category: {Scene: ID|{"code": code, "diy": diyCode}}}
.brightness
- Get or set the brightness level of all segments if it's the same, elsenull
.color
- Get or set the color of all segments if it's the same, elsenull
.- Note that if their brightness isn't the same, the API will still say it's in segment mode.
peek
- Read a register or range of registers (33
).- Accepts a comma-separated list of hex registers or ranges.
multi
- Initiate a multi-packet write command (a5
) which writes scene parameters.- Accepts hex data, not base64.
USE AT YOUR OWN RISK
poke
- Write a register (33
).- Accepts a space-separated hex register address and hex data to write.
raw
- Send raw data to the device.asm
- Send commands from the command language to the device.
python govee.py
Scans for compatible Govee devices and enters a command prompt tailored for reverse-engineering. A custom command language is used to send commands to the device.
Very loose command language with no validation. Numbers are always hex and keywords get replaced with their byte equivalents.
r
read (aa
)w
write (33
)- prefix / change / suffix... eg
r05/aa,bb,cc/01
= queue commandsaa05aa01
aa05bb01
aa05cc01
xx-yy
= range of valuesw0504/00-ff
= send 255 write commands to0504
with successive values00-ff
scene
=0504
egw scene 10
=33050410
param
=04
restart
=w 0e 00
orrestart xx
=w 0e xx
- Spaces are mostly ignored except for keyword word boundaries.
python consolidate.py
Consolidates the raw data into a more readable format. This is a one-time operation and is not intended to be run regularly.
- Map registers (partially complete)
- MQTT client to act as a persistent bridge.
- Set the color and brightness of segment subsets.
- Get current colors and brightness of segments from device.
- Set a parameterized scene.
- Clean up API dump data (jsons -> scenes).
- Set scenes by name.
- Use snoop log to get DUNE collaboration scenes.
- Implement color temperature feature.
- Reverse engineer scene param encoding (try DIY scenes in the app?).
- Finish mapping where scene param data actually goes.
- Able to extract all scene data from the device.
- What do sleep, wake, and timer actually do?
- Better understand color buffer in register
a5xx
. - Distinguish
w mode 05
"mic mode" anda5
"audio mode".- Reverse engineer them too.
- What are the extra services and characteristics?
- Unknown registers:
0f
,11
,12
,23
,40
,ee
,ef
,ff
. - What are the extra
ea86
bytes appended to the little-endian MAC in register0702
? - Why is the HW version repeated in
06
and0703
?
- govee_ble_lights (Homeassistant)
- API dumps for BLE govee lights.
- Practical implementations of some parts of the API.
- Govee-Reverse-Engineering
- Reverse-engineering of incompatible APIs which still helped inform my own research.