Skip to content

Factory Functions

Factory functions provide the easiest way to create emulated LIFX devices with sensible defaults.

Overview

All factory functions return an EmulatedLifxDevice instance configured for a specific product type. They automatically load product-specific defaults (like zone counts and tile dimensions) from the product registry.

Device factory for creating emulated LIFX devices.

This package provides a clean, testable API for creating LIFX devices using: - Builder pattern for flexible device construction - Separate services for serial generation, color config, firmware config - Product registry integration for accurate device specifications

FUNCTION DESCRIPTION
create_color_light

Create a regular color light (LIFX Color).

create_color_temperature_light

Create a color temperature light (LIFX Mini White to Warm).

create_infrared_light

Create an infrared-enabled light (LIFX A19 Night Vision).

create_hev_light

Create an HEV-enabled light (LIFX Clean).

create_multizone_light

Create a multizone light (LIFX Beam)

create_tile_device

Create a tile device (LIFX Tile)

create_switch

Create a LIFX Switch device.

create_device

Create a device for any LIFX product using the product registry.

Functions

create_color_light

create_color_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a regular color light (LIFX Color).

PARAMETER DESCRIPTION
serial

Optional serial (auto-generated if not provided)

TYPE: str | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

advertised_services

Optional list of (service_id, port) tuples emitted as one StateService reply each, in order, in response to GetService. Defaults to a single UDP reply on the device's port.

TYPE: list[tuple[int, int]] | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured as a LIFX Color light

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_color_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a regular color light (LIFX Color).

    Args:
        serial: Optional serial (auto-generated if not provided)
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager for testing
        advertised_services: Optional list of (service_id, port) tuples
            emitted as one StateService reply each, in order, in response
            to GetService. Defaults to a single UDP reply on the device's
            port.
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.

    Returns:
        EmulatedLifxDevice configured as a LIFX Color light
    """
    return create_device(
        91,
        serial=serial,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )  # LIFX Color

create_color_temperature_light

create_color_temperature_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a color temperature light (LIFX Mini White to Warm).

Variable color temperature, no RGB.

PARAMETER DESCRIPTION
serial

Optional serial (auto-generated if not provided)

TYPE: str | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

advertised_services

Optional list of (service_id, port) tuples emitted as one StateService reply each, in order, in response to GetService. Defaults to a single UDP reply on the device's port.

TYPE: list[tuple[int, int]] | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured as a LIFX Mini White to Warm light

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_color_temperature_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a color temperature light (LIFX Mini White to Warm).

    Variable color temperature, no RGB.

    Args:
        serial: Optional serial (auto-generated if not provided)
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager for testing
        advertised_services: Optional list of (service_id, port) tuples
            emitted as one StateService reply each, in order, in response
            to GetService. Defaults to a single UDP reply on the device's
            port.
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.

    Returns:
        EmulatedLifxDevice configured as a LIFX Mini White to Warm light
    """
    return create_device(
        50,
        serial=serial,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )  # LIFX Mini White to Warm

create_infrared_light

create_infrared_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create an infrared-enabled light (LIFX A19 Night Vision).

PARAMETER DESCRIPTION
serial

Optional serial (auto-generated if not provided)

TYPE: str | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

advertised_services

Optional list of (service_id, port) tuples emitted as one StateService reply each, in order, in response to GetService. Defaults to a single UDP reply on the device's port.

TYPE: list[tuple[int, int]] | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured as a LIFX A19 Night Vision light

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_infrared_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create an infrared-enabled light (LIFX A19 Night Vision).

    Args:
        serial: Optional serial (auto-generated if not provided)
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager for testing
        advertised_services: Optional list of (service_id, port) tuples
            emitted as one StateService reply each, in order, in response
            to GetService. Defaults to a single UDP reply on the device's
            port.
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.

    Returns:
        EmulatedLifxDevice configured as a LIFX A19 Night Vision light
    """
    return create_device(
        29,
        serial=serial,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )  # LIFX A19 Night Vision

create_hev_light

create_hev_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create an HEV-enabled light (LIFX Clean).

PARAMETER DESCRIPTION
serial

Optional serial (auto-generated if not provided)

TYPE: str | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

advertised_services

Optional list of (service_id, port) tuples emitted as one StateService reply each, in order, in response to GetService. Defaults to a single UDP reply on the device's port.

TYPE: list[tuple[int, int]] | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured as a LIFX Clean light

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_hev_light(
    serial: str | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create an HEV-enabled light (LIFX Clean).

    Args:
        serial: Optional serial (auto-generated if not provided)
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager for testing
        advertised_services: Optional list of (service_id, port) tuples
            emitted as one StateService reply each, in order, in response
            to GetService. Defaults to a single UDP reply on the device's
            port.
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.

    Returns:
        EmulatedLifxDevice configured as a LIFX Clean light
    """
    return create_device(
        90,
        serial=serial,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )  # LIFX Clean

create_multizone_light

create_multizone_light(
    serial: str | None = None,
    zone_count: int | None = None,
    extended_multizone: bool = True,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a multizone light (LIFX Beam)

PARAMETER DESCRIPTION
serial

Optional serial

TYPE: str | None DEFAULT: None

zone_count

Optional zone count (uses product default if not specified)

TYPE: int | None DEFAULT: None

extended_multizone

enables support for extended multizone requests

TYPE: bool DEFAULT: True

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager

TYPE: HierarchicalScenarioManager | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_multizone_light(
    serial: str | None = None,
    zone_count: int | None = None,
    extended_multizone: bool = True,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a multizone light (LIFX Beam)

    Args:
        serial: Optional serial
        zone_count: Optional zone count (uses product default if not specified)
        extended_multizone: enables support for extended multizone requests
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.
    """
    return create_device(
        38,
        serial=serial,
        zone_count=zone_count,
        extended_multizone=extended_multizone,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )

create_tile_device

create_tile_device(
    serial: str | None = None,
    tile_count: int | None = None,
    tile_width: int | None = None,
    tile_height: int | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a tile device (LIFX Tile)

PARAMETER DESCRIPTION
serial

Optional serial

TYPE: str | None DEFAULT: None

tile_count

Optional number of tiles on the chain, 1 to 5 (default: 5)

TYPE: int | None DEFAULT: None

tile_width

Deprecated and ignored; the LIFX Tile is always 8x8. Will be removed in the next major release.

TYPE: int | None DEFAULT: None

tile_height

Deprecated and ignored; the LIFX Tile is always 8x8. Will be removed in the next major release.

TYPE: int | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager

TYPE: HierarchicalScenarioManager | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. LIFX Tile (product 55) has a terminal firmware ceiling of 3.50, below the Thread floor of 4.200, so connectivity="thread" always raises ValueError for this factory.

TYPE: Connectivity | str | None DEFAULT: None

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_tile_device(
    serial: str | None = None,
    tile_count: int | None = None,
    tile_width: int | None = None,
    tile_height: int | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a tile device (LIFX Tile)

    Args:
        serial: Optional serial
        tile_count: Optional number of tiles on the chain, 1 to 5 (default: 5)
        tile_width: Deprecated and ignored; the LIFX Tile is always 8x8.
            Will be removed in the next major release.
        tile_height: Deprecated and ignored; the LIFX Tile is always 8x8.
            Will be removed in the next major release.
        firmware_version: Optional firmware version tuple (major, minor)
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. LIFX Tile (product 55) has a terminal firmware ceiling
            of 3.50, below the Thread floor of 4.200, so
            connectivity="thread" always raises ValueError for this
            factory.
    """
    _warn_if_tile_dimensions(tile_width, tile_height, stacklevel=3)
    return create_device(
        55,
        serial=serial,
        tile_count=tile_count,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )  # LIFX Tile

create_switch

create_switch(
    serial: str | None = None,
    product_id: int = 70,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a LIFX Switch device.

Switches have has_relays and has_buttons capabilities but no lighting. They respond with StateUnhandled (223) to Light, MultiZone, and Tile packets.

PARAMETER DESCRIPTION
serial

Device serial (auto-generated if None)

TYPE: str | None DEFAULT: None

product_id

Switch product ID (default: 70 - LIFX Switch)

TYPE: int DEFAULT: 70

firmware_version

Optional firmware version (major, minor)

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional persistence backend

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured as a switch

Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_switch(
    serial: str | None = None,
    product_id: int = 70,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a LIFX Switch device.

    Switches have has_relays and has_buttons capabilities but no lighting.
    They respond with StateUnhandled (223) to Light, MultiZone, and Tile packets.

    Args:
        serial: Device serial (auto-generated if None)
        product_id: Switch product ID (default: 70 - LIFX Switch)
        firmware_version: Optional firmware version (major, minor)
        storage: Optional persistence backend
        scenario_manager: Optional scenario manager for testing
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
            Connectivity member). None means unspecified and resolves to
            WiFi. An explicit string that is not "wifi" or "thread" raises
            ValueError.

    Returns:
        EmulatedLifxDevice configured as a switch
    """
    return create_device(
        product_id,
        serial=serial,
        firmware_version=firmware_version,
        storage=storage,
        scenario_manager=scenario_manager,
        advertised_services=advertised_services,
        connectivity=connectivity,
        mdns_enabled=mdns_enabled,
        mdns_address=mdns_address,
        clock=clock,
        location_label=location_label,
        location_id=location_id,
        group_label=group_label,
        group_id=group_id,
    )

create_device

create_device(
    product_id: int,
    serial: str | None = None,
    zone_count: int | None = None,
    extended_multizone: bool | None = None,
    tile_count: int | None = None,
    tile_width: int | None = None,
    tile_height: int | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    persist_initial_state: bool = False,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice

Create a device for any LIFX product using the product registry.

This function uses the DeviceBuilder pattern to construct devices with clean separation of concerns and testable components.

PARAMETER DESCRIPTION
product_id

Product ID from the LIFX product registry

TYPE: int

serial

Optional serial (auto-generated if not provided)

TYPE: str | None DEFAULT: None

zone_count

Number of zones for multizone devices (auto-determined)

TYPE: int | None DEFAULT: None

extended_multizone

Enable extended multizone requests

TYPE: bool | None DEFAULT: None

tile_count

Number of tiles for matrix devices (uses product default)

TYPE: int | None DEFAULT: None

tile_width

Deprecated and ignored; every matrix product has a fixed tile size from specs.yml. Will be removed in the next major release.

TYPE: int | None DEFAULT: None

tile_height

Deprecated and ignored, as tile_width.

TYPE: int | None DEFAULT: None

firmware_version

Optional firmware version tuple (major, minor). If not specified, uses 3.70 for extended_multizone or 2.60 otherwise

TYPE: tuple[int, int] | None DEFAULT: None

storage

Optional storage for persistence

TYPE: DevicePersistenceAsyncFile | None DEFAULT: None

scenario_manager

Optional scenario manager for testing

TYPE: HierarchicalScenarioManager | None DEFAULT: None

advertised_services

Optional list of (service_id, port) tuples emitted as one StateService reply each, in order, in response to GetService. service_id is a raw uint8 (0-255) and may be outside the DeviceService enum. Defaults to a single UDP reply on the device's port.

TYPE: list[tuple[int, int]] | None DEFAULT: None

mdns_enabled

Advertise this device when server mDNS is enabled.

TYPE: bool DEFAULT: True

mdns_address

Explicit matching-family address, or concrete bind fallback.

TYPE: str | None DEFAULT: None

clock

Optional monotonic nanosecond clock used for uptime and for evaluating colour and power transitions (defaults to time.monotonic_ns).

TYPE: Callable[[], int] | None DEFAULT: None

location_label

Optional location label (defaults to "Test Location"). Devices sharing a label share a location.

TYPE: str | None DEFAULT: None

location_id

Optional explicit 16-byte location identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

group_label

Optional group label (defaults to "Test Group"). Devices sharing a label share a group.

TYPE: str | None DEFAULT: None

group_id

Optional explicit 16-byte group identifier; derived from the label when omitted.

TYPE: bytes | None DEFAULT: None

connectivity

Optional connectivity ("wifi", "thread", or a Connectivity member). None means unspecified and resolves to WiFi. An explicit string that is not "wifi" or "thread" raises ValueError.

TYPE: Connectivity | str | None DEFAULT: None

persist_initial_state

Whether construction should schedule the initial persistence save. Admission workflows disable this and activate persistence only after a successful add.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
EmulatedLifxDevice

EmulatedLifxDevice configured for the specified product

RAISES DESCRIPTION
ValueError

If product_id is not found in registry

Examples:

>>> # Create LIFX A19 (PID 27)
>>> device = create_device(27)
>>> # Create LIFX Z strip (PID 32) with 24 zones
>>> strip = create_device(32, zone_count=24)
>>> # Create LIFX Tile (PID 55) with 3 tiles
>>> tiles = create_device(55, tile_count=3)
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
def create_device(
    product_id: int,
    serial: str | None = None,
    zone_count: int | None = None,
    extended_multizone: bool | None = None,
    tile_count: int | None = None,
    tile_width: int | None = None,
    tile_height: int | None = None,
    firmware_version: tuple[int, int] | None = None,
    storage: DevicePersistenceAsyncFile | None = None,
    scenario_manager: HierarchicalScenarioManager | None = None,
    advertised_services: list[tuple[int, int]] | None = None,
    connectivity: Connectivity | str | None = None,
    persist_initial_state: bool = False,
    *,
    mdns_enabled: bool = True,
    mdns_address: str | None = None,
    clock: Callable[[], int] | None = None,
    location_label: str | None = None,
    location_id: bytes | None = None,
    group_label: str | None = None,
    group_id: bytes | None = None,
) -> EmulatedLifxDevice:
    """Create a device for any LIFX product using the product registry.

    This function uses the DeviceBuilder pattern to construct devices with
    clean separation of concerns and testable components.

    Args:
        product_id: Product ID from the LIFX product registry
        serial: Optional serial (auto-generated if not provided)
        zone_count: Number of zones for multizone devices (auto-determined)
        extended_multizone: Enable extended multizone requests
        tile_count: Number of tiles for matrix devices (uses product default)
        tile_width: Deprecated and ignored; every matrix product has a fixed
                         tile size from specs.yml. Will be removed in the next
                         major release.
        tile_height: Deprecated and ignored, as tile_width.
        firmware_version: Optional firmware version tuple (major, minor).
                         If not specified, uses 3.70 for extended_multizone
                         or 2.60 otherwise
        storage: Optional storage for persistence
        scenario_manager: Optional scenario manager for testing
        advertised_services: Optional list of (service_id, port) tuples emitted
                         as one StateService reply each, in order, in response
                         to GetService. service_id is a raw uint8 (0-255) and
                         may be outside the DeviceService enum. Defaults to a
                         single UDP reply on the device's port.
        mdns_enabled: Advertise this device when server mDNS is enabled.
        mdns_address: Explicit matching-family address, or concrete bind fallback.
        clock: Optional monotonic nanosecond clock used for uptime and for
            evaluating colour and power transitions (defaults to
            ``time.monotonic_ns``).
        location_label: Optional location label (defaults to
            "Test Location"). Devices sharing a label share a location.
        location_id: Optional explicit 16-byte location identifier;
            derived from the label when omitted.
        group_label: Optional group label (defaults to "Test Group").
            Devices sharing a label share a group.
        group_id: Optional explicit 16-byte group identifier; derived
            from the label when omitted.
        connectivity: Optional connectivity ("wifi", "thread", or a
                         Connectivity member). None means unspecified and
                         resolves to WiFi. An explicit string that is not
                         "wifi" or "thread" raises ValueError.
        persist_initial_state: Whether construction should schedule the initial
                         persistence save. Admission workflows disable this and
                         activate persistence only after a successful add.

    Returns:
        EmulatedLifxDevice configured for the specified product

    Raises:
        ValueError: If product_id is not found in registry

    Examples:
        >>> # Create LIFX A19 (PID 27)
        >>> device = create_device(27)
        >>> # Create LIFX Z strip (PID 32) with 24 zones
        >>> strip = create_device(32, zone_count=24)
        >>> # Create LIFX Tile (PID 55) with 3 tiles
        >>> tiles = create_device(55, tile_count=3)
    """
    _warn_if_tile_dimensions(tile_width, tile_height, stacklevel=3)

    # Get product info from registry
    product_info = get_product(product_id)
    if product_info is None:
        raise ValueError(f"Unknown product ID: {product_id}")

    # Build device using builder pattern
    builder = DeviceBuilder(product_info)

    if serial is not None:
        builder.with_serial(serial)

    if zone_count is not None:
        builder.with_zone_count(zone_count)

    if extended_multizone is not None:
        builder.with_extended_multizone(extended_multizone)

    if tile_count is not None:
        builder.with_tile_count(tile_count)

    if firmware_version is not None:
        builder.with_firmware_version(*firmware_version)

    if storage is not None:
        builder.with_storage(storage)

    if scenario_manager is not None:
        builder.with_scenario_manager(scenario_manager)

    if advertised_services is not None:
        builder.with_advertised_services(advertised_services)

    if connectivity is not None:
        builder.with_connectivity(connectivity)

    builder.with_mdns(mdns_enabled, mdns_address)
    builder.with_location(location_label, location_id)
    builder.with_group(group_label, group_id)
    builder.with_initial_persistence(persist_initial_state)
    if clock is not None:
        builder.with_clock(clock)

    return builder.build()

Usage Examples

Color Light

Create a standard full-colour light (LIFX Color 800lm):

from lifx_emulator import create_color_light

# Auto-generated serial
device = create_color_light()

# Custom serial
device = create_color_light("d073d5000001")

# Access state
print(f"Label: {device.state.label}")
print(f"Product: {device.state.product}")  # 91 (LIFX Color 800lm)
print(f"Has color: {device.state.has_color}")  # True

Color Temperature Light

Create a white light with variable color temperature:

from lifx_emulator import create_color_temperature_light

device = create_color_temperature_light("d073d5000001")

print(f"Has color: {device.state.has_color}")  # False
print(f"Product: {device.state.product}")  # 50 (LIFX Mini DD)

Infrared Light

Create a light with infrared capability:

from lifx_emulator import create_infrared_light

device = create_infrared_light("d073d5000002")

print(f"Has infrared: {device.state.has_infrared}")  # True
print(f"Product: {device.state.product}")  # 29 (LIFX+ A19)
print(f"IR brightness: {device.state.infrared_brightness}")  # 16384 (25%)

HEV Light

Create a light with HEV cleaning capability:

from lifx_emulator import create_hev_light

device = create_hev_light("d073d5000003")

print(f"Has HEV: {device.state.has_hev}")  # True
print(f"Product: {device.state.product}")  # 90 (LIFX Clean A19 1100lm)
print(f"HEV cycle duration: {device.state.hev_cycle_duration_s}")  # 7200 (2 hours)

Multizone Light

Create a linear multizone device. create_multizone_light() always creates a LIFX Beam; use create_device() with a product ID for other strips such as the LIFX Z:

from lifx_emulator import create_multizone_light
from lifx_emulator.factories import create_device

# LIFX Beam with the default 80 zones and extended multizone support
beam = create_multizone_light("d073d5800001")

# Custom zone count
beam_custom = create_multizone_light("d073d5800002", zone_count=60)

# Standard multizone only (no extended multizone packets, firmware 2.60)
beam_standard = create_multizone_light("d073d5800003", extended_multizone=False)

# LIFX Z (product ID 32) with the default 16 zones
strip = create_device(32, serial="d073d5800004")

print(f"Beam zones: {beam.state.zone_count}")   # 80
print(f"Strip zones: {strip.state.zone_count}")  # 16
print(f"Beam product: {beam.state.product}")    # 38 (LIFX Beam)
print(f"Strip product: {strip.state.product}")  # 32 (LIFX Z)

Tile Device

Create a matrix tile device:

from lifx_emulator.factories import create_device, create_tile_device

# Default configuration (5 tiles of 8x8)
tiles = create_tile_device("d073d5900001")

# Custom tile count (LIFX Tile chains have 1 to 5 tiles)
tiles_custom = create_tile_device("d073d5900002", tile_count=3)

# Large matrix device with 16x8 zones (LIFX Ceiling 13x26", product 201)
large_tile = create_device(201, serial="d073d5900003")

print(f"Tile count: {tiles.state.tile_count}")      # 5
print(f"Tile width: {tiles.state.tile_width}")      # 8
print(f"Tile height: {tiles.state.tile_height}")    # 8
print(f"Product: {tiles.state.product}")            # 55 (LIFX Tile)

Deprecated: tile_width and tile_height

Every matrix product has a fixed tile size from its specs: the LIFX Tile is always 8x8. The tile_width/tile_height arguments of create_tile_device() and create_device(), and DeviceBuilder.with_tile_dimensions(), are accepted but ignored and raise a DeprecationWarning. They will be removed in the next major release. For a tile with more than 64 zones, create the product that has one:

from lifx_emulator.factories import create_device

# LIFX Ceiling 13x26": one 16x8 tile, so reading it takes two Get64 requests
large_matrix = create_device(201, serial="d073d5900003")
print(large_matrix.state.tile_width * large_matrix.state.tile_height)  # 128

Generic Device Creation

Create any device by product ID:

from lifx_emulator.factories import create_device

# LIFX A19 (product ID 27)
a19 = create_device(27, serial="d073d5000001")

# LIFX Z (product ID 32) with custom zones
z_strip = create_device(32, serial="d073d5800001", zone_count=24)

# LIFX Tile (product ID 55) with 3 tiles on its chain
tiles = create_device(55, serial="d073d5900001", tile_count=3)

# LIFX Candle (product ID 57) - loads 5x6 dimensions from product defaults
candle = create_device(57, serial="d073d5900002")
print(f"Candle size: {candle.state.tile_width}x{candle.state.tile_height}")  # 5x6

Serial Format

Serials must be 12 hex characters (6 bytes):

from lifx_emulator import create_color_light

# Valid formats
device = create_color_light("d073d5000001")  # Serial with LIFX prefix ("d073d5")
device = create_color_light("cafe00abcdef")  # Serial with custom prefix
device = create_color_light()                # Auto-generate serial

# Invalid (raises ValueError)
for bad_serial in ("123", "xyz"):  # Too short / not hex
    try:
        create_color_light(bad_serial)
    except ValueError as e:
        print(e)  # Serial must be exactly 12 ASCII hexadecimal characters

Auto-generated serials use prefixes based on device type:

  • d073d5 - Regular lights
  • d073d6 - Infrared lights
  • d073d7 - HEV lights
  • d073d8 - Multizone strips/beams
  • d073d9 - Matrix tiles

Product Defaults

When parameters like zone_count or tile_count are not specified, the factory functions automatically load defaults from the product registry's specs system:

from lifx_emulator import create_multizone_light, create_tile_device
from lifx_emulator.factories import create_device

# Uses product default (16 zones for LIFX Z)
strip = create_device(32, serial="d073d5800001")

# Uses product default (80 zones for LIFX Beam)
beam = create_multizone_light("d073d5800002")

# Uses product default (5 tiles for LIFX Tile)
tiles = create_tile_device("d073d5900001")

# Uses product default (5x6 for LIFX Candle)
candle = create_device(57, serial="d073d5900002")

See Product Registry for all product definitions and defaults.

Device State Access

After creation, access device state:

from lifx_emulator import create_color_light

device = create_color_light("d073d5000001")

# Device identity
print(device.state.serial)          # "d073d5000001"
print(device.state.label)           # "LIFX Color 800lm 000001"
print(device.state.vendor)          # 1 (LIFX)
print(device.state.product)         # 91 (LIFX Color 800lm)

# Device capabilities
print(device.state.has_color)       # True
print(device.state.has_infrared)    # False
print(device.state.has_multizone)   # False
print(device.state.has_matrix)      # False
print(device.state.has_hev)         # False

# Light state
print(device.state.power_level)     # 65535 (on)
print(device.state.color)           # LightHsbk(...)
print(device.state.port)            # 56700 (default)

# Firmware version
print(device.state.version_major)   # 3
print(device.state.version_minor)   # 70

Multiple Devices

Create multiple devices for testing:

import asyncio

from lifx_emulator import (
    EmulatedLifxServer,
    create_color_light,
    create_multizone_light,
    create_tile_device,
)
from lifx_emulator.devices import DeviceManager
from lifx_emulator.factories import create_device
from lifx_emulator.repositories import DeviceRepository


async def main():
    # Create a diverse set of devices
    devices = [
        create_color_light("d073d5000001"),
        create_color_light("d073d5000002"),
        create_device(32, serial="d073d5800001", zone_count=16),  # LIFX Z
        create_multizone_light("d073d5800002", zone_count=82),  # LIFX Beam
        create_tile_device("d073d5900001", tile_count=5),
    ]

    # Start server with all devices
    server = EmulatedLifxServer(
        devices, DeviceManager(DeviceRepository()), "127.0.0.1", 56700
    )
    async with server:
        await asyncio.sleep(60)


asyncio.run(main())

Locations and Groups

Every factory accepts location_label, location_id, group_label and group_id. Identifiers are derived from labels with a name-based UUID (version 5) under a fixed emulator namespace, so devices that share a label share an identifier in any process on any host. The defaults are "Test Location" and "Test Group", so a default fleet reports one location and one group. Pass an explicit 16-byte identifier to override the derived value:

from lifx_emulator import create_color_light
from lifx_emulator.devices import derive_group_id

kitchen = create_color_light(
    "d073d5000001", location_label="Kitchen", group_label="Downlights"
)
pantry = create_color_light(
    "d073d5000002", location_label="Kitchen", group_label="Downlights"
)
assert kitchen.state.location_id == pantry.state.location_id
assert kitchen.state.group_id == derive_group_id("Downlights")

# Same labels, private group
lamp = create_color_light(
    "d073d5000003",
    location_label="Kitchen",
    group_label="Downlights",
    group_id=bytes(range(16)),
)
assert lamp.state.group_id != kitchen.state.group_id

SetLocation and SetGroup packets change only the device they target and use the identifier carried in the packet. DeviceBuilder exposes the same options as with_location(label, location_id) and with_group(label, group_id).

Advanced Options

Persistent Storage

Devices can persist state across restarts. State-changing protocol packets (SetColor, SetLabel, ...) queue a save automatically; direct assignments to device.state need an explicit save_device_state():

import asyncio

from lifx_emulator import create_color_light
from lifx_emulator.devices import DevicePersistenceAsyncFile


async def main():
    # Create storage (uses ~/.lifx-emulator by default)
    storage = DevicePersistenceAsyncFile()

    # Create device with storage enabled; saved state is restored here
    device = create_color_light("d073d5000001", storage=storage)

    # Direct assignments are not saved automatically, so queue a save
    device.state.label = "My Light"
    await storage.save_device_state(device.state)

    # Flush pending writes before the event loop exits
    await storage.shutdown()

    # On next run, state is automatically restored from disk


asyncio.run(main())

See Storage API for details.

Test Scenarios

Inject test scenarios (packet loss, delays, etc.) for error testing:

from lifx_emulator import create_color_light
from lifx_emulator.scenarios import HierarchicalScenarioManager, ScenarioConfig

# Create scenario manager
manager = HierarchicalScenarioManager()

# Create device with scenario support
device = create_color_light("d073d5000001", scenario_manager=manager)

# Configure scenarios for testing error handling
manager.set_device_scenario(
    device.state.serial,
    ScenarioConfig(
        drop_packets={101: 0.3},  # Drop 30% of GetColor packets
        response_delays={107: 0.5},  # Delay StateColor (107) replies by 500ms
    )
)

When the device runs inside an EmulatedLifxServer, pass the same manager to the server as scenario_manager=manager: the server assigns its own scenario manager to every device it manages, so scenarios registered only on the factory's manager would be ignored.

Custom Firmware Versions

Override firmware version for compatibility testing:

from lifx_emulator import create_color_light

# Simulate older firmware
old_device = create_color_light(
    "d073d5000001",
    firmware_version=(2, 60)
)

# Simulate newer firmware
new_device = create_color_light(
    "d073d5000002",
    firmware_version=(3, 90)
)

Injected Clock

Every factory accepts a clock argument: a callable returning monotonic nanoseconds that the device uses for uptime and for evaluating colour fades, power ramps and waveforms. Injecting a fake clock lets a test drive a SetColor with a duration, or a SetWaveform, forward without sleeping:

from lifx_emulator.factories import create_color_light

now_ns = [0]
device = create_color_light("d073d5000001", clock=lambda: now_ns[0])
now_ns[0] += 2_500_000_000
print(device.get_uptime_ns())  # 2500000000

See Transitions and waveforms for how fades are evaluated.

Next Steps