These are very rough notes.

I have successfully been able to control Philips Hue lightbulbs (white and colour variants) without a Philips Hue bridge. Here are my notes on how I did it.


What is ZigBee

ZigBee is a 2.4GHz radio protocol for low-power IoT networking/control. The Philips Hue lightbulbs are ZigBee Certified Devices, which means they implement ZigBee and have been verified by the ZigBee Alliance as having a good ZigBee implementation.

Typically Philips Hue lightbulbs will use the ZigBee Light Link protocol, which is a subset of the ZigBee Home Automation profile that uses a 'secret' key burned into all ZLL products and known only to ZigBee Alliance members. Fortunately, ZLL is a subset of ZHA - so we can just use that.

ZigBee devces form a mesh network that can relay messages to each other. A bridging device is required to get commands in/out of the mesh network. A ZigBee network is composed of three different types of nodes:

Setup coordinator

Get the Digi XCTU Software. Plug in the XBee and let the software find it. Mine came with sleep mode enabled which would kick in after 30 seconds or so and make the XBee unresponsive. Quickly disabling sleep mode (setting SM parameter to 0) will get around this issue.

Now use the firmware explorer to flash the ZigBee Coordinator API function set firmware. You also need to write the following parameters:

ZS=2, ZigBee-PRO Stack Profile
EE=1, Enable Encryption
EO=1, Encryption Options
AP=1, Enable API
AO=1, Explicit API Output

If you don't set an encryption key (KY), one will be randomly picked.

Now, plug in a Philips Hue Lightbulb and give it power. Switch to Network working mode in XCTU and hit scan - after a while your lightbulb should show up. You can identify it by an R for router (as opposed to C for coordinator). Take a note of the lightbulbs 64bit address, you'll need this to send it commands.

Send commands with XCTU

To send commands to the Hue lightbulbs, you need to tell the XBee what to do via the API. There are two layers of API to understand; the XBee and ZigBee itself. You can read about the API in the Digi XBee S2B User Guide.

Using XCTUs Console working mode you can create frames using the Frame Creator Tool, and send them to devices in your network.

You want to send an Explicit Addressing Zigbee Command frame. The Digi XBee S2B User Guide describe the format in API Operation, API frames.

Turn on example

Here is an example for turning on a lightbulb with the address 00 11 22 33 44 55 66 77

7E 00 19 11 01 00 11 22 33 44 55 66 77 FF FE E8 0B 00 06 01 04 00 00 01 00 01 00 10 04

The XBee API Frames Generator does a good job of explaining what each parameter is. Also, refer to the User Guide for more details. Summary:

Delimiter           7E
Length              00 19
Frame type          11
Frame ID            01
64-bit dest         00 11 22 33 44 55 66 77
16-bit dest         FF FE
Source endpoint     E8
Dest endpoint       0B
Cluster ID          00 06 (ZigBee cluster for 'on/off' control, more on this later)
Profile ID          01 04 (ZigBee Home Automation Profile ID)
Broadcast Radius    00
Options             00
Data payload        01 00 01 00 10
Checksum            04

I will endeavour to update this page as I understand the signifcance of things not adequately explained. I patched most of this together by reading undocumented code.

Here is a frame log, including responses, of me turning my bedroom light on, off, and then toggling it (three different commands). Addresses have been changed. Checksums are broken.

# on
05-30-2018 23:34:47.837,0,SENT,7E001911010011223344556677FFFEE80B0006010400000100010010FF
05-30-2018 23:34:47.921,1,RECV,7E00078B01680700000004
05-30-2018 23:34:47.921,2,RECV,7E001791001122334455667768070BE8000601040118000B01002D

# off
05-30-2018 23:34:51.205,3,SENT,7E001911010011223344556677FFFEE80B0006010400000100000010FF
05-30-2018 23:34:51.276,4,RECV,7E00078B01680700000004
05-30-2018 23:34:51.326,5,RECV,7E001791001122334455667768070BE8000601040118000B00002E

# toggle
05-30-2018 23:34:54.917,6,SENT,7E001911010011223344556677FFFEE80B0006010400000100020010FF
05-30-2018 23:34:54.983,7,RECV,7E00078B01680700000004
05-30-2018 23:34:54.993,8,RECV,7E001791001122334455667768070BE8000601040118000B02002C

Here are three data payloads and what they do:

ON:     01 00 01 00 10
OFF:    01 00 00 00 10
TOGGLE: 01 00 02 00 10

Sections of note in the ZigBee Cluster Library:

Other payloads:


Cluster ID is 0x0008 (level control).

01 00 04 XX 10 00 10

Where XX is brigthness range: 0-255.


Cluster ID is 0x0300, (colour cluster control).

01 00 0a XX XX 10 00 10

Where XX XX is the integer part of 1000000 / value (for value in range 2700-6500). Little endian.


Cluster ID is 0x0300, (colour cluster control).

01 00 06 XX YY 10 00 10

Where XX is hue range: 0-254 and YY is saturation range: 0-254.

Read attributes

There is an example of reading attribute in Digi XBee S2B User Guide, see API Operation, Sending ZCL Commands with API.

Here is a frame log of reading the OnOff attribute. See ZigBee Cluster Library section

# toggle on
05-27-2018 23:47:47.054,0,SENT,7E001911010011223344556677FFFEE80B000601040000010002001030
05-27-2018 23:47:47.107,1,RECV,7E00078B01DBE5000000B3
05-27-2018 23:47:47.157,2,RECV,7E0017910011223344556677DBE50BE8000601040118000B0200DB

# read
05-27-2018 23:47:50.573,3,SENT,7E001911010011223344556677FFFEE80B0006010400000066000000DD
05-27-2018 23:47:50.632,4,RECV,7E00078B01DBE5000000B3
05-27-2018 23:47:50.692,5,RECV,7E001A910011223344556677DBE50BE80006010401186601000000100170

# toggle off
05-27-2018 23:47:54.405,6,SENT,7E001911010011223344556677FFFEE80B000601040000010002001030
05-27-2018 23:47:54.468,7,RECV,7E00078B01DBE5000000B3
05-27-2018 23:47:54.479,8,RECV,7E0017910011223344556677DBE50BE8000601040118000B0200DB

# read
05-27-2018 23:47:57.301,9,SENT,7E001911010011223344556677FFFEE80B0006010400000066000000DD
05-27-2018 23:47:57.373,10,RECV,7E00078B01DBE5000000B3
05-27-2018 23:47:57.423,11,RECV,7E001A910011223344556677DBE50BE80006010401186601000000100071

18 66 01 00 00 00 10 01 - read when on
18 66 01 00 00 00 10 00 - read when off

You can see the last byte changing from 01 to 00.

It's also possible to read the current level (brightness):

05-28-2018 00:21:39.809,-,API," ,0013A20040A03F51,ZigBee Router API,23A7,/dev/ttyUSB0 - 9600/8/N/1/N,0"

05-28-2018 00:20:27.493,0,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:20:27.565,1,RECV,7E00078B01DBE5000000B3
05-28-2018 00:20:27.615,2,RECV,7E001A910011223344556677DBE50BE8000801040118660100000020FE61

05-28-2018 00:20:42.077,3,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:20:42.137,4,RECV,7E00078B01DBE5000000B3
05-28-2018 00:20:42.148,5,RECV,7E001A910011223344556677DBE50BE8000801040118660100000020C798

05-28-2018 00:20:51.353,6,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:20:51.402,7,RECV,7E00078B01DBE5000000B3
05-28-2018 00:20:51.472,8,RECV,7E001A910011223344556677DBE50BE80008010401186601000000209CC3

05-28-2018 00:20:58.825,9,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:20:58.913,10,RECV,7E00078B01DBE5000000B3
05-28-2018 00:20:58.914,11,RECV,7E001A910011223344556677DBE50BE80008010401186601000000206BF4

05-28-2018 00:21:04.625,12,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:21:04.672,13,RECV,7E00078B01DBE5000000B3
05-28-2018 00:21:04.703,14,RECV,7E001A910011223344556677DBE50BE80008010401186601000000203E21

05-28-2018 00:21:15.361,15,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:21:15.409,16,RECV,7E00078B01DBE5000000B3
05-28-2018 00:21:15.480,17,RECV,7E001A910011223344556677DBE50BE8000801040118660100000020015E

05-28-2018 00:21:20.473,18,SENT,7E001911010011223344556677FFFEE80B0008010400000066000000DB
05-28-2018 00:21:20.547,19,RECV,7E00078B01DBE5000000B3
05-28-2018 00:21:20.597,20,RECV,7E001A910011223344556677DBE50BE8000801040118660100000020055A

7E001A910011223344556677DBE50BE8 0008 0104 01 18660100000020FE 61 - maximum brightness
7E001A910011223344556677DBE50BE8 0008 0104 01 18660100000020C7 98
7E001A910011223344556677DBE50BE8 0008 0104 01 186601000000209C C3
7E001A910011223344556677DBE50BE8 0008 0104 01 186601000000206B F4
7E001A910011223344556677DBE50BE8 0008 0104 01 186601000000203E 21
7E001A910011223344556677DBE50BE8 0008 0104 01 1866010000002001 5E - minimum brightness
7E001A910011223344556677DBE50BE8 0008 0104 01 1866010000002005 5A

You can see the last byte of the response payload changing (and the checksum, but ignore that).

I was also able to successfully read hue, and saturation.

I don't currently know what the rest of the bytes mean.

Send commands with Python

Digi provide digi-xbee Python Library.

The Python APIs start_network_discovery() method doesn't appear to find my Hue lightbulbs but XCTUs network discovery does. I believe this is because the Python lib doesn't natively support the Node Identification Frame (0x95). Adding the device manually works.