lifx-emulator 1.0.2__py3-none-any.whl → 2.0.0__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.
Files changed (57) hide show
  1. lifx_emulator/__init__.py +1 -1
  2. lifx_emulator/__main__.py +26 -51
  3. lifx_emulator/api/__init__.py +18 -0
  4. lifx_emulator/api/app.py +154 -0
  5. lifx_emulator/api/mappers/__init__.py +5 -0
  6. lifx_emulator/api/mappers/device_mapper.py +114 -0
  7. lifx_emulator/api/models.py +133 -0
  8. lifx_emulator/api/routers/__init__.py +11 -0
  9. lifx_emulator/api/routers/devices.py +130 -0
  10. lifx_emulator/api/routers/monitoring.py +52 -0
  11. lifx_emulator/api/routers/scenarios.py +247 -0
  12. lifx_emulator/api/services/__init__.py +8 -0
  13. lifx_emulator/api/services/device_service.py +198 -0
  14. lifx_emulator/{api.py → api/templates/dashboard.html} +0 -942
  15. lifx_emulator/devices/__init__.py +37 -0
  16. lifx_emulator/devices/device.py +333 -0
  17. lifx_emulator/devices/manager.py +256 -0
  18. lifx_emulator/{async_storage.py → devices/persistence.py} +3 -3
  19. lifx_emulator/{state_restorer.py → devices/state_restorer.py} +2 -2
  20. lifx_emulator/devices/states.py +333 -0
  21. lifx_emulator/factories/__init__.py +37 -0
  22. lifx_emulator/factories/builder.py +371 -0
  23. lifx_emulator/factories/default_config.py +158 -0
  24. lifx_emulator/factories/factory.py +221 -0
  25. lifx_emulator/factories/firmware_config.py +59 -0
  26. lifx_emulator/factories/serial_generator.py +82 -0
  27. lifx_emulator/handlers/base.py +1 -1
  28. lifx_emulator/handlers/device_handlers.py +10 -28
  29. lifx_emulator/handlers/light_handlers.py +5 -9
  30. lifx_emulator/handlers/multizone_handlers.py +1 -1
  31. lifx_emulator/handlers/tile_handlers.py +1 -1
  32. lifx_emulator/products/generator.py +389 -170
  33. lifx_emulator/products/registry.py +52 -40
  34. lifx_emulator/products/specs.py +12 -13
  35. lifx_emulator/protocol/base.py +115 -61
  36. lifx_emulator/protocol/generator.py +18 -5
  37. lifx_emulator/protocol/packets.py +7 -7
  38. lifx_emulator/repositories/__init__.py +22 -0
  39. lifx_emulator/repositories/device_repository.py +155 -0
  40. lifx_emulator/repositories/storage_backend.py +107 -0
  41. lifx_emulator/scenarios/__init__.py +22 -0
  42. lifx_emulator/{scenario_manager.py → scenarios/manager.py} +11 -91
  43. lifx_emulator/scenarios/models.py +112 -0
  44. lifx_emulator/{scenario_persistence.py → scenarios/persistence.py} +82 -47
  45. lifx_emulator/server.py +38 -64
  46. {lifx_emulator-1.0.2.dist-info → lifx_emulator-2.0.0.dist-info}/METADATA +1 -1
  47. lifx_emulator-2.0.0.dist-info/RECORD +62 -0
  48. lifx_emulator/device.py +0 -750
  49. lifx_emulator/device_states.py +0 -114
  50. lifx_emulator/factories.py +0 -380
  51. lifx_emulator/storage_protocol.py +0 -100
  52. lifx_emulator-1.0.2.dist-info/RECORD +0 -40
  53. /lifx_emulator/{observers.py → devices/observers.py} +0 -0
  54. /lifx_emulator/{state_serializer.py → devices/state_serializer.py} +0 -0
  55. {lifx_emulator-1.0.2.dist-info → lifx_emulator-2.0.0.dist-info}/WHEEL +0 -0
  56. {lifx_emulator-1.0.2.dist-info → lifx_emulator-2.0.0.dist-info}/entry_points.txt +0 -0
  57. {lifx_emulator-1.0.2.dist-info → lifx_emulator-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,114 +0,0 @@
1
- """Focused state dataclasses following Single Responsibility Principle."""
2
-
3
- from __future__ import annotations
4
-
5
- import time
6
- import uuid
7
- from dataclasses import dataclass, field
8
- from typing import Any
9
-
10
- from lifx_emulator.constants import LIFX_UDP_PORT
11
- from lifx_emulator.protocol.protocol_types import LightHsbk
12
-
13
-
14
- @dataclass
15
- class CoreDeviceState:
16
- """Core device identification and basic state."""
17
-
18
- serial: str
19
- label: str
20
- power_level: int
21
- color: LightHsbk
22
- vendor: int
23
- product: int
24
- version_major: int
25
- version_minor: int
26
- build_timestamp: int
27
- uptime_ns: int = 0
28
- mac_address: bytes = field(default_factory=lambda: bytes.fromhex("d073d5123456"))
29
- port: int = LIFX_UDP_PORT
30
-
31
-
32
- @dataclass
33
- class NetworkState:
34
- """Network and connectivity state."""
35
-
36
- wifi_signal: float = -45.0
37
-
38
-
39
- @dataclass
40
- class LocationState:
41
- """Device location metadata."""
42
-
43
- location_id: bytes = field(default_factory=lambda: uuid.uuid4().bytes)
44
- location_label: str = "Test Location"
45
- location_updated_at: int = field(default_factory=lambda: int(time.time() * 1e9))
46
-
47
-
48
- @dataclass
49
- class GroupState:
50
- """Device group metadata."""
51
-
52
- group_id: bytes = field(default_factory=lambda: uuid.uuid4().bytes)
53
- group_label: str = "Test Group"
54
- group_updated_at: int = field(default_factory=lambda: int(time.time() * 1e9))
55
-
56
-
57
- @dataclass
58
- class InfraredState:
59
- """Infrared capability state."""
60
-
61
- infrared_brightness: int = 0 # 0-65535
62
-
63
-
64
- @dataclass
65
- class HevState:
66
- """HEV (germicidal UV) capability state."""
67
-
68
- hev_cycle_duration_s: int = 7200 # 2 hours default
69
- hev_cycle_remaining_s: int = 0
70
- hev_cycle_last_power: bool = False
71
- hev_indication: bool = True
72
- hev_last_result: int = 0 # 0=success
73
-
74
-
75
- @dataclass
76
- class MultiZoneState:
77
- """Multizone (strip/beam) capability state."""
78
-
79
- zone_count: int
80
- zone_colors: list[LightHsbk]
81
- effect_type: int = 0 # 0=OFF, 1=MOVE, 2=RESERVED
82
- effect_speed: int = 5 # Duration of one cycle in seconds
83
-
84
-
85
- @dataclass
86
- class MatrixState:
87
- """Matrix (tile/candle) capability state."""
88
-
89
- tile_count: int
90
- tile_devices: list[dict[str, Any]]
91
- tile_width: int
92
- tile_height: int
93
- effect_type: int = 0 # 0=OFF, 2=MORPH, 3=FLAME
94
- effect_speed: int = 5 # Duration of one cycle in seconds
95
- effect_palette_count: int = 0
96
- effect_palette: list[LightHsbk] = field(default_factory=list)
97
-
98
-
99
- @dataclass
100
- class WaveformState:
101
- """Waveform effect state."""
102
-
103
- waveform_active: bool = False
104
- waveform_type: int = 0
105
- waveform_transient: bool = False
106
- waveform_color: LightHsbk = field(
107
- default_factory=lambda: LightHsbk(
108
- hue=0, saturation=0, brightness=0, kelvin=3500
109
- )
110
- )
111
- waveform_period_ms: int = 0
112
- waveform_cycles: float = 0
113
- waveform_duty_cycle: int = 0
114
- waveform_skew_ratio: int = 0
@@ -1,380 +0,0 @@
1
- """Factory functions for creating emulated LIFX devices."""
2
-
3
- from __future__ import annotations
4
-
5
- import random
6
- import time
7
- from typing import TYPE_CHECKING
8
-
9
- from lifx_emulator.device import DeviceState, EmulatedLifxDevice
10
- from lifx_emulator.device_states import (
11
- CoreDeviceState,
12
- GroupState,
13
- HevState,
14
- InfraredState,
15
- LocationState,
16
- MatrixState,
17
- MultiZoneState,
18
- NetworkState,
19
- WaveformState,
20
- )
21
- from lifx_emulator.products.registry import ProductInfo, get_product
22
- from lifx_emulator.products.specs import (
23
- get_default_tile_count,
24
- get_default_zone_count,
25
- get_tile_dimensions,
26
- )
27
- from lifx_emulator.protocol.protocol_types import LightHsbk
28
- from lifx_emulator.state_restorer import StateRestorer
29
-
30
- if TYPE_CHECKING:
31
- from lifx_emulator.async_storage import AsyncDeviceStorage
32
- from lifx_emulator.scenario_manager import HierarchicalScenarioManager
33
-
34
-
35
- def create_color_light(
36
- serial: str | None = None,
37
- firmware_version: tuple[int, int] | None = None,
38
- storage: AsyncDeviceStorage | None = None,
39
- scenario_manager: HierarchicalScenarioManager | None = None,
40
- ) -> EmulatedLifxDevice:
41
- """Create a regular color light (LIFX Color)"""
42
- return create_device(
43
- 91,
44
- serial=serial,
45
- firmware_version=firmware_version,
46
- storage=storage,
47
- scenario_manager=scenario_manager,
48
- ) # LIFX Color
49
-
50
-
51
- def create_infrared_light(
52
- serial: str | None = None,
53
- firmware_version: tuple[int, int] | None = None,
54
- storage: AsyncDeviceStorage | None = None,
55
- scenario_manager: HierarchicalScenarioManager | None = None,
56
- ) -> EmulatedLifxDevice:
57
- """Create an infrared-enabled light (LIFX A19 Night Vision)"""
58
- return create_device(
59
- 29,
60
- serial=serial,
61
- firmware_version=firmware_version,
62
- storage=storage,
63
- scenario_manager=scenario_manager,
64
- ) # LIFX A19 Night Vision
65
-
66
-
67
- def create_hev_light(
68
- serial: str | None = None,
69
- firmware_version: tuple[int, int] | None = None,
70
- storage: AsyncDeviceStorage | None = None,
71
- scenario_manager: HierarchicalScenarioManager | None = None,
72
- ) -> EmulatedLifxDevice:
73
- """Create an HEV-enabled light (LIFX Clean)"""
74
- return create_device(
75
- 90,
76
- serial=serial,
77
- firmware_version=firmware_version,
78
- storage=storage,
79
- scenario_manager=scenario_manager,
80
- ) # LIFX Clean
81
-
82
-
83
- def create_multizone_light(
84
- serial: str | None = None,
85
- zone_count: int | None = None,
86
- extended_multizone: bool = True,
87
- firmware_version: tuple[int, int] | None = None,
88
- storage: AsyncDeviceStorage | None = None,
89
- scenario_manager: HierarchicalScenarioManager | None = None,
90
- ) -> EmulatedLifxDevice:
91
- """Create a multizone light (LIFX Beam)
92
-
93
- Args:
94
- serial: Optional serial
95
- zone_count: Optional zone count (uses product default if not specified)
96
- extended_multizone: enables support for extended multizone requests
97
- firmware_version: Optional firmware version tuple (major, minor)
98
- storage: Optional storage for persistence
99
- scenario_manager: Optional scenario manager
100
- """
101
- return create_device(
102
- 38,
103
- serial=serial,
104
- zone_count=zone_count,
105
- extended_multizone=extended_multizone,
106
- firmware_version=firmware_version,
107
- storage=storage,
108
- scenario_manager=scenario_manager,
109
- )
110
-
111
-
112
- def create_tile_device(
113
- serial: str | None = None,
114
- tile_count: int | None = None,
115
- tile_width: int | None = None,
116
- tile_height: int | None = None,
117
- firmware_version: tuple[int, int] | None = None,
118
- storage: AsyncDeviceStorage | None = None,
119
- scenario_manager: HierarchicalScenarioManager | None = None,
120
- ) -> EmulatedLifxDevice:
121
- """Create a tile device (LIFX Tile)
122
-
123
- Args:
124
- serial: Optional serial
125
- tile_count: Optional tile count (uses product default)
126
- tile_width: Optional tile width in pixels (uses product default)
127
- tile_height: Optional tile height in pixels (uses product default)
128
- firmware_version: Optional firmware version tuple (major, minor)
129
- storage: Optional storage for persistence
130
- scenario_manager: Optional scenario manager
131
- """
132
- return create_device(
133
- 55,
134
- serial=serial,
135
- tile_count=tile_count,
136
- tile_width=tile_width,
137
- tile_height=tile_height,
138
- firmware_version=firmware_version,
139
- storage=storage,
140
- scenario_manager=scenario_manager,
141
- ) # LIFX Tile
142
-
143
-
144
- def create_color_temperature_light(
145
- serial: str | None = None,
146
- firmware_version: tuple[int, int] | None = None,
147
- storage: AsyncDeviceStorage | None = None,
148
- scenario_manager: HierarchicalScenarioManager | None = None,
149
- ) -> EmulatedLifxDevice:
150
- """Create a color temperature light (LIFX Mini White to Warm).
151
-
152
- Variable color temperature, no RGB.
153
- """
154
- return create_device(
155
- 50,
156
- serial=serial,
157
- firmware_version=firmware_version,
158
- storage=storage,
159
- scenario_manager=scenario_manager,
160
- ) # LIFX Mini White to Warm
161
-
162
-
163
- def create_device(
164
- product_id: int,
165
- serial: str | None = None,
166
- zone_count: int | None = None,
167
- extended_multizone: bool | None = None,
168
- tile_count: int | None = None,
169
- tile_width: int | None = None,
170
- tile_height: int | None = None,
171
- firmware_version: tuple[int, int] | None = None,
172
- storage: AsyncDeviceStorage | None = None,
173
- scenario_manager: HierarchicalScenarioManager | None = None,
174
- ) -> EmulatedLifxDevice:
175
- """Create a device for any LIFX product using the product registry.
176
-
177
- Args:
178
- product_id: Product ID from the LIFX product registry
179
- serial: Optional serial (auto-generated if not provided)
180
- zone_count: Number of zones for multizone devices (auto-determined)
181
- extended_multizone: Enable extended multizone requests
182
- tile_count: Number of tiles for matrix devices (default: 5)
183
- tile_width: Width of each tile in pixels (default: 8)
184
- tile_height: Height of each tile in pixels (default: 8)
185
- firmware_version: Optional firmware version tuple (major, minor).
186
- If not specified, uses 3.70 for extended_multizone
187
- or 2.60 otherwise
188
- storage: Optional storage for persistence
189
-
190
- Returns:
191
- EmulatedLifxDevice configured for the specified product
192
-
193
- Raises:
194
- ValueError: If product_id is not found in registry
195
-
196
- Examples:
197
- >>> # Create LIFX A19 (PID 27)
198
- >>> device = create_device(27)
199
- >>> # Create LIFX Z strip (PID 32) with 24 zones
200
- >>> strip = create_device(32, zone_count=24)
201
- >>> # Create LIFX Tile (PID 55) with 10 tiles
202
- >>> tiles = create_device(55, tile_count=10)
203
- """
204
- # Get product info from registry
205
- product_info: ProductInfo | None = get_product(product_id)
206
- if product_info is None:
207
- raise ValueError(f"Unknown product ID: {product_id}")
208
-
209
- # Generate serial if not provided
210
- if not serial:
211
- # Use different prefixes for product types for easier identification
212
- if product_info.has_matrix:
213
- prefix = "d073d9" # Tiles
214
- elif product_info.has_multizone:
215
- prefix = "d073d8" # Strips/Beams
216
- elif product_info.has_hev:
217
- prefix = "d073d7" # HEV
218
- elif product_info.has_infrared:
219
- prefix = "d073d6" # Infrared
220
- else:
221
- prefix = "d073d5" # Regular lights
222
- serial = f"{prefix}{random.randint(100000, 999999):06x}" # nosec
223
-
224
- # Determine zone count for multizone devices
225
- if product_info.has_multizone and zone_count is None:
226
- # Try to get default from specs first
227
- zone_count = get_default_zone_count(product_id) or 16
228
-
229
- # Determine tile configuration for matrix devices
230
- if product_info.has_matrix:
231
- # Get tile dimensions from specs (always use specs for dimensions)
232
- tile_dims = get_tile_dimensions(product_id)
233
- if tile_dims:
234
- tile_width, tile_height = tile_dims
235
- else:
236
- # Fallback to standard 8x8 tiles
237
- if tile_width is None:
238
- tile_width = 8
239
- if tile_height is None:
240
- tile_height = 8
241
-
242
- # Get default tile count from specs
243
- if tile_count is None:
244
- specs_tile_count = get_default_tile_count(product_id)
245
- if specs_tile_count is not None:
246
- tile_count = specs_tile_count
247
- else:
248
- tile_count = 5 # Generic default
249
-
250
- # Create default color based on product type
251
- if (
252
- not product_info.has_color
253
- and product_info.temperature_range is not None
254
- and product_info.temperature_range.min == product_info.temperature_range.max
255
- ):
256
- # Brightness only light
257
- default_color = LightHsbk(hue=0, saturation=0, brightness=32768, kelvin=2700)
258
- elif (
259
- not product_info.has_color
260
- and product_info.temperature_range is not None
261
- and product_info.temperature_range.min != product_info.temperature_range.max
262
- ):
263
- # Color temperature adjustable light
264
- default_color = LightHsbk(hue=0, saturation=0, brightness=32768, kelvin=3500)
265
- else:
266
- # Color devices - use a unique hue per device type
267
- hue_map = {
268
- "matrix": 43690, # Cyan
269
- "multizone": 0, # Red
270
- "hev": 32768, # Green
271
- "infrared": 0, # Red
272
- "color": 21845, # Orange
273
- }
274
- if product_info.has_matrix:
275
- hue = hue_map["matrix"]
276
- elif product_info.has_multizone:
277
- hue = hue_map["multizone"]
278
- elif product_info.has_hev:
279
- hue = hue_map["hev"]
280
- elif product_info.has_infrared:
281
- hue = hue_map["infrared"]
282
- else:
283
- hue = hue_map["color"]
284
- default_color = LightHsbk(
285
- hue=hue, saturation=65535, brightness=32768, kelvin=3500
286
- )
287
-
288
- # Get a simplified label from product name
289
- label = f"{product_info.name} {serial[-6:]}"
290
-
291
- # Determine firmware version: use extended_multizone to set default,
292
- # then override with explicit firmware_version if provided
293
- # None defaults to True (3.70), only explicit False gives 2.60
294
- if extended_multizone is False:
295
- version_major = 2
296
- version_minor = 60
297
- else:
298
- version_major = 3
299
- version_minor = 70
300
-
301
- # Override with explicit firmware_version if provided
302
- if firmware_version is not None:
303
- version_major, version_minor = firmware_version
304
-
305
- core = CoreDeviceState(
306
- serial=serial,
307
- label=label,
308
- power_level=65535, # Default to on
309
- color=default_color,
310
- vendor=product_info.vendor,
311
- product=product_info.pid,
312
- version_major=version_major,
313
- version_minor=version_minor,
314
- build_timestamp=int(time.time()),
315
- mac_address=bytes.fromhex(serial[:12]),
316
- )
317
-
318
- # Create network, location, group, and waveform state
319
- network = NetworkState()
320
- location = LocationState()
321
- group = GroupState()
322
- waveform = WaveformState()
323
-
324
- # Create capability-specific state objects
325
- infrared_state = (
326
- InfraredState(infrared_brightness=16384) if product_info.has_infrared else None
327
- )
328
- hev_state = HevState() if product_info.has_hev else None
329
-
330
- multizone_state = None
331
- if product_info.has_multizone and zone_count:
332
- multizone_state = MultiZoneState(
333
- zone_count=zone_count,
334
- zone_colors=[], # Will be initialized by EmulatedLifxDevice
335
- )
336
-
337
- matrix_state = None
338
- if product_info.has_matrix and tile_count:
339
- matrix_state = MatrixState(
340
- tile_count=tile_count,
341
- tile_devices=[], # Will be initialized by EmulatedLifxDevice
342
- tile_width=tile_width or 8,
343
- tile_height=tile_height or 8,
344
- )
345
-
346
- # Determine if device supports extended multizone
347
- firmware_version_int = (version_major << 16) | version_minor
348
- has_extended_multizone = product_info.supports_extended_multizone(
349
- firmware_version_int
350
- )
351
-
352
- # Create composed device state
353
- state = DeviceState(
354
- core=core,
355
- network=network,
356
- location=location,
357
- group=group,
358
- waveform=waveform,
359
- infrared=infrared_state,
360
- hev=hev_state,
361
- multizone=multizone_state,
362
- matrix=matrix_state,
363
- has_color=product_info.has_color,
364
- has_infrared=product_info.has_infrared,
365
- has_multizone=product_info.has_multizone,
366
- has_extended_multizone=has_extended_multizone,
367
- has_matrix=product_info.has_matrix,
368
- has_hev=product_info.has_hev,
369
- )
370
-
371
- # Restore saved state if persistence is enabled
372
- if storage:
373
- restorer = StateRestorer(storage)
374
- restorer.restore_if_available(state)
375
-
376
- return EmulatedLifxDevice(
377
- state,
378
- storage=storage,
379
- scenario_manager=scenario_manager,
380
- )
@@ -1,100 +0,0 @@
1
- """Storage protocol definition for device state persistence.
2
-
3
- This module defines the common interface that all storage implementations
4
- must follow, enabling polymorphic use and easier testing.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from typing import Any, Protocol, runtime_checkable
10
-
11
-
12
- @runtime_checkable
13
- class StorageProtocol(Protocol):
14
- """Protocol defining the interface for device state storage.
15
-
16
- Both synchronous (DeviceStorage) and asynchronous (AsyncDeviceStorage)
17
- implementations must provide these methods.
18
-
19
- The protocol allows for polymorphic usage and dependency injection,
20
- improving testability and adherence to SOLID principles.
21
- """
22
-
23
- def load_device_state(self, serial: str) -> dict[str, Any] | None:
24
- """Load device state from persistent storage.
25
-
26
- This method is synchronous in both implementations because loading
27
- primarily happens at device initialization where blocking is acceptable.
28
-
29
- Args:
30
- serial: Device serial number
31
-
32
- Returns:
33
- Dictionary with device state, or None if not found
34
- """
35
- ...
36
-
37
- def delete_device_state(self, serial: str) -> None:
38
- """Delete device state from persistent storage.
39
-
40
- This method is synchronous because deletion is rare and blocking
41
- is acceptable for this operation.
42
-
43
- Args:
44
- serial: Device serial number
45
- """
46
- ...
47
-
48
- def delete_all_device_states(self) -> int:
49
- """Delete all device states from persistent storage.
50
-
51
- This method is synchronous because it's typically used for cleanup
52
- operations where blocking is acceptable.
53
-
54
- Returns:
55
- Number of devices deleted
56
- """
57
- ...
58
-
59
- def list_devices(self) -> list[str]:
60
- """List all devices with saved state.
61
-
62
- This method is synchronous because listing is typically used for
63
- administrative/query operations where blocking is acceptable.
64
-
65
- Returns:
66
- List of device serial numbers
67
- """
68
- ...
69
-
70
-
71
- @runtime_checkable
72
- class AsyncStorageProtocol(StorageProtocol, Protocol):
73
- """Extended protocol for asynchronous storage implementations.
74
-
75
- Adds async save method for high-performance non-blocking writes.
76
- """
77
-
78
- async def save_device_state(self, device_state: Any) -> None:
79
- """Queue device state for saving (non-blocking).
80
-
81
- Args:
82
- device_state: DeviceState instance to persist
83
- """
84
- ...
85
-
86
-
87
- @runtime_checkable
88
- class SyncStorageProtocol(StorageProtocol, Protocol):
89
- """Extended protocol for synchronous storage implementations.
90
-
91
- Adds synchronous save method for simple blocking writes.
92
- """
93
-
94
- def save_device_state(self, device_state: Any) -> None:
95
- """Save device state to persistent storage (blocking).
96
-
97
- Args:
98
- device_state: DeviceState instance to persist
99
- """
100
- ...
@@ -1,40 +0,0 @@
1
- lifx_emulator/__init__.py,sha256=WzD3GRp_QFlYF9IexitPxuTVyOUDNv2ZApKhfRP0UGA,818
2
- lifx_emulator/__main__.py,sha256=Irr5g-OBIoWn6shPzfWIPjJSVM2Sq5Y1R1ETxdhCjx8,22570
3
- lifx_emulator/api.py,sha256=ATVLtPNh-9TySn_orFV03vJvZ20D5VZyuVQPgvzUuFg,67416
4
- lifx_emulator/async_storage.py,sha256=-J4s_4Cxika_EDlFjBegKAaM8sY78wbTDWnttWH4Eik,10504
5
- lifx_emulator/constants.py,sha256=DFZkUsdewE-x_3MgO28tMGkjUCWPeYc3xLj_EXViGOw,1032
6
- lifx_emulator/device.py,sha256=qaTNgeyvg1S-g1rC91NnQpvDe4p1razMNVDTlv0vKMI,24109
7
- lifx_emulator/device_states.py,sha256=3juEk1PpBySOwB3XpvH4UqKAPek5ZNCbvepJ3Y97Sck,2855
8
- lifx_emulator/factories.py,sha256=vLsGFuP8E_9bbdo2Sry3G6Ax6qSl5QfpXsRfXE3SfBU,13017
9
- lifx_emulator/observers.py,sha256=-KnUgFcKdhlNo7CNVstP-u0wU2W0JAGg055ZPV15Sj0,3874
10
- lifx_emulator/scenario_manager.py,sha256=xv2NwdZjDcF83MTt7JjKgDvua6zx6SkGQT1P4156-RU,14154
11
- lifx_emulator/scenario_persistence.py,sha256=oytK5W2si1N-O31jBACHhGQyo5IrINlqfWE0K2bZq_8,7394
12
- lifx_emulator/server.py,sha256=mWkssl42Z_5m78DAn5_-DRACLGvymSbouTE4j8dVAXA,17239
13
- lifx_emulator/state_restorer.py,sha256=isgCsmcjxc4aQrTXoIYr_FoIU4H5Swg7GNaoh_-X7zU,9793
14
- lifx_emulator/state_serializer.py,sha256=O4Cp3bbGkd4eZf5jzb0MKzWDTgiNhrSGgypmMWaB4dg,5097
15
- lifx_emulator/storage_protocol.py,sha256=j5wC4gI-2DtPgmY4kM3mKDI6OYpmiNpGulzdJnZnqYY,2929
16
- lifx_emulator/handlers/__init__.py,sha256=3Hj1hRo3yL3E7GKwG9TaYh33ymk_N3bRiQ8nvqSQULA,1306
17
- lifx_emulator/handlers/base.py,sha256=Kn_OoVOQPlJOkl-xiHRYsoNKsS054sxk7sZ8rI9NvXQ,1653
18
- lifx_emulator/handlers/device_handlers.py,sha256=MDUanFFDT9RWBcWtEZ3sXeGmoYjFW50T4zVIkkK2iB4,10239
19
- lifx_emulator/handlers/light_handlers.py,sha256=ycwOAD6skmBLVUlyeg5kBprHDlrllP-_0JRzQrDGFos,11778
20
- lifx_emulator/handlers/multizone_handlers.py,sha256=949FEt1Ilhp-LCxxY0xfHxYJccGNy_xnNhO14ePENaw,7913
21
- lifx_emulator/handlers/registry.py,sha256=s1ht4PmPhXhAcwu1hoY4yW39wy3SPJBMY-9Uxd0FWuE,3292
22
- lifx_emulator/handlers/tile_handlers.py,sha256=jzguqNr_aZheuqd4mikfQlkRcVeqs3HAEAseFSlkBlE,10038
23
- lifx_emulator/products/__init__.py,sha256=qcNop_kRYFF3zSjNemzQEgu3jPrIxfyQyLv9GsnaLEI,627
24
- lifx_emulator/products/generator.py,sha256=ovXSwh-3bFNeNJJntYDSURUrzM-WAtXeGKI0-sP1miI,26431
25
- lifx_emulator/products/registry.py,sha256=oKO7Nb4KsWMMc3bkr5FWKgdXsi4xbrRCD9xP27rQPPQ,45757
26
- lifx_emulator/products/specs.py,sha256=2bpgr2z2Ugb_0IBl9LY8LGotB031Bim8zizwvtviFZ0,7186
27
- lifx_emulator/products/specs.yml,sha256=uxzdKFREAHphk8XSPiCHvQE2vwoPfT2m1xy-zC4ZIl4,8552
28
- lifx_emulator/protocol/__init__.py,sha256=-wjC-wBcb7fxi5I-mJr2Ad8K2YRflJFdLLdobfD-W1Q,56
29
- lifx_emulator/protocol/base.py,sha256=OH2fLGJCAFYCgaxkKB3-pLclRaCIMIQtLtlgS_Qe4Ko,12090
30
- lifx_emulator/protocol/const.py,sha256=ilhv-KcQpHtKh2MDCaIbMLQAsxKO_uTaxyR63v1W8cc,226
31
- lifx_emulator/protocol/generator.py,sha256=KuNuWUUNikBRudcnBNIFcJ_2h2zliceVmyxfyyHIpXc,48980
32
- lifx_emulator/protocol/header.py,sha256=RXMJ5YZG1jyxl4Mz46ZGJBYX41Jdp7J95BHuY-scYC0,5499
33
- lifx_emulator/protocol/packets.py,sha256=_xYYPuu6WfBpg1LwYVdzQdytFwTyMqpZRadmDdwZXWk,41567
34
- lifx_emulator/protocol/protocol_types.py,sha256=2Mccm9717EuTXQYaW44W_yReI4EtnlPp3-WEVASgdGY,24820
35
- lifx_emulator/protocol/serializer.py,sha256=2bZz7TddxaMRO4_6LujRGCS1w7GxD4E3rRk3r-hpEIE,10738
36
- lifx_emulator-1.0.2.dist-info/METADATA,sha256=gASUaAnvN-dLyu3rBoyYb4lYq_guVFwTXznmTOzAAvM,4549
37
- lifx_emulator-1.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- lifx_emulator-1.0.2.dist-info/entry_points.txt,sha256=R9C_K_tTgt6yXEmhzH4r2Yx2Tu1rLlnYzeG4RFUVzSc,62
39
- lifx_emulator-1.0.2.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
40
- lifx_emulator-1.0.2.dist-info/RECORD,,
File without changes