lifx-emulator 2.4.0__py3-none-any.whl → 3.0.1__py3-none-any.whl
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-3.0.1.dist-info/METADATA +102 -0
- lifx_emulator-3.0.1.dist-info/RECORD +18 -0
- lifx_emulator-3.0.1.dist-info/entry_points.txt +2 -0
- lifx_emulator_app/__init__.py +10 -0
- {lifx_emulator → lifx_emulator_app}/__main__.py +2 -3
- {lifx_emulator → lifx_emulator_app}/api/__init__.py +1 -1
- {lifx_emulator → lifx_emulator_app}/api/app.py +3 -3
- {lifx_emulator → lifx_emulator_app}/api/mappers/__init__.py +1 -1
- {lifx_emulator → lifx_emulator_app}/api/mappers/device_mapper.py +1 -1
- {lifx_emulator → lifx_emulator_app}/api/models.py +1 -2
- lifx_emulator_app/api/routers/__init__.py +11 -0
- {lifx_emulator → lifx_emulator_app}/api/routers/devices.py +2 -2
- {lifx_emulator → lifx_emulator_app}/api/routers/monitoring.py +1 -1
- {lifx_emulator → lifx_emulator_app}/api/routers/scenarios.py +1 -1
- lifx_emulator_app/api/services/__init__.py +8 -0
- {lifx_emulator → lifx_emulator_app}/api/services/device_service.py +3 -2
- lifx_emulator/__init__.py +0 -31
- lifx_emulator/api/routers/__init__.py +0 -11
- lifx_emulator/api/services/__init__.py +0 -8
- lifx_emulator/constants.py +0 -33
- lifx_emulator/devices/__init__.py +0 -37
- lifx_emulator/devices/device.py +0 -395
- lifx_emulator/devices/manager.py +0 -256
- lifx_emulator/devices/observers.py +0 -139
- lifx_emulator/devices/persistence.py +0 -308
- lifx_emulator/devices/state_restorer.py +0 -259
- lifx_emulator/devices/state_serializer.py +0 -157
- lifx_emulator/devices/states.py +0 -381
- lifx_emulator/factories/__init__.py +0 -39
- lifx_emulator/factories/builder.py +0 -375
- lifx_emulator/factories/default_config.py +0 -158
- lifx_emulator/factories/factory.py +0 -252
- lifx_emulator/factories/firmware_config.py +0 -77
- lifx_emulator/factories/serial_generator.py +0 -82
- lifx_emulator/handlers/__init__.py +0 -39
- lifx_emulator/handlers/base.py +0 -49
- lifx_emulator/handlers/device_handlers.py +0 -322
- lifx_emulator/handlers/light_handlers.py +0 -503
- lifx_emulator/handlers/multizone_handlers.py +0 -249
- lifx_emulator/handlers/registry.py +0 -110
- lifx_emulator/handlers/tile_handlers.py +0 -488
- lifx_emulator/products/__init__.py +0 -28
- lifx_emulator/products/generator.py +0 -1079
- lifx_emulator/products/registry.py +0 -1530
- lifx_emulator/products/specs.py +0 -284
- lifx_emulator/products/specs.yml +0 -386
- lifx_emulator/protocol/__init__.py +0 -1
- lifx_emulator/protocol/base.py +0 -446
- lifx_emulator/protocol/const.py +0 -8
- lifx_emulator/protocol/generator.py +0 -1384
- lifx_emulator/protocol/header.py +0 -159
- lifx_emulator/protocol/packets.py +0 -1351
- lifx_emulator/protocol/protocol_types.py +0 -817
- lifx_emulator/protocol/serializer.py +0 -379
- lifx_emulator/repositories/__init__.py +0 -22
- lifx_emulator/repositories/device_repository.py +0 -155
- lifx_emulator/repositories/storage_backend.py +0 -107
- lifx_emulator/scenarios/__init__.py +0 -22
- lifx_emulator/scenarios/manager.py +0 -322
- lifx_emulator/scenarios/models.py +0 -112
- lifx_emulator/scenarios/persistence.py +0 -241
- lifx_emulator/server.py +0 -464
- lifx_emulator-2.4.0.dist-info/METADATA +0 -107
- lifx_emulator-2.4.0.dist-info/RECORD +0 -62
- lifx_emulator-2.4.0.dist-info/entry_points.txt +0 -2
- lifx_emulator-2.4.0.dist-info/licenses/LICENSE +0 -35
- {lifx_emulator-2.4.0.dist-info → lifx_emulator-3.0.1.dist-info}/WHEEL +0 -0
- {lifx_emulator → lifx_emulator_app}/api/templates/dashboard.html +0 -0
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
"""Factory functions for creating emulated LIFX devices."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
from lifx_emulator.devices import EmulatedLifxDevice
|
|
8
|
-
from lifx_emulator.factories.builder import DeviceBuilder
|
|
9
|
-
from lifx_emulator.products.registry import get_product
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from lifx_emulator.devices import DevicePersistenceAsyncFile
|
|
13
|
-
from lifx_emulator.scenarios import HierarchicalScenarioManager
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def create_color_light(
|
|
17
|
-
serial: str | None = None,
|
|
18
|
-
firmware_version: tuple[int, int] | None = None,
|
|
19
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
20
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
21
|
-
) -> EmulatedLifxDevice:
|
|
22
|
-
"""Create a regular color light (LIFX Color)"""
|
|
23
|
-
return create_device(
|
|
24
|
-
91,
|
|
25
|
-
serial=serial,
|
|
26
|
-
firmware_version=firmware_version,
|
|
27
|
-
storage=storage,
|
|
28
|
-
scenario_manager=scenario_manager,
|
|
29
|
-
) # LIFX Color
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def create_infrared_light(
|
|
33
|
-
serial: str | None = None,
|
|
34
|
-
firmware_version: tuple[int, int] | None = None,
|
|
35
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
36
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
37
|
-
) -> EmulatedLifxDevice:
|
|
38
|
-
"""Create an infrared-enabled light (LIFX A19 Night Vision)"""
|
|
39
|
-
return create_device(
|
|
40
|
-
29,
|
|
41
|
-
serial=serial,
|
|
42
|
-
firmware_version=firmware_version,
|
|
43
|
-
storage=storage,
|
|
44
|
-
scenario_manager=scenario_manager,
|
|
45
|
-
) # LIFX A19 Night Vision
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def create_hev_light(
|
|
49
|
-
serial: str | None = None,
|
|
50
|
-
firmware_version: tuple[int, int] | None = None,
|
|
51
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
52
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
53
|
-
) -> EmulatedLifxDevice:
|
|
54
|
-
"""Create an HEV-enabled light (LIFX Clean)"""
|
|
55
|
-
return create_device(
|
|
56
|
-
90,
|
|
57
|
-
serial=serial,
|
|
58
|
-
firmware_version=firmware_version,
|
|
59
|
-
storage=storage,
|
|
60
|
-
scenario_manager=scenario_manager,
|
|
61
|
-
) # LIFX Clean
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def create_multizone_light(
|
|
65
|
-
serial: str | None = None,
|
|
66
|
-
zone_count: int | None = None,
|
|
67
|
-
extended_multizone: bool = True,
|
|
68
|
-
firmware_version: tuple[int, int] | None = None,
|
|
69
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
70
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
71
|
-
) -> EmulatedLifxDevice:
|
|
72
|
-
"""Create a multizone light (LIFX Beam)
|
|
73
|
-
|
|
74
|
-
Args:
|
|
75
|
-
serial: Optional serial
|
|
76
|
-
zone_count: Optional zone count (uses product default if not specified)
|
|
77
|
-
extended_multizone: enables support for extended multizone requests
|
|
78
|
-
firmware_version: Optional firmware version tuple (major, minor)
|
|
79
|
-
storage: Optional storage for persistence
|
|
80
|
-
scenario_manager: Optional scenario manager
|
|
81
|
-
"""
|
|
82
|
-
return create_device(
|
|
83
|
-
38,
|
|
84
|
-
serial=serial,
|
|
85
|
-
zone_count=zone_count,
|
|
86
|
-
extended_multizone=extended_multizone,
|
|
87
|
-
firmware_version=firmware_version,
|
|
88
|
-
storage=storage,
|
|
89
|
-
scenario_manager=scenario_manager,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def create_tile_device(
|
|
94
|
-
serial: str | None = None,
|
|
95
|
-
tile_count: int | None = None,
|
|
96
|
-
tile_width: int | None = None,
|
|
97
|
-
tile_height: int | None = None,
|
|
98
|
-
firmware_version: tuple[int, int] | None = None,
|
|
99
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
100
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
101
|
-
) -> EmulatedLifxDevice:
|
|
102
|
-
"""Create a tile device (LIFX Tile)
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
serial: Optional serial
|
|
106
|
-
tile_count: Optional tile count (uses product default)
|
|
107
|
-
tile_width: Optional tile width in zones (uses product default)
|
|
108
|
-
tile_height: Optional tile height in zones (uses product default)
|
|
109
|
-
firmware_version: Optional firmware version tuple (major, minor)
|
|
110
|
-
storage: Optional storage for persistence
|
|
111
|
-
scenario_manager: Optional scenario manager
|
|
112
|
-
"""
|
|
113
|
-
return create_device(
|
|
114
|
-
55,
|
|
115
|
-
serial=serial,
|
|
116
|
-
tile_count=tile_count,
|
|
117
|
-
tile_width=tile_width,
|
|
118
|
-
tile_height=tile_height,
|
|
119
|
-
firmware_version=firmware_version,
|
|
120
|
-
storage=storage,
|
|
121
|
-
scenario_manager=scenario_manager,
|
|
122
|
-
) # LIFX Tile
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def create_color_temperature_light(
|
|
126
|
-
serial: str | None = None,
|
|
127
|
-
firmware_version: tuple[int, int] | None = None,
|
|
128
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
129
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
130
|
-
) -> EmulatedLifxDevice:
|
|
131
|
-
"""Create a color temperature light (LIFX Mini White to Warm).
|
|
132
|
-
|
|
133
|
-
Variable color temperature, no RGB.
|
|
134
|
-
"""
|
|
135
|
-
return create_device(
|
|
136
|
-
50,
|
|
137
|
-
serial=serial,
|
|
138
|
-
firmware_version=firmware_version,
|
|
139
|
-
storage=storage,
|
|
140
|
-
scenario_manager=scenario_manager,
|
|
141
|
-
) # LIFX Mini White to Warm
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def create_switch(
|
|
145
|
-
serial: str | None = None,
|
|
146
|
-
product_id: int = 70,
|
|
147
|
-
firmware_version: tuple[int, int] | None = None,
|
|
148
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
149
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
150
|
-
) -> EmulatedLifxDevice:
|
|
151
|
-
"""Create a LIFX Switch device.
|
|
152
|
-
|
|
153
|
-
Switches have has_relays and has_buttons capabilities but no lighting.
|
|
154
|
-
They respond with StateUnhandled (223) to Light, MultiZone, and Tile packets.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
serial: Device serial number (auto-generated if None)
|
|
158
|
-
product_id: Switch product ID (default: 70 - LIFX Switch)
|
|
159
|
-
firmware_version: Optional firmware version (major, minor)
|
|
160
|
-
storage: Optional persistence backend
|
|
161
|
-
scenario_manager: Optional scenario manager for testing
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
EmulatedLifxDevice configured as a switch
|
|
165
|
-
"""
|
|
166
|
-
return create_device(
|
|
167
|
-
product_id,
|
|
168
|
-
serial=serial,
|
|
169
|
-
firmware_version=firmware_version,
|
|
170
|
-
storage=storage,
|
|
171
|
-
scenario_manager=scenario_manager,
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def create_device(
|
|
176
|
-
product_id: int,
|
|
177
|
-
serial: str | None = None,
|
|
178
|
-
zone_count: int | None = None,
|
|
179
|
-
extended_multizone: bool | None = None,
|
|
180
|
-
tile_count: int | None = None,
|
|
181
|
-
tile_width: int | None = None,
|
|
182
|
-
tile_height: int | None = None,
|
|
183
|
-
firmware_version: tuple[int, int] | None = None,
|
|
184
|
-
storage: DevicePersistenceAsyncFile | None = None,
|
|
185
|
-
scenario_manager: HierarchicalScenarioManager | None = None,
|
|
186
|
-
) -> EmulatedLifxDevice:
|
|
187
|
-
"""Create a device for any LIFX product using the product registry.
|
|
188
|
-
|
|
189
|
-
This function uses the DeviceBuilder pattern to construct devices with
|
|
190
|
-
clean separation of concerns and testable components.
|
|
191
|
-
|
|
192
|
-
Args:
|
|
193
|
-
product_id: Product ID from the LIFX product registry
|
|
194
|
-
serial: Optional serial (auto-generated if not provided)
|
|
195
|
-
zone_count: Number of zones for multizone devices (auto-determined)
|
|
196
|
-
extended_multizone: Enable extended multizone requests
|
|
197
|
-
tile_count: Number of tiles for matrix devices (default: 5)
|
|
198
|
-
tile_width: Width of each tile in zones (default: 8)
|
|
199
|
-
tile_height: Height of each tile in zones (default: 8)
|
|
200
|
-
firmware_version: Optional firmware version tuple (major, minor).
|
|
201
|
-
If not specified, uses 3.70 for extended_multizone
|
|
202
|
-
or 2.60 otherwise
|
|
203
|
-
storage: Optional storage for persistence
|
|
204
|
-
scenario_manager: Optional scenario manager for testing
|
|
205
|
-
|
|
206
|
-
Returns:
|
|
207
|
-
EmulatedLifxDevice configured for the specified product
|
|
208
|
-
|
|
209
|
-
Raises:
|
|
210
|
-
ValueError: If product_id is not found in registry
|
|
211
|
-
|
|
212
|
-
Examples:
|
|
213
|
-
>>> # Create LIFX A19 (PID 27)
|
|
214
|
-
>>> device = create_device(27)
|
|
215
|
-
>>> # Create LIFX Z strip (PID 32) with 24 zones
|
|
216
|
-
>>> strip = create_device(32, zone_count=24)
|
|
217
|
-
>>> # Create LIFX Tile (PID 55) with 10 tiles
|
|
218
|
-
>>> tiles = create_device(55, tile_count=10)
|
|
219
|
-
"""
|
|
220
|
-
# Get product info from registry
|
|
221
|
-
product_info = get_product(product_id)
|
|
222
|
-
if product_info is None:
|
|
223
|
-
raise ValueError(f"Unknown product ID: {product_id}")
|
|
224
|
-
|
|
225
|
-
# Build device using builder pattern
|
|
226
|
-
builder = DeviceBuilder(product_info)
|
|
227
|
-
|
|
228
|
-
if serial is not None:
|
|
229
|
-
builder.with_serial(serial)
|
|
230
|
-
|
|
231
|
-
if zone_count is not None:
|
|
232
|
-
builder.with_zone_count(zone_count)
|
|
233
|
-
|
|
234
|
-
if extended_multizone is not None:
|
|
235
|
-
builder.with_extended_multizone(extended_multizone)
|
|
236
|
-
|
|
237
|
-
if tile_count is not None:
|
|
238
|
-
builder.with_tile_count(tile_count)
|
|
239
|
-
|
|
240
|
-
if tile_width is not None and tile_height is not None:
|
|
241
|
-
builder.with_tile_dimensions(tile_width, tile_height)
|
|
242
|
-
|
|
243
|
-
if firmware_version is not None:
|
|
244
|
-
builder.with_firmware_version(*firmware_version)
|
|
245
|
-
|
|
246
|
-
if storage is not None:
|
|
247
|
-
builder.with_storage(storage)
|
|
248
|
-
|
|
249
|
-
if scenario_manager is not None:
|
|
250
|
-
builder.with_scenario_manager(scenario_manager)
|
|
251
|
-
|
|
252
|
-
return builder.build()
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"""Firmware version configuration for devices."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from lifx_emulator.products.specs import get_default_firmware_version
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class FirmwareConfig:
|
|
9
|
-
"""Determines firmware versions for devices.
|
|
10
|
-
|
|
11
|
-
Extended multizone support requires firmware 3.70+.
|
|
12
|
-
Devices without extended multizone use firmware 2.60.
|
|
13
|
-
|
|
14
|
-
Examples:
|
|
15
|
-
>>> config = FirmwareConfig()
|
|
16
|
-
>>> major, minor = config.get_firmware_version(extended_multizone=True)
|
|
17
|
-
>>> (major, minor)
|
|
18
|
-
(3, 70)
|
|
19
|
-
>>> major, minor = config.get_firmware_version(extended_multizone=False)
|
|
20
|
-
>>> (major, minor)
|
|
21
|
-
(2, 60)
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
# Firmware versions
|
|
25
|
-
VERSION_EXTENDED = (3, 70) # Extended multizone support
|
|
26
|
-
VERSION_LEGACY = (2, 60) # Legacy firmware
|
|
27
|
-
|
|
28
|
-
def get_firmware_version(
|
|
29
|
-
self,
|
|
30
|
-
product_id: int | None = None,
|
|
31
|
-
extended_multizone: bool | None = None,
|
|
32
|
-
override: tuple[int, int] | None = None,
|
|
33
|
-
) -> tuple[int, int]:
|
|
34
|
-
"""Get firmware version based on product specs or extended multizone support.
|
|
35
|
-
|
|
36
|
-
Precedence order:
|
|
37
|
-
1. Explicit override parameter
|
|
38
|
-
2. Product-specific default from specs.yml
|
|
39
|
-
3. Extended multizone flag (3.70 for True/None, 2.60 for False)
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
product_id: Optional product ID to check specs for defaults
|
|
43
|
-
extended_multizone: Whether device supports extended multizone.
|
|
44
|
-
None or True defaults to 3.70, False gives 2.60
|
|
45
|
-
override: Optional explicit firmware version to use
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
Tuple of (major, minor) firmware version
|
|
49
|
-
|
|
50
|
-
Examples:
|
|
51
|
-
>>> config = FirmwareConfig()
|
|
52
|
-
>>> config.get_firmware_version(extended_multizone=True)
|
|
53
|
-
(3, 70)
|
|
54
|
-
>>> config.get_firmware_version(extended_multizone=False)
|
|
55
|
-
(2, 60)
|
|
56
|
-
>>> config.get_firmware_version(override=(4, 0))
|
|
57
|
-
(4, 0)
|
|
58
|
-
>>> # With product_id, uses specs if defined
|
|
59
|
-
>>> config.get_firmware_version(product_id=27) # doctest: +SKIP
|
|
60
|
-
(3, 70)
|
|
61
|
-
"""
|
|
62
|
-
# Explicit override takes precedence
|
|
63
|
-
if override is not None:
|
|
64
|
-
return override
|
|
65
|
-
|
|
66
|
-
# Check product-specific defaults from specs
|
|
67
|
-
if product_id is not None:
|
|
68
|
-
specs_version = get_default_firmware_version(product_id)
|
|
69
|
-
if specs_version is not None:
|
|
70
|
-
return specs_version
|
|
71
|
-
|
|
72
|
-
# None or True defaults to extended (3.70)
|
|
73
|
-
# Only explicit False gives legacy (2.60)
|
|
74
|
-
if extended_multizone is False:
|
|
75
|
-
return self.VERSION_LEGACY
|
|
76
|
-
else:
|
|
77
|
-
return self.VERSION_EXTENDED
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"""Serial number generation service for LIFX devices."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import random
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from lifx_emulator.products.registry import ProductInfo
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class SerialGenerator:
|
|
13
|
-
"""Generates serial numbers for emulated LIFX devices.
|
|
14
|
-
|
|
15
|
-
Serial numbers are 12-character hex strings with different prefixes
|
|
16
|
-
based on device capabilities for easier identification.
|
|
17
|
-
|
|
18
|
-
Prefixes:
|
|
19
|
-
- d073d9: Matrix/Tile devices
|
|
20
|
-
- d073d8: Multizone devices (strips/beams)
|
|
21
|
-
- d073d7: HEV devices
|
|
22
|
-
- d073d6: Infrared devices
|
|
23
|
-
- d073d5: Regular color/temperature lights
|
|
24
|
-
|
|
25
|
-
Examples:
|
|
26
|
-
>>> generator = SerialGenerator()
|
|
27
|
-
>>> serial = generator.generate(product_info)
|
|
28
|
-
>>> len(serial)
|
|
29
|
-
12
|
|
30
|
-
>>> serial.startswith("d073d")
|
|
31
|
-
True
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
# Device type prefixes for easy identification
|
|
35
|
-
PREFIX_MATRIX = "d073d9"
|
|
36
|
-
PREFIX_MULTIZONE = "d073d8"
|
|
37
|
-
PREFIX_HEV = "d073d7"
|
|
38
|
-
PREFIX_INFRARED = "d073d6"
|
|
39
|
-
PREFIX_DEFAULT = "d073d5"
|
|
40
|
-
|
|
41
|
-
def generate(self, product_info: ProductInfo) -> str:
|
|
42
|
-
"""Generate a serial number based on product capabilities.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
product_info: Product information from registry
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
12-character hex serial number
|
|
49
|
-
|
|
50
|
-
Examples:
|
|
51
|
-
>>> from lifx_emulator.products.registry import get_product
|
|
52
|
-
>>> generator = SerialGenerator()
|
|
53
|
-
>>> product = get_product(55) # LIFX Tile
|
|
54
|
-
>>> serial = generator.generate(product)
|
|
55
|
-
>>> serial.startswith("d073d9") # Matrix prefix
|
|
56
|
-
True
|
|
57
|
-
"""
|
|
58
|
-
prefix = self._determine_prefix(product_info)
|
|
59
|
-
suffix = random.randint(100000, 999999) # nosec
|
|
60
|
-
return f"{prefix}{suffix:06x}"
|
|
61
|
-
|
|
62
|
-
def _determine_prefix(self, product_info: ProductInfo) -> str:
|
|
63
|
-
"""Determine the prefix based on product capabilities.
|
|
64
|
-
|
|
65
|
-
Precedence: matrix > multizone > hev > infrared > default
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
product_info: Product information from registry
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
6-character hex prefix
|
|
72
|
-
"""
|
|
73
|
-
if product_info.has_matrix:
|
|
74
|
-
return self.PREFIX_MATRIX
|
|
75
|
-
elif product_info.has_multizone:
|
|
76
|
-
return self.PREFIX_MULTIZONE
|
|
77
|
-
elif product_info.has_hev:
|
|
78
|
-
return self.PREFIX_HEV
|
|
79
|
-
elif product_info.has_infrared:
|
|
80
|
-
return self.PREFIX_INFRARED
|
|
81
|
-
else:
|
|
82
|
-
return self.PREFIX_DEFAULT
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"""Packet handler infrastructure using Strategy pattern.
|
|
2
|
-
|
|
3
|
-
This module provides the base classes and registry for handling LIFX protocol packets.
|
|
4
|
-
Each packet type has a dedicated handler class that implements the business logic.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from lifx_emulator.handlers.base import PacketHandler
|
|
8
|
-
from lifx_emulator.handlers.device_handlers import ALL_DEVICE_HANDLERS
|
|
9
|
-
from lifx_emulator.handlers.light_handlers import ALL_LIGHT_HANDLERS
|
|
10
|
-
from lifx_emulator.handlers.multizone_handlers import ALL_MULTIZONE_HANDLERS
|
|
11
|
-
from lifx_emulator.handlers.registry import HandlerRegistry
|
|
12
|
-
from lifx_emulator.handlers.tile_handlers import ALL_TILE_HANDLERS
|
|
13
|
-
|
|
14
|
-
__all__ = [
|
|
15
|
-
"PacketHandler",
|
|
16
|
-
"HandlerRegistry",
|
|
17
|
-
"ALL_DEVICE_HANDLERS",
|
|
18
|
-
"ALL_LIGHT_HANDLERS",
|
|
19
|
-
"ALL_MULTIZONE_HANDLERS",
|
|
20
|
-
"ALL_TILE_HANDLERS",
|
|
21
|
-
"create_default_registry",
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def create_default_registry() -> HandlerRegistry:
|
|
26
|
-
"""Create a handler registry with all default handlers registered.
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
HandlerRegistry with all built-in handlers
|
|
30
|
-
"""
|
|
31
|
-
registry = HandlerRegistry()
|
|
32
|
-
|
|
33
|
-
# Register all handler categories
|
|
34
|
-
registry.register_all(ALL_DEVICE_HANDLERS)
|
|
35
|
-
registry.register_all(ALL_LIGHT_HANDLERS)
|
|
36
|
-
registry.register_all(ALL_MULTIZONE_HANDLERS)
|
|
37
|
-
registry.register_all(ALL_TILE_HANDLERS)
|
|
38
|
-
|
|
39
|
-
return registry
|
lifx_emulator/handlers/base.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Base classes for packet handlers."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from lifx_emulator.devices import DeviceState
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class PacketHandler(ABC):
|
|
13
|
-
"""Base class for all packet handlers.
|
|
14
|
-
|
|
15
|
-
Each handler implements the logic for processing a specific packet type
|
|
16
|
-
and optionally generating a response packet.
|
|
17
|
-
|
|
18
|
-
Handlers are stateless and operate on the provided DeviceState.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
# Subclasses must define the packet type they handle
|
|
22
|
-
PKT_TYPE: int
|
|
23
|
-
|
|
24
|
-
@abstractmethod
|
|
25
|
-
def handle(
|
|
26
|
-
self, device_state: DeviceState, packet: Any | None, res_required: bool
|
|
27
|
-
) -> list[Any]:
|
|
28
|
-
"""Handle the packet and return response packet(s).
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
device_state: Current device state to read/modify
|
|
32
|
-
packet: Unpacked packet object (None for packets with no payload)
|
|
33
|
-
res_required: Whether client requested a response (res_required
|
|
34
|
-
flag from header)
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
List of response packets (empty list if no response needed).
|
|
38
|
-
This unified return type simplifies packet processing logic.
|
|
39
|
-
|
|
40
|
-
Notes:
|
|
41
|
-
- Handlers should modify device_state directly for SET operations
|
|
42
|
-
- Handlers should check device capabilities before processing
|
|
43
|
-
- Return empty list [] if the device doesn't support this packet type
|
|
44
|
-
- Always return a list, even for single responses: [packet]
|
|
45
|
-
"""
|
|
46
|
-
pass
|
|
47
|
-
|
|
48
|
-
def __repr__(self) -> str:
|
|
49
|
-
return f"{self.__class__.__name__}(PKT_TYPE={self.PKT_TYPE})"
|