What is in a LIFX message

The LIFX protocol is a binary messaging protocol that is used to communicate with LIFX devices, sending them instructions or querying them for state.

LIFX protocol messages are structured to fit into a single UDP packet. Each LIFX protocol message has two components concatenated together: a header and a payload. The header contains metadata common to all messages and describes the type of payload to follow; the payload provides the data for the specific action being requested. Sometimes the action requires no data, in which case the payload will be 0 bytes long.

LIFX protocol messages are often used in pairs. For querying state, one would send the device a Get message, and the device would respond to the originating IP address with the corresponding State message (e.g. LightGet and LightState). For changing state, one would send the device a Set message, and the device would perform the change, optionally respond with a Device Acknowledgement message, and respond with a State message (e.g. DeviceSetPower, Acknowledgement, DeviceStatePower).

Header

The header portion of a LIFX header contains 3 components:

  • Frame Header (8 bytes)
  • Frame Address (16 bytes)
  • Protocol Header (12 bytes)

Any fields named reserved should be set to all 0s.

πŸ“˜

Byte order

Numeric data-type byte-order is little-endian, which means that multi-byte fields will start with the least significant byte.

Frame Header

The Frame header contains information about the following:

  • Size of the entire message
  • LIFX Protocol number: must be 1024 (decimal)
  • Use of the Frame Address target field
  • Source identifier
FieldBit Size0-Indexed LocationTypeDescription
size16Lower octet: Byte 0.
Upper octet: Byte 1.
Uint16Size of entire message in bytes including this field
protocol12Lower octet: Byte 2.
Upper 4 bits: Byte 3, Bits 0-3.
Uint16Protocol number: must be 1024 (decimal)
addressable1Byte 3, Bit 4.BoolMessage includes a target address: must be one (1)
tagged1Byte 3, Bit 5.BoolDetermines usage of the Frame Address target field
origin2Byte 3, Bits 6-7.Uint8Message origin indicator: must be zero (0)
source32Lowest octet: Byte 4.
Highest octet: Byte 7.
Uint32Source identifier: unique value set by the client, used by responses

The tagged field is a boolean flag that indicates whether the Frame Address target field is being used to address an individual device or all devices. When you broadcast a message to the network (i.e. broadcasting a GetService (2) for discovery) you should set this field to 1 and for all other messages you should set this to 0.

The source identifier allows each client to provide a unique value, which will be included in the corresponding field in Acknowledgement (45) and State packets the device sends back to you.

Due to the behavior of some older versions of firmware you should set this value to anything other than zero or one. In some versions of the firmware a source value of 1 will be ignored. If you set source to 0 then the device may broadcast the reply on port 56700 which can be received by all clients on the same subnet and may not be the port on which your client is listening for replies.

Frame Address

The Frame Address section contains the following routing information:

  • Target address
  • A flag to say if an Acknowledgement (45) is required
  • A flag to say if a State response message is required
  • Message sequence number
FieldBits Size0-Indexed LocationTypeDescription
target64Bytes 8 - 15.8 Uint8 integers6 byte device serial number or zero (0) means all devices. The last two bytes should always be 0 bytes.
reserved48Bytes 16 - 21.6 Reserved bytesReserved
res_required1Byte 22, Bit 0.BoolState message required.
However, Get messages will send State messages anyway
and State messages to Set messages are usually not useful.
ack_required1Byte 22, Bit 1.BoolAcknowledgement message required
reserved6Byte 22, Bits 2-7.6 Reserved bitsReserved
sequence8Byte 23.Uint8Wrap around message sequence number

The target field represents a device serial number and appears as a hex number of the form d073d5xxxxxx. When you address a device you should left-justify the first 6 bytes of the target field with the serial number and then zero-fill the last two bytes. You should set this value to all zero's if you want to broadcast a message to the network.

The replies you get back from devices will always contain the serial number of the device sending the reply. For example, if you are discovering devices, the StateService (3) message will tell you the serial number for each LIFX device on your network.

There are two flags that configure what replies you get from a LIFX device when you send them a message.

  • ack_required set to one 1 will cause the device to send an Acknowledgement (45) message
  • res_required set to one 1 will make the device send back one or more State messages

It is recommended you set ack_required=1 and res_required=0 when you change the device with a Set message. When you send a Get message it is best to set ack_required=0 and res_required=0, because these messages trigger an implicit State response. Note that when you ask for a response with a Set message that changes the visual state of the device, you will get the old values in the State message sent back.

The sequence number allows the client to provide a unique value, which will be included by the LIFX device in any message that is sent in response to a message sent by the client. This allows the client to distinguish between different messages sent with the same source identifier in the Frame Header. We recommend that your program has one source value and keeps incrementing sequence per device for each message you send. Once sequence reaches the maximum value of 255 for that device, roll it back to 0 and keep incrementing from there.

You can associate replies to their corresponding requests by matching the (source, sequence, target) values from the request and response packets.

Protocol Header

The Protocol header allows you to specify which message type you are sending to the device.

FieldBit Size0-Indexed LocationTypeDescription
reserved64Lowest octet: Byte 24.
Highest octet: Byte 31.
8 Reserved bytesReserved
type16Lower octet: Byte 32.
Upper octet: Byte 33.
Uint16Message type determines the payload being used
reserved16Bytes 34 - 35.2 Reserved bytesReserved

The type of the message is a unique number assigned to each message type. Corresponding Set/Get/State messages will each have a unique value. This indicates to the client whether there will be a payload and the type of payload.

You may represent the header of a packet with something like:

#pragma pack(push, 1)
typedef struct {
  /* frame */
  uint16_t size;
  uint16_t protocol:12;
  uint8_t  addressable:1;
  uint8_t  tagged:1;
  uint8_t  origin:2;
  uint32_t source;
  /* frame address */
  uint8_t  target[8];
  uint8_t  reserved[6];
  uint8_t  res_required:1;
  uint8_t  ack_required:1;
  uint8_t  :6;
  uint8_t  sequence;
  /* protocol header */
  uint64_t :64;
  uint16_t type;
  uint16_t :16;
  /* variable length payload follows */
} lx_protocol_header_t;
#pragma pack(pop)

Payload

The payload will have different values as determined by the type field in the Protocol Header. We refer to the entire packet as a LIFX message and each message has a different purpose. You may find what messages are available in the messages section of this documentation.