lightify-binary-protocol icon indicating copy to clipboard operation
lightify-binary-protocol copied to clipboard

Documentation of the OSRAM Lightify Binary Protocol for communication between Lightify Gateway and applications

= OSRAM Lightify Binary Protocol Documentation Christoph Engelbert https://github.com/noctarius[@noctarius2k] // Settings: :compat-mode!: :idseperator: - // Aliases: :project-name: OSRAM Lightify Binary Protocol :project-handle: osram-lightify-binary-protocol :toc:

OSRAM link:https://www.osram.com/osram_com/tools-and-services/tools/lightify---smart-connected-light/[Lightify] is a smart home, connected lightning technology. Lights, switches and other paired devices are controlled using the Lightify gateway device. This gateway is an always-on, always-connected device which can be controlled using the official Lightify REST API on the Lightify Cloud. That, however, means that all commands need to be routed through the internet.

The official Lightify app, on the other hand, communicates directly with the gateway and uses a proprietary binary protocol which is not publicly specified.

This document is meant to create a public specification of the known facts about how to discover gateway devices, paired devices and zones/groups, as well as how to control those devices.

== Disclaimer

All information in this document are collected using reverse engineering practices or are available by other implementations / people on the internet. Certain information might be incorrect, outdated or unspecific. Any help completing this document is appreciated, please file pull requests.

Using and/or implementing information in this document might brick your device, render it unfunctional, change behavior of any connected device and/or may void your warranty. Any action taken upon information in this document is strictly at your own risk. The author(s) are not liable for any losses and damages in connection with the use of this document.

The author(s) are not affiliated to OSRAM Light AG in any way. Furthermore is this document not official publicated or approved by OSRAM Licht AG.

== Basics about the Protocol

The OSRAM Lightify gateway protocol is a binary protocol with a specified header.

The underlying protocol is a persistent TCP connection which does not seem to support multiplexing, therefore multiple requests should be sent one after another. To support multithreading, multiple connections should be used.

The TCP protocol works over port 4000 and discovery can be implemented using mDNS (multicast DNS, aka Bonjour). Further information in <<Lightify Gateway Discovery>>.

Multibyte values encoded using little-endian encoding and considered unsigned values.

String values are encoded using UTF-8 encoding and of fixed length. Unused bytes are filled with 0x00 and should be trimmed out to get the real string.

=== Headers

The protocol defines a common header for both requests and responses. The header consists of the packet's length, a packet type and the command id.

Furthermore the header contains a request id which is recommended, but not required, to be monotonly increasing but must wrapping around to startover when surpassing 0xFFFFFFFF. The response will feature the same request id and can be used to correlate responses with requests.

.Packet Header |=== | Byte(s) | Description | Data type

| 0-1 | Length of the packet, excl header length | uint16_t

| 2 | Packet type (flag) | uint8_t: enum { + 0x00: Light Device + 0x01: Device response? + 0x02: Broadcast / Zone Command + 0x03: Zone Response? + }

| 3 | Command Id | uint8_t: See <<Lightify Commands>>

| 4-7 | Unique, increasing request id | uint32_t

|===

Response packets have an additional field in the header, which seems to return a status / error code for the request.

.Response Header Status Code |=== | Byte(s) | Description | Data type

| 8 | Status code? | uint8_t: enum { + 0x00: No error? + 0x01: Wrong (number of) parameter(s)? + 0x14: ? + 0x15: Command is not a broadcast? + 0x16: Resync required (gateway expects the next packet to be <<PACKET_LIST_PAIRED_DEVICES>>) 0xA7: ? + 0x0B: ? + 0xC2: ? + 0xD1: ? + 0xFF: Unknown command? + }

|===

If the packet is not a broadcast (addressing type != 0x02), the device or zone address header is following up right after the header. In case of a broadcast, the header is followed by the commands data. Addressing of specific zones or devices is defined in the following section.

=== Error Response

In case of an illegally addressed packet or any other type of error, an error response packet is returned from the gateway. The packet consists of the header only and the last byte seems to define an error code. See previous table for a list of known? error codes.

== Device Types

The OSRAM Lightify gateway uses the Zigbee Light Link communication protocol, however it is also able to communicate with certain other device types of the Lightify series, such as switches, motion sensors and power plugs / sockets.

A device type is sent with status updates to identify the type of the device as well as the capabilities of a specific device.

.Device Type |=== | Id | Description

| 1 | Bulb: Fixed white, dimmable, non-softswitch

| 2 | Bulb: Tunable white, dimmable, soft-switchable

| 4 | Bulb: Fixed white, dimmable, soft-switchable

| 10 | Bulb: RGB, tunable white, dimmable, soft-switchable

| 16 | Plug / Power socket

| 32 | Motion Sensor

| 64 | Switch (2 switches)

| 65 | Switch (4 switches)

|===

== Device and Zone Addressing

Each paired device has a unique address (MAC). Multiple paired devices can be controlled at once by adding them to zones / groups, which are addressed using the zone's id.

An address always contains 8 byte, no matter it's adressing a device or zone and is directly followed by the command's specific data.

.Addressing Header |=== | Byte(s) | Description | Data type

| 8-15 | Address | uint64_t: See the following specification

| 16-... | Command specific data | See <<Lightify Commands>>

|===

=== Device address

Devices are addressed by, what seems to be, a hardware address, similar to MAC addresses used in networking devices.

.Device Addressing |=== | Byte(s) | Description | Data type

| 0-7 | Device address | uint64_t

|===

While discovering devices the device's address is made known to the application, controlling the gateway, and the paired device can be addressed directly (whereas the command packet is still routed through the gateway).

Attention: Device addresses are transmitted as 8 bytes, not as strings!

=== Zone address

Zones are identified by their zone id. Addressing itself, however, is still using 8 bytes, even if zone ids seem to be limited to 0xFFFF. That said, the addressing is built as following:

.Zone Addressing |=== | Byte | Data type

| 1 | uint8_t: lower significant byte

| 2 | uint8_t: higher significant byte

| 3-7 | uint8_t[6]: 0x00

|===

Attention: Also note, that zone commands have a packet type of 0x02 at byte position 2 in the packet's header.

== Lightify Gateway Discovery

To discover the OSRAM Lightify gateway's IP address, a link:https://en.wikipedia.org/wiki/Multicast_DNS[mDNS (multicast DNS)] request is used. mDNS is also known as Bonjour and is originally developed by Apple.

To find the gateway's address a SSDP lookup request is sent to the UDP broadcast address 224.0.0.251 (IPv4) or FF02::FB (IPv6). The service type to search for is _http._tcp which will find a Lightify device named as Lightify-XXXXXXXX, where XXXXXXXX is a part of the gateway's serial number (S/N: OSRXXXXXXXX-YY) which is also used in the gateway's own SSID (last 6 numbers of the code).

Since more items, especially of other vendors, might be found, the instance name should be tested for starting with Lightify- to make sure only the Lightify gateway is discovered.

According to the search type and the mDNS response, there is supposed to be a HTTP service on port 80, which does not seem to exist. However, the gateway seems to communicate over link:https://en.wikipedia.org/wiki/QUIC[QUIC] to the OSRAM servers, so maybe the port 80 is also available using QUIC.

After discovering the gateway's IP address, the communication port to use the described protocol is TCP/4000.

Lightify devices and zones will be discovered using the gateway binary protocol, using tge commands <<PACKET_LIST_PAIRED_DEVICES>> and <<PACKET_LIST_ZONES>>.

== Lightify Commands

Lightify commands are either used for broadcasts, like device or zone discovery, or contain information to control a specfic device or zone.

The following table is most probably incomplete and more commands are available. Response packets often follow a very similar scheme, therefore it should be easy to find new packets and analyze their content.

Known command ids are put into the following list:

.Commands |=== | Command Id | Description | Addressing | Packet Definition

| 0x02 | Unknown, 1 byte data => no error | BROADCAST? | ???

| 0x0A | Unknown, byte data => no error | BROADCAST? | ???

| 0x0B | Unknown, 1 byte data => error 0x01 | BROADCAST? | ???

| 0x13 | List paired devices | BROADCAST | <<PACKET_LIST_PAIRED_DEVICES>>

| 0x15 | Unknown, 1 byte data => no error | BROADCAST? | ???

| 0x16 | Unknown, error code 15 (wrong addressing) | ZONE?, DEVICE? | ???

| 0x1C | Unknown, 1 byte data => error 0x0B, 0x19 | BROADCAST? | ???

| 0x1D | Unknown, 1 byte data => no error | BROADCAST? | ???

| 0x1E | List configured zones | BROADCAST | <<PACKET_LIST_ZONES>>

| 0x1F | List defined scenes | BROADCAST | <<PACKET_LIST_SCENES>>

| 0x20 | Add Device to Zone | DEVICE? | ???

| 0x21 | Remove Device from Zone | DEVICE? | ???

| 0x26 | Get Zone information | ZONE | <<PACKET_GET_ZONE_INFO>>

| 0x27 | Set Zone name | ZONE? | ???

| 0x28 | Set Device name | DEVICE? | ???

| 0x29 | Unknown, 1 byte data => ~1k bytes returned (all zero) | BROADCAST? | ???

| 0x31 | Set luminance of light or zone | ZONE, DEVICE | <<PACKET_SET_LUMINANCE>>

| 0x32 | Set power switch on/off (also set default???) | ZONE, DEVICE | <<PACKET_SET_SWITCH>>

| 0x33 | Set white light temperature | ZONE, DEVICE | <<PACKET_SET_TEMPERATURE>>

| 0x34 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x36 | Set light color (RGB) | ZONE, DEVICE | <<PACKET_SET_COLOR>>

| 0x37 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x38 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x51 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x52 | Activate scene | BROADCAST | <<PACKET_ACTIVATE_SCENE>>

| 0x53 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x54 | Unknown, returned actual data (uint16_t(0,0)) | BROADCAST? | ???

| 0x55 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x56 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x57 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x58 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x61 | Unknown, retured unknown error code 0xD1, 0xC2 | BROADCAST? | ???

| 0x62 | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0x63 | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0x64 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x66 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x67 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x68 | Get device information | DEVICE | <<PACKET_GET_DEVICE_INFO>>

| 0x6A | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0x6B | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0x6D | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0x6F | Gateway Firmware version | BROADCAST | <<PACKET_GET_GATEWAY_FIRMWARE_VERSION>>

| 0x70 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x71 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x76 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x79 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x7A | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0x7B | Unknown, 1 byte data => no error | BROADCAST? | ???

| 0x7C | Unknown, 1 byte data => wrong addressing | ZONE?, DEVICE? | ???

| 0x7D | Unknown, retured unknown error code 0x16 - no return with data, maybe firmware update? | ??? | ???

| 0x91 | Unknown, retured unknown error code 0xA7, 0xC2 | ??? | ???

| 0xC0 | Unknown, no error | BROADCAST? | ???

| 0xC1 | Unknown, no error | BROADCAST? | ???

| 0xC3 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xC4 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xC6 | Unknown, no error | BROADCAST? | ???

| 0xC7 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xC8 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xD0 | Unknown, retured unknown error code 0xD1 | ??? | ???

| 0xD1 | Unknown, no response, system reset? | ??? | ???

| 0xD2 | Unknown, 1 byte data => 0xD1, crashed? needs restart | ??? | ???

| 0xD3 | Unknown, no answer (0x00), firmware update or more data? | ??? | ???

| 0xD4 | Unknown, no answer (0x00), firmware update or more data? | ??? | ???

| 0xD5 | Set Color Wheel? HSL? | ZONE?, DEVICE? | ???

| 0xD6 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xD8 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xD9 | Unknown, wrong addressing (scene builder???) | ZONE?, DEVICE? | ???

| 0xDA | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xDB | Soft on, wrong addressing | ZONE?, DEVICE? | ???

| 0xDC | Soft off, wrong addressing | ZONE?, DEVICE? | ???

| 0xDD | Remove all scenes and groups | ZONE?, BROADCAST? | ???

| 0xD0 | Unknown, no error (0x00) | BROADCAST? | ???

| 0xE1 | Unknown, wrong addressing | ZONE?, DEVICE? | ???

| 0xE2 | Unknown, no answer | BROADCAST? | ???

| 0xE3 | Get / Scan / Set Wifi Configuration | BROADCAST | <<PACKET_GET_WIFI_CONFIGURATION>>

| 0xE4 | Unknown, activates light (with 1 byte data) - seems to reset the light | BROADCAST? | ???

| 0xE5 | Unknown, returned actual data (uint64_t(1,0,0,0)) | BROADCAST? | ???

| 0xE6 | Unknown, returned actual data (uint16_t(1,15)) | BROADCAST? | ???

| 0xE6 | Unknown, returned actual data (uint16_t(1,15)) | BROADCAST? | ???

| 0xE7 | Unknown, no answer | BROADCAST? | ???

| 0xE8 | Unknown, retured unknown error code 0x16 | BROADCAST? | ???

| 0xE9 | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

| 0xEA | Unknown, retured unknown error code 0xD1 | BROADCAST? | ???

|===

As visible from the list, a lot of command ids seem either unused or, what is more presumable, unknown at the current point in time.

== Command data

Most commands carry additional information starting after the header (for broadcast packets) or after the addressing header (non-broadcast packets).

The following sections define the packet's structure after either of both headers, according to the command type.

=== PACKET_LIST_PAIRED_DEVICES

Returns a list of all paired devices.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Unknown | uint8_t: always? 0x01

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Number of devices | uint16_t

| ...50 bytes each device | Device status information | See following table

|===

.Device status information |=== | Byte(s) | Description | Data type

| 0-1 | Device id? | uint16_t

| 2-9 | Device address | uint64_t: See <<Device address>>

| 10 | Device type? | uint8_t: See <<Device Types>>

| 11-14 | Firmware version | uint8_t[5]: Translation into firmware version string -> + {%02d, uint8_t[0]}+{%02d, uint8_t[1]}+{%02d, uint8_t[2]}+uint8_t[3]

| 15 | Reachable | uint8_t: 0x00 online, 0xFF offline

| 16-17 | Zone Id | uint16_t

| 18 | Power switch status | uint8_t: bool

| 19 | Luminance value / Battery value (Motion Detector) | uint8_t

| 20-21 | Temparature value (in Kelvin) | uint16_t: 2,000 >= x <= 6,500

| 22 | Red value / Enabled value (Motion Detector) | uint8_t

| 23 | Green value / Triggered value (Motion Detector) | uint8_t

| 24 | Blue value | uint8_t

| 25 | Alpha value | uint8_t: always? 0xFF

| 26-41 | Device name | uint8_t[16]: UTF-8 encoded, zero terminated string

| 42-45 | Time since last seen by gateway | uint32_t

| 46-49 | Joining? | uint32_t

|===

=== PACKET_LIST_ZONES

Returns a list of all configured zones.

.Request data |=== | Byte(s) | Description | Data type

| -

No additional information to send

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Number of zones | uint16_t

| ...18 bytes each zone | Zone information | See following table

|===

.Zone information |=== | Byte(s) | Description | Data type

| 0-1 | Zone id | uint16_t

| 2-17 | Zone name | uint8_t[16]: UTF-8 encoded, zero terminated string

|===

Assigned devices need to be discovered using <<PACKET_GET_ZONE_INFO>> after the zone id has been seen with this packet.

=== PACKET_LIST_SCENES ===

Returns a list of defined scenes.

.Request data |=== | Byte(s) | Description | Data type

| -

No additional information to send

|===

.Response data |=== | Byte(s) | Description | Data type

| 0-1 | Number of Scenes | uint16_t

| ...20 bytes each scene | Scene information | See following table

|===

.Scene information |=== | Byte(s) | Description | Data type

| 0 | Scene id | uint8_t

| 1 | Unknown, seems to always be 0x64 (@-sign) | uint8_t

| 2-17 | Scene name | uint8_t[16]: UTF-8 encoded, zero terminated string

| 18-19 | Unknown | uint16_t

| 20 | Next Scene id (chaining scenes?) | uint8_t

|===

=== PACKET_GET_ZONE_INFO

Returns information about the requested zone, including assigned devices.

.Request data |=== | Byte(s) | Description | Data type

| -

No additional information to send

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Zone id | uint32_t

| 11-27 | Zone name | uint8_t[16]: UTF-8 encoded, zero terminated string

| 28 | Number of assigned devices | uint8_t

| ...8 bytes each device | Device addresses | See <<Device address>>

|===

=== PACKET_SET_LUMINANCE

Sets the luminance value of the addressed device or zone.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Luminance value | uint8_t

| 17-18 | Transition time in millis | uint16_t

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Device or zone id | uint16_t

| 11-18 | Device or zone address | uint64_t: See <<Device and Zone Addressing>>

|===

=== PACKET_SET_SWITCH

Sets the power switch state of the addressed device or zone.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Power switch state | uint8_t: bool

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | WRONG:Device or zone id??? Number of devices changed? | uint16_t

| 11-18 | Device or zone address | uint64_t: See <<Device and Zone Addressing>>

|===

=== PACKET_SET_TEMPERATURE

Sets the white light temperature of the addressed device or zone between 2,000 and 6,500 Kelvin.

.Request data |=== | Byte(s) | Description | Data type

| 16 | White light temperature | uint16_t: 2,000 >= x <= 6,500

| 17-18 | Transition time in millis | uint16_t

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Device or zone id | uint16_t

| 11-18 | Device or zone address | uint64_t: See <<Device and Zone Addressing>>

|===

=== PACKET_SET_COLOR

Sets the RGB color value of the addressed device or zone.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Red value | uint8_t

| 17 | Green value | uint8_t

| 18 | Blue value | uint8_t

| 19 | Alpha value | uint8_t: always 0xFF?

| 20-21 | Transition time in millis | uint16_t

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Device or zone id | uint16_t

| 11-18 | Device or zone address | uint64_t: See <<Device and Zone Addressing>>

|===

=== PACKET_ACTIVATE_SCENE

Activates a predefined scene on the addressed device or zone.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Scene id | uint8_t: 0x00 to 0x0F for the different predefined scenes

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Device or zone id | uint16_t

| 11-18 | Device or zone address | uint64_t: See <<Device and Zone Addressing>>

|===

=== PACKET_GET_DEVICE_INFO

Returns information about the requested device.

.Request data |=== | Byte(s) | Description | Possible values

| -

No additional information to send

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-10 | Device id? | uint16_t

| 11-18 | Device address | uint64_t: See <<Device address>>

| 19 | Reachable | uint8_t: 0x00 online, 0xFF offline

| 20 | Unknown | uint8_t: ???

| 21 | Power switch status | uint8_t: bool

| 22 | Luminance value / Battery value (Motion Detector) | uint8_t

| 23-24 | Temparature value (in Kelvin) | uint16_t: 2,000 >= x <= 6,500

| 25 | Red value / Enabled value (Motion Detector) | uint8_t

| 26 | Green value / Triggered value (Motion Detector) | uint8_t

| 27 | Blue value | uint8_t

| 28 | Alpha value | uint8_t: always 0xFF?

| 29-31 | Unknown | uint8_t[3]: ???

|===

If the response packet's byte at index 19 is 0xFF (the device is offline / non-reachable) the packet is only those 20 bytes, otherwise the full packet comes back.

=== PACKET_GET_WIFI_CONFIGURATION

Retrieves or configures the wifi configuration.

.Request data |=== | Byte(s) | Description | Data type

| 16 | Subcommand | uint8_t: enum { + 0x00: Get wifi configuration + 0x01: Set wifi configuration + 0x03: Scan wifi configuration + }

|===

.Response data |=== | Byte(s) | Description | Data type

| 9 | Number of profiles | uint8_t

| ...97 bytes each profile | Profile information | See following table

|===

.Profile information |=== | Byte(s) | Description | Data type

| 0-31 | Profile Name | uint8_t[32]: UTF-8 encoded, zero terminated string

| 32-64 | SSID | uint8_t[33]: UTF-8 encoded, zero terminated string

| 65-70 | BSSID | uint8_t[6]: UTF-8 encoded, zero terminated string

| 71-74 | Channel | uint32_t

| 75-76 | Unknown | uint16_t: ???

| 77-80 | IP Address | uint64_t: 4 bytes of IP address

| 81-84 | Gateway | uint64_t: 4 bytes of IP address

| 85-88 | Netmask | uint64_t: 4 bytes of IP address

| 89-92 | DNS #1 | uint64_t: 4 bytes of IP address

| 93-96 | DNS #2 | uint64_t: 4 bytes of IP address

|===

=== PACKET_GET_GATEWAY_FIRMWARE_VERSION

Retrieves the current firmware version of the gateway.

.Request data |=== | Byte(s) | Description | Data type

| -

No additional information to send

|===

.Response data |=== | Byte(s) | Description | Data type

| 9-12 | Firmware version | uint8_t[4]: Translation into firmware version string -> + {%02d, uint8_t[0]}+{%02d, uint8_t[1]}+{%02d, uint8_t[2]}+{%02d, uint8_t[3]}

|===