lifx-emulator 2.3.0__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/CLAUDE.md +5 -5
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/PKG-INFO +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/advanced/device-management-api.md +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/advanced/scenario-api.md +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/device.md +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/products.md +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/protocol.md +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/architecture/overview.md +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/changelog.md +8 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/getting-started/cli.md +4 -4
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/device-types.md +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/products-and-specs.md +10 -10
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/web-interface.md +3 -3
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/index.md +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/04-advanced-scenarios.md +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/pyproject.toml +3 -8
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/__main__.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/models.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/templates/dashboard.html +8 -8
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/device.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/states.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/builder.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/factory.py +4 -4
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/light_handlers.py +155 -20
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/tile_handlers.py +22 -24
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/products/generator.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/products/specs.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/products/specs.yml +8 -8
- lifx_emulator-2.3.1/tests/test_backwards_compatibility.py +943 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_device.py +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_integration.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_tile_handlers_extended.py +2 -2
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/uv.lock +1 -1
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/.github/workflows/ci.yml +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/.github/workflows/docs.yml +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/.gitignore +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/.pre-commit-config.yaml +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/LICENSE +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/README.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/advanced/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/advanced/scenarios.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/advanced/storage.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/factories.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/server.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/api/storage.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/architecture/device-state.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/architecture/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/architecture/packet-flow.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/architecture/protocol.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/assets/favicon.png +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/faq.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/getting-started/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/getting-started/installation.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/getting-started/quickstart.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/best-practices.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/framebuffers.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/integration-testing.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/guide/testing-scenarios.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/reference/glossary.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/reference/troubleshooting.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/stylesheets/extra.css +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/01-first-device.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/02-basic.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/03-integration.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/05-cicd.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/docs/tutorials/index.md +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/mkdocs.yml +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/renovate.json +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/app.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/mappers/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/mappers/device_mapper.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/routers/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/routers/devices.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/routers/monitoring.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/routers/scenarios.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/services/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/api/services/device_service.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/constants.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/manager.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/observers.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/persistence.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/state_restorer.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/devices/state_serializer.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/default_config.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/firmware_config.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/factories/serial_generator.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/base.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/device_handlers.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/multizone_handlers.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/handlers/registry.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/products/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/products/registry.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/base.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/const.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/generator.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/header.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/packets.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/protocol_types.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/protocol/serializer.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/repositories/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/repositories/device_repository.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/repositories/storage_backend.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/scenarios/__init__.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/scenarios/manager.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/scenarios/models.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/scenarios/persistence.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/src/lifx_emulator/server.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/conftest.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_api.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_api_validation.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_async_storage.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_cli.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_cli_validation.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_device_edge_cases.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_device_handlers_extended.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_device_manager.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_handler_registry.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_light_handlers_extended.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_multizone_handlers_extended.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_observers.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_products_generator.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_products_specs.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_protocol_generator.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_protocol_types_coverage.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_repositories.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_scenario_manager.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_scenario_persistence.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_serializer.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_server.py +0 -0
- {lifx_emulator-2.3.0 → lifx_emulator-2.3.1}/tests/test_state_restorer.py +0 -0
|
@@ -128,8 +128,8 @@ lifx-emulator --help
|
|
|
128
128
|
- `--multizone-extended`: Enable extended multizone support (default: True, use --no-multizone-extended to disable)
|
|
129
129
|
- `--tile`: Number of tile devices
|
|
130
130
|
- `--tile-count`: Tiles per device (uses product default if not specified)
|
|
131
|
-
- `--tile-width`: Width of each tile in
|
|
132
|
-
- `--tile-height`: Height of each tile in
|
|
131
|
+
- `--tile-width`: Width of each tile in zones (uses product default if not specified)
|
|
132
|
+
- `--tile-height`: Height of each tile in zones (uses product default if not specified)
|
|
133
133
|
- `--serial-prefix`: serial prefix (6 hex chars, default: d073d5)
|
|
134
134
|
- `--serial-start`: Starting serial suffix (default: 1)
|
|
135
135
|
- `--api`: Enable HTTP API server for monitoring and management (default: False)
|
|
@@ -140,9 +140,9 @@ lifx-emulator --help
|
|
|
140
140
|
**Product Defaults:**
|
|
141
141
|
Device parameters like `--multizone-zones` and `--tile-count` automatically use product-specific defaults from the specs system when not specified:
|
|
142
142
|
- LIFX Beam: Extended multizone support enabled and 80 zones by default
|
|
143
|
-
- LIFX Tile: 5 tiles of 8x8
|
|
144
|
-
- LIFX Candle: 1 tile of 5x6
|
|
145
|
-
- LIFX Ceiling: 1 tile of 8x8
|
|
143
|
+
- LIFX Tile: 5 tiles of 8x8 zones by default
|
|
144
|
+
- LIFX Candle: 1 tile of 5x6 zones by default
|
|
145
|
+
- LIFX Ceiling: 1 tile of 8x8 zones by default
|
|
146
146
|
- These defaults can be overridden with command-line parameters
|
|
147
147
|
|
|
148
148
|
**Firmware Version:**
|
|
@@ -266,8 +266,8 @@ Creates a new emulated device by product ID. The device will be added to the emu
|
|
|
266
266
|
- `serial` (optional): Device serial (auto-generated if not provided)
|
|
267
267
|
- `zone_count` (optional): Number of zones for multizone devices
|
|
268
268
|
- `tile_count` (optional): Number of tiles for matrix devices
|
|
269
|
-
- `tile_width` (optional): Width of each tile in
|
|
270
|
-
- `tile_height` (optional): Height of each tile in
|
|
269
|
+
- `tile_width` (optional): Width of each tile in zones
|
|
270
|
+
- `tile_height` (optional): Height of each tile in zones
|
|
271
271
|
- `firmware_major` (optional): Firmware major version
|
|
272
272
|
- `firmware_minor` (optional): Firmware minor version
|
|
273
273
|
|
|
@@ -788,8 +788,8 @@ jobs:
|
|
|
788
788
|
| 506 | StateMultiZone | Response with zone colors |
|
|
789
789
|
| 512 | ExtendedStateMultiZone | Response with extended zones |
|
|
790
790
|
| 701 | GetDeviceChain | Get tile chain info |
|
|
791
|
-
| 707 | Get64 | Get tile
|
|
792
|
-
| 715 | Set64 | Set tile
|
|
791
|
+
| 707 | Get64 | Get tile zone data |
|
|
792
|
+
| 715 | Set64 | Set tile zone data |
|
|
793
793
|
|
|
794
794
|
## Tips and Best Practices
|
|
795
795
|
|
|
@@ -90,8 +90,8 @@ Dataclass holding all stateful information for an emulated LIFX device.
|
|
|
90
90
|
|
|
91
91
|
- **`tile_count`** (`int` = `0`) - Number of tiles in chain
|
|
92
92
|
- **`tile_devices`** (`list[dict]` = `[]`) - Per-tile state (position, colors)
|
|
93
|
-
- **`tile_width`** (`int` = `8`) - Width of each tile in
|
|
94
|
-
- **`tile_height`** (`int` = `8`) - Height of each tile in
|
|
93
|
+
- **`tile_width`** (`int` = `8`) - Width of each tile in zones
|
|
94
|
+
- **`tile_height`** (`int` = `8`) - Height of each tile in zones
|
|
95
95
|
|
|
96
96
|
#### Effects (Waveforms & Animations)
|
|
97
97
|
|
|
@@ -239,8 +239,8 @@ Get detailed specifications for a product.
|
|
|
239
239
|
- `zone_count`: Number of zones (multizone devices)
|
|
240
240
|
- `extended_multizone`: Extended multizone support flag
|
|
241
241
|
- `tile_count`: Default number of tiles (matrix devices)
|
|
242
|
-
- `tile_width`: Tile width in
|
|
243
|
-
- `tile_height`: Tile height in
|
|
242
|
+
- `tile_width`: Tile width in zones (matrix devices)
|
|
243
|
+
- `tile_height`: Tile height in zones (matrix devices)
|
|
244
244
|
|
|
245
245
|
**Example:**
|
|
246
246
|
```python
|
|
@@ -322,8 +322,8 @@ class TileStateDevice:
|
|
|
322
322
|
accel_meas_z: int
|
|
323
323
|
user_x: float # User-configured X position
|
|
324
324
|
user_y: float # User-configured Y position
|
|
325
|
-
width: int # Tile width in
|
|
326
|
-
height: int # Tile height in
|
|
325
|
+
width: int # Tile width in zones (e.g., 8)
|
|
326
|
+
height: int # Tile height in zones (e.g., 8)
|
|
327
327
|
device_version_vendor: int
|
|
328
328
|
device_version_product: int
|
|
329
329
|
device_version_version: int
|
|
@@ -253,7 +253,7 @@ Devices advertise capabilities through boolean flags:
|
|
|
253
253
|
| `has_infrared` | IR brightness | LIFX A19 Night Vision |
|
|
254
254
|
| `has_multizone` | Linear zones | LIFX Z, LIFX Beam |
|
|
255
255
|
| `has_extended_multizone` | >16 zones | LIFX Beam |
|
|
256
|
-
| `has_matrix` | 2D
|
|
256
|
+
| `has_matrix` | 2D zone grid | LIFX Tile, LIFX Candle |
|
|
257
257
|
| `has_hev` | HEV cleaning | LIFX Clean |
|
|
258
258
|
|
|
259
259
|
## Packet Types
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v2.3.1 (2025-11-18)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **tile**: Implement backwards compatibility on tile and multizone devices
|
|
10
|
+
([`be473f7`](https://github.com/Djelibeybi/lifx-emulator/commit/be473f74f9a49be7f07082d5bfba16f5d74f46f6))
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
## v2.3.0 (2025-11-18)
|
|
6
14
|
|
|
7
15
|
### Features
|
|
@@ -197,16 +197,16 @@ Number of tiles per tile device. If not specified, uses product default (5 for L
|
|
|
197
197
|
- **Default:** `None` (uses product defaults)
|
|
198
198
|
- **Example:** `--tile-count 10`
|
|
199
199
|
|
|
200
|
-
### `--tile-width <
|
|
200
|
+
### `--tile-width <zones>`
|
|
201
201
|
|
|
202
|
-
Width of each tile in
|
|
202
|
+
Width of each tile in zones. If not specified, uses product default (typically 8).
|
|
203
203
|
|
|
204
204
|
- **Default:** `None` (uses product defaults)
|
|
205
205
|
- **Example:** `--tile-width 16`
|
|
206
206
|
|
|
207
|
-
### `--tile-height <
|
|
207
|
+
### `--tile-height <zones>`
|
|
208
208
|
|
|
209
|
-
Height of each tile in
|
|
209
|
+
Height of each tile in zones. If not specified, uses product default (typically 8).
|
|
210
210
|
|
|
211
211
|
- **Default:** `None` (uses product defaults)
|
|
212
212
|
- **Example:** `--tile-height 8`
|
|
@@ -297,7 +297,7 @@ print(f"Tile height: {device.state.tile_height}") # 8
|
|
|
297
297
|
|
|
298
298
|
# Access tile devices
|
|
299
299
|
for i, tile in enumerate(device.state.tile_devices):
|
|
300
|
-
print(f"Tile {i}: {tile.width}x{tile.height}
|
|
300
|
+
print(f"Tile {i}: {tile.width}x{tile.height} zones")
|
|
301
301
|
```
|
|
302
302
|
|
|
303
303
|
### Matrix Packet Types
|
|
@@ -31,7 +31,7 @@ Contains product-specific details not available in the upstream catalog:
|
|
|
31
31
|
- Min/max zone counts
|
|
32
32
|
- Default tile counts for matrix devices
|
|
33
33
|
- Min/max tile counts
|
|
34
|
-
- Tile dimensions (width x height in
|
|
34
|
+
- Tile dimensions (width x height in zones)
|
|
35
35
|
- Product-specific notes
|
|
36
36
|
|
|
37
37
|
### `specs.py`
|
|
@@ -81,8 +81,8 @@ products:
|
|
|
81
81
|
default_tile_count: <number> # Typical number of tiles in chain
|
|
82
82
|
min_tile_count: <number> # Minimum tiles supported
|
|
83
83
|
max_tile_count: <number> # Maximum tiles supported
|
|
84
|
-
tile_width: <
|
|
85
|
-
tile_height: <
|
|
84
|
+
tile_width: <zones> # Width of each tile
|
|
85
|
+
tile_height: <zones> # Height of each tile
|
|
86
86
|
notes: "<description>"
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -95,7 +95,7 @@ products:
|
|
|
95
95
|
max_tile_count: 5
|
|
96
96
|
tile_width: 8
|
|
97
97
|
tile_height: 8
|
|
98
|
-
notes: "LIFX Tile, 8x8
|
|
98
|
+
notes: "LIFX Tile, 8x8 zone matrix, chainable up to 5"
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
**Example - LIFX Candle:**
|
|
@@ -107,7 +107,7 @@ products:
|
|
|
107
107
|
max_tile_count: 1
|
|
108
108
|
tile_width: 5
|
|
109
109
|
tile_height: 6
|
|
110
|
-
notes: "LIFX Candle, 5x6
|
|
110
|
+
notes: "LIFX Candle, 5x6 zone matrix, single unit"
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
**Example - LIFX Ceiling:**
|
|
@@ -119,7 +119,7 @@ products:
|
|
|
119
119
|
max_tile_count: 1
|
|
120
120
|
tile_width: 22
|
|
121
121
|
tile_height: 22
|
|
122
|
-
notes: "LIFX Ceiling, 22x22
|
|
122
|
+
notes: "LIFX Ceiling, 22x22 zone matrix"
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
## How Specifications Are Used
|
|
@@ -149,16 +149,16 @@ When creating a matrix device:
|
|
|
149
149
|
2. **Tile count**: From `specs.yml` if not specified by user
|
|
150
150
|
|
|
151
151
|
```python
|
|
152
|
-
# Uses specification: 5 tiles of 8x8
|
|
152
|
+
# Uses specification: 5 tiles of 8x8 zones
|
|
153
153
|
device = create_device(55)
|
|
154
154
|
|
|
155
155
|
# Custom tile count, specification dimensions
|
|
156
|
-
device = create_device(55, tile_count=3) # 3 tiles of 8x8
|
|
156
|
+
device = create_device(55, tile_count=3) # 3 tiles of 8x8 zones
|
|
157
157
|
|
|
158
|
-
# Candle: 1 tile of 5x5
|
|
158
|
+
# Candle: 1 tile of 5x5 zones (from specification)
|
|
159
159
|
device = create_device(57)
|
|
160
160
|
|
|
161
|
-
# Ceiling: 1 tile of 22x22
|
|
161
|
+
# Ceiling: 1 tile of 22x22 zones (from specification)
|
|
162
162
|
device = create_device(176)
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -142,7 +142,7 @@ For matrix/tile devices:
|
|
|
142
142
|
```
|
|
143
143
|
▸ Show tiles (5) (click to expand)
|
|
144
144
|
┌──────────┐
|
|
145
|
-
│ T1 │ (8×8
|
|
145
|
+
│ T1 │ (8×8 zone grid)
|
|
146
146
|
│ ████████ │
|
|
147
147
|
│ ████████ │
|
|
148
148
|
│ ████████ │
|
|
@@ -249,8 +249,8 @@ For matrix devices (tiles, candles, ceiling):
|
|
|
249
249
|
|
|
250
250
|
1. Locate device in Devices section
|
|
251
251
|
2. Click "▸ Show tiles" to expand
|
|
252
|
-
3. Grid display shows
|
|
253
|
-
4. Each small square is one
|
|
252
|
+
3. Grid display shows zone colors
|
|
253
|
+
4. Each small square is one zone
|
|
254
254
|
5. Tiles labeled T1, T2, etc.
|
|
255
255
|
6. Click again to collapse
|
|
256
256
|
|
|
@@ -71,7 +71,7 @@ LIFX Emulator is a Python library and CLI tool that creates virtual LIFX devices
|
|
|
71
71
|
| Infrared | LIFX A19 Night Vision | IR brightness control |
|
|
72
72
|
| HEV | LIFX Clean | HEV cleaning cycle |
|
|
73
73
|
| Multizone | LIFX Z, LIFX Beam | Linear zones (up to 82) |
|
|
74
|
-
| Matrix | LIFX Tile, LIFX Candle | 2D
|
|
74
|
+
| Matrix | LIFX Tile, LIFX Candle | 2D zone arrays |
|
|
75
75
|
|
|
76
76
|
## Use Cases
|
|
77
77
|
|
|
@@ -84,7 +84,7 @@ async def main():
|
|
|
84
84
|
# Create a LIFX Tile with 5 tiles in the chain
|
|
85
85
|
device = create_tile_device("d073d9000001", tile_count=5)
|
|
86
86
|
|
|
87
|
-
# Each tile is 8x8
|
|
87
|
+
# Each tile is 8x8 zones (64 zones)
|
|
88
88
|
print(f"Tile device configuration:")
|
|
89
89
|
print(f" Tiles: {len(device.state.tile_devices)}")
|
|
90
90
|
for i, tile in enumerate(device.state.tile_devices):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lifx-emulator"
|
|
3
|
-
version = "2.3.
|
|
3
|
+
version = "2.3.1"
|
|
4
4
|
description = "LIFX Emulator for testing LIFX LAN protocol libraries"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -73,7 +73,7 @@ docstring-code-format = true
|
|
|
73
73
|
docstring-code-line-length = "dynamic"
|
|
74
74
|
|
|
75
75
|
[tool.ruff.lint]
|
|
76
|
-
select = ["E", "F", "I", "N", "W", "UP"
|
|
76
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
77
77
|
ignore = []
|
|
78
78
|
|
|
79
79
|
# Complexity and code quality limits
|
|
@@ -86,13 +86,8 @@ max-branches = 12
|
|
|
86
86
|
max-statements = 50
|
|
87
87
|
|
|
88
88
|
[tool.ruff.lint.per-file-ignores]
|
|
89
|
-
"src/lifx_emulator/
|
|
90
|
-
"src/lifx_emulator/devices/state_restorer.py" = ["C901"]
|
|
91
|
-
"src/lifx_emulator/handlers/tile_handlers.py" = ["C901"]
|
|
92
|
-
"src/lifx_emulator/protocol/generator.py" = ["E501", "C901"]
|
|
89
|
+
"src/lifx_emulator/protocol/generator.py" = ["E501"]
|
|
93
90
|
"src/lifx_emulator/protocol/packets.py" = ["E501"]
|
|
94
|
-
"src/lifx_emulator/products/registry.py" = ["C901"]
|
|
95
|
-
"src/lifx_emulator/scenarios/persistence.py" = ["C901"]
|
|
96
91
|
|
|
97
92
|
|
|
98
93
|
[tool.pyright]
|
|
@@ -286,9 +286,9 @@ async def run(
|
|
|
286
286
|
tile: Number of tile/matrix chain devices.
|
|
287
287
|
tile_count: Number of tiles per device. Uses product defaults if not
|
|
288
288
|
specified (5 for Tile, 1 for Candle/Ceiling).
|
|
289
|
-
tile_width: Width of each tile in
|
|
289
|
+
tile_width: Width of each tile in zones. Uses product defaults if not
|
|
290
290
|
specified (8 for most devices).
|
|
291
|
-
tile_height: Height of each tile in
|
|
291
|
+
tile_height: Height of each tile in zones. Uses product defaults if
|
|
292
292
|
not specified (8 for most devices).
|
|
293
293
|
serial_prefix: Serial number prefix as 6 hex characters.
|
|
294
294
|
serial_start: Starting serial suffix for auto-incrementing device serials.
|
|
@@ -25,10 +25,10 @@ class DeviceCreateRequest(BaseModel):
|
|
|
25
25
|
None, description="Number of tiles for matrix devices", ge=0, le=100
|
|
26
26
|
)
|
|
27
27
|
tile_width: int | None = Field(
|
|
28
|
-
None, description="Width of each tile in
|
|
28
|
+
None, description="Width of each tile in zones", ge=1, le=256
|
|
29
29
|
)
|
|
30
30
|
tile_height: int | None = Field(
|
|
31
|
-
None, description="Height of each tile in
|
|
31
|
+
None, description="Height of each tile in zones", ge=1, le=256
|
|
32
32
|
)
|
|
33
33
|
firmware_major: int | None = Field(
|
|
34
34
|
None, description="Firmware major version", ge=0, le=255
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
gap: 2px;
|
|
164
164
|
margin-top: 4px;
|
|
165
165
|
}
|
|
166
|
-
.tile-
|
|
166
|
+
.tile-zone {
|
|
167
167
|
width: 8px;
|
|
168
168
|
height: 8px;
|
|
169
169
|
border-radius: 1px;
|
|
@@ -644,7 +644,7 @@
|
|
|
644
644
|
`;
|
|
645
645
|
} else if (dev.has_matrix && dev.tile_devices &&
|
|
646
646
|
dev.tile_devices.length > 0) {
|
|
647
|
-
// Render actual tile
|
|
647
|
+
// Render actual tile zones
|
|
648
648
|
const tilesHtml = dev.tile_devices.map((tile, tileIndex) => {
|
|
649
649
|
if (!tile.colors || tile.colors.length === 0) {
|
|
650
650
|
return '<div style="color: #666;">No color data</div>';
|
|
@@ -652,14 +652,14 @@
|
|
|
652
652
|
|
|
653
653
|
const width = tile.width || 8;
|
|
654
654
|
const height = tile.height || 8;
|
|
655
|
-
const
|
|
655
|
+
const totalzones = width * height;
|
|
656
656
|
|
|
657
|
-
// Create grid of
|
|
658
|
-
const slicedColors = tile.colors.slice(0,
|
|
659
|
-
const
|
|
657
|
+
// Create grid of zones
|
|
658
|
+
const slicedColors = tile.colors.slice(0, totalzones);
|
|
659
|
+
const zonesHtml = slicedColors.map(color => {
|
|
660
660
|
const rgb = hsbkToRgb(color);
|
|
661
661
|
const bgStyle = `background: ${rgb};`;
|
|
662
|
-
return `<div class="tile-
|
|
662
|
+
return `<div class="tile-zone" style="${bgStyle}"></div>`;
|
|
663
663
|
}).join('');
|
|
664
664
|
|
|
665
665
|
const labelStyle = (
|
|
@@ -675,7 +675,7 @@
|
|
|
675
675
|
T${tileIndex + 1}
|
|
676
676
|
</div>
|
|
677
677
|
<div class="tile-grid" style="${gridStyle}">
|
|
678
|
-
${
|
|
678
|
+
${zonesHtml}
|
|
679
679
|
</div>
|
|
680
680
|
</div>
|
|
681
681
|
`;
|
|
@@ -90,10 +90,10 @@ class EmulatedLifxDevice:
|
|
|
90
90
|
if self.state.has_matrix and self.state.tile_count > 0:
|
|
91
91
|
if not self.state.tile_devices:
|
|
92
92
|
for i in range(self.state.tile_count):
|
|
93
|
-
|
|
93
|
+
zones = self.state.tile_width * self.state.tile_height
|
|
94
94
|
tile_colors = [
|
|
95
95
|
LightHsbk(hue=0, saturation=0, brightness=32768, kelvin=3500)
|
|
96
|
-
for _ in range(
|
|
96
|
+
for _ in range(zones)
|
|
97
97
|
]
|
|
98
98
|
|
|
99
99
|
self.state.tile_devices.append(
|
|
@@ -100,10 +100,10 @@ class TileFramebuffers:
|
|
|
100
100
|
"""Get framebuffer by index, creating it if needed."""
|
|
101
101
|
if fb_index not in self.framebuffers:
|
|
102
102
|
# Initialize with default black color
|
|
103
|
-
|
|
103
|
+
zones = width * height
|
|
104
104
|
self.framebuffers[fb_index] = [
|
|
105
105
|
LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500)
|
|
106
|
-
for _ in range(
|
|
106
|
+
for _ in range(zones)
|
|
107
107
|
]
|
|
108
108
|
return self.framebuffers[fb_index]
|
|
109
109
|
|
|
@@ -136,8 +136,8 @@ class DeviceBuilder:
|
|
|
136
136
|
"""Set tile dimensions for matrix devices.
|
|
137
137
|
|
|
138
138
|
Args:
|
|
139
|
-
width: Tile width in
|
|
140
|
-
height: Tile height in
|
|
139
|
+
width: Tile width in zones
|
|
140
|
+
height: Tile height in zones
|
|
141
141
|
|
|
142
142
|
Returns:
|
|
143
143
|
Self for method chaining
|
|
@@ -104,8 +104,8 @@ def create_tile_device(
|
|
|
104
104
|
Args:
|
|
105
105
|
serial: Optional serial
|
|
106
106
|
tile_count: Optional tile count (uses product default)
|
|
107
|
-
tile_width: Optional tile width in
|
|
108
|
-
tile_height: Optional tile height in
|
|
107
|
+
tile_width: Optional tile width in zones (uses product default)
|
|
108
|
+
tile_height: Optional tile height in zones (uses product default)
|
|
109
109
|
firmware_version: Optional firmware version tuple (major, minor)
|
|
110
110
|
storage: Optional storage for persistence
|
|
111
111
|
scenario_manager: Optional scenario manager
|
|
@@ -164,8 +164,8 @@ def create_device(
|
|
|
164
164
|
zone_count: Number of zones for multizone devices (auto-determined)
|
|
165
165
|
extended_multizone: Enable extended multizone requests
|
|
166
166
|
tile_count: Number of tiles for matrix devices (default: 5)
|
|
167
|
-
tile_width: Width of each tile in
|
|
168
|
-
tile_height: Height of each tile in
|
|
167
|
+
tile_width: Width of each tile in zones (default: 8)
|
|
168
|
+
tile_height: Height of each tile in zones (default: 8)
|
|
169
169
|
firmware_version: Optional firmware version tuple (major, minor).
|
|
170
170
|
If not specified, uses 3.70 for extended_multizone
|
|
171
171
|
or 2.60 otherwise
|