lifx-emulator 1.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.
- lifx_emulator/__init__.py +31 -0
- lifx_emulator/__main__.py +607 -0
- lifx_emulator/api.py +1825 -0
- lifx_emulator/async_storage.py +308 -0
- lifx_emulator/constants.py +33 -0
- lifx_emulator/device.py +750 -0
- lifx_emulator/device_states.py +114 -0
- lifx_emulator/factories.py +380 -0
- lifx_emulator/handlers/__init__.py +39 -0
- lifx_emulator/handlers/base.py +49 -0
- lifx_emulator/handlers/device_handlers.py +340 -0
- lifx_emulator/handlers/light_handlers.py +372 -0
- lifx_emulator/handlers/multizone_handlers.py +249 -0
- lifx_emulator/handlers/registry.py +110 -0
- lifx_emulator/handlers/tile_handlers.py +309 -0
- lifx_emulator/observers.py +139 -0
- lifx_emulator/products/__init__.py +28 -0
- lifx_emulator/products/generator.py +771 -0
- lifx_emulator/products/registry.py +1446 -0
- lifx_emulator/products/specs.py +242 -0
- lifx_emulator/products/specs.yml +327 -0
- lifx_emulator/protocol/__init__.py +1 -0
- lifx_emulator/protocol/base.py +334 -0
- lifx_emulator/protocol/const.py +8 -0
- lifx_emulator/protocol/generator.py +1371 -0
- lifx_emulator/protocol/header.py +159 -0
- lifx_emulator/protocol/packets.py +1351 -0
- lifx_emulator/protocol/protocol_types.py +844 -0
- lifx_emulator/protocol/serializer.py +379 -0
- lifx_emulator/scenario_manager.py +402 -0
- lifx_emulator/scenario_persistence.py +206 -0
- lifx_emulator/server.py +482 -0
- lifx_emulator/state_restorer.py +259 -0
- lifx_emulator/state_serializer.py +130 -0
- lifx_emulator/storage_protocol.py +100 -0
- lifx_emulator-1.0.0.dist-info/METADATA +445 -0
- lifx_emulator-1.0.0.dist-info/RECORD +40 -0
- lifx_emulator-1.0.0.dist-info/WHEEL +4 -0
- lifx_emulator-1.0.0.dist-info/entry_points.txt +2 -0
- lifx_emulator-1.0.0.dist-info/licenses/LICENSE +35 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
"""Auto-generated LIFX protocol types.
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT THIS FILE MANUALLY.
|
|
4
|
+
Generated from https://github.com/LIFX/public-protocol/blob/main/protocol.yml
|
|
5
|
+
by protocol/generator.py
|
|
6
|
+
|
|
7
|
+
Uses Pythonic naming conventions (snake_case fields, shortened enums) while
|
|
8
|
+
maintaining compatibility with the official LIFX protocol through mappings.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from enum import IntEnum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DeviceService(IntEnum):
|
|
18
|
+
"""Auto-generated enum."""
|
|
19
|
+
|
|
20
|
+
UDP = 1
|
|
21
|
+
RESERVED_0 = 2
|
|
22
|
+
RESERVED_1 = 3
|
|
23
|
+
RESERVED_2 = 4
|
|
24
|
+
RESERVED_3 = 5
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LightLastHevCycleResult(IntEnum):
|
|
28
|
+
"""Auto-generated enum."""
|
|
29
|
+
|
|
30
|
+
SUCCESS = 0
|
|
31
|
+
BUSY = 1
|
|
32
|
+
INTERRUPTED_BY_RESET = 2
|
|
33
|
+
INTERRUPTED_BY_HOMEKIT = 3
|
|
34
|
+
INTERRUPTED_BY_LAN = 4
|
|
35
|
+
INTERRUPTED_BY_CLOUD = 5
|
|
36
|
+
NONE = 255
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LightWaveform(IntEnum):
|
|
40
|
+
"""Auto-generated enum."""
|
|
41
|
+
|
|
42
|
+
SAW = 0
|
|
43
|
+
SINE = 1
|
|
44
|
+
HALF_SINE = 2
|
|
45
|
+
TRIANGLE = 3
|
|
46
|
+
PULSE = 4
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class MultiZoneApplicationRequest(IntEnum):
|
|
50
|
+
"""Auto-generated enum."""
|
|
51
|
+
|
|
52
|
+
NO_APPLY = 0
|
|
53
|
+
APPLY = 1
|
|
54
|
+
APPLY_ONLY = 2
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class MultiZoneEffectType(IntEnum):
|
|
58
|
+
"""Auto-generated enum."""
|
|
59
|
+
|
|
60
|
+
OFF = 0
|
|
61
|
+
MOVE = 1
|
|
62
|
+
RESERVED_0 = 2
|
|
63
|
+
RESERVED_1 = 3
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MultiZoneExtendedApplicationRequest(IntEnum):
|
|
67
|
+
"""Auto-generated enum."""
|
|
68
|
+
|
|
69
|
+
NO_APPLY = 0
|
|
70
|
+
APPLY = 1
|
|
71
|
+
APPLY_ONLY = 2
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TileEffectSkyPalette(IntEnum):
|
|
75
|
+
"""Auto-generated enum."""
|
|
76
|
+
|
|
77
|
+
CLOUDS_SKY = 0
|
|
78
|
+
NIGHT_SKY = 1
|
|
79
|
+
DAWN_SKY = 2
|
|
80
|
+
DAWN_SUN = 3
|
|
81
|
+
FULL_SUN = 4
|
|
82
|
+
FINAL_SUN = 5
|
|
83
|
+
NUM_COLOURS = 6
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TileEffectSkyType(IntEnum):
|
|
87
|
+
"""Auto-generated enum."""
|
|
88
|
+
|
|
89
|
+
SUNRISE = 0
|
|
90
|
+
SUNSET = 1
|
|
91
|
+
CLOUDS = 2
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TileEffectType(IntEnum):
|
|
95
|
+
"""Auto-generated enum."""
|
|
96
|
+
|
|
97
|
+
OFF = 0
|
|
98
|
+
RESERVED_0 = 1
|
|
99
|
+
MORPH = 2
|
|
100
|
+
FLAME = 3
|
|
101
|
+
RESERVED_1 = 4
|
|
102
|
+
SKY = 5
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class DeviceStateHostFirmware:
|
|
107
|
+
"""Auto-generated field structure."""
|
|
108
|
+
|
|
109
|
+
build: int
|
|
110
|
+
version_minor: int
|
|
111
|
+
version_major: int
|
|
112
|
+
|
|
113
|
+
def pack(self) -> bytes:
|
|
114
|
+
"""Pack to bytes."""
|
|
115
|
+
from lifx_emulator.protocol import serializer
|
|
116
|
+
|
|
117
|
+
result = b""
|
|
118
|
+
|
|
119
|
+
# build: uint64
|
|
120
|
+
result += serializer.pack_value(self.build, "uint64")
|
|
121
|
+
# Reserved 8 bytes
|
|
122
|
+
result += serializer.pack_reserved(8)
|
|
123
|
+
# version_minor: uint16
|
|
124
|
+
result += serializer.pack_value(self.version_minor, "uint16")
|
|
125
|
+
# version_major: uint16
|
|
126
|
+
result += serializer.pack_value(self.version_major, "uint16")
|
|
127
|
+
|
|
128
|
+
return result
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def unpack(
|
|
132
|
+
cls, data: bytes, offset: int = 0
|
|
133
|
+
) -> tuple[DeviceStateHostFirmware, int]:
|
|
134
|
+
"""Unpack from bytes."""
|
|
135
|
+
from lifx_emulator.protocol import serializer
|
|
136
|
+
|
|
137
|
+
current_offset = offset
|
|
138
|
+
# build: uint64
|
|
139
|
+
build, current_offset = serializer.unpack_value(data, "uint64", current_offset)
|
|
140
|
+
# Skip reserved 8 bytes
|
|
141
|
+
current_offset += 8
|
|
142
|
+
# version_minor: uint16
|
|
143
|
+
version_minor, current_offset = serializer.unpack_value(
|
|
144
|
+
data, "uint16", current_offset
|
|
145
|
+
)
|
|
146
|
+
# version_major: uint16
|
|
147
|
+
version_major, current_offset = serializer.unpack_value(
|
|
148
|
+
data, "uint16", current_offset
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return cls(
|
|
152
|
+
build=build, version_minor=version_minor, version_major=version_major
|
|
153
|
+
), current_offset
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class DeviceStateVersion:
|
|
158
|
+
"""Auto-generated field structure."""
|
|
159
|
+
|
|
160
|
+
vendor: int
|
|
161
|
+
product: int
|
|
162
|
+
|
|
163
|
+
def pack(self) -> bytes:
|
|
164
|
+
"""Pack to bytes."""
|
|
165
|
+
from lifx_emulator.protocol import serializer
|
|
166
|
+
|
|
167
|
+
result = b""
|
|
168
|
+
|
|
169
|
+
# vendor: uint32
|
|
170
|
+
result += serializer.pack_value(self.vendor, "uint32")
|
|
171
|
+
# product: uint32
|
|
172
|
+
result += serializer.pack_value(self.product, "uint32")
|
|
173
|
+
# Reserved 4 bytes
|
|
174
|
+
result += serializer.pack_reserved(4)
|
|
175
|
+
|
|
176
|
+
return result
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[DeviceStateVersion, int]:
|
|
180
|
+
"""Unpack from bytes."""
|
|
181
|
+
from lifx_emulator.protocol import serializer
|
|
182
|
+
|
|
183
|
+
current_offset = offset
|
|
184
|
+
# vendor: uint32
|
|
185
|
+
vendor, current_offset = serializer.unpack_value(data, "uint32", current_offset)
|
|
186
|
+
# product: uint32
|
|
187
|
+
product, current_offset = serializer.unpack_value(
|
|
188
|
+
data, "uint32", current_offset
|
|
189
|
+
)
|
|
190
|
+
# Skip reserved 4 bytes
|
|
191
|
+
current_offset += 4
|
|
192
|
+
|
|
193
|
+
return cls(vendor=vendor, product=product), current_offset
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@dataclass
|
|
197
|
+
class LightHsbk:
|
|
198
|
+
"""Auto-generated field structure."""
|
|
199
|
+
|
|
200
|
+
hue: int
|
|
201
|
+
saturation: int
|
|
202
|
+
brightness: int
|
|
203
|
+
kelvin: int
|
|
204
|
+
|
|
205
|
+
def pack(self) -> bytes:
|
|
206
|
+
"""Pack to bytes."""
|
|
207
|
+
from lifx_emulator.protocol import serializer
|
|
208
|
+
|
|
209
|
+
result = b""
|
|
210
|
+
|
|
211
|
+
# hue: uint16
|
|
212
|
+
result += serializer.pack_value(self.hue, "uint16")
|
|
213
|
+
# saturation: uint16
|
|
214
|
+
result += serializer.pack_value(self.saturation, "uint16")
|
|
215
|
+
# brightness: uint16
|
|
216
|
+
result += serializer.pack_value(self.brightness, "uint16")
|
|
217
|
+
# kelvin: uint16
|
|
218
|
+
result += serializer.pack_value(self.kelvin, "uint16")
|
|
219
|
+
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
@classmethod
|
|
223
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[LightHsbk, int]:
|
|
224
|
+
"""Unpack from bytes."""
|
|
225
|
+
from lifx_emulator.protocol import serializer
|
|
226
|
+
|
|
227
|
+
current_offset = offset
|
|
228
|
+
# hue: uint16
|
|
229
|
+
hue, current_offset = serializer.unpack_value(data, "uint16", current_offset)
|
|
230
|
+
# saturation: uint16
|
|
231
|
+
saturation, current_offset = serializer.unpack_value(
|
|
232
|
+
data, "uint16", current_offset
|
|
233
|
+
)
|
|
234
|
+
# brightness: uint16
|
|
235
|
+
brightness, current_offset = serializer.unpack_value(
|
|
236
|
+
data, "uint16", current_offset
|
|
237
|
+
)
|
|
238
|
+
# kelvin: uint16
|
|
239
|
+
kelvin, current_offset = serializer.unpack_value(data, "uint16", current_offset)
|
|
240
|
+
|
|
241
|
+
return cls(
|
|
242
|
+
hue=hue, saturation=saturation, brightness=brightness, kelvin=kelvin
|
|
243
|
+
), current_offset
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass
|
|
247
|
+
class MultiZoneEffectParameter:
|
|
248
|
+
"""Auto-generated field structure."""
|
|
249
|
+
|
|
250
|
+
parameter0: int
|
|
251
|
+
parameter1: int
|
|
252
|
+
parameter2: int
|
|
253
|
+
parameter3: int
|
|
254
|
+
parameter4: int
|
|
255
|
+
parameter5: int
|
|
256
|
+
parameter6: int
|
|
257
|
+
parameter7: int
|
|
258
|
+
|
|
259
|
+
def pack(self) -> bytes:
|
|
260
|
+
"""Pack to bytes."""
|
|
261
|
+
from lifx_emulator.protocol import serializer
|
|
262
|
+
|
|
263
|
+
result = b""
|
|
264
|
+
|
|
265
|
+
# parameter0: uint32
|
|
266
|
+
result += serializer.pack_value(self.parameter0, "uint32")
|
|
267
|
+
# parameter1: uint32
|
|
268
|
+
result += serializer.pack_value(self.parameter1, "uint32")
|
|
269
|
+
# parameter2: uint32
|
|
270
|
+
result += serializer.pack_value(self.parameter2, "uint32")
|
|
271
|
+
# parameter3: uint32
|
|
272
|
+
result += serializer.pack_value(self.parameter3, "uint32")
|
|
273
|
+
# parameter4: uint32
|
|
274
|
+
result += serializer.pack_value(self.parameter4, "uint32")
|
|
275
|
+
# parameter5: uint32
|
|
276
|
+
result += serializer.pack_value(self.parameter5, "uint32")
|
|
277
|
+
# parameter6: uint32
|
|
278
|
+
result += serializer.pack_value(self.parameter6, "uint32")
|
|
279
|
+
# parameter7: uint32
|
|
280
|
+
result += serializer.pack_value(self.parameter7, "uint32")
|
|
281
|
+
|
|
282
|
+
return result
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def unpack(
|
|
286
|
+
cls, data: bytes, offset: int = 0
|
|
287
|
+
) -> tuple[MultiZoneEffectParameter, int]:
|
|
288
|
+
"""Unpack from bytes."""
|
|
289
|
+
from lifx_emulator.protocol import serializer
|
|
290
|
+
|
|
291
|
+
current_offset = offset
|
|
292
|
+
# parameter0: uint32
|
|
293
|
+
parameter0, current_offset = serializer.unpack_value(
|
|
294
|
+
data, "uint32", current_offset
|
|
295
|
+
)
|
|
296
|
+
# parameter1: uint32
|
|
297
|
+
parameter1, current_offset = serializer.unpack_value(
|
|
298
|
+
data, "uint32", current_offset
|
|
299
|
+
)
|
|
300
|
+
# parameter2: uint32
|
|
301
|
+
parameter2, current_offset = serializer.unpack_value(
|
|
302
|
+
data, "uint32", current_offset
|
|
303
|
+
)
|
|
304
|
+
# parameter3: uint32
|
|
305
|
+
parameter3, current_offset = serializer.unpack_value(
|
|
306
|
+
data, "uint32", current_offset
|
|
307
|
+
)
|
|
308
|
+
# parameter4: uint32
|
|
309
|
+
parameter4, current_offset = serializer.unpack_value(
|
|
310
|
+
data, "uint32", current_offset
|
|
311
|
+
)
|
|
312
|
+
# parameter5: uint32
|
|
313
|
+
parameter5, current_offset = serializer.unpack_value(
|
|
314
|
+
data, "uint32", current_offset
|
|
315
|
+
)
|
|
316
|
+
# parameter6: uint32
|
|
317
|
+
parameter6, current_offset = serializer.unpack_value(
|
|
318
|
+
data, "uint32", current_offset
|
|
319
|
+
)
|
|
320
|
+
# parameter7: uint32
|
|
321
|
+
parameter7, current_offset = serializer.unpack_value(
|
|
322
|
+
data, "uint32", current_offset
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
cls(
|
|
327
|
+
parameter0=parameter0,
|
|
328
|
+
parameter1=parameter1,
|
|
329
|
+
parameter2=parameter2,
|
|
330
|
+
parameter3=parameter3,
|
|
331
|
+
parameter4=parameter4,
|
|
332
|
+
parameter5=parameter5,
|
|
333
|
+
parameter6=parameter6,
|
|
334
|
+
parameter7=parameter7,
|
|
335
|
+
),
|
|
336
|
+
current_offset,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@dataclass
|
|
341
|
+
class MultiZoneEffectSettings:
|
|
342
|
+
"""Auto-generated field structure."""
|
|
343
|
+
|
|
344
|
+
instanceid: int
|
|
345
|
+
type: MultiZoneEffectType
|
|
346
|
+
speed: int
|
|
347
|
+
duration: int
|
|
348
|
+
parameter: MultiZoneEffectParameter
|
|
349
|
+
|
|
350
|
+
def pack(self) -> bytes:
|
|
351
|
+
"""Pack to bytes."""
|
|
352
|
+
from lifx_emulator.protocol import serializer
|
|
353
|
+
|
|
354
|
+
result = b""
|
|
355
|
+
|
|
356
|
+
# instanceid: uint32
|
|
357
|
+
result += serializer.pack_value(self.instanceid, "uint32")
|
|
358
|
+
# type: MultiZoneEffectType (enum)
|
|
359
|
+
result += serializer.pack_value(int(self.type), "uint8")
|
|
360
|
+
# Reserved 2 bytes
|
|
361
|
+
result += serializer.pack_reserved(2)
|
|
362
|
+
# speed: uint32
|
|
363
|
+
result += serializer.pack_value(self.speed, "uint32")
|
|
364
|
+
# duration: uint64
|
|
365
|
+
result += serializer.pack_value(self.duration, "uint64")
|
|
366
|
+
# Reserved 4 bytes
|
|
367
|
+
result += serializer.pack_reserved(4)
|
|
368
|
+
# Reserved 4 bytes
|
|
369
|
+
result += serializer.pack_reserved(4)
|
|
370
|
+
# parameter: MultiZoneEffectParameter
|
|
371
|
+
result += self.parameter.pack()
|
|
372
|
+
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
@classmethod
|
|
376
|
+
def unpack(
|
|
377
|
+
cls, data: bytes, offset: int = 0
|
|
378
|
+
) -> tuple[MultiZoneEffectSettings, int]:
|
|
379
|
+
"""Unpack from bytes."""
|
|
380
|
+
from lifx_emulator.protocol import serializer
|
|
381
|
+
|
|
382
|
+
current_offset = offset
|
|
383
|
+
# instanceid: uint32
|
|
384
|
+
instanceid, current_offset = serializer.unpack_value(
|
|
385
|
+
data, "uint32", current_offset
|
|
386
|
+
)
|
|
387
|
+
# type: MultiZoneEffectType (enum)
|
|
388
|
+
type_raw, current_offset = serializer.unpack_value(
|
|
389
|
+
data, "uint8", current_offset
|
|
390
|
+
)
|
|
391
|
+
type = MultiZoneEffectType(type_raw)
|
|
392
|
+
# Skip reserved 2 bytes
|
|
393
|
+
current_offset += 2
|
|
394
|
+
# speed: uint32
|
|
395
|
+
speed, current_offset = serializer.unpack_value(data, "uint32", current_offset)
|
|
396
|
+
# duration: uint64
|
|
397
|
+
duration, current_offset = serializer.unpack_value(
|
|
398
|
+
data, "uint64", current_offset
|
|
399
|
+
)
|
|
400
|
+
# Skip reserved 4 bytes
|
|
401
|
+
current_offset += 4
|
|
402
|
+
# Skip reserved 4 bytes
|
|
403
|
+
current_offset += 4
|
|
404
|
+
# parameter: MultiZoneEffectParameter
|
|
405
|
+
parameter, current_offset = MultiZoneEffectParameter.unpack(
|
|
406
|
+
data, current_offset
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
return (
|
|
410
|
+
cls(
|
|
411
|
+
instanceid=instanceid,
|
|
412
|
+
type=type,
|
|
413
|
+
speed=speed,
|
|
414
|
+
duration=duration,
|
|
415
|
+
parameter=parameter,
|
|
416
|
+
),
|
|
417
|
+
current_offset,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@dataclass
|
|
422
|
+
class TileAccelMeas:
|
|
423
|
+
"""Auto-generated field structure."""
|
|
424
|
+
|
|
425
|
+
x: int
|
|
426
|
+
y: int
|
|
427
|
+
z: int
|
|
428
|
+
|
|
429
|
+
def pack(self) -> bytes:
|
|
430
|
+
"""Pack to bytes."""
|
|
431
|
+
from lifx_emulator.protocol import serializer
|
|
432
|
+
|
|
433
|
+
result = b""
|
|
434
|
+
|
|
435
|
+
# x: int16
|
|
436
|
+
result += serializer.pack_value(self.x, "int16")
|
|
437
|
+
# y: int16
|
|
438
|
+
result += serializer.pack_value(self.y, "int16")
|
|
439
|
+
# z: int16
|
|
440
|
+
result += serializer.pack_value(self.z, "int16")
|
|
441
|
+
|
|
442
|
+
return result
|
|
443
|
+
|
|
444
|
+
@classmethod
|
|
445
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[TileAccelMeas, int]:
|
|
446
|
+
"""Unpack from bytes."""
|
|
447
|
+
from lifx_emulator.protocol import serializer
|
|
448
|
+
|
|
449
|
+
current_offset = offset
|
|
450
|
+
# x: int16
|
|
451
|
+
x, current_offset = serializer.unpack_value(data, "int16", current_offset)
|
|
452
|
+
# y: int16
|
|
453
|
+
y, current_offset = serializer.unpack_value(data, "int16", current_offset)
|
|
454
|
+
# z: int16
|
|
455
|
+
z, current_offset = serializer.unpack_value(data, "int16", current_offset)
|
|
456
|
+
|
|
457
|
+
return cls(x=x, y=y, z=z), current_offset
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@dataclass
|
|
461
|
+
class TileBufferRect:
|
|
462
|
+
"""Auto-generated field structure."""
|
|
463
|
+
|
|
464
|
+
fb_index: int
|
|
465
|
+
x: int
|
|
466
|
+
y: int
|
|
467
|
+
width: int
|
|
468
|
+
|
|
469
|
+
def pack(self) -> bytes:
|
|
470
|
+
"""Pack to bytes."""
|
|
471
|
+
from lifx_emulator.protocol import serializer
|
|
472
|
+
|
|
473
|
+
result = b""
|
|
474
|
+
|
|
475
|
+
# fb_index: uint8
|
|
476
|
+
result += serializer.pack_value(self.fb_index, "uint8")
|
|
477
|
+
# x: uint8
|
|
478
|
+
result += serializer.pack_value(self.x, "uint8")
|
|
479
|
+
# y: uint8
|
|
480
|
+
result += serializer.pack_value(self.y, "uint8")
|
|
481
|
+
# width: uint8
|
|
482
|
+
result += serializer.pack_value(self.width, "uint8")
|
|
483
|
+
|
|
484
|
+
return result
|
|
485
|
+
|
|
486
|
+
@classmethod
|
|
487
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[TileBufferRect, int]:
|
|
488
|
+
"""Unpack from bytes."""
|
|
489
|
+
from lifx_emulator.protocol import serializer
|
|
490
|
+
|
|
491
|
+
current_offset = offset
|
|
492
|
+
# fb_index: uint8
|
|
493
|
+
fb_index, current_offset = serializer.unpack_value(
|
|
494
|
+
data, "uint8", current_offset
|
|
495
|
+
)
|
|
496
|
+
# x: uint8
|
|
497
|
+
x, current_offset = serializer.unpack_value(data, "uint8", current_offset)
|
|
498
|
+
# y: uint8
|
|
499
|
+
y, current_offset = serializer.unpack_value(data, "uint8", current_offset)
|
|
500
|
+
# width: uint8
|
|
501
|
+
width, current_offset = serializer.unpack_value(data, "uint8", current_offset)
|
|
502
|
+
|
|
503
|
+
return cls(fb_index=fb_index, x=x, y=y, width=width), current_offset
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
@dataclass
|
|
507
|
+
class TileEffectParameter:
|
|
508
|
+
"""Auto-generated field structure."""
|
|
509
|
+
|
|
510
|
+
parameter0: int
|
|
511
|
+
parameter1: int
|
|
512
|
+
parameter2: int
|
|
513
|
+
parameter3: int
|
|
514
|
+
parameter4: int
|
|
515
|
+
parameter5: int
|
|
516
|
+
parameter6: int
|
|
517
|
+
parameter7: int
|
|
518
|
+
|
|
519
|
+
def pack(self) -> bytes:
|
|
520
|
+
"""Pack to bytes."""
|
|
521
|
+
from lifx_emulator.protocol import serializer
|
|
522
|
+
|
|
523
|
+
result = b""
|
|
524
|
+
|
|
525
|
+
# parameter0: uint32
|
|
526
|
+
result += serializer.pack_value(self.parameter0, "uint32")
|
|
527
|
+
# parameter1: uint32
|
|
528
|
+
result += serializer.pack_value(self.parameter1, "uint32")
|
|
529
|
+
# parameter2: uint32
|
|
530
|
+
result += serializer.pack_value(self.parameter2, "uint32")
|
|
531
|
+
# parameter3: uint32
|
|
532
|
+
result += serializer.pack_value(self.parameter3, "uint32")
|
|
533
|
+
# parameter4: uint32
|
|
534
|
+
result += serializer.pack_value(self.parameter4, "uint32")
|
|
535
|
+
# parameter5: uint32
|
|
536
|
+
result += serializer.pack_value(self.parameter5, "uint32")
|
|
537
|
+
# parameter6: uint32
|
|
538
|
+
result += serializer.pack_value(self.parameter6, "uint32")
|
|
539
|
+
# parameter7: uint32
|
|
540
|
+
result += serializer.pack_value(self.parameter7, "uint32")
|
|
541
|
+
|
|
542
|
+
return result
|
|
543
|
+
|
|
544
|
+
@classmethod
|
|
545
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[TileEffectParameter, int]:
|
|
546
|
+
"""Unpack from bytes."""
|
|
547
|
+
from lifx_emulator.protocol import serializer
|
|
548
|
+
|
|
549
|
+
current_offset = offset
|
|
550
|
+
# parameter0: uint32
|
|
551
|
+
parameter0, current_offset = serializer.unpack_value(
|
|
552
|
+
data, "uint32", current_offset
|
|
553
|
+
)
|
|
554
|
+
# parameter1: uint32
|
|
555
|
+
parameter1, current_offset = serializer.unpack_value(
|
|
556
|
+
data, "uint32", current_offset
|
|
557
|
+
)
|
|
558
|
+
# parameter2: uint32
|
|
559
|
+
parameter2, current_offset = serializer.unpack_value(
|
|
560
|
+
data, "uint32", current_offset
|
|
561
|
+
)
|
|
562
|
+
# parameter3: uint32
|
|
563
|
+
parameter3, current_offset = serializer.unpack_value(
|
|
564
|
+
data, "uint32", current_offset
|
|
565
|
+
)
|
|
566
|
+
# parameter4: uint32
|
|
567
|
+
parameter4, current_offset = serializer.unpack_value(
|
|
568
|
+
data, "uint32", current_offset
|
|
569
|
+
)
|
|
570
|
+
# parameter5: uint32
|
|
571
|
+
parameter5, current_offset = serializer.unpack_value(
|
|
572
|
+
data, "uint32", current_offset
|
|
573
|
+
)
|
|
574
|
+
# parameter6: uint32
|
|
575
|
+
parameter6, current_offset = serializer.unpack_value(
|
|
576
|
+
data, "uint32", current_offset
|
|
577
|
+
)
|
|
578
|
+
# parameter7: uint32
|
|
579
|
+
parameter7, current_offset = serializer.unpack_value(
|
|
580
|
+
data, "uint32", current_offset
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
return (
|
|
584
|
+
cls(
|
|
585
|
+
parameter0=parameter0,
|
|
586
|
+
parameter1=parameter1,
|
|
587
|
+
parameter2=parameter2,
|
|
588
|
+
parameter3=parameter3,
|
|
589
|
+
parameter4=parameter4,
|
|
590
|
+
parameter5=parameter5,
|
|
591
|
+
parameter6=parameter6,
|
|
592
|
+
parameter7=parameter7,
|
|
593
|
+
),
|
|
594
|
+
current_offset,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
@dataclass
|
|
599
|
+
class TileEffectSettings:
|
|
600
|
+
"""Auto-generated field structure."""
|
|
601
|
+
|
|
602
|
+
instanceid: int
|
|
603
|
+
type: TileEffectType
|
|
604
|
+
speed: int
|
|
605
|
+
duration: int
|
|
606
|
+
parameter: TileEffectParameter
|
|
607
|
+
palette_count: int
|
|
608
|
+
palette: list[LightHsbk]
|
|
609
|
+
|
|
610
|
+
def pack(self) -> bytes:
|
|
611
|
+
"""Pack to bytes."""
|
|
612
|
+
from lifx_emulator.protocol import serializer
|
|
613
|
+
|
|
614
|
+
result = b""
|
|
615
|
+
|
|
616
|
+
# instanceid: uint32
|
|
617
|
+
result += serializer.pack_value(self.instanceid, "uint32")
|
|
618
|
+
# type: TileEffectType (enum)
|
|
619
|
+
result += serializer.pack_value(int(self.type), "uint8")
|
|
620
|
+
# speed: uint32
|
|
621
|
+
result += serializer.pack_value(self.speed, "uint32")
|
|
622
|
+
# duration: uint64
|
|
623
|
+
result += serializer.pack_value(self.duration, "uint64")
|
|
624
|
+
# Reserved 4 bytes
|
|
625
|
+
result += serializer.pack_reserved(4)
|
|
626
|
+
# Reserved 4 bytes
|
|
627
|
+
result += serializer.pack_reserved(4)
|
|
628
|
+
# parameter: TileEffectParameter
|
|
629
|
+
result += self.parameter.pack()
|
|
630
|
+
# palette_count: uint8
|
|
631
|
+
result += serializer.pack_value(self.palette_count, "uint8")
|
|
632
|
+
# palette: list[LightHsbk]
|
|
633
|
+
for item in self.palette:
|
|
634
|
+
result += item.pack()
|
|
635
|
+
|
|
636
|
+
return result
|
|
637
|
+
|
|
638
|
+
@classmethod
|
|
639
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[TileEffectSettings, int]:
|
|
640
|
+
"""Unpack from bytes."""
|
|
641
|
+
from lifx_emulator.protocol import serializer
|
|
642
|
+
|
|
643
|
+
current_offset = offset
|
|
644
|
+
# instanceid: uint32
|
|
645
|
+
instanceid, current_offset = serializer.unpack_value(
|
|
646
|
+
data, "uint32", current_offset
|
|
647
|
+
)
|
|
648
|
+
# type: TileEffectType (enum)
|
|
649
|
+
type_raw, current_offset = serializer.unpack_value(
|
|
650
|
+
data, "uint8", current_offset
|
|
651
|
+
)
|
|
652
|
+
type = TileEffectType(type_raw)
|
|
653
|
+
# speed: uint32
|
|
654
|
+
speed, current_offset = serializer.unpack_value(data, "uint32", current_offset)
|
|
655
|
+
# duration: uint64
|
|
656
|
+
duration, current_offset = serializer.unpack_value(
|
|
657
|
+
data, "uint64", current_offset
|
|
658
|
+
)
|
|
659
|
+
# Skip reserved 4 bytes
|
|
660
|
+
current_offset += 4
|
|
661
|
+
# Skip reserved 4 bytes
|
|
662
|
+
current_offset += 4
|
|
663
|
+
# parameter: TileEffectParameter
|
|
664
|
+
parameter, current_offset = TileEffectParameter.unpack(data, current_offset)
|
|
665
|
+
# palette_count: uint8
|
|
666
|
+
palette_count, current_offset = serializer.unpack_value(
|
|
667
|
+
data, "uint8", current_offset
|
|
668
|
+
)
|
|
669
|
+
# palette: list[LightHsbk]
|
|
670
|
+
palette = []
|
|
671
|
+
for _ in range(16):
|
|
672
|
+
item, current_offset = LightHsbk.unpack(data, current_offset)
|
|
673
|
+
palette.append(item)
|
|
674
|
+
|
|
675
|
+
return (
|
|
676
|
+
cls(
|
|
677
|
+
instanceid=instanceid,
|
|
678
|
+
type=type,
|
|
679
|
+
speed=speed,
|
|
680
|
+
duration=duration,
|
|
681
|
+
parameter=parameter,
|
|
682
|
+
palette_count=palette_count,
|
|
683
|
+
palette=palette,
|
|
684
|
+
),
|
|
685
|
+
current_offset,
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
@dataclass
|
|
690
|
+
class TileStateDevice:
|
|
691
|
+
"""Auto-generated field structure."""
|
|
692
|
+
|
|
693
|
+
accel_meas: TileAccelMeas
|
|
694
|
+
user_x: float
|
|
695
|
+
user_y: float
|
|
696
|
+
width: int
|
|
697
|
+
height: int
|
|
698
|
+
device_version: DeviceStateVersion
|
|
699
|
+
firmware: DeviceStateHostFirmware
|
|
700
|
+
|
|
701
|
+
def pack(self) -> bytes:
|
|
702
|
+
"""Pack to bytes."""
|
|
703
|
+
from lifx_emulator.protocol import serializer
|
|
704
|
+
|
|
705
|
+
result = b""
|
|
706
|
+
|
|
707
|
+
# accel_meas: TileAccelMeas
|
|
708
|
+
result += self.accel_meas.pack()
|
|
709
|
+
# Reserved 1 bytes
|
|
710
|
+
result += serializer.pack_reserved(1)
|
|
711
|
+
# Reserved 1 bytes
|
|
712
|
+
result += serializer.pack_reserved(1)
|
|
713
|
+
# user_x: float32
|
|
714
|
+
result += serializer.pack_value(self.user_x, "float32")
|
|
715
|
+
# user_y: float32
|
|
716
|
+
result += serializer.pack_value(self.user_y, "float32")
|
|
717
|
+
# width: uint8
|
|
718
|
+
result += serializer.pack_value(self.width, "uint8")
|
|
719
|
+
# height: uint8
|
|
720
|
+
result += serializer.pack_value(self.height, "uint8")
|
|
721
|
+
# Reserved 1 bytes
|
|
722
|
+
result += serializer.pack_reserved(1)
|
|
723
|
+
# device_version: DeviceStateVersion
|
|
724
|
+
result += self.device_version.pack()
|
|
725
|
+
# firmware: DeviceStateHostFirmware
|
|
726
|
+
result += self.firmware.pack()
|
|
727
|
+
# Reserved 4 bytes
|
|
728
|
+
result += serializer.pack_reserved(4)
|
|
729
|
+
|
|
730
|
+
return result
|
|
731
|
+
|
|
732
|
+
@classmethod
|
|
733
|
+
def unpack(cls, data: bytes, offset: int = 0) -> tuple[TileStateDevice, int]:
|
|
734
|
+
"""Unpack from bytes."""
|
|
735
|
+
from lifx_emulator.protocol import serializer
|
|
736
|
+
|
|
737
|
+
current_offset = offset
|
|
738
|
+
# accel_meas: TileAccelMeas
|
|
739
|
+
accel_meas, current_offset = TileAccelMeas.unpack(data, current_offset)
|
|
740
|
+
# Skip reserved 1 bytes
|
|
741
|
+
current_offset += 1
|
|
742
|
+
# Skip reserved 1 bytes
|
|
743
|
+
current_offset += 1
|
|
744
|
+
# user_x: float32
|
|
745
|
+
user_x, current_offset = serializer.unpack_value(
|
|
746
|
+
data, "float32", current_offset
|
|
747
|
+
)
|
|
748
|
+
# user_y: float32
|
|
749
|
+
user_y, current_offset = serializer.unpack_value(
|
|
750
|
+
data, "float32", current_offset
|
|
751
|
+
)
|
|
752
|
+
# width: uint8
|
|
753
|
+
width, current_offset = serializer.unpack_value(data, "uint8", current_offset)
|
|
754
|
+
# height: uint8
|
|
755
|
+
height, current_offset = serializer.unpack_value(data, "uint8", current_offset)
|
|
756
|
+
# Skip reserved 1 bytes
|
|
757
|
+
current_offset += 1
|
|
758
|
+
# device_version: DeviceStateVersion
|
|
759
|
+
device_version, current_offset = DeviceStateVersion.unpack(data, current_offset)
|
|
760
|
+
# firmware: DeviceStateHostFirmware
|
|
761
|
+
firmware, current_offset = DeviceStateHostFirmware.unpack(data, current_offset)
|
|
762
|
+
# Skip reserved 4 bytes
|
|
763
|
+
current_offset += 4
|
|
764
|
+
|
|
765
|
+
return (
|
|
766
|
+
cls(
|
|
767
|
+
accel_meas=accel_meas,
|
|
768
|
+
user_x=user_x,
|
|
769
|
+
user_y=user_y,
|
|
770
|
+
width=width,
|
|
771
|
+
height=height,
|
|
772
|
+
device_version=device_version,
|
|
773
|
+
firmware=firmware,
|
|
774
|
+
),
|
|
775
|
+
current_offset,
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
# Type aliases for convenience
|
|
780
|
+
TileDevice = TileStateDevice # Pythonic alias
|
|
781
|
+
|
|
782
|
+
# Field name mappings: Python name -> Protocol name
|
|
783
|
+
# Used by serializer to translate between conventions
|
|
784
|
+
FIELD_MAPPINGS: dict[str, dict[str, str]] = {
|
|
785
|
+
"DeviceStateHostFirmware": {
|
|
786
|
+
"build": "Build",
|
|
787
|
+
"version_minor": "VersionMinor",
|
|
788
|
+
"version_major": "VersionMajor",
|
|
789
|
+
},
|
|
790
|
+
"DeviceStateVersion": {"vendor": "Vendor", "product": "Product"},
|
|
791
|
+
"LightHsbk": {
|
|
792
|
+
"hue": "Hue",
|
|
793
|
+
"saturation": "Saturation",
|
|
794
|
+
"brightness": "Brightness",
|
|
795
|
+
"kelvin": "Kelvin",
|
|
796
|
+
},
|
|
797
|
+
"MultiZoneEffectParameter": {
|
|
798
|
+
"parameter0": "Parameter0",
|
|
799
|
+
"parameter1": "Parameter1",
|
|
800
|
+
"parameter2": "Parameter2",
|
|
801
|
+
"parameter3": "Parameter3",
|
|
802
|
+
"parameter4": "Parameter4",
|
|
803
|
+
"parameter5": "Parameter5",
|
|
804
|
+
"parameter6": "Parameter6",
|
|
805
|
+
"parameter7": "Parameter7",
|
|
806
|
+
},
|
|
807
|
+
"MultiZoneEffectSettings": {
|
|
808
|
+
"duration": "Duration",
|
|
809
|
+
"instanceid": "Instanceid",
|
|
810
|
+
"parameter": "Parameter",
|
|
811
|
+
"speed": "Speed",
|
|
812
|
+
"type": "Type",
|
|
813
|
+
},
|
|
814
|
+
"TileAccelMeas": {"x": "X", "y": "Y", "z": "Z"},
|
|
815
|
+
"TileBufferRect": {"fb_index": "FbIndex", "x": "X", "y": "Y", "width": "Width"},
|
|
816
|
+
"TileEffectParameter": {
|
|
817
|
+
"parameter0": "Parameter0",
|
|
818
|
+
"parameter1": "Parameter1",
|
|
819
|
+
"parameter2": "Parameter2",
|
|
820
|
+
"parameter3": "Parameter3",
|
|
821
|
+
"parameter4": "Parameter4",
|
|
822
|
+
"parameter5": "Parameter5",
|
|
823
|
+
"parameter6": "Parameter6",
|
|
824
|
+
"parameter7": "Parameter7",
|
|
825
|
+
},
|
|
826
|
+
"TileEffectSettings": {
|
|
827
|
+
"duration": "Duration",
|
|
828
|
+
"instanceid": "Instanceid",
|
|
829
|
+
"palette": "Palette",
|
|
830
|
+
"palette_count": "PaletteCount",
|
|
831
|
+
"parameter": "Parameter",
|
|
832
|
+
"speed": "Speed",
|
|
833
|
+
"type": "Type",
|
|
834
|
+
},
|
|
835
|
+
"TileStateDevice": {
|
|
836
|
+
"accel_meas": "AccelMeas",
|
|
837
|
+
"device_version": "DeviceVersion",
|
|
838
|
+
"firmware": "Firmware",
|
|
839
|
+
"height": "Height",
|
|
840
|
+
"user_x": "UserX",
|
|
841
|
+
"user_y": "UserY",
|
|
842
|
+
"width": "Width",
|
|
843
|
+
},
|
|
844
|
+
}
|