lifx-emulator 1.0.1__tar.gz → 1.0.2__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-1.0.1 → lifx_emulator-1.0.2}/CLAUDE.md +42 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/PKG-INFO +1 -1
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/changelog.md +8 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/pyproject.toml +9 -1
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/__main__.py +4 -2
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/products/generator.py +30 -18
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/products/registry.py +63 -25
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/uv.lock +1 -1
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/.github/workflows/ci.yml +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/.github/workflows/docs.yml +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/.gitignore +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/.pre-commit-config.yaml +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/LICENSE +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/README.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/advanced/device-management-api.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/advanced/scenario-api.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/advanced/scenarios.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/advanced/storage.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/device.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/factories.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/index.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/products.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/protocol.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/server.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/api/storage.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/architecture/device-state.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/architecture/overview.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/architecture/packet-flow.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/architecture/protocol.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/assets/favicon.png +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/faq.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/getting-started/cli.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/getting-started/installation.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/getting-started/quickstart.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/best-practices.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/device-types.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/integration-testing.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/overview.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/products-and-specs.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/testing-scenarios.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/guide/web-interface.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/index.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/reference/glossary.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/reference/troubleshooting.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/stylesheets/extra.css +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/01-first-device.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/02-basic.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/03-advanced.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/04-integration.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/05-cicd.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/docs/tutorials/index.md +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/mkdocs.yml +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/renovate.json +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/__init__.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/api.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/async_storage.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/constants.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/device.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/device_states.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/factories.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/__init__.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/base.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/device_handlers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/light_handlers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/multizone_handlers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/registry.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/tile_handlers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/observers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/products/__init__.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/products/specs.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/products/specs.yml +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/__init__.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/base.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/const.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/generator.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/header.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/packets.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/protocol_types.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/protocol/serializer.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/scenario_manager.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/scenario_persistence.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/server.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/state_restorer.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/state_serializer.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/storage_protocol.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/conftest.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_api.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_async_storage.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_cli.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_cli_validation.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_device.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_device_edge_cases.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_device_handlers_extended.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_handler_registry.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_integration.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_light_handlers_extended.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_multizone_handlers_extended.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_observers.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_products_generator.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_protocol_generator.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_protocol_types_coverage.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_scenario_manager.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_scenario_persistence.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_serializer.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_server.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_state_restorer.py +0 -0
- {lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/tests/test_tile_handlers_extended.py +0 -0
|
@@ -553,6 +553,48 @@ All factory functions now use the specs system to load product-specific defaults
|
|
|
553
553
|
- Tile dimensions (e.g., 8x8 for Tiles, 5x6 for Candles)
|
|
554
554
|
- Users can override these defaults by passing explicit parameters
|
|
555
555
|
|
|
556
|
+
### Product Registry
|
|
557
|
+
|
|
558
|
+
**Product Registry** (`src/lifx_emulator/products/registry.py`):
|
|
559
|
+
- Auto-generated from official LIFX products.json (https://github.com/LIFX/products)
|
|
560
|
+
- Contains 137+ product definitions with capabilities, temperature ranges, and firmware requirements
|
|
561
|
+
- Pre-built `ProductInfo` instances for efficient runtime lookups
|
|
562
|
+
- Capability flags: `COLOR`, `INFRARED`, `MULTIZONE`, `CHAIN`, `MATRIX`, `RELAYS`, `BUTTONS`, `HEV`, `EXTENDED_MULTIZONE`
|
|
563
|
+
- Never edit this file manually - regenerate using the generator
|
|
564
|
+
|
|
565
|
+
**Product Registry Generator** (`src/lifx_emulator/products/generator.py`):
|
|
566
|
+
- Downloads latest products.json from LIFX GitHub repository
|
|
567
|
+
- Generates optimized Python code with pre-built product definitions
|
|
568
|
+
- Handles extended multizone capability detection:
|
|
569
|
+
- **Native support**: Products with `extended_multizone: true` in features (no firmware requirement)
|
|
570
|
+
- Examples: LIFX Z US (PID 117), LIFX Beam US (PID 119), LIFX Neon, LIFX Permanent Outdoor
|
|
571
|
+
- **Firmware upgrade**: Products with `extended_multizone` in upgrades section (requires minimum firmware)
|
|
572
|
+
- Examples: LIFX Z (PID 32, requires firmware 2.77+), LIFX Beam (PID 38, requires firmware 2.77+)
|
|
573
|
+
- Updates specs.yml with templates for new multizone/matrix products
|
|
574
|
+
- Run with: `python -m lifx_emulator.products.generator`
|
|
575
|
+
|
|
576
|
+
**Product Specs** (`src/lifx_emulator/products/specs.yml`):
|
|
577
|
+
- Product-specific configuration not available in upstream products.json
|
|
578
|
+
- Default zone counts, tile configurations, and device-specific defaults
|
|
579
|
+
- Used by factory functions to create realistic device configurations
|
|
580
|
+
- Manually maintained for accurate product specifications
|
|
581
|
+
|
|
582
|
+
**ProductInfo API:**
|
|
583
|
+
```python
|
|
584
|
+
from lifx_emulator.products.registry import get_product
|
|
585
|
+
|
|
586
|
+
product = get_product(117) # LIFX Z US
|
|
587
|
+
product.has_extended_multizone # True
|
|
588
|
+
product.min_ext_mz_firmware # None (native support)
|
|
589
|
+
product.supports_extended_multizone() # True
|
|
590
|
+
|
|
591
|
+
product = get_product(32) # LIFX Z (older model)
|
|
592
|
+
product.has_extended_multizone # True
|
|
593
|
+
product.min_ext_mz_firmware # 131149 (firmware 2.77)
|
|
594
|
+
product.supports_extended_multizone(131149) # True (meets requirement)
|
|
595
|
+
product.supports_extended_multizone(131148) # False (below requirement)
|
|
596
|
+
```
|
|
597
|
+
|
|
556
598
|
## Key Implementation Details
|
|
557
599
|
|
|
558
600
|
### MultiZone Handling
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.0.2 (2025-11-10)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Extended_multizone added to products correctly by generator
|
|
10
|
+
([`b6c4f78`](https://github.com/Djelibeybi/lifx-emulator/commit/b6c4f78c7353313b961acdb4283023a595141151))
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
## v1.0.1 (2025-11-10)
|
|
6
14
|
|
|
7
15
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lifx-emulator"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
description = "LIFX Emulator for testing LIFX LAN protocol libraries"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -103,6 +103,14 @@ addopts = """\
|
|
|
103
103
|
asyncio_mode = "auto"
|
|
104
104
|
asyncio_default_fixture_loop_scope = "function"
|
|
105
105
|
|
|
106
|
+
[tool.coverage.run]
|
|
107
|
+
omit = [
|
|
108
|
+
"src/lifx_emulator/protocol/generator.py",
|
|
109
|
+
"src/lifx_emulator/protocol/protocol_types.py",
|
|
110
|
+
"src/lifx_emulator/products/generator.py",
|
|
111
|
+
"src/lifx_emulator/products/registry.py",
|
|
112
|
+
]
|
|
113
|
+
|
|
106
114
|
[tool.coverage.report]
|
|
107
115
|
exclude_lines = [
|
|
108
116
|
"pragma: no cover",
|
|
@@ -102,10 +102,12 @@ def _format_product_capabilities(product: ProductInfo) -> str:
|
|
|
102
102
|
# Add additional capabilities
|
|
103
103
|
if product.has_infrared:
|
|
104
104
|
caps.append("infrared")
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
# Extended multizone is backwards compatible with multizone,
|
|
106
|
+
# so only show multizone if extended multizone is not present
|
|
107
107
|
if product.has_extended_multizone:
|
|
108
108
|
caps.append("extended-multizone")
|
|
109
|
+
elif product.has_multizone:
|
|
110
|
+
caps.append("multizone")
|
|
109
111
|
if product.has_matrix:
|
|
110
112
|
caps.append("matrix")
|
|
111
113
|
if product.has_hev:
|
|
@@ -97,16 +97,22 @@ def generate_product_definitions(
|
|
|
97
97
|
if features.get("hev"):
|
|
98
98
|
capabilities.append("ProductCapability.HEV")
|
|
99
99
|
|
|
100
|
-
# Check for extended multizone
|
|
100
|
+
# Check for extended multizone capability
|
|
101
101
|
min_ext_mz_firmware = None
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
|
|
103
|
+
# First check if it's a native feature (no firmware requirement)
|
|
104
|
+
if features.get("extended_multizone"):
|
|
105
|
+
capabilities.append("ProductCapability.EXTENDED_MULTIZONE")
|
|
106
|
+
else:
|
|
107
|
+
# Check if it's available as an upgrade (requires minimum firmware)
|
|
108
|
+
for upgrade in product.get("upgrades", []):
|
|
109
|
+
if upgrade.get("features", {}).get("extended_multizone"):
|
|
110
|
+
capabilities.append("ProductCapability.EXTENDED_MULTIZONE")
|
|
111
|
+
# Parse firmware version (major.minor format)
|
|
112
|
+
major = upgrade.get("major", 0)
|
|
113
|
+
minor = upgrade.get("minor", 0)
|
|
114
|
+
min_ext_mz_firmware = (major << 16) | minor
|
|
115
|
+
break
|
|
110
116
|
|
|
111
117
|
# Build capabilities expression
|
|
112
118
|
if capabilities:
|
|
@@ -357,16 +363,22 @@ class ProductRegistry:
|
|
|
357
363
|
if features.get("hev"):
|
|
358
364
|
capabilities |= ProductCapability.HEV
|
|
359
365
|
|
|
360
|
-
# Check for extended multizone
|
|
366
|
+
# Check for extended multizone capability
|
|
361
367
|
min_ext_mz_firmware = None
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
|
|
369
|
+
# First check if it's a native feature (no firmware requirement)
|
|
370
|
+
if features.get("extended_multizone"):
|
|
371
|
+
capabilities |= ProductCapability.EXTENDED_MULTIZONE
|
|
372
|
+
else:
|
|
373
|
+
# Check if it's available as an upgrade (requires minimum firmware)
|
|
374
|
+
for upgrade in product.get("upgrades", []):
|
|
375
|
+
if upgrade.get("features", {}).get("extended_multizone"):
|
|
376
|
+
capabilities |= ProductCapability.EXTENDED_MULTIZONE
|
|
377
|
+
# Parse firmware version (major.minor format)
|
|
378
|
+
major = upgrade.get("major", 0)
|
|
379
|
+
minor = upgrade.get("minor", 0)
|
|
380
|
+
min_ext_mz_firmware = (major << 16) | minor
|
|
381
|
+
break
|
|
370
382
|
|
|
371
383
|
# Parse temperature range
|
|
372
384
|
temp_range = None
|
|
@@ -677,7 +677,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
677
677
|
pid=117,
|
|
678
678
|
name="LIFX Z US",
|
|
679
679
|
vendor=1,
|
|
680
|
-
capabilities=ProductCapability.COLOR
|
|
680
|
+
capabilities=ProductCapability.COLOR
|
|
681
|
+
| ProductCapability.MULTIZONE
|
|
682
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
681
683
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
682
684
|
min_ext_mz_firmware=None,
|
|
683
685
|
),
|
|
@@ -685,7 +687,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
685
687
|
pid=118,
|
|
686
688
|
name="LIFX Z Intl",
|
|
687
689
|
vendor=1,
|
|
688
|
-
capabilities=ProductCapability.COLOR
|
|
690
|
+
capabilities=ProductCapability.COLOR
|
|
691
|
+
| ProductCapability.MULTIZONE
|
|
692
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
689
693
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
690
694
|
min_ext_mz_firmware=None,
|
|
691
695
|
),
|
|
@@ -693,7 +697,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
693
697
|
pid=119,
|
|
694
698
|
name="LIFX Beam US",
|
|
695
699
|
vendor=1,
|
|
696
|
-
capabilities=ProductCapability.COLOR
|
|
700
|
+
capabilities=ProductCapability.COLOR
|
|
701
|
+
| ProductCapability.MULTIZONE
|
|
702
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
697
703
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
698
704
|
min_ext_mz_firmware=None,
|
|
699
705
|
),
|
|
@@ -701,7 +707,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
701
707
|
pid=120,
|
|
702
708
|
name="LIFX Beam Intl",
|
|
703
709
|
vendor=1,
|
|
704
|
-
capabilities=ProductCapability.COLOR
|
|
710
|
+
capabilities=ProductCapability.COLOR
|
|
711
|
+
| ProductCapability.MULTIZONE
|
|
712
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
705
713
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
706
714
|
min_ext_mz_firmware=None,
|
|
707
715
|
),
|
|
@@ -853,7 +861,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
853
861
|
pid=141,
|
|
854
862
|
name="LIFX Neon US",
|
|
855
863
|
vendor=1,
|
|
856
|
-
capabilities=ProductCapability.COLOR
|
|
864
|
+
capabilities=ProductCapability.COLOR
|
|
865
|
+
| ProductCapability.MULTIZONE
|
|
866
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
857
867
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
858
868
|
min_ext_mz_firmware=None,
|
|
859
869
|
),
|
|
@@ -861,7 +871,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
861
871
|
pid=142,
|
|
862
872
|
name="LIFX Neon Intl",
|
|
863
873
|
vendor=1,
|
|
864
|
-
capabilities=ProductCapability.COLOR
|
|
874
|
+
capabilities=ProductCapability.COLOR
|
|
875
|
+
| ProductCapability.MULTIZONE
|
|
876
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
865
877
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
866
878
|
min_ext_mz_firmware=None,
|
|
867
879
|
),
|
|
@@ -869,7 +881,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
869
881
|
pid=143,
|
|
870
882
|
name="LIFX String US",
|
|
871
883
|
vendor=1,
|
|
872
|
-
capabilities=ProductCapability.COLOR
|
|
884
|
+
capabilities=ProductCapability.COLOR
|
|
885
|
+
| ProductCapability.MULTIZONE
|
|
886
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
873
887
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
874
888
|
min_ext_mz_firmware=None,
|
|
875
889
|
),
|
|
@@ -877,7 +891,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
877
891
|
pid=144,
|
|
878
892
|
name="LIFX String Intl",
|
|
879
893
|
vendor=1,
|
|
880
|
-
capabilities=ProductCapability.COLOR
|
|
894
|
+
capabilities=ProductCapability.COLOR
|
|
895
|
+
| ProductCapability.MULTIZONE
|
|
896
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
881
897
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
882
898
|
min_ext_mz_firmware=None,
|
|
883
899
|
),
|
|
@@ -885,7 +901,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
885
901
|
pid=161,
|
|
886
902
|
name="LIFX Outdoor Neon US",
|
|
887
903
|
vendor=1,
|
|
888
|
-
capabilities=ProductCapability.COLOR
|
|
904
|
+
capabilities=ProductCapability.COLOR
|
|
905
|
+
| ProductCapability.MULTIZONE
|
|
906
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
889
907
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
890
908
|
min_ext_mz_firmware=None,
|
|
891
909
|
),
|
|
@@ -893,7 +911,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
893
911
|
pid=162,
|
|
894
912
|
name="LIFX Outdoor Neon Intl",
|
|
895
913
|
vendor=1,
|
|
896
|
-
capabilities=ProductCapability.COLOR
|
|
914
|
+
capabilities=ProductCapability.COLOR
|
|
915
|
+
| ProductCapability.MULTIZONE
|
|
916
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
897
917
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
898
918
|
min_ext_mz_firmware=None,
|
|
899
919
|
),
|
|
@@ -1101,7 +1121,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1101
1121
|
pid=203,
|
|
1102
1122
|
name="LIFX String US",
|
|
1103
1123
|
vendor=1,
|
|
1104
|
-
capabilities=ProductCapability.COLOR
|
|
1124
|
+
capabilities=ProductCapability.COLOR
|
|
1125
|
+
| ProductCapability.MULTIZONE
|
|
1126
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1105
1127
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1106
1128
|
min_ext_mz_firmware=None,
|
|
1107
1129
|
),
|
|
@@ -1109,7 +1131,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1109
1131
|
pid=204,
|
|
1110
1132
|
name="LIFX String Intl",
|
|
1111
1133
|
vendor=1,
|
|
1112
|
-
capabilities=ProductCapability.COLOR
|
|
1134
|
+
capabilities=ProductCapability.COLOR
|
|
1135
|
+
| ProductCapability.MULTIZONE
|
|
1136
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1113
1137
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1114
1138
|
min_ext_mz_firmware=None,
|
|
1115
1139
|
),
|
|
@@ -1117,7 +1141,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1117
1141
|
pid=205,
|
|
1118
1142
|
name="LIFX Indoor Neon US",
|
|
1119
1143
|
vendor=1,
|
|
1120
|
-
capabilities=ProductCapability.COLOR
|
|
1144
|
+
capabilities=ProductCapability.COLOR
|
|
1145
|
+
| ProductCapability.MULTIZONE
|
|
1146
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1121
1147
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1122
1148
|
min_ext_mz_firmware=None,
|
|
1123
1149
|
),
|
|
@@ -1125,7 +1151,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1125
1151
|
pid=206,
|
|
1126
1152
|
name="LIFX Indoor Neon Intl",
|
|
1127
1153
|
vendor=1,
|
|
1128
|
-
capabilities=ProductCapability.COLOR
|
|
1154
|
+
capabilities=ProductCapability.COLOR
|
|
1155
|
+
| ProductCapability.MULTIZONE
|
|
1156
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1129
1157
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1130
1158
|
min_ext_mz_firmware=None,
|
|
1131
1159
|
),
|
|
@@ -1133,7 +1161,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1133
1161
|
pid=213,
|
|
1134
1162
|
name="LIFX Permanent Outdoor US",
|
|
1135
1163
|
vendor=1,
|
|
1136
|
-
capabilities=ProductCapability.COLOR
|
|
1164
|
+
capabilities=ProductCapability.COLOR
|
|
1165
|
+
| ProductCapability.MULTIZONE
|
|
1166
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1137
1167
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1138
1168
|
min_ext_mz_firmware=None,
|
|
1139
1169
|
),
|
|
@@ -1141,7 +1171,9 @@ PRODUCTS: dict[int, ProductInfo] = {
|
|
|
1141
1171
|
pid=214,
|
|
1142
1172
|
name="LIFX Permanent Outdoor Intl",
|
|
1143
1173
|
vendor=1,
|
|
1144
|
-
capabilities=ProductCapability.COLOR
|
|
1174
|
+
capabilities=ProductCapability.COLOR
|
|
1175
|
+
| ProductCapability.MULTIZONE
|
|
1176
|
+
| ProductCapability.EXTENDED_MULTIZONE,
|
|
1145
1177
|
temperature_range=TemperatureRange(min=1500, max=9000),
|
|
1146
1178
|
min_ext_mz_firmware=None,
|
|
1147
1179
|
),
|
|
@@ -1300,16 +1332,22 @@ class ProductRegistry:
|
|
|
1300
1332
|
if features.get("hev"):
|
|
1301
1333
|
capabilities |= ProductCapability.HEV
|
|
1302
1334
|
|
|
1303
|
-
# Check for extended multizone
|
|
1335
|
+
# Check for extended multizone capability
|
|
1304
1336
|
min_ext_mz_firmware = None
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1337
|
+
|
|
1338
|
+
# First check if it's a native feature (no firmware requirement)
|
|
1339
|
+
if features.get("extended_multizone"):
|
|
1340
|
+
capabilities |= ProductCapability.EXTENDED_MULTIZONE
|
|
1341
|
+
else:
|
|
1342
|
+
# Check if it's available as an upgrade (requires minimum firmware)
|
|
1343
|
+
for upgrade in product.get("upgrades", []):
|
|
1344
|
+
if upgrade.get("features", {}).get("extended_multizone"):
|
|
1345
|
+
capabilities |= ProductCapability.EXTENDED_MULTIZONE
|
|
1346
|
+
# Parse firmware version (major.minor format)
|
|
1347
|
+
major = upgrade.get("major", 0)
|
|
1348
|
+
minor = upgrade.get("minor", 0)
|
|
1349
|
+
min_ext_mz_firmware = (major << 16) | minor
|
|
1350
|
+
break
|
|
1313
1351
|
|
|
1314
1352
|
# Parse temperature range
|
|
1315
1353
|
temp_range = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lifx_emulator-1.0.1 → lifx_emulator-1.0.2}/src/lifx_emulator/handlers/multizone_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|