Milight Bridge problems and protocol

January 6 , 2018

While building my smart-home solution, one of the objectives was to integrate existing Milight lamps. Those lamps are similar to Philips Hue. Unlike hue, however, those lamps are a lot cheaper and are also available in a wider range of sockets. They are connected to the home WLAN using a bridge similar to hue bridge and can be controlled using an app on your smartphone after they are connected. The app uses a simple UDP protocol to connect to the bridge, which can be easily sniffed using Wireshark.

Unfortunately, those lamps are a lot less developed than the original, which makes it hard to develop a good integration for it.

  1. No confirmation for commands
  2. No error detection between bridge and lamp
  3. No direct option for choosing a group when setting either brightness or colour
  4. Almost no vendor documentation
  5. Some commands have multiple results depending on the state
  6. No security

There is no response sent by the bridge whether or not a command was received or not. You can reduce the impact by changing the protocol from UDP to TCP in the controllers webui. This ensures on the network level that commands are received in the correct order and allow for detection of dropped packets. Changing this, however, renders the vendor provided app unusable and some newer versions of the bridge come with the web interface removed, which removes this option.

It seems like there is no error detection or correction in the protocol between bridge and lamp, which results in some commands getting misunderstood by the lamp. However, it is not possible to solve this without rewriting the firmware of both bridge and lamp, at which point it is a better option to just build a lamp yourself. The only option to reduce this problem is by resending all commands from time to time and accept that there are some short intervals of wrong colours being displayed.

The commands for "on", "off" and choosing white as the colour allow specifying a group in the command. However, all other commands do not allow for this and instead apply to the group which was turned on last. However, if the command to turn on that particular group did not arrive the command gets applied to the wrong group. Looks like this can not be solved without a new firmware as well. You can just repeat the command multiple times and hope for the best.

There is no vendor-provided documentation for older versions of the bridge. Only a short description for bridges which use protocol version 6 or higher, but they use a different protocol.

There is only a single command which is used for both turning on and pairing new lamps. There is no separate command to pair lamps to the bridge.

There are no options to secure access to the bridge. Everyone with access to local network a bridge is connected to can control them. To prevent this you need to put the bridge on a separate network and restrict access using a firewall on the network level.

To control the lamp using C++ you need to create a UDP or TCP socket (depending on the protocol selected in the webui of the controller, with the default being UDP) to port 8899. After you can send commands which consist of three bytes.

CommandByte 1Byte 2Byte 3
Change colour0x40<colour>0x55
Change brightness0x4e<brightness>0x55
Group 1 on0x450x000x55
Group 2 on0x470x000x55
Group 3 on0x490x000x55
Group 4 on0x4B0x000x55
Group 1 off0x460x000x55
Group 2 off0x480x000x55
Group 3 off0x4A0x000x55
Group 4 off0x4C0x000x55
Group 1 white0xC50x000x55
Group 2 white0xC70x000x55
Group 3 white0xC90x000x55
Group 4 white0xCB0x000x55

You can control the brightness of a lamp in 24 steps with a value between 0x02 and 0x18.

The colour value can be calculated from the hue component of an HSV colour. In order to get a command colour from a hue in the range of 0-360 you first need to rotate the colour by 110° and invert it. Afterwards, the value between 0 and 360 gets scaled to fit into a byte. It looks like this when converted into code:

hue = 360 - ((hue + 110)%360);
uint8_t t = h*255 / 360;
send_command(0x40, t);

The above code assumes that hue is an int variable in the range between 0 and 360.

Discovering bridges in the local network

To discover bridges in the local network you can send a UDP broadcast packet with "Link_Wi-Fi" as content to the port 48899. All bridges in the network will reply with a UDP packet in the format "<ip>,<mac>,".