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

Field

Bit Size

0-Indexed Location

Type

Description

size

16

Lower octet: Byte 0.
Upper octet: Byte 1.

Uint16

Size of entire message in bytes including this field

protocol

12

Lower octet: Byte 2.
Upper 4 bits: Byte 3, Bits 0-3.

Uint16

Protocol number: must be 1024 (decimal)

addressable

1

Byte 3, Bit 4.

Bool

Message includes a target address: must be one (1)

tagged

1

Byte 3, Bit 5.

Bool

Determines usage of the Frame Address target field

origin

2

Byte 3, Bits 6-7.

Uint8

Message origin indicator: must be zero (0)

source

32

Lowest octet: Byte 4.
Highest octet: Byte 7.

Uint32

Source 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 device 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

Field

Bits Size

0-Indexed Location

Type

Description

target

64

Bytes 8 - 15.

8 Uint8 integers

6 byte device address (MAC address) or zero (0) means all devices. The last two bytes should be 0 bytes.

reserved

48

Bytes 16 - 21.

6 Reserved bytes

Reserved

res_required

1

Byte 22, Bit 0.

Bool

State message required.
However, Get messages will send State messages anyway
and State messages to Set messages are usually not useful.

ack_required

1

Byte 22, Bit 1.

Bool

Acknowledgement message required

reserved

6

Byte 22, Bits 2-7.

6 Reserved bits

Reserved

sequence

8

Byte 23.

Uint8

Wrap around message sequence number

The target field represents the MAC address of the device. This is also known as the device's serial 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 MAC address 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 MAC address of the device sending the reply. So for example if you are discovering devices, the StateService (3) message will tell you the serial 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.

Field

Bit Size

0-Indexed Location

Type

Description

reserved

64

Lowest octet: Byte 24.
Highest octet: Byte 31.

8 Reserved bytes

Reserved

type

16

Lower octet: Byte 32.
Upper octet: Byte 33.

Uint16

Message type determines the payload being used

reserved

16

Bytes 34 - 35.

2 Reserved bytes

Reserved

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.