337 lines
13 KiB
Markdown
337 lines
13 KiB
Markdown
# The Color of Magic: Reversing the Hue Zigbee Clusters
|
|
|
|
This document, which builds on the [initial work](hue-zigbee-format.md), aims to
|
|
compile all available information about the custom Zigbee messages used by
|
|
Philips Hue devices, and in particular, lights.
|
|
|
|
The following text refers to commands and attributes on Hue devices. This has
|
|
been researched using the following units:
|
|
|
|
## "Hue Bulb"
|
|
|
|
- "Hue white and color ambiance E27 1100lm"
|
|
- Model `LCA006`
|
|
- Firmware 1.122.2 (20240902)
|
|
|
|
## "Hue Gradient strip"
|
|
|
|
- "Hue Play gradient lightstrip for PC"
|
|
- Model `LCX005`
|
|
- Firmware 1.122.2 (20240902)
|
|
|
|
## Nomenclature
|
|
|
|
The following short names are used to refer to zigbee data types and concepts:
|
|
|
|
| Name used here | Zigbee meaning |
|
|
|----------------|---------------------------------------------|
|
|
| N/S | Attribute not supported |
|
|
| u8 | Unsigned, 8-bit integer |
|
|
| u16 | Unsigned, 16-bit integer |
|
|
| i16 | Signed, 16-bit integer |
|
|
| b8 | 8-bit bitmap value |
|
|
| b32 | 32-bit bitmap value |
|
|
| e8 | 8-bit enum value |
|
|
| hex | "Octet string" (byte array) in hex notation |
|
|
|
|
# Cluster 0xFC00: Hue Button events
|
|
|
|
Used by hue buttons to report button events and other state changes.
|
|
|
|
## Cluster-specific commands
|
|
|
|
### Command 0: Button Event
|
|
|
|
These are mostly documented elsewhere, and because they are button events, they
|
|
are not the main focus of this document.
|
|
|
|
# Cluster 0xFC01: Entertainment
|
|
|
|
This cluster is used to control "Entertainment Zones", a defining feature of the
|
|
Hue ecosystem.
|
|
|
|
## Cluster-specific commands
|
|
|
|
### Command 1: Update entertainment zone
|
|
|
|
This is the major command used to send a "frame" of Hue Entertainment data.
|
|
|
|
Sending it to a Hue bulb will cause that bulb to repeat it in broadcast mode,
|
|
for other devices to pick up.
|
|
|
|
```text
|
|
┌───────────┬───┬───┬───┬───┬───┬───┬───┬───┐
|
|
│ Byte Bit ► 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 │
|
|
├─▼─────────┼───┴───┴───┴───┴───┴───┴───┴───┤
|
|
│ 0 │ .counter │
|
|
│ │ │
|
|
│ 1 │ │
|
|
│ │ │
|
|
│ 2 │ │
|
|
│ │ │
|
|
│ 3 │ │
|
|
├───────────┼───────────────────────────────┤
|
|
│ 4 │ .smoothing │\
|
|
│ │ Defaults to 0x0400 │ } Smoothing factor
|
|
│ 5 │ (encoded as "0004") │/
|
|
├───────────┼───────────────────────────────┤
|
|
│ 6 │ Light data block 0 │\
|
|
│ │ │ \
|
|
│ .. │ │ } Repeated for each light
|
|
│ │ │ /
|
|
│ 12 │ │/
|
|
├───────────┼───────────────────────────────┤
|
|
: 13 : Light data block 1 :
|
|
: : :
|
|
```
|
|
|
|
The "smoothing factor" is a value that controls how agressively the
|
|
color/brightness will change from the previous frame. A value of `0x0000` is the
|
|
fastest possible (and generally not very pleasant to look at), while a value of
|
|
`0x1000` is quite slow, giving very smooth animations, but without any quick changes.
|
|
|
|
Very high values (e.g. above `0x4000`) are so slow that they are unlikely to be
|
|
useful in most cases.
|
|
|
|
The existing Hue Entertainment clients all seem to use `0x0400`, which is a
|
|
reasonable starting point. Note that this property does NOT seem to be exposed
|
|
over any known API, but it is available over Bifrost.
|
|
|
|
Each "light data block" is a 7-byte packed structure describing the desired
|
|
state for a light (a bulb, or single segment of a multi-segment light source).
|
|
|
|
```text
|
|
┌───────────┬───┬───┬───┬───┬───┬───┬───┬───┐
|
|
│ Byte Bit ► 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 │
|
|
├─▼─────────┼───┴───┴───┴───┴───┴───┴───┴───┤
|
|
│ 0 │ .addr │
|
|
│ │ Zigbee address (or alias) │
|
|
│ 1 │ for the target light │
|
|
├───────────┼───────────┬───────────────────┤
|
|
│ 2 │(low 3 bit)│ .mode (5 bit enum)│
|
|
│ │─ ─ ─ ─ ─ ─└───────────────────┤
|
|
│ 3 │ .brightnes (high 8 bits) │
|
|
├───────────┼───────────────────────────────┤
|
|
│ 4 │ .color_x (low 8 bits) │\
|
|
│ ├───────────────┐─ ─ ─ ─ ─ ─ ─ ─│ \
|
|
│ 5 │ (low 4 bits) │ (high 4 bits) │ same format as for composite updates
|
|
│ │─ ─ ─ ─ ─ ─ ─ ─└───────────────┤ /
|
|
│ 6 │ .color_y (high 8 bits) │/
|
|
└───────────┴───────────────────────────────┘
|
|
```
|
|
|
|
The `.mode` field is an odd one. Only two values have ever been observed:
|
|
|
|
```rust
|
|
// the names might change, as we learn more about these bits
|
|
enum LightRecordMode {
|
|
Segment = 0b00000,
|
|
Device = 0b01011,
|
|
}
|
|
```
|
|
|
|
Normal bulbs must be contacted with the `LightRecordMode::Device` option, while
|
|
updates for segments on a gradient strip must use the `LightRecordMode::Segment`
|
|
mode. Otherwise, the entire segment only lights up in the first color.
|
|
|
|
Current hypothesis: This values determines if real network addresses or virtual
|
|
segment addresses are used, but this is currently not tested.
|
|
|
|
### Command 3: Synchronize entertainment zone
|
|
|
|
This command is used to synchronize the sequence number in an entertainment
|
|
group. The first two bytes are unknown.
|
|
|
|
```c
|
|
struct {
|
|
x0: u8, // only seen as 0
|
|
x1: u8, // seen as 0 or 1. unknown function
|
|
counter: u32, // frame counter for entertainment group
|
|
}
|
|
```
|
|
|
|
### Command 4: Retrieve segment mapping
|
|
|
|
This command is used to retrieve the segment mapping for a hue multi-segment
|
|
light.
|
|
|
|
#### Request
|
|
|
|
A single byte is sent. Only observed as `00` (might be an index for highly
|
|
addressable devices?).
|
|
|
|
#### Response
|
|
|
|
```c
|
|
struct Response {
|
|
x0: u8, // unknown
|
|
x1: u8, // unknown
|
|
count: u8, // number of segments
|
|
segments: [Segment], // segment descriptors
|
|
}
|
|
|
|
struct Segment {
|
|
start: u8, // start index for segment
|
|
length: u8, // segment length
|
|
}
|
|
```
|
|
|
|
As an example, the following is a real response from a Hue Gradient light strip:
|
|
|
|
```
|
|
┌───┬───First segment descriptor
|
|
│ │
|
|
00 00 07 00 01 01 01 02 01 03 01 04 01 05 01 06 01
|
|
│ │ │ │
|
|
└header┘ └───────Seven segment descriptors───────┘
|
|
```
|
|
|
|
This tells us the segments are arranged thus:
|
|
|
|
- Start at `00`, length `01`
|
|
- Start at `01`, length `01`
|
|
- Start at `02`, length `01`
|
|
- ...
|
|
|
|
These are all length 1. In other words, the layout is:
|
|
|
|
`0, 1, 2, 3, 4, 5, 6`
|
|
|
|
### Command 7: Configure segments for entertainment mode (req/rsp)
|
|
|
|
Hue Entertainment frames consists of brightness and color data for up to 10
|
|
lights, all in a single frame.
|
|
|
|
Each light is identified by 2 bytes containing its zigbee network (short)
|
|
address.
|
|
|
|
For Hue devices that contain multiple lights (such as gradient strips), this
|
|
presents a problem, since the entire strip only has a single zigbee address!
|
|
|
|
To solve that problem, this command can be used on multi-segment devices to
|
|
configure each segment with a virtual address.
|
|
|
|
#### Request
|
|
|
|
```c
|
|
struct {
|
|
count: u16,
|
|
addresses: [count x u16],
|
|
}
|
|
```
|
|
|
|
Here is an example of a command that sets seven virtual addresses for a gradient
|
|
light strip with 7 segments:
|
|
|
|
```
|
|
┌───┬───Segment index 0
|
|
│ │
|
|
00 07 97 d2 98 d2 99 d2 9a d2 9b d2 9c d2 9d d2
|
|
│ │ │ │
|
|
└cnt┘ └───────Seven segment indices───────────┘
|
|
|
|
```
|
|
|
|
After this, the segments will respond the these addresses:
|
|
|
|
- `0xD297`
|
|
- `0xD298`
|
|
- `0xD299`
|
|
- `0xD29A`
|
|
- `0xD29B`
|
|
- `0xD29C`
|
|
- `0xD29D`
|
|
|
|
#### Response
|
|
|
|
```c
|
|
struct {
|
|
x0: u16,
|
|
}
|
|
```
|
|
|
|
The only observed response is `0000`, which probably indicates success.
|
|
|
|
Running this command on a Hue device that does not have multiple segments (i.e,
|
|
a regular Hue bulb) gets a "Command Not Supported" standard Zigbee response, so
|
|
returning `0000` seems to be a safe way to detect success.
|
|
|
|
## Attributes
|
|
|
|
| Attr | Type | Desc | Strip | Bulb | Firmware |
|
|
|--------|------|----------------------------|-------|------|----------------------------------------|
|
|
| `0000` | `b8` | ? | `0F` | `0B` | |
|
|
| `0001` | `e8` | ? | `00` | `00` | |
|
|
| `0002` | `u8` | Probably max segment count | `0A` | N/S | |
|
|
| `0003` | `u8` | Probably gradient-related | `04` | N/S | |
|
|
| `0004` | `u8` | Probably segment count | `07` | N/S | |
|
|
| `0005` | `u8` | Light balance factor | `FE` | `FE` | Fails on `1.76.11`, works on `1.122.2` |
|
|
|
|
Notice that attributes `0002`, `0003` and `0004` are not present on the hue
|
|
bulb. This supports the idea that these attributes are gradient-related.
|
|
|
|
So far the only attribute known on this cluster is `0x005`, which sets the light
|
|
level balancing for entertainment mode.
|
|
|
|
This is a feature where lights can be dimmed relatively, so certain lights
|
|
aren't blindingly bright. Just like regular brightness updates, the valid range
|
|
is `0x01` to `0xFE`. This should always be set to `0xFE`, unless you want to dim
|
|
the light in entertainment mode.
|
|
|
|
# Cluster 0xFC02
|
|
|
|
Never seen. Maybe they skipped a number?
|
|
|
|
# Cluster 0xFC03: Gradients, Effects, Animations
|
|
|
|
## Cluster-specific commands
|
|
|
|
### Command 0: Write combined state
|
|
|
|
This is perhaps the single most complicated Hue command. It is used to
|
|
simultaneously set all supported properties of a Hue bulb.
|
|
|
|
It has been extensively [documented in a separate document](hue-zigbee-format.md).
|
|
|
|
After setting the state with this command, it can be read back as property
|
|
`0x0002` (see below).
|
|
|
|
## Attributes
|
|
|
|
Sample values:
|
|
|
|
| Attr | Type | Desc | Strip | Bulb |
|
|
|--------|-------|-----------------|--------------------|--------------------|
|
|
| `0001` | `b32` | ? | `0000000F` | `00000007` |
|
|
| `0002` | `hex` | Composite state | `0700010a6e01` | `070001176f01` |
|
|
| `0010` | `b16` | ? | `0001` | `0001` |
|
|
| `0011` | `b64` | ? | `000000000003FE0E` | `000000000003FE0E` |
|
|
| `0012` | `b32` | ? | `00000003` | `00000000` |
|
|
| `0013` | `b16` | ? | `0007` | N/S |
|
|
| `0031` | `u16` | ? | `04E2` | N/S |
|
|
| `0032` | `u8` | ? | `00` | N/S |
|
|
| `0033` | `u8` | ? | `00` | N/S |
|
|
| `0034` | `u8` | ? | `03` | N/S |
|
|
| `0035` | `u8` | ? | `FE` | N/S |
|
|
| `0036` | `u8` | ? | `4F` | N/S |
|
|
| `0038` | `u16` | ? | `0007` | N/S |
|
|
|
|
The bulb supports noticably fewer properties, which makes it likely that the
|
|
missing ones are related to gradient handling.
|
|
|
|
# Cluster 0xFC04
|
|
|
|
Very rarely observed. Only seen with ZCL: Read Attributes.
|
|
|
|
## Attributes
|
|
|
|
| Attr | Type | Desc | Strip | Bulb |
|
|
|--------|-------|------|------------|------------|
|
|
| `0000` | `b16` | ? | `1007` | `1007` |
|
|
| `0001` | `b16` | ? | `0000` | `0000` |
|
|
| `0002` | `b16` | ? | `0000` | `0000` |
|
|
| `0010` | `u32` | ? | `00000000` | `00000000` |
|
|
| `0011` | `u32` | ? | `00000000` | `00000000` |
|
|
| `0012` | `u32` | ? | `00000000` | `00000000` |
|
|
| `0013` | `u32` | ? | `00000000` | `00000000` |
|