Skip to content

Protocol API Reference

LIFX LAN Protocol implementation - packet types, headers, and serialization

The protocol module provides a complete implementation of the LIFX LAN binary protocol, including packet definitions, header parsing, type serialization, and packet registry management.


Table of Contents

Core Components

Concepts


LifxHeader

The LIFX protocol header is a 36-byte structure that precedes every packet payload.

Structure

@dataclass
class LifxHeader:
    # Class constants
    HEADER_SIZE: ClassVar[int] = 36
    PROTOCOL_NUMBER: ClassVar[int] = 1024
    ORIGIN: ClassVar[int] = 0        # Message origin (always 0)
    ADDRESSABLE: ClassVar[int] = 1   # Addressable flag (always 1)

    # Frame
    size: int = 0                # Total packet size (header + payload)
    protocol: int = 1024         # Protocol number (must be 1024)
    source: int = 0              # Unique client identifier
    tagged: bool = False         # Broadcast flag (True = all devices)

    # Frame Address
    target: bytes = b'\x00' * 8  # 6-byte serial + 2 null bytes
    ack_required: bool = False   # Request acknowledgment
    res_required: bool = False   # Request response
    sequence: int = 0            # Message sequence number (0-255)
    thread_connection: bool = False  # Flags bit 3: set by Thread devices on replies

    # Protocol Header
    pkt_type: int = 0            # Packet type number (e.g., 2, 101, 116)

Key Fields

tagged (bool)

  • True: Broadcast packet - sent to all devices on network
  • False: Unicast packet - sent to specific device (requires valid target)

target (bytes)

  • 8-byte field: 6-byte device serial + 2 null bytes
  • Example: b'\xd0\x73\xd5\x00\x00\x01\x00\x00' for serial d073d5000001
  • All zeros (b'\x00' * 8) when tagged=True

ack_required (bool)

  • True: Device must send acknowledgment (packet type 45)
  • False: No acknowledgment needed

res_required (bool)

  • True: Device must send response packet (e.g., Light.StateColor for Light.GetColor)
  • False: No response packet expected

source (int)

  • 32-bit client identifier
  • Used to match responses to requests
  • Should be unique per client instance

sequence (int)

  • 8-bit sequence number (0-255, wraps around)
  • Used to match responses to specific requests
  • Client should increment for each request

pkt_type (int)

  • Identifies the packet payload type
  • Common types: 2 (GetService), 101 (GetColor), 102 (SetColor), 107 (StateColor), etc.

Methods

pack() -> bytes

Serialize header to 36-byte binary format.

Returns: bytes - 36-byte header

Example:

from lifx_emulator.protocol.header import LifxHeader

header = LifxHeader(
    size=36,
    source=12345,
    target=bytes.fromhex("d073d5000001") + b'\x00\x00',
    res_required=True,
    sequence=1,
    pkt_type=101,  # Light.GetColor
)
raw_header = header.pack()
# Returns: 36 bytes

unpack(data: bytes) -> LifxHeader

Parse 36 bytes into a LifxHeader object.

Parameters: - data (bytes) - 36-byte header data

Returns: LifxHeader - Parsed header object

Example:

from lifx_emulator.protocol.header import LifxHeader

raw_header = sock.recv(36)
header = LifxHeader.unpack(raw_header)
print(f"Packet type: {header.pkt_type}")
print(f"Source: {header.source}")
print(f"Target: {header.target.hex()}")


Packet Types

The protocol module provides classes for all LIFX protocol packets, organized into namespaces:

Packet Organization

from lifx_emulator.protocol.packets import Button, Device, Light, MultiZone, Sensor, Tile

# Device discovery and information
Device.GetService             # Type 2
Device.StateService           # Type 3
Device.GetHostFirmware        # Type 14
Device.StateHostFirmware      # Type 15
Device.GetVersion             # Type 32
Device.StateVersion           # Type 33
Device.GetLocation            # Type 48
Device.StateLocation          # Type 50
Device.GetGroup               # Type 51
Device.StateGroup             # Type 53
Device.Acknowledgement        # Type 45
Device.EchoRequest            # Type 58
Device.EchoResponse           # Type 59
Device.StateUnhandled         # Type 223

# Light control (all color-capable devices)
Light.GetColor                # Type 101
Light.SetColor                # Type 102
Light.SetWaveform             # Type 103
Light.StateColor              # Type 107
Light.GetPower                # Type 116
Light.SetPower                # Type 117
Light.StatePower              # Type 118
Light.SetWaveformOptional     # Type 119
Light.GetInfrared             # Type 120 (infrared devices only)
Light.StateInfrared           # Type 121
Light.SetInfrared             # Type 122

# HEV (germicidal light)
Light.GetHevCycle                     # Type 142
Light.SetHevCycle                     # Type 143
Light.StateHevCycle                   # Type 144
Light.GetHevCycleConfiguration        # Type 145
Light.SetHevCycleConfiguration        # Type 146
Light.StateHevCycleConfiguration      # Type 147
Light.GetLastHevCycleResult           # Type 148
Light.StateLastHevCycleResult         # Type 149

# Multizone control (strips/beams)
MultiZone.SetColorZones       # Type 501
MultiZone.GetColorZones       # Type 502
MultiZone.StateZone           # Type 503
MultiZone.StateMultiZone      # Type 506
MultiZone.GetEffect           # Type 507
MultiZone.SetEffect           # Type 508
MultiZone.StateEffect         # Type 509

# Extended multizone
MultiZone.ExtendedSetColorZones       # Type 510
MultiZone.ExtendedGetColorZones       # Type 511
MultiZone.ExtendedStateMultiZone      # Type 512

# Tile/Matrix control (2D arrangements)
Tile.GetDeviceChain           # Type 701
Tile.StateDeviceChain         # Type 702
Tile.SetUserPosition          # Type 703
Tile.Get64                    # Type 707
Tile.State64                  # Type 711
Tile.Set64                    # Type 715
Tile.CopyFrameBuffer          # Type 716
Tile.GetEffect                # Type 718
Tile.SetEffect                # Type 719
Tile.StateEffect              # Type 720

# Ambient light sensor
Sensor.GetAmbientLight        # Type 401
Sensor.StateAmbientLight      # Type 402

# Buttons (switches)
Button.Get                    # Type 905
Button.Set                    # Type 906
Button.State                  # Type 907
Button.GetConfig              # Type 909
Button.SetConfig              # Type 910
Button.StateConfig            # Type 911

Packet Class Structure

Each packet class is a dataclass with no field defaults, so every field must be passed to the constructor. Each class has:

  • PKT_TYPE: Class constant with packet type number
  • pack(): Serialize to binary format
  • unpack(data): Parse from binary format

Example: Light.SetColor

from lifx_emulator.protocol.packets import Light
from lifx_emulator.protocol.protocol_types import LightHsbk

# Create SetColor packet
packet = Light.SetColor(
    color=LightHsbk(hue=21845, saturation=65535, brightness=32768, kelvin=3500),
    duration=1000,  # Transition time in milliseconds
)

# Serialize to bytes
raw_payload = packet.pack()

# Parse from bytes
received_packet = Light.SetColor.unpack(raw_payload)
print(f"Color: H={received_packet.color.hue} S={received_packet.color.saturation}")
print(f"Duration: {received_packet.duration}ms")

Example: MultiZone.GetColorZones

from lifx_emulator.protocol.packets import MultiZone

# Request zones 0-7
packet = MultiZone.GetColorZones(
    start_index=0,
    end_index=7,
)

raw_payload = packet.pack()

Example: Tile.Get64

from lifx_emulator.protocol.packets import Tile
from lifx_emulator.protocol.protocol_types import TileBufferRect

# Request 8x8 rectangle starting at (0,0) from tile 0
packet = Tile.Get64(
    tile_index=0,
    length=1,      # Number of tiles to read, starting at tile_index
    rect=TileBufferRect(fb_index=0, x=0, y=0, width=8),
)

raw_payload = packet.pack()

Protocol Types

Protocol types are structured data types used within packets.

LightHsbk

HSBK color representation (Hue, Saturation, Brightness, Kelvin).

@dataclass
class LightHsbk:
    hue: int          # 0-65535 (0° to 360°)
    saturation: int   # 0-65535 (0% to 100%)
    brightness: int   # 0-65535 (0% to 100%)
    kelvin: int       # 1500-9000 (color temperature)

Color Conversion: - Hue: degrees * 65535 / 360 - Saturation: percent * 65535 / 100 - Brightness: percent * 65535 / 100 - Kelvin: Absolute value (e.g., 3500 for warm white)

Examples:

from lifx_emulator.protocol.protocol_types import LightHsbk

# Red at 50% brightness
red = LightHsbk(hue=0, saturation=65535, brightness=32768, kelvin=3500)

# Green at full brightness
green = LightHsbk(hue=21845, saturation=65535, brightness=65535, kelvin=3500)

# Blue at 75% brightness
blue = LightHsbk(hue=43690, saturation=65535, brightness=49152, kelvin=3500)

# Warm white (no color)
warm_white = LightHsbk(hue=0, saturation=0, brightness=65535, kelvin=2700)

# Cool white (no color)
cool_white = LightHsbk(hue=0, saturation=0, brightness=65535, kelvin=6500)

TileStateDevice

Tile position and metadata in a matrix chain.

@dataclass
class TileStateDevice:
    accel_meas: TileAccelMeas               # Accelerometer reading (x, y, z)
    user_x: float                           # User-configured X position
    user_y: float                           # User-configured Y position
    width: int                              # Tile width in zones (e.g., 8)
    height: int                             # Tile height in zones (e.g., 8)
    device_version: DeviceStateVersion      # vendor, product
    firmware: DeviceStateHostFirmware       # build, version_minor, version_major

Enums

The protocol defines several enums for packet fields:

from lifx_emulator.protocol.protocol_types import (
    DeviceService,
    LightWaveform,
    LightLastHevCycleResult,
    MultiZoneEffectType,
    MultiZoneApplicationRequest,
    TileEffectType,
)

# Service types
DeviceService.UDP                          # 1

# Waveform types for Light.SetWaveform
LightWaveform.SAW                          # 0
LightWaveform.SINE                         # 1
LightWaveform.HALF_SINE                    # 2
LightWaveform.TRIANGLE                     # 3
LightWaveform.PULSE                        # 4

# Multizone effects
MultiZoneEffectType.OFF                    # 0
MultiZoneEffectType.MOVE                   # 1

# Tile effects
TileEffectType.OFF                         # 0
TileEffectType.MORPH                       # 2
TileEffectType.FLAME                       # 3
TileEffectType.SKY                         # 5

Packet Registry

The PACKET_REGISTRY maps packet type numbers to packet classes.

Usage

from lifx_emulator.protocol.header import LifxHeader
from lifx_emulator.protocol.packets import PACKET_REGISTRY, get_packet_class

# Get packet class by type number
packet_class = PACKET_REGISTRY[102]  # Light.SetColor
print(packet_class.PKT_TYPE)  # 102

# Or use the helper function
packet_class = get_packet_class(102)

# Parse unknown packet type
data = sock.recv(1024)
header = LifxHeader.unpack(data[:36])
raw_payload = data[36:]
packet_class = get_packet_class(header.pkt_type)
if packet_class:
    packet = packet_class.unpack(raw_payload)
else:
    print(f"Unknown packet type: {header.pkt_type}")

Complete Packet Type List

Type Packet Description
2 Device.GetService Device discovery request
3 Device.StateService Device discovery response
14 Device.GetHostFirmware Get host firmware
15 Device.StateHostFirmware Host firmware response
16 Device.GetWifiInfo Get WiFi info
17 Device.StateWifiInfo WiFi info response
18 Device.GetWifiFirmware Get WiFi firmware
19 Device.StateWifiFirmware WiFi firmware response
20 Device.GetPower Get device power
21 Device.SetPower Set device power
22 Device.StatePower Device power response
23 Device.GetLabel Get device label
24 Device.SetLabel Set device label
25 Device.StateLabel Device label response
32 Device.GetVersion Get firmware version
33 Device.StateVersion Firmware version response
34 Device.GetInfo Get device info
35 Device.StateInfo Device info response
38 Device.SetReboot Reboot device
45 Device.Acknowledgement Acknowledgment response
48 Device.GetLocation Get location
49 Device.SetLocation Set location
50 Device.StateLocation Location response
51 Device.GetGroup Get group
52 Device.SetGroup Set group
53 Device.StateGroup Group response
58 Device.EchoRequest Echo request
59 Device.EchoResponse Echo response
101 Light.GetColor Get light state
102 Light.SetColor Set color
103 Light.SetWaveform Set waveform effect
107 Light.StateColor Light state response (color, power, label)
116 Light.GetPower Get light power
117 Light.SetPower Set light power
118 Light.StatePower Light power response
119 Light.SetWaveformOptional Set waveform effect (optional components)
120 Light.GetInfrared Get IR brightness
121 Light.StateInfrared IR brightness response
122 Light.SetInfrared Set IR brightness
142 Light.GetHevCycle Get HEV cycle
143 Light.SetHevCycle Set HEV cycle
144 Light.StateHevCycle HEV cycle response
145 Light.GetHevCycleConfiguration Get HEV cycle configuration
146 Light.SetHevCycleConfiguration Set HEV cycle configuration
147 Light.StateHevCycleConfiguration HEV cycle configuration response
148 Light.GetLastHevCycleResult Get last HEV cycle result
149 Light.StateLastHevCycleResult Last HEV cycle result response
223 Device.StateUnhandled Unsupported packet response
401 Sensor.GetAmbientLight Get ambient light level
402 Sensor.StateAmbientLight Ambient light level response
501 MultiZone.SetColorZones Set zone colors
502 MultiZone.GetColorZones Get zone colors
503 MultiZone.StateZone Single zone response
506 MultiZone.StateMultiZone Multiple zones response
507 MultiZone.GetEffect Get zone effect
508 MultiZone.SetEffect Set zone effect
509 MultiZone.StateEffect Zone effect response
510 MultiZone.ExtendedSetColorZones Set extended zones
511 MultiZone.ExtendedGetColorZones Get extended zones
512 MultiZone.ExtendedStateMultiZone Extended zones response
701 Tile.GetDeviceChain Get tile chain info
702 Tile.StateDeviceChain Tile chain response
703 Tile.SetUserPosition Set tile position
707 Tile.Get64 Get tile colors (64 zones)
711 Tile.State64 Tile colors response
715 Tile.Set64 Set tile colors
716 Tile.CopyFrameBuffer Copy between frame buffers
718 Tile.GetEffect Get tile effect
719 Tile.SetEffect Set tile effect
720 Tile.StateEffect Tile effect response
905 Button.Get Get button state
906 Button.Set Set button state
907 Button.State Button state response
909 Button.GetConfig Get button configuration
910 Button.SetConfig Set button configuration
911 Button.StateConfig Button configuration response

Binary Format Overview

Packet Structure

Every LIFX packet consists of:

  1. Header (36 bytes) - See LifxHeader
  2. Payload (variable length) - Packet-specific data
┌─────────────────────────────────────┐
│         Header (36 bytes)           │
│  - Frame (8 bytes)                  │
│  - Frame Address (16 bytes)         │
│  - Protocol Header (12 bytes)       │
├─────────────────────────────────────┤
│         Payload (variable)          │
│  - Packet-specific fields           │
└─────────────────────────────────────┘

Header Binary Layout

Bytes 0-1:   Size (uint16, little-endian)
Bytes 2-3:   Protocol/Origin/Tagged/Addressable (bitfield)
Bytes 4-7:   Source (uint32, little-endian)
Bytes 8-15:  Target (6-byte serial + 2 reserved bytes)
Bytes 16-21: Reserved
Byte  22:    Flags: bit 0 res_required, bit 1 ack_required, bit 3 thread_connection
Byte  23:    Sequence (uint8)
Bytes 24-31: Reserved
Bytes 32-33: Packet type (uint16, little-endian)
Bytes 34-35: Reserved

Payload Encoding

Payload fields are serialized using little-endian byte order:

  • Integers: uint8, uint16, uint32, uint64
  • Floats: float32 (IEEE 754)
  • Booleans: uint8 (0=False, 1=True)
  • Strings: UTF-8 encoded, null-padded to fixed length
  • Bytes: Raw byte arrays
  • Nested types: Recursively serialized structures
  • Arrays: Consecutive serialized elements

Serialization and Deserialization

Packing (Python → Binary)

from lifx_emulator.protocol.header import LifxHeader
from lifx_emulator.protocol.packets import Light
from lifx_emulator.protocol.protocol_types import LightHsbk

# Create header
header = LifxHeader(
    size=0,  # Will be calculated
    source=12345,
    target=bytes.fromhex("d073d5000001") + b'\x00\x00',
    res_required=True,
    sequence=1,
    pkt_type=102,  # SetColor
)

# Create packet
packet = Light.SetColor(
    color=LightHsbk(hue=21845, saturation=65535, brightness=32768, kelvin=3500),
    duration=1000,  # Transition time in milliseconds
)

# Pack to binary
payload = packet.pack()
header.size = 36 + len(payload)
header_bytes = header.pack()

# Send over UDP
full_packet = header_bytes + payload
sock.sendto(full_packet, (device_ip, 56700))

Unpacking (Binary → Python)

from lifx_emulator.protocol.header import LifxHeader
from lifx_emulator.protocol.packets import get_packet_class

# Receive packet
data, addr = sock.recvfrom(1024)

# Parse header
header = LifxHeader.unpack(data[:36])
print(f"Received packet type: {header.pkt_type}")

# Parse payload
packet_class = get_packet_class(header.pkt_type)
if packet_class:
    payload_data = data[36:]
    packet = packet_class.unpack(payload_data)
    print(f"Packet: {packet}")
else:
    print(f"Unknown packet type: {header.pkt_type}")

Working with Packets

Complete Request/Response Example

import socket
from lifx_emulator.protocol.header import LifxHeader
from lifx_emulator.protocol.packets import Light
from lifx_emulator.protocol.protocol_types import LightHsbk

# Create UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)

# Build GetService request (discovery)
header = LifxHeader(
    size=36,
    tagged=True,  # Broadcast
    source=12345,
    res_required=True,
    sequence=1,
    pkt_type=2,  # GetService
)

# Send discovery
sock.sendto(header.pack(), ('<broadcast>', 56700))

# Receive StateService response
data, addr = sock.recvfrom(1024)
resp_header = LifxHeader.unpack(data[:36])
print(f"Device found at {addr}: serial={resp_header.target[:6].hex()}")

# Get light state
header = LifxHeader(
    size=36,
    source=12345,
    target=resp_header.target,
    res_required=True,
    sequence=2,
    pkt_type=101,  # Light.GetColor
)
sock.sendto(header.pack(), addr)

# Receive Light.StateColor (107) response
data, addr = sock.recvfrom(1024)
resp_header = LifxHeader.unpack(data[:36])
state = Light.StateColor.unpack(data[36:])
print(f"Color: H={state.color.hue} S={state.color.saturation} B={state.color.brightness}")
print(f"Power: {state.power}")
print(f"Label: {state.label}")

sock.close()

Handling Acknowledgments

from lifx_emulator.protocol.header import LifxHeader
from lifx_emulator.protocol.packets import Light

payload = Light.SetPower(level=65535, duration=0).pack()

# Request acknowledgment
header = LifxHeader(
    size=36 + len(payload),
    source=12345,
    target=device_target,
    ack_required=True,  # Request ack
    res_required=False,  # Don't need state response
    sequence=3,
    pkt_type=117,  # SetPower
)

sock.sendto(header.pack() + payload, addr)

# Wait for acknowledgment (type 45)
data, addr = sock.recvfrom(1024)
ack_header = LifxHeader.unpack(data[:36])
if ack_header.pkt_type == 45:
    print("Command acknowledged")

Type Conversion

HSBK to RGB

import colorsys

from lifx_emulator.protocol.protocol_types import LightHsbk

def hsbk_to_rgb(hsbk: LightHsbk) -> tuple[int, int, int]:
    """Convert HSBK to RGB (0-255 range)."""
    h = hsbk.hue / 65535.0  # 0-1
    s = hsbk.saturation / 65535.0  # 0-1
    v = hsbk.brightness / 65535.0  # 0-1

    r, g, b = colorsys.hsv_to_rgb(h, s, v)
    return int(r * 255), int(g * 255), int(b * 255)

RGB to HSBK

import colorsys

from lifx_emulator.protocol.protocol_types import LightHsbk

def rgb_to_hsbk(r: int, g: int, b: int, kelvin: int = 3500) -> LightHsbk:
    """Convert RGB (0-255) to HSBK."""
    h, s, v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)

    return LightHsbk(
        hue=int(h * 65535),
        saturation=int(s * 65535),
        brightness=int(v * 65535),
        kelvin=kelvin,
    )

Percentage Conversions

def percent_to_uint16(percent: float) -> int:
    """Convert percentage (0-100) to uint16 (0-65535)."""
    return int(percent * 65535 / 100)

def uint16_to_percent(value: int) -> float:
    """Convert uint16 (0-65535) to percentage (0-100)."""
    return value * 100 / 65535

References

Source Files: - packages/lifx-emulator-core/src/lifx_emulator/protocol/header.py - Header implementation - packages/lifx-emulator-core/src/lifx_emulator/protocol/packets.py - Packet definitions (auto-generated) - packages/lifx-emulator-core/src/lifx_emulator/protocol/protocol_types.py - Type definitions (auto-generated) - packages/lifx-emulator-core/src/lifx_emulator/protocol/serializer.py - Serialization utilities - packages/lifx-emulator-core/src/lifx_emulator/protocol/generator.py - Code generator from YAML spec

Related Documentation: - Device API - Device state and packet processing - Server API - UDP server implementation - LIFX LAN Protocol Specification - Official protocol docs - Architecture Overview - System architecture

Protocol Specification: - Auto-generated from LIFX public-protocol - Generator: packages/lifx-emulator-core/src/lifx_emulator/protocol/generator.py - Source: protocol.yml from LIFX/public-protocol repository