Milight Bridge problems and protocol

Jan 6 2021

While building my smart-home solution, one of the objectives was to integrate existing Milight lamps. Those lamps are similar to Philips Hue, however, those lamps are a lot cheaper and are available in a wider range of sockets. They are connected to the home wireless network 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 researched, which makes it hard to develop a good integration for it. In addition the protocol used for communicating with the bridge suffers from a number of issues.

  1. No confirmation for commands
  2. No error detection between bridge and lamp
  3. No option for choosing a group when setting either brightness or color
  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 if a command was received, which makes it hard to detect lost packets. You can reduce the impact by changing the protocol from UDP to TCP in the controllers webui. This ensures that commands received in the correct order and allow for crude 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.

It seems like there is no error detection or correction in the wireless protocol between bridge and lamp, which results in some commands getting mangled in transit. The only option to reduce this problem is by resending all commands at regular intervals and accept that there might be some short intervals of wrong colors being displayed.

The commands for "on", "off" and choosing white as the color 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.

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, which is 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 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. This socket can then be used to transmit commands which consist of three bytes each.

Command Byte 1 Byte 2 Byte 3
Change color 0x40 <color> 0x55
Change brightness 0x4e <brightness> 0x55
Group 1 on 0x45 0x00 0x55
Group 2 on 0x47 0x00 0x55
Group 3 on 0x49 0x00 0x55
Group 4 on 0x4B 0x00 0x55
Group 1 off 0x46 0x00 0x55
Group 2 off 0x48 0x00 0x55
Group 3 off 0x4A 0x00 0x55
Group 4 off 0x4C 0x00 0x55
Group 1 white 0xC5 0x00 0x55
Group 2 white 0xC7 0x00 0x55
Group 3 white 0xC9 0x00 0x55
Group 4 white 0xCB 0x00 0x55

It is possible to control the brightness of a lamp in 24 steps with a value between 0x02 and 0x18.

The color value can be calculated from the hue component of an HSV color. In order to get a command color from a hue in the range of 0-360 you first need to rotate the color 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>,".