lifx-emulator 2.4.0__py3-none-any.whl → 3.1.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-3.1.0.dist-info/METADATA +103 -0
- lifx_emulator-3.1.0.dist-info/RECORD +19 -0
- {lifx_emulator-2.4.0.dist-info → lifx_emulator-3.1.0.dist-info}/WHEEL +1 -1
- lifx_emulator-3.1.0.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 +9 -4
- {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_app/api/static/dashboard.js +588 -0
- lifx_emulator_app/api/templates/dashboard.html +357 -0
- lifx_emulator/__init__.py +0 -31
- lifx_emulator/api/routers/__init__.py +0 -11
- lifx_emulator/api/services/__init__.py +0 -8
- lifx_emulator/api/templates/dashboard.html +0 -899
- 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
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>LIFX Emulator Monitor</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
body {
|
|
14
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
15
|
+
Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
16
|
+
background: #0a0a0a;
|
|
17
|
+
color: #e0e0e0;
|
|
18
|
+
line-height: 1.6;
|
|
19
|
+
padding: 20px;
|
|
20
|
+
}
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 1400px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
h1 {
|
|
26
|
+
color: #fff;
|
|
27
|
+
margin-bottom: 10px;
|
|
28
|
+
font-size: 2em;
|
|
29
|
+
}
|
|
30
|
+
.subtitle {
|
|
31
|
+
color: #888;
|
|
32
|
+
margin-bottom: 30px;
|
|
33
|
+
}
|
|
34
|
+
.grid {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
37
|
+
gap: 15px;
|
|
38
|
+
margin-bottom: 25px;
|
|
39
|
+
}
|
|
40
|
+
.devices-grid {
|
|
41
|
+
display: grid;
|
|
42
|
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
43
|
+
gap: 10px;
|
|
44
|
+
}
|
|
45
|
+
.card {
|
|
46
|
+
background: #1a1a1a;
|
|
47
|
+
border: 1px solid #333;
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
padding: 20px;
|
|
50
|
+
}
|
|
51
|
+
.card h2 {
|
|
52
|
+
color: #fff;
|
|
53
|
+
font-size: 1.2em;
|
|
54
|
+
margin-bottom: 15px;
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 10px;
|
|
58
|
+
}
|
|
59
|
+
.stat {
|
|
60
|
+
display: flex;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
padding: 8px 0;
|
|
63
|
+
border-bottom: 1px solid #2a2a2a;
|
|
64
|
+
}
|
|
65
|
+
.stat:last-child {
|
|
66
|
+
border-bottom: none;
|
|
67
|
+
}
|
|
68
|
+
.stat-label {
|
|
69
|
+
color: #888;
|
|
70
|
+
}
|
|
71
|
+
.stat-value {
|
|
72
|
+
color: #fff;
|
|
73
|
+
font-weight: 600;
|
|
74
|
+
}
|
|
75
|
+
.device {
|
|
76
|
+
background: #252525;
|
|
77
|
+
border: 1px solid #333;
|
|
78
|
+
border-radius: 6px;
|
|
79
|
+
padding: 8px;
|
|
80
|
+
margin-bottom: 8px;
|
|
81
|
+
font-size: 0.85em;
|
|
82
|
+
}
|
|
83
|
+
.device-header {
|
|
84
|
+
display: flex;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
align-items: center;
|
|
87
|
+
margin-bottom: 6px;
|
|
88
|
+
}
|
|
89
|
+
.device-serial {
|
|
90
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
91
|
+
color: #4a9eff;
|
|
92
|
+
font-weight: bold;
|
|
93
|
+
font-size: 0.9em;
|
|
94
|
+
}
|
|
95
|
+
.device-label {
|
|
96
|
+
color: #aaa;
|
|
97
|
+
font-size: 0.85em;
|
|
98
|
+
}
|
|
99
|
+
.zones-container {
|
|
100
|
+
margin-top: 8px;
|
|
101
|
+
padding-top: 8px;
|
|
102
|
+
border-top: 1px solid #333;
|
|
103
|
+
}
|
|
104
|
+
.zones-toggle, .metadata-toggle {
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
color: #4a9eff;
|
|
107
|
+
font-size: 0.8em;
|
|
108
|
+
margin-top: 4px;
|
|
109
|
+
user-select: none;
|
|
110
|
+
}
|
|
111
|
+
.zones-toggle:hover, .metadata-toggle:hover {
|
|
112
|
+
color: #6bb0ff;
|
|
113
|
+
}
|
|
114
|
+
.zones-display, .metadata-display {
|
|
115
|
+
display: none;
|
|
116
|
+
margin-top: 6px;
|
|
117
|
+
}
|
|
118
|
+
.zones-display.show, .metadata-display.show {
|
|
119
|
+
display: block;
|
|
120
|
+
}
|
|
121
|
+
.metadata-display {
|
|
122
|
+
font-size: 0.75em;
|
|
123
|
+
color: #888;
|
|
124
|
+
padding: 6px;
|
|
125
|
+
background: #1a1a1a;
|
|
126
|
+
border-radius: 3px;
|
|
127
|
+
border: 1px solid #333;
|
|
128
|
+
}
|
|
129
|
+
.metadata-row {
|
|
130
|
+
display: flex;
|
|
131
|
+
justify-content: space-between;
|
|
132
|
+
padding: 2px 0;
|
|
133
|
+
}
|
|
134
|
+
.metadata-label {
|
|
135
|
+
color: #666;
|
|
136
|
+
}
|
|
137
|
+
.metadata-value {
|
|
138
|
+
color: #aaa;
|
|
139
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
140
|
+
}
|
|
141
|
+
.zone-strip {
|
|
142
|
+
display: flex;
|
|
143
|
+
height: 20px;
|
|
144
|
+
border-radius: 3px;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
margin-bottom: 4px;
|
|
147
|
+
}
|
|
148
|
+
.zone-segment {
|
|
149
|
+
flex: 1;
|
|
150
|
+
min-width: 4px;
|
|
151
|
+
}
|
|
152
|
+
.color-swatch {
|
|
153
|
+
display: inline-block;
|
|
154
|
+
width: 16px;
|
|
155
|
+
height: 16px;
|
|
156
|
+
border-radius: 3px;
|
|
157
|
+
border: 1px solid #333;
|
|
158
|
+
vertical-align: middle;
|
|
159
|
+
margin-right: 4px;
|
|
160
|
+
}
|
|
161
|
+
.tile-grid {
|
|
162
|
+
display: grid;
|
|
163
|
+
gap: 2px;
|
|
164
|
+
margin-top: 4px;
|
|
165
|
+
}
|
|
166
|
+
.tile-zone {
|
|
167
|
+
width: 8px;
|
|
168
|
+
height: 8px;
|
|
169
|
+
border-radius: 1px;
|
|
170
|
+
}
|
|
171
|
+
.tiles-container {
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-wrap: wrap;
|
|
174
|
+
gap: 8px;
|
|
175
|
+
margin-top: 4px;
|
|
176
|
+
}
|
|
177
|
+
.tile-item {
|
|
178
|
+
display: inline-block;
|
|
179
|
+
}
|
|
180
|
+
.badge {
|
|
181
|
+
display: inline-block;
|
|
182
|
+
padding: 2px 6px;
|
|
183
|
+
border-radius: 3px;
|
|
184
|
+
font-size: 0.7em;
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
margin-right: 4px;
|
|
187
|
+
margin-bottom: 2px;
|
|
188
|
+
}
|
|
189
|
+
.badge-power-on {
|
|
190
|
+
background: #2d5;
|
|
191
|
+
color: #000;
|
|
192
|
+
}
|
|
193
|
+
.badge-power-off {
|
|
194
|
+
background: #555;
|
|
195
|
+
color: #aaa;
|
|
196
|
+
}
|
|
197
|
+
.badge-capability {
|
|
198
|
+
background: #333;
|
|
199
|
+
color: #4a9eff;
|
|
200
|
+
}
|
|
201
|
+
.badge-extended-mz {
|
|
202
|
+
background: #2d4a2d;
|
|
203
|
+
color: #5dff5d;
|
|
204
|
+
}
|
|
205
|
+
.activity-log {
|
|
206
|
+
background: #0d0d0d;
|
|
207
|
+
border: 1px solid #333;
|
|
208
|
+
border-radius: 6px;
|
|
209
|
+
padding: 15px;
|
|
210
|
+
max-height: 400px;
|
|
211
|
+
overflow-y: auto;
|
|
212
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
213
|
+
font-size: 0.85em;
|
|
214
|
+
}
|
|
215
|
+
.activity-item {
|
|
216
|
+
padding: 6px 0;
|
|
217
|
+
border-bottom: 1px solid #1a1a1a;
|
|
218
|
+
display: flex;
|
|
219
|
+
gap: 10px;
|
|
220
|
+
}
|
|
221
|
+
.activity-item:last-child {
|
|
222
|
+
border-bottom: none;
|
|
223
|
+
}
|
|
224
|
+
.activity-time {
|
|
225
|
+
color: #666;
|
|
226
|
+
min-width: 80px;
|
|
227
|
+
}
|
|
228
|
+
.activity-rx {
|
|
229
|
+
color: #4a9eff;
|
|
230
|
+
}
|
|
231
|
+
.activity-tx {
|
|
232
|
+
color: #f9a825;
|
|
233
|
+
}
|
|
234
|
+
.activity-packet {
|
|
235
|
+
color: #aaa;
|
|
236
|
+
}
|
|
237
|
+
.btn {
|
|
238
|
+
background: #4a9eff;
|
|
239
|
+
color: #000;
|
|
240
|
+
border: none;
|
|
241
|
+
padding: 4px 8px;
|
|
242
|
+
border-radius: 3px;
|
|
243
|
+
cursor: pointer;
|
|
244
|
+
font-weight: 600;
|
|
245
|
+
font-size: 0.75em;
|
|
246
|
+
}
|
|
247
|
+
.btn:hover {
|
|
248
|
+
background: #6bb0ff;
|
|
249
|
+
}
|
|
250
|
+
.btn-delete {
|
|
251
|
+
background: #d32f2f;
|
|
252
|
+
color: #fff;
|
|
253
|
+
}
|
|
254
|
+
.btn-delete:hover {
|
|
255
|
+
background: #e57373;
|
|
256
|
+
}
|
|
257
|
+
.form-group {
|
|
258
|
+
margin-bottom: 15px;
|
|
259
|
+
}
|
|
260
|
+
.form-group label {
|
|
261
|
+
display: block;
|
|
262
|
+
color: #aaa;
|
|
263
|
+
margin-bottom: 5px;
|
|
264
|
+
font-size: 0.9em;
|
|
265
|
+
}
|
|
266
|
+
.form-group input, .form-group select {
|
|
267
|
+
width: 100%;
|
|
268
|
+
background: #0d0d0d;
|
|
269
|
+
border: 1px solid #333;
|
|
270
|
+
color: #fff;
|
|
271
|
+
padding: 8px;
|
|
272
|
+
border-radius: 4px;
|
|
273
|
+
}
|
|
274
|
+
.status-indicator {
|
|
275
|
+
display: inline-block;
|
|
276
|
+
width: 8px;
|
|
277
|
+
height: 8px;
|
|
278
|
+
border-radius: 50%;
|
|
279
|
+
background: #2d5;
|
|
280
|
+
animation: pulse 2s infinite;
|
|
281
|
+
}
|
|
282
|
+
@keyframes pulse {
|
|
283
|
+
0%, 100% { opacity: 1; }
|
|
284
|
+
50% { opacity: 0.5; }
|
|
285
|
+
}
|
|
286
|
+
.no-devices {
|
|
287
|
+
text-align: center;
|
|
288
|
+
color: #666;
|
|
289
|
+
padding: 40px;
|
|
290
|
+
}
|
|
291
|
+
</style>
|
|
292
|
+
</head>
|
|
293
|
+
<body>
|
|
294
|
+
<div class="container">
|
|
295
|
+
<h1>LIFX Emulator Monitor</h1>
|
|
296
|
+
<p class="subtitle">Real-time monitoring and device management</p>
|
|
297
|
+
|
|
298
|
+
<div class="grid">
|
|
299
|
+
<div class="card">
|
|
300
|
+
<h2><span class="status-indicator"></span> Server Statistics</h2>
|
|
301
|
+
<div id="stats">
|
|
302
|
+
<div class="stat">
|
|
303
|
+
<span class="stat-label">Loading...</span>
|
|
304
|
+
<span class="stat-value"></span>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div class="card">
|
|
310
|
+
<h2>Add Device</h2>
|
|
311
|
+
<form id="add-device-form">
|
|
312
|
+
<div class="form-group">
|
|
313
|
+
<label>Product ID</label>
|
|
314
|
+
<select id="product-id" required>
|
|
315
|
+
<option value="27">27 - LIFX A19</option>
|
|
316
|
+
<option value="29">29 - LIFX A19 Night Vision</option>
|
|
317
|
+
<option value="32">32 - LIFX Z (Strip)</option>
|
|
318
|
+
<option value="38">38 - LIFX Beam</option>
|
|
319
|
+
<option value="50">50 - LIFX Mini White to Warm</option>
|
|
320
|
+
<option value="55">55 - LIFX Tile</option>
|
|
321
|
+
<option value="90">90 - LIFX Clean (HEV)</option>
|
|
322
|
+
</select>
|
|
323
|
+
</div>
|
|
324
|
+
<button type="submit" class="btn">Add Device</button>
|
|
325
|
+
</form>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div class="card">
|
|
330
|
+
<h2>
|
|
331
|
+
Devices (<span id="device-count">0</span>)
|
|
332
|
+
<span style="float: right; display: flex; gap: 8px;">
|
|
333
|
+
<button
|
|
334
|
+
class="btn btn-delete"
|
|
335
|
+
onclick="removeAllDevices()"
|
|
336
|
+
title="Remove all devices from server (runtime only)"
|
|
337
|
+
>Remove All</button>
|
|
338
|
+
<button
|
|
339
|
+
class="btn btn-delete"
|
|
340
|
+
onclick="clearStorage()"
|
|
341
|
+
id="clear-storage-btn"
|
|
342
|
+
title="Delete all persistent device state files"
|
|
343
|
+
>Clear Storage</button>
|
|
344
|
+
</span>
|
|
345
|
+
</h2>
|
|
346
|
+
<div id="devices" class="devices-grid"></div>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<div class="card" id="activity-card">
|
|
350
|
+
<h2>Recent Activity</h2>
|
|
351
|
+
<div class="activity-log" id="activity-log"></div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<script src="/static/dashboard.js"></script>
|
|
356
|
+
</body>
|
|
357
|
+
</html>
|
lifx_emulator/__init__.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""LIFX Emulator
|
|
2
|
-
|
|
3
|
-
A comprehensive LIFX emulator for testing LIFX LAN protocol libraries.
|
|
4
|
-
Implements the binary UDP protocol documented at https://lan.developer.lifx.com
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from importlib.metadata import version as get_version
|
|
8
|
-
|
|
9
|
-
from lifx_emulator.devices import EmulatedLifxDevice
|
|
10
|
-
from lifx_emulator.factories import (
|
|
11
|
-
create_color_light,
|
|
12
|
-
create_color_temperature_light,
|
|
13
|
-
create_hev_light,
|
|
14
|
-
create_infrared_light,
|
|
15
|
-
create_multizone_light,
|
|
16
|
-
create_tile_device,
|
|
17
|
-
)
|
|
18
|
-
from lifx_emulator.server import EmulatedLifxServer
|
|
19
|
-
|
|
20
|
-
__version__ = get_version("lifx_emulator")
|
|
21
|
-
|
|
22
|
-
__all__ = [
|
|
23
|
-
"EmulatedLifxServer",
|
|
24
|
-
"EmulatedLifxDevice",
|
|
25
|
-
"create_color_light",
|
|
26
|
-
"create_color_temperature_light",
|
|
27
|
-
"create_hev_light",
|
|
28
|
-
"create_infrared_light",
|
|
29
|
-
"create_multizone_light",
|
|
30
|
-
"create_tile_device",
|
|
31
|
-
]
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"""API routers for LIFX emulator endpoints."""
|
|
2
|
-
|
|
3
|
-
from lifx_emulator.api.routers.devices import create_devices_router
|
|
4
|
-
from lifx_emulator.api.routers.monitoring import create_monitoring_router
|
|
5
|
-
from lifx_emulator.api.routers.scenarios import create_scenarios_router
|
|
6
|
-
|
|
7
|
-
__all__ = [
|
|
8
|
-
"create_monitoring_router",
|
|
9
|
-
"create_devices_router",
|
|
10
|
-
"create_scenarios_router",
|
|
11
|
-
]
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"""Business logic services for API endpoints."""
|
|
2
|
-
|
|
3
|
-
from lifx_emulator.api.services.device_service import DeviceService
|
|
4
|
-
|
|
5
|
-
# TODO: Create ScenarioService (Phase 1.1b completion)
|
|
6
|
-
# from lifx_emulator.api.services.scenario_service import ScenarioService
|
|
7
|
-
|
|
8
|
-
__all__ = ["DeviceService"]
|