@bobfrankston/lxlan 0.1.0 → 0.1.3
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.
- package/.gitattributes +5 -0
- package/client.d.ts +36 -18
- package/client.d.ts.map +1 -1
- package/client.js +92 -35
- package/client.js.map +1 -1
- package/device.d.ts +58 -4
- package/device.d.ts.map +1 -1
- package/device.js +111 -11
- package/device.js.map +1 -1
- package/events.d.ts +24 -0
- package/events.d.ts.map +1 -0
- package/events.js +7 -0
- package/events.js.map +1 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +14 -6
- package/protocol.d.ts +23 -0
- package/protocol.d.ts.map +1 -1
- package/protocol.js +27 -0
- package/protocol.js.map +1 -1
- package/transport.d.ts +30 -0
- package/transport.d.ts.map +1 -0
- package/transport.js +9 -0
- package/transport.js.map +1 -0
- package/types.d.ts +13 -0
- package/types.d.ts.map +1 -1
- package/types.js +8 -0
- package/types.js.map +1 -1
- package/.claude/settings.local.json +0 -13
- package/client.ts +0 -214
- package/device.ts +0 -170
- package/index.ts +0 -4
- package/notes.md +0 -530
- package/protocol.ts +0 -209
- package/tsconfig.json +0 -20
- package/types.ts +0 -129
package/notes.md
DELETED
|
@@ -1,530 +0,0 @@
|
|
|
1
|
-
# LIFX LAN Library Design - lxlan
|
|
2
|
-
|
|
3
|
-
**Updated: 2026-01-11 by Claude Code**
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
TypeScript library for LIFX device control over LAN using UDP protocol. Designed for simplicity and portability.
|
|
8
|
-
|
|
9
|
-
## Architecture
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
┌─────────────────────────────────────────────────────────┐
|
|
13
|
-
│ Application │
|
|
14
|
-
├─────────────────────────────────────────────────────────┤
|
|
15
|
-
│ @bobfrankston/lxlan │
|
|
16
|
-
│ ├── LxClient - main entry, event emitter │
|
|
17
|
-
│ ├── LxDevice - device abstraction │
|
|
18
|
-
│ └── LxProtocol - message encode/decode │
|
|
19
|
-
├─────────────────────────────────────────────────────────┤
|
|
20
|
-
│ @bobfrankston/lxudp (separate package) │
|
|
21
|
-
│ └── UDP transport with port sharing, retry, timeouts │
|
|
22
|
-
│ Future: web server proxy for browser UDP access │
|
|
23
|
-
│ May complement/replace y:\dev\homecontrol\utils\netsupport │
|
|
24
|
-
├─────────────────────────────────────────────────────────┤
|
|
25
|
-
│ @bobfrankston/colorlib (separate package) │
|
|
26
|
-
│ └── Color conversions: RGB ↔ HSL ↔ HSBK ↔ Kelvin │
|
|
27
|
-
└─────────────────────────────────────────────────────────┘
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Design Decisions
|
|
31
|
-
|
|
32
|
-
### Async Model (Fire-and-Forget)
|
|
33
|
-
- **Send methods return when packet sent**, not when response received
|
|
34
|
-
- **Results delivered via EventEmitter**
|
|
35
|
-
- Sequence numbers track request/response internally
|
|
36
|
-
|
|
37
|
-
### Event Emission Policy
|
|
38
|
-
- **Emit on every message** from device, even if state unchanged
|
|
39
|
-
- **Cache state** on device object for sync queries
|
|
40
|
-
- Traffic is light - no throttling needed
|
|
41
|
-
|
|
42
|
-
### Retry Policy
|
|
43
|
-
- **Auto-retry** with configurable count (default 3)
|
|
44
|
-
- **Configurable via API** at runtime, not just constructor
|
|
45
|
-
- **Long timeouts supported** - up to 10+ seconds for network issues
|
|
46
|
-
- Settings: retryCount, retryDelay, responseTimeout
|
|
47
|
-
|
|
48
|
-
### Port Sharing
|
|
49
|
-
- Uses `SO_REUSEADDR` via dgram `reuseAddr: true`
|
|
50
|
-
- Multiple instances share port 56700
|
|
51
|
-
|
|
52
|
-
### Scope - v1
|
|
53
|
-
- **No multizone** - treat as single device (see Future section)
|
|
54
|
-
- **Include label/group/location get/set** - for naming management
|
|
55
|
-
- Strategy: set device labels to factory-default-based names, use external DB for management
|
|
56
|
-
|
|
57
|
-
### Separation of Concerns
|
|
58
|
-
- **lxcolor**: Pure color math, no dependencies
|
|
59
|
-
- **lxudp**: Generic UDP transport, reusable
|
|
60
|
-
- **lxlan**: LIFX protocol only, uses lxudp
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## lxudp API (separate package)
|
|
65
|
-
|
|
66
|
-
Reusable UDP transport with port sharing, retry logic, configurable timeouts.
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { EventEmitter } from 'events';
|
|
70
|
-
|
|
71
|
-
interface UdpSocketOptions {
|
|
72
|
-
port?: number; /** default 0 (ephemeral) */
|
|
73
|
-
reuseAddr?: boolean; /** default true */
|
|
74
|
-
broadcastAddr?: string; /** default "255.255.255.255" */
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface UdpRetryOptions {
|
|
78
|
-
retryCount?: number; /** default 3 */
|
|
79
|
-
retryDelay?: number; /** ms between retries, default 100 */
|
|
80
|
-
responseTimeout?: number; /** ms to wait for response, default 1000 */
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
class UdpSocket extends EventEmitter {
|
|
84
|
-
constructor(options?: UdpSocketOptions);
|
|
85
|
-
|
|
86
|
-
// Lifecycle
|
|
87
|
-
bind(): Promise<void>;
|
|
88
|
-
close(): void;
|
|
89
|
-
|
|
90
|
-
// Runtime config
|
|
91
|
-
setRetryOptions(options: UdpRetryOptions): void;
|
|
92
|
-
getRetryOptions(): UdpRetryOptions;
|
|
93
|
-
|
|
94
|
-
// Send
|
|
95
|
-
send(ip: string, port: number, data: Buffer): void; /** fire-and-forget */
|
|
96
|
-
broadcast(data: Buffer, port?: number): void;
|
|
97
|
-
|
|
98
|
-
// Events:
|
|
99
|
-
// 'message' (data: Buffer, rinfo: { address: string, port: number })
|
|
100
|
-
// 'error' (err: Error)
|
|
101
|
-
// 'bound' ()
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Future: UdpProxy for web server bridge
|
|
105
|
-
// class UdpProxyServer { ... }
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## colorlib API (separate package)
|
|
111
|
-
|
|
112
|
-
Generic color conversion library. No LIFX specifics.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// Types
|
|
116
|
-
interface RGB { r: number; g: number; b: number; } /** 0-255 */
|
|
117
|
-
interface HSL { h: number; s: number; l: number; } /** h: 0-360, s/l: 0-100 */
|
|
118
|
-
interface HSB { h: number; s: number; b: number; } /** h: 0-360, s/b: 0-100 */
|
|
119
|
-
interface HSBK { h: number; s: number; b: number; k: number; } /** k: 1500-9000 Kelvin */
|
|
120
|
-
|
|
121
|
-
// Conversions
|
|
122
|
-
function rgbToHsl(rgb: RGB): HSL;
|
|
123
|
-
function hslToRgb(hsl: HSL): RGB;
|
|
124
|
-
function rgbToHsb(rgb: RGB): HSB;
|
|
125
|
-
function hsbToRgb(hsb: HSB): RGB;
|
|
126
|
-
function hsbToHsbk(hsb: HSB, kelvin?: number): HSBK; /** default 3500K */
|
|
127
|
-
function kelvinToRgb(kelvin: number): RGB; /** approximate white point */
|
|
128
|
-
|
|
129
|
-
// Parsing - flexible input
|
|
130
|
-
function parseColor(input: string | RGB | HSL | HSB | HSBK | number): HSBK;
|
|
131
|
-
// Accepts: "#ff0000", "rgb(255,0,0)", "red", {r,g,b}, {h,s,l}, {h,s,b,k}, 6500 (kelvin)
|
|
132
|
-
|
|
133
|
-
// 16-bit wire format conversion
|
|
134
|
-
function hsbkTo16(hsbk: HSBK): HSBK16; /** h/s/b: 0-0xFFFF, k: 1500-9000 */
|
|
135
|
-
function hsbk16ToHsbk(hsbk16: HSBK16): HSBK;
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## lxlan API
|
|
141
|
-
|
|
142
|
-
### LxClient - Main Entry Point
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { EventEmitter } from 'events';
|
|
146
|
-
|
|
147
|
-
interface LxClientOptions {
|
|
148
|
-
port?: number; /** default 56700 */
|
|
149
|
-
broadcastAddr?: string; /** default "255.255.255.255" */
|
|
150
|
-
discoveryInterval?: number; /** ms, 0 = manual only */
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
class LxClient extends EventEmitter {
|
|
154
|
-
constructor(options?: LxClientOptions);
|
|
155
|
-
|
|
156
|
-
// Lifecycle
|
|
157
|
-
start(): void;
|
|
158
|
-
stop(): void;
|
|
159
|
-
|
|
160
|
-
// Runtime configuration
|
|
161
|
-
setRetryOptions(options: { retryCount?: number; retryDelay?: number; responseTimeout?: number }): void;
|
|
162
|
-
getRetryOptions(): { retryCount: number; retryDelay: number; responseTimeout: number };
|
|
163
|
-
|
|
164
|
-
// Discovery
|
|
165
|
-
discover(): void;
|
|
166
|
-
|
|
167
|
-
// Device access (sync - reads cache)
|
|
168
|
-
devices: Map<string, LxDevice>;
|
|
169
|
-
getDevice(mac: string): LxDevice;
|
|
170
|
-
addDevice(mac: string, ip: string, port?: number): LxDevice; /** manual registration */
|
|
171
|
-
|
|
172
|
-
// Events:
|
|
173
|
-
// 'device' (device: LxDevice) - new device discovered
|
|
174
|
-
// 'message' (device: LxDevice, msg: LxMessage) - any message from device
|
|
175
|
-
// 'state' (device: LxDevice) - state message (always emits)
|
|
176
|
-
// 'power' (device: LxDevice) - power message
|
|
177
|
-
// 'label' (device: LxDevice) - label changed
|
|
178
|
-
// 'group' (device: LxDevice) - group info received
|
|
179
|
-
// 'location' (device: LxDevice) - location info received
|
|
180
|
-
// 'online' (device: LxDevice) - device online
|
|
181
|
-
// 'offline' (device: LxDevice) - device offline
|
|
182
|
-
// 'error' (error: Error) - transport/protocol error
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### LxDevice - Device Abstraction
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
interface LxDevice {
|
|
190
|
-
// Identity
|
|
191
|
-
mac: string;
|
|
192
|
-
ip: string;
|
|
193
|
-
port: number;
|
|
194
|
-
label: string;
|
|
195
|
-
|
|
196
|
-
// Cached state
|
|
197
|
-
power: boolean;
|
|
198
|
-
color: HSBK;
|
|
199
|
-
online: boolean;
|
|
200
|
-
lastSeen: number;
|
|
201
|
-
|
|
202
|
-
// Product info
|
|
203
|
-
vendor: number;
|
|
204
|
-
product: number;
|
|
205
|
-
productName: string;
|
|
206
|
-
|
|
207
|
-
// Commands - fire-and-forget with auto-retry
|
|
208
|
-
setPower(on: boolean): void;
|
|
209
|
-
setColor(color: string | RGB | HSL | HSB | HSBK | number, duration?: number): void;
|
|
210
|
-
setWhite(kelvin: number, brightness?: number, duration?: number): void;
|
|
211
|
-
|
|
212
|
-
// Queries - trigger fetch, results via events
|
|
213
|
-
getState(): void;
|
|
214
|
-
getPower(): void;
|
|
215
|
-
|
|
216
|
-
// Label/Group/Location (supported but not fundamental)
|
|
217
|
-
setLabel(label: string): void;
|
|
218
|
-
getLabel(): void;
|
|
219
|
-
setGroup(id: string, label: string): void;
|
|
220
|
-
getGroup(): void;
|
|
221
|
-
setLocation(id: string, label: string): void;
|
|
222
|
-
getLocation(): void;
|
|
223
|
-
|
|
224
|
-
// Cached group/location info
|
|
225
|
-
group: { id: string; label: string; updatedAt: number };
|
|
226
|
-
location: { id: string; label: string; updatedAt: number };
|
|
227
|
-
|
|
228
|
-
// Raw
|
|
229
|
-
send(type: number, payload?: Buffer): void;
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### LxProtocol - Message Encoding
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
enum LxMessageType {
|
|
237
|
-
GetService = 2,
|
|
238
|
-
StateService = 3,
|
|
239
|
-
GetPower = 20,
|
|
240
|
-
SetPower = 21,
|
|
241
|
-
StatePower = 22,
|
|
242
|
-
GetLabel = 23,
|
|
243
|
-
SetLabel = 24,
|
|
244
|
-
StateLabel = 25,
|
|
245
|
-
GetVersion = 32,
|
|
246
|
-
StateVersion = 33,
|
|
247
|
-
GetLocation = 48,
|
|
248
|
-
SetLocation = 49,
|
|
249
|
-
StateLocation = 50,
|
|
250
|
-
GetGroup = 51,
|
|
251
|
-
SetGroup = 52,
|
|
252
|
-
StateGroup = 53,
|
|
253
|
-
Get = 101,
|
|
254
|
-
SetColor = 102,
|
|
255
|
-
State = 107,
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
interface LxMessage {
|
|
259
|
-
size: number;
|
|
260
|
-
tagged: boolean;
|
|
261
|
-
source: number;
|
|
262
|
-
target: string;
|
|
263
|
-
sequence: number;
|
|
264
|
-
type: LxMessageType;
|
|
265
|
-
payload: Buffer;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function encodeMessage(msg: Partial<LxMessage>): Buffer;
|
|
269
|
-
function decodeMessage(data: Buffer): LxMessage;
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
## Usage Examples
|
|
275
|
-
|
|
276
|
-
### Basic Discovery and Control
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
import { LxClient } from '@bobfrankston/lxlan';
|
|
280
|
-
|
|
281
|
-
const client = new LxClient();
|
|
282
|
-
|
|
283
|
-
client.on('device', (device) => {
|
|
284
|
-
console.log(`Found: ${device.label} (${device.mac})`);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
client.on('state', (device) => {
|
|
288
|
-
console.log(`${device.label}: power=${device.power}`);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
client.start();
|
|
292
|
-
client.discover();
|
|
293
|
-
|
|
294
|
-
// Adjust for slow network
|
|
295
|
-
client.setRetryOptions({ retryCount: 5, responseTimeout: 10000 });
|
|
296
|
-
|
|
297
|
-
// Control
|
|
298
|
-
const bulb = client.getDevice('d0:73:d5:xx:xx:xx');
|
|
299
|
-
bulb.setPower(true);
|
|
300
|
-
bulb.setColor('#ff0000', 1000);
|
|
301
|
-
bulb.setWhite(2700, 80);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
## LIFX Protocol Summary
|
|
307
|
-
|
|
308
|
-
### Header (36 bytes)
|
|
309
|
-
- Frame: size, protocol, tagged, source
|
|
310
|
-
- Address: target MAC, sequence
|
|
311
|
-
- Protocol: message type
|
|
312
|
-
|
|
313
|
-
### Color (HSBK) - 8 bytes
|
|
314
|
-
| Field | Bits | Range | Maps to |
|
|
315
|
-
|-------|------|-------|---------|
|
|
316
|
-
| Hue | 16 | 0-65535 | 0-360° |
|
|
317
|
-
| Saturation | 16 | 0-65535 | 0-100% |
|
|
318
|
-
| Brightness | 16 | 0-65535 | 0-100% |
|
|
319
|
-
| Kelvin | 16 | 1500-9000 | temp |
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## File Structure
|
|
324
|
-
|
|
325
|
-
```
|
|
326
|
-
lxudp/
|
|
327
|
-
├── index.ts
|
|
328
|
-
├── socket.ts - UdpSocket class
|
|
329
|
-
├── address.ts - getBroadcastAddresses(), computeBroadcast()
|
|
330
|
-
├── types.ts
|
|
331
|
-
├── package.json
|
|
332
|
-
└── tsconfig.json
|
|
333
|
-
|
|
334
|
-
colorlib/
|
|
335
|
-
├── index.ts
|
|
336
|
-
├── convert.ts
|
|
337
|
-
├── parse.ts
|
|
338
|
-
├── types.ts
|
|
339
|
-
├── package.json
|
|
340
|
-
└── tsconfig.json
|
|
341
|
-
|
|
342
|
-
lxlan/
|
|
343
|
-
├── index.ts
|
|
344
|
-
├── client.ts
|
|
345
|
-
├── device.ts
|
|
346
|
-
├── protocol.ts
|
|
347
|
-
├── types.ts
|
|
348
|
-
├── package.json
|
|
349
|
-
└── tsconfig.json
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
## Implementation Order
|
|
353
|
-
|
|
354
|
-
1. **lxcolor** - pure color math
|
|
355
|
-
2. **lxudp** - UDP transport with retry
|
|
356
|
-
3. **lxlan/types.ts, protocol.ts** - message handling
|
|
357
|
-
4. **lxlan/device.ts, client.ts** - device abstraction
|
|
358
|
-
5. **tests/** - discovery, control
|
|
359
|
-
|
|
360
|
-
---
|
|
361
|
-
|
|
362
|
-
## Future - v2 Considerations
|
|
363
|
-
|
|
364
|
-
### Multizone Support (LIFX Beam, Z strips)
|
|
365
|
-
Multizone devices have individually addressable LED segments.
|
|
366
|
-
|
|
367
|
-
**Additional message types:**
|
|
368
|
-
| Type | Name | Description |
|
|
369
|
-
|------|------|-------------|
|
|
370
|
-
| 501 | SetColorZones | Set color for zone range |
|
|
371
|
-
| 502 | GetColorZones | Query zone colors |
|
|
372
|
-
| 503 | StateZone | Single zone response |
|
|
373
|
-
| 506 | StateMultiZone | Multiple zones response |
|
|
374
|
-
|
|
375
|
-
**API additions for LxDevice:**
|
|
376
|
-
```typescript
|
|
377
|
-
interface LxDevice {
|
|
378
|
-
// Multizone properties
|
|
379
|
-
zoneCount: number; /** 0 = not multizone */
|
|
380
|
-
zones: HSBK[]; /** cached zone colors */
|
|
381
|
-
|
|
382
|
-
// Multizone commands
|
|
383
|
-
setZoneColor(start: number, end: number, color: HSBK, duration?: number): void;
|
|
384
|
-
getZones(start?: number, end?: number): void;
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
**Events:**
|
|
389
|
-
- `'zones'` (device: LxDevice) - zone state updated
|
|
390
|
-
|
|
391
|
-
### UDP Web Proxy (lxudp-server)
|
|
392
|
-
HTTP/WebSocket server exposing UDP operations for browsers.
|
|
393
|
-
```typescript
|
|
394
|
-
// Future API sketch
|
|
395
|
-
const proxy = new UdpProxyServer({ httpPort: 8080 });
|
|
396
|
-
proxy.start();
|
|
397
|
-
// Browser: ws://localhost:8080/udp
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### Related: y:\dev\homecontrol\utils\netsupport
|
|
401
|
-
Evaluate overlap with existing netsupport utilities. lxudp may complement or replace portions.
|
|
402
|
-
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
## Separate Future Project: lxhttp
|
|
406
|
-
|
|
407
|
-
**Entirely separate library** - LIFX Cloud HTTP API. Not part of lxlan.
|
|
408
|
-
|
|
409
|
-
### Purpose
|
|
410
|
-
- Enumerate devices including **offline** ones (cloud knows all registered devices)
|
|
411
|
-
- Alternative control path when LAN unavailable
|
|
412
|
-
- Account-level operations
|
|
413
|
-
|
|
414
|
-
### Key Differences from lxlan
|
|
415
|
-
| Aspect | lxlan (UDP) | lxhttp (Cloud) |
|
|
416
|
-
|--------|-------------|----------------|
|
|
417
|
-
| Offline devices | No | Yes |
|
|
418
|
-
| Latency | ~ms | ~100ms+ |
|
|
419
|
-
| Rate limits | None | Yes (cloud) |
|
|
420
|
-
| Auth | None | OAuth token |
|
|
421
|
-
| Local network | Required | Not required |
|
|
422
|
-
|
|
423
|
-
### API Sketch (future)
|
|
424
|
-
```typescript
|
|
425
|
-
class LxHttpClient extends EventEmitter {
|
|
426
|
-
constructor(token: string);
|
|
427
|
-
|
|
428
|
-
listDevices(): void; /** includes offline */
|
|
429
|
-
getDevice(id: string): void;
|
|
430
|
-
setPower(id: string, on: boolean): void;
|
|
431
|
-
setColor(id: string, color: HSBK, duration?: number): void;
|
|
432
|
-
// ... similar to lxlan but via HTTP
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
### lxmanager (future app)
|
|
437
|
-
Application using **both** lxlan and lxhttp:
|
|
438
|
-
- lxhttp for full device enumeration (including offline)
|
|
439
|
-
- lxlan for fast local control
|
|
440
|
-
- External database for device/group management
|
|
441
|
-
- Device labels set to factory-default-based names
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
## UDP Proxy for Android/Browser
|
|
446
|
-
|
|
447
|
-
Android and browsers cannot send UDP directly. Need a local proxy server.
|
|
448
|
-
|
|
449
|
-
### Architecture
|
|
450
|
-
```
|
|
451
|
-
┌──────────────┐ HTTP/WS ┌──────────────┐ UDP ┌──────────┐
|
|
452
|
-
│ Android App │ ◄──────────────► │ UDP Proxy │ ◄────────────► │ LIFX │
|
|
453
|
-
│ or Browser │ │ (Node.js) │ │ Bulbs │
|
|
454
|
-
└──────────────┘ └──────────────┘ └──────────┘
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
### Proxy REST API (suggested)
|
|
458
|
-
|
|
459
|
-
**Discovery**
|
|
460
|
-
```
|
|
461
|
-
POST /discover
|
|
462
|
-
→ triggers broadcast, returns immediately
|
|
463
|
-
|
|
464
|
-
GET /devices
|
|
465
|
-
→ returns cached device list
|
|
466
|
-
|
|
467
|
-
GET /devices/:mac
|
|
468
|
-
→ returns single device state
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
**Control**
|
|
472
|
-
```
|
|
473
|
-
POST /devices/:mac/power
|
|
474
|
-
Body: { "on": true }
|
|
475
|
-
|
|
476
|
-
POST /devices/:mac/color
|
|
477
|
-
Body: { "color": "#ff0000", "duration": 1000 }
|
|
478
|
-
Body: { "color": { "h": 120, "s": 100, "b": 100 }, "duration": 500 }
|
|
479
|
-
Body: { "kelvin": 2700, "brightness": 80 }
|
|
480
|
-
|
|
481
|
-
POST /devices/:mac/label
|
|
482
|
-
Body: { "label": "Kitchen Light" }
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
**Events (WebSocket)**
|
|
486
|
-
```
|
|
487
|
-
WS /events
|
|
488
|
-
→ receives: { "event": "state", "mac": "d0:73:...", "device": {...} }
|
|
489
|
-
→ receives: { "event": "device", "mac": "d0:73:...", "device": {...} }
|
|
490
|
-
→ receives: { "event": "offline", "mac": "d0:73:..." }
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Implementation Notes
|
|
494
|
-
- Proxy uses lxlan internally
|
|
495
|
-
- Stateless HTTP for commands
|
|
496
|
-
- WebSocket for real-time events
|
|
497
|
-
- Single proxy instance per LAN
|
|
498
|
-
- Can run on Raspberry Pi, NAS, or any Node.js host
|
|
499
|
-
|
|
500
|
-
---
|
|
501
|
-
|
|
502
|
-
## LIFX Cloud HTTP API Reference
|
|
503
|
-
|
|
504
|
-
Official API: https://api.lifx.com/
|
|
505
|
-
|
|
506
|
-
### Authentication
|
|
507
|
-
```
|
|
508
|
-
Authorization: Bearer <token>
|
|
509
|
-
```
|
|
510
|
-
Get token from https://cloud.lifx.com/settings
|
|
511
|
-
|
|
512
|
-
### Key Endpoints
|
|
513
|
-
```
|
|
514
|
-
GET /v1/lights/all → list all devices
|
|
515
|
-
GET /v1/lights/:selector → get device(s) state
|
|
516
|
-
PUT /v1/lights/:selector/state → set power/color/brightness
|
|
517
|
-
POST /v1/lights/:selector/toggle → toggle power
|
|
518
|
-
POST /v1/lights/:selector/effects/... → effects (breathe, pulse, etc.)
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### Selector Format
|
|
522
|
-
- `all` - all devices
|
|
523
|
-
- `id:d073d5xxxxxx` - by device ID
|
|
524
|
-
- `label:Kitchen` - by label
|
|
525
|
-
- `group:Living Room` - by group
|
|
526
|
-
- `location:Home` - by location
|
|
527
|
-
|
|
528
|
-
### Rate Limits
|
|
529
|
-
- 120 requests per 60 seconds per token
|
|
530
|
-
- Responses include `X-RateLimit-*` headers
|