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.
Files changed (68) hide show
  1. lifx_emulator-3.0.1.dist-info/METADATA +102 -0
  2. lifx_emulator-3.0.1.dist-info/RECORD +18 -0
  3. lifx_emulator-3.0.1.dist-info/entry_points.txt +2 -0
  4. lifx_emulator_app/__init__.py +10 -0
  5. {lifx_emulator → lifx_emulator_app}/__main__.py +2 -3
  6. {lifx_emulator → lifx_emulator_app}/api/__init__.py +1 -1
  7. {lifx_emulator → lifx_emulator_app}/api/app.py +3 -3
  8. {lifx_emulator → lifx_emulator_app}/api/mappers/__init__.py +1 -1
  9. {lifx_emulator → lifx_emulator_app}/api/mappers/device_mapper.py +1 -1
  10. {lifx_emulator → lifx_emulator_app}/api/models.py +1 -2
  11. lifx_emulator_app/api/routers/__init__.py +11 -0
  12. {lifx_emulator → lifx_emulator_app}/api/routers/devices.py +2 -2
  13. {lifx_emulator → lifx_emulator_app}/api/routers/monitoring.py +1 -1
  14. {lifx_emulator → lifx_emulator_app}/api/routers/scenarios.py +1 -1
  15. lifx_emulator_app/api/services/__init__.py +8 -0
  16. {lifx_emulator → lifx_emulator_app}/api/services/device_service.py +3 -2
  17. lifx_emulator/__init__.py +0 -31
  18. lifx_emulator/api/routers/__init__.py +0 -11
  19. lifx_emulator/api/services/__init__.py +0 -8
  20. lifx_emulator/constants.py +0 -33
  21. lifx_emulator/devices/__init__.py +0 -37
  22. lifx_emulator/devices/device.py +0 -395
  23. lifx_emulator/devices/manager.py +0 -256
  24. lifx_emulator/devices/observers.py +0 -139
  25. lifx_emulator/devices/persistence.py +0 -308
  26. lifx_emulator/devices/state_restorer.py +0 -259
  27. lifx_emulator/devices/state_serializer.py +0 -157
  28. lifx_emulator/devices/states.py +0 -381
  29. lifx_emulator/factories/__init__.py +0 -39
  30. lifx_emulator/factories/builder.py +0 -375
  31. lifx_emulator/factories/default_config.py +0 -158
  32. lifx_emulator/factories/factory.py +0 -252
  33. lifx_emulator/factories/firmware_config.py +0 -77
  34. lifx_emulator/factories/serial_generator.py +0 -82
  35. lifx_emulator/handlers/__init__.py +0 -39
  36. lifx_emulator/handlers/base.py +0 -49
  37. lifx_emulator/handlers/device_handlers.py +0 -322
  38. lifx_emulator/handlers/light_handlers.py +0 -503
  39. lifx_emulator/handlers/multizone_handlers.py +0 -249
  40. lifx_emulator/handlers/registry.py +0 -110
  41. lifx_emulator/handlers/tile_handlers.py +0 -488
  42. lifx_emulator/products/__init__.py +0 -28
  43. lifx_emulator/products/generator.py +0 -1079
  44. lifx_emulator/products/registry.py +0 -1530
  45. lifx_emulator/products/specs.py +0 -284
  46. lifx_emulator/products/specs.yml +0 -386
  47. lifx_emulator/protocol/__init__.py +0 -1
  48. lifx_emulator/protocol/base.py +0 -446
  49. lifx_emulator/protocol/const.py +0 -8
  50. lifx_emulator/protocol/generator.py +0 -1384
  51. lifx_emulator/protocol/header.py +0 -159
  52. lifx_emulator/protocol/packets.py +0 -1351
  53. lifx_emulator/protocol/protocol_types.py +0 -817
  54. lifx_emulator/protocol/serializer.py +0 -379
  55. lifx_emulator/repositories/__init__.py +0 -22
  56. lifx_emulator/repositories/device_repository.py +0 -155
  57. lifx_emulator/repositories/storage_backend.py +0 -107
  58. lifx_emulator/scenarios/__init__.py +0 -22
  59. lifx_emulator/scenarios/manager.py +0 -322
  60. lifx_emulator/scenarios/models.py +0 -112
  61. lifx_emulator/scenarios/persistence.py +0 -241
  62. lifx_emulator/server.py +0 -464
  63. lifx_emulator-2.4.0.dist-info/METADATA +0 -107
  64. lifx_emulator-2.4.0.dist-info/RECORD +0 -62
  65. lifx_emulator-2.4.0.dist-info/entry_points.txt +0 -2
  66. lifx_emulator-2.4.0.dist-info/licenses/LICENSE +0 -35
  67. {lifx_emulator-2.4.0.dist-info → lifx_emulator-3.0.1.dist-info}/WHEEL +0 -0
  68. {lifx_emulator → lifx_emulator_app}/api/templates/dashboard.html +0 -0
@@ -1,159 +0,0 @@
1
- """LIFX protocol header implementation.
2
-
3
- The LIFX header is 36 bytes total, consisting of:
4
- - Frame (8 bytes)
5
- - Frame Address (16 bytes)
6
- - Protocol Header (12 bytes)
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import struct
12
- from dataclasses import dataclass
13
- from typing import ClassVar
14
-
15
- from lifx_emulator.constants import LIFX_PROTOCOL_VERSION
16
-
17
- # Pre-compiled struct for entire 36-byte header (performance optimization)
18
- _HEADER_STRUCT = struct.Struct("<HHI Q6sBB QHH")
19
-
20
-
21
- @dataclass
22
- class LifxHeader:
23
- """LIFX protocol header (36 bytes).
24
-
25
- Attributes:
26
- size: Total packet size in bytes (header + payload)
27
- protocol: Protocol number (must be 1024)
28
- source: Unique client identifier
29
- target: Device serial (8 bytes)
30
- tagged: True for broadcast discovery, False for targeted messages
31
- ack_required: Request acknowledgement from device
32
- res_required: Request response from device
33
- sequence: Sequence number for matching requests/responses
34
- pkt_type: Packet type identifier
35
- """
36
-
37
- HEADER_SIZE: ClassVar[int] = 36
38
- PROTOCOL_NUMBER: ClassVar[int] = 1024
39
- ORIGIN: ClassVar[int] = 0 # Always 0
40
- ADDRESSABLE: ClassVar[int] = 1 # Always 1
41
-
42
- size: int = 0
43
- protocol: int = LIFX_PROTOCOL_VERSION
44
- source: int = 0
45
- target: bytes = b"\x00" * 8 # Stored as 8 bytes internally
46
- tagged: bool = False
47
- ack_required: bool = False
48
- res_required: bool = False
49
- sequence: int = 0
50
- pkt_type: int = 0
51
-
52
- def __post_init__(self) -> None:
53
- """Validate header fields and auto-pad serial if needed."""
54
- # Ensure target is 8 bytes
55
- if len(self.target) < 8:
56
- self.target = self.target + b"\x00" * (8 - len(self.target))
57
- elif len(self.target) > 8:
58
- self.target = self.target[:8]
59
-
60
- # Validate protocol
61
- if self.protocol != self.PROTOCOL_NUMBER:
62
- raise ValueError(
63
- f"Invalid protocol number: {self.protocol}"
64
- f"(expected {self.PROTOCOL_NUMBER})"
65
- )
66
-
67
- def pack(self) -> bytes:
68
- """Pack header into 36 bytes using optimized single struct call.
69
-
70
- Returns:
71
- Packed header bytes
72
- """
73
- # Calculate flag fields
74
- frame_flags = (
75
- (self.protocol & 0xFFF)
76
- | (self.ADDRESSABLE << 12)
77
- | ((1 if self.tagged else 0) << 13)
78
- | ((self.ORIGIN & 0x3) << 14)
79
- )
80
- target_int = int.from_bytes(self.target[:8], byteorder="little")
81
- addr_flags = (1 if self.res_required else 0) | (
82
- (1 if self.ack_required else 0) << 1
83
- )
84
-
85
- # Pack entire header in single struct call (15-20% faster than 3 separate calls)
86
- return _HEADER_STRUCT.pack(
87
- self.size, # H - Frame: size
88
- frame_flags, # H - Frame: flags (protocol, tagged, addressable, origin)
89
- self.source, # I - Frame: source
90
- target_int, # Q - Frame Address: target (8 bytes as uint64)
91
- b"\x00" * 6, # 6s - Frame Address: reserved (6 bytes)
92
- addr_flags, # B - Frame Address: flags (ack_required, res_required)
93
- self.sequence, # B - Frame Address: sequence
94
- 0, # Q - Protocol Header: reserved (8 bytes)
95
- self.pkt_type, # H - Protocol Header: packet type
96
- 0, # H - Protocol Header: reserved (2 bytes)
97
- )
98
-
99
- @classmethod
100
- def unpack(cls, data: bytes) -> LifxHeader:
101
- """Unpack header from bytes.
102
-
103
- Args:
104
- data: Header bytes (at least 36 bytes)
105
-
106
- Returns:
107
- LifxHeader instance
108
-
109
- Raises:
110
- ValueError: If data is too short or invalid
111
- """
112
- if len(data) < cls.HEADER_SIZE:
113
- raise ValueError(f"Header data must be at least {cls.HEADER_SIZE} bytes")
114
-
115
- # Unpack Frame (8 bytes)
116
- size, protocol_field, source = struct.unpack("<HHI", data[0:8])
117
-
118
- # Extract protocol field components
119
- origin = (protocol_field >> 14) & 0b11
120
- tagged = bool((protocol_field >> 13) & 0b1)
121
- addressable = bool((protocol_field >> 12) & 0b1)
122
- protocol = protocol_field & 0xFFF
123
-
124
- # Validate origin and addressable
125
- if origin != cls.ORIGIN:
126
- raise ValueError(f"Invalid origin: {origin}")
127
- if not addressable:
128
- raise ValueError("Addressable bit must be set")
129
-
130
- # Unpack Frame Address (16 bytes)
131
- target_int, _reserved, flags, sequence = struct.unpack("<Q6sBB", data[8:24])
132
- target = target_int.to_bytes(8, byteorder="little")
133
-
134
- res_required = bool(flags & 0b1)
135
- ack_required = bool((flags >> 1) & 0b1)
136
-
137
- # Unpack Protocol Header (12 bytes)
138
- _reserved1, pkt_type, _reserved2 = struct.unpack("<QHH", data[24:36])
139
-
140
- return cls(
141
- size=size,
142
- protocol=protocol,
143
- source=source,
144
- target=target,
145
- tagged=tagged,
146
- ack_required=ack_required,
147
- res_required=res_required,
148
- sequence=sequence,
149
- pkt_type=pkt_type,
150
- )
151
-
152
- def __repr__(self) -> str:
153
- """String representation of header."""
154
- return (
155
- f"LifxHeader(size={self.size}, protocol={self.protocol}, "
156
- f"source={self.source}, target={self.target.hex()}, "
157
- f"tagged={self.tagged}, ack={self.ack_required}, "
158
- f"res={self.res_required}, seq={self.sequence}, pkt_type={self.pkt_type})"
159
- )