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:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
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.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
| RETURNS | DESCRIPTION |
|---|---|
EmulatedLifxDevice
|
EmulatedLifxDevice configured as a LIFX Color light |
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
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:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
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.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
| 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
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:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
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.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
| 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
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:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
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.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
| RETURNS | DESCRIPTION |
|---|---|
EmulatedLifxDevice
|
EmulatedLifxDevice configured as a LIFX Clean light |
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
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:
|
zone_count
|
Optional zone count (uses product default if not specified)
TYPE:
|
extended_multizone
|
enables support for extended multizone requests
TYPE:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager
TYPE:
|
mdns_enabled
|
Advertise this device when server mDNS is enabled.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
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:
|
tile_count
|
Optional number of tiles on the chain, 1 to 5 (default: 5)
TYPE:
|
tile_width
|
Deprecated and ignored; the LIFX Tile is always 8x8. Will be removed in the next major release.
TYPE:
|
tile_height
|
Deprecated and ignored; the LIFX Tile is always 8x8. Will be removed in the next major release.
TYPE:
|
firmware_version
|
Optional firmware version tuple (major, minor) |
storage
|
Optional storage for persistence
TYPE:
|
scenario_manager
|
Optional scenario manager
TYPE:
|
mdns_enabled
|
Advertise this device when server mDNS is enabled.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
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:
|
product_id
|
Switch product ID (default: 70 - LIFX Switch)
TYPE:
|
firmware_version
|
Optional firmware version (major, minor) |
storage
|
Optional persistence backend
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
mdns_enabled
|
Advertise this device when server mDNS is enabled.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
| RETURNS | DESCRIPTION |
|---|---|
EmulatedLifxDevice
|
EmulatedLifxDevice configured as a switch |
Source code in packages/lifx-emulator-core/src/lifx_emulator/factories/factory.py
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:
|
serial
|
Optional serial (auto-generated if not provided)
TYPE:
|
zone_count
|
Number of zones for multizone devices (auto-determined)
TYPE:
|
extended_multizone
|
Enable extended multizone requests
TYPE:
|
tile_count
|
Number of tiles for matrix devices (uses product default)
TYPE:
|
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:
|
tile_height
|
Deprecated and ignored, as tile_width.
TYPE:
|
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
TYPE:
|
scenario_manager
|
Optional scenario manager for testing
TYPE:
|
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.
TYPE:
|
mdns_address
|
Explicit matching-family address, or concrete bind fallback.
TYPE:
|
clock
|
Optional monotonic nanosecond clock used for uptime and for
evaluating colour and power transitions (defaults to
|
location_label
|
Optional location label (defaults to "Test Location"). Devices sharing a label share a location.
TYPE:
|
location_id
|
Optional explicit 16-byte location identifier; derived from the label when omitted.
TYPE:
|
group_label
|
Optional group label (defaults to "Test Group"). Devices sharing a label share a group.
TYPE:
|
group_id
|
Optional explicit 16-byte group identifier; derived from the label when omitted.
TYPE:
|
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:
|
persist_initial_state
|
Whether construction should schedule the initial persistence save. Admission workflows disable this and activate persistence only after a successful add.
TYPE:
|
| 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
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 | |
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:
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 lightsd073d6- Infrared lightsd073d7- HEV lightsd073d8- Multizone strips/beamsd073d9- 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¶
- Server API - Running the emulator server
- Device API - Device and state details
- Product Registry - All available products
- Basic Tutorial - Complete usage examples