@ecopoesis/homebridge-dmx 0.2.0 → 0.4.0
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/CLAUDE.md +1 -0
- package/ENCRYPTION.md +207 -0
- package/PROTOCOL.md +277 -0
- package/README.md +22 -0
- package/config.schema.json +8 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/controller.d.ts +14 -3
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +41 -5
- package/dist/controller.js.map +1 -1
- package/dist/platform.d.ts +1 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +8 -6
- package/dist/platform.js.map +1 -1
- package/dist/platformAccessory.d.ts +5 -5
- package/dist/platformAccessory.d.ts.map +1 -1
- package/dist/platformAccessory.js +58 -29
- package/dist/platformAccessory.js.map +1 -1
- package/dist/settings.d.ts +9 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +9 -0
- package/dist/settings.js.map +1 -1
- package/dist/stateRegistry.d.ts +21 -0
- package/dist/stateRegistry.d.ts.map +1 -0
- package/dist/stateRegistry.js +65 -0
- package/dist/stateRegistry.js.map +1 -0
- package/dist/zoneAccessory.d.ts +20 -4
- package/dist/zoneAccessory.d.ts.map +1 -1
- package/dist/zoneAccessory.js +103 -40
- package/dist/zoneAccessory.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +10 -1
- package/src/controller.ts +39 -5
- package/src/platform.ts +8 -6
- package/src/platformAccessory.ts +53 -31
- package/src/settings.ts +11 -0
- package/src/stateRegistry.ts +71 -0
- package/src/zoneAccessory.ts +106 -40
- package/tools/stick-power-cycle.py +121 -0
package/dist/controller.d.ts
CHANGED
|
@@ -5,28 +5,39 @@ export declare class StickController {
|
|
|
5
5
|
private readonly ip;
|
|
6
6
|
private readonly log;
|
|
7
7
|
private readonly debounceMs;
|
|
8
|
+
private readonly refreshMs;
|
|
8
9
|
private targets;
|
|
9
10
|
private lastChangeAt;
|
|
11
|
+
private lastTxEndAt;
|
|
10
12
|
private streaming;
|
|
13
|
+
private refreshTimer?;
|
|
11
14
|
/** Tracks whether the very first subprocess in this controller's
|
|
12
15
|
* lifetime should set RUN_PROBE=1. After a Stick power-cycle (which
|
|
13
16
|
* we can't detect in-band) the probe is required to (re-)register
|
|
14
17
|
* the client. Subsequent subprocesses can skip it. */
|
|
15
18
|
private needsProbe;
|
|
16
|
-
constructor(ip: string, log: Logger, debounceMs?: number);
|
|
19
|
+
constructor(ip: string, log: Logger, debounceMs?: number, refreshMs?: number);
|
|
20
|
+
/** Idle keepalive: the Stick wedges (sessions succeed, output dark) after
|
|
21
|
+
* hours without traffic. Re-send the current universe state whenever the
|
|
22
|
+
* controller has been idle for refreshMs. Also re-asserts state after an
|
|
23
|
+
* out-of-band power-cycle of the Stick. */
|
|
24
|
+
private maybeRefresh;
|
|
17
25
|
/** Stage a fixture's bytes into the universe target buffer and ensure
|
|
18
26
|
* the streaming loop is running. */
|
|
19
27
|
setFixture(fixture: Fixture, state: HomeKitLightState): void;
|
|
20
28
|
private universeBuf;
|
|
21
29
|
/** Build the CLI args from current targets. Only non-zero channels are
|
|
22
|
-
* passed — send_dmx initialises the universe to 0 so omission == 0.
|
|
30
|
+
* passed — send_dmx initialises the universe to 0 so omission == 0.
|
|
31
|
+
* When everything is 0 (all lights off), send an explicit zero so
|
|
32
|
+
* send_dmx still has at least one assignment to transact. */
|
|
23
33
|
private buildArgs;
|
|
24
34
|
/** Subprocess approach: spawn tools/send_dmx.mjs for each transaction.
|
|
25
35
|
* Fresh node process = no in-process state poisoning that breaks
|
|
26
36
|
* session 2+ in the long-lived plugin. Costs ~1.5-3s per spawn (process
|
|
27
37
|
* startup + handshake + 750ms stream + RST) but is empirically reliable. */
|
|
28
38
|
private run;
|
|
29
|
-
/** Shutdown hook.
|
|
39
|
+
/** Shutdown hook. Stop the keepalive timer; the subprocess approach has
|
|
40
|
+
* no other long-lived state. */
|
|
30
41
|
shutdown(): void;
|
|
31
42
|
}
|
|
32
43
|
//# sourceMappingURL=controller.d.ts.map
|
package/dist/controller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKzC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASrD,qBAAa,eAAe;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKzC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASrD,qBAAa,eAAe;IAaxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAf5B,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC;;;2DAGuD;IACvD,OAAO,CAAC,UAAU,CAAQ;gBAGP,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAoB,EAChC,SAAS,GAAE,MAAU;IAQxC;;;gDAG4C;IAC5C,OAAO,CAAC,YAAY;IAcpB;yCACqC;IACrC,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAmB5D,OAAO,CAAC,WAAW;IAMnB;;;kEAG8D;IAC9D,OAAO,CAAC,SAAS;IAcjB;;;iFAG6E;YAC/D,GAAG;IAgDjB;qCACiC;IACjC,QAAQ,IAAI,IAAI;CAGjB"}
|
package/dist/controller.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import path from 'node:path';
|
|
13
|
-
import { DEBOUNCE_MS } from './settings.js';
|
|
13
|
+
import { DEBOUNCE_MS, REFRESH_CHECK_MS } from './settings.js';
|
|
14
14
|
import { sleep } from './stick/protocol.js';
|
|
15
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
16
|
const __dirname = path.dirname(__filename);
|
|
@@ -20,18 +20,45 @@ export class StickController {
|
|
|
20
20
|
ip;
|
|
21
21
|
log;
|
|
22
22
|
debounceMs;
|
|
23
|
+
refreshMs;
|
|
23
24
|
targets = new Map();
|
|
24
25
|
lastChangeAt = 0;
|
|
26
|
+
lastTxEndAt = 0;
|
|
25
27
|
streaming = false;
|
|
28
|
+
refreshTimer;
|
|
26
29
|
/** Tracks whether the very first subprocess in this controller's
|
|
27
30
|
* lifetime should set RUN_PROBE=1. After a Stick power-cycle (which
|
|
28
31
|
* we can't detect in-band) the probe is required to (re-)register
|
|
29
32
|
* the client. Subsequent subprocesses can skip it. */
|
|
30
33
|
needsProbe = true;
|
|
31
|
-
constructor(ip, log, debounceMs = DEBOUNCE_MS) {
|
|
34
|
+
constructor(ip, log, debounceMs = DEBOUNCE_MS, refreshMs = 0) {
|
|
32
35
|
this.ip = ip;
|
|
33
36
|
this.log = log;
|
|
34
37
|
this.debounceMs = debounceMs;
|
|
38
|
+
this.refreshMs = refreshMs;
|
|
39
|
+
if (refreshMs > 0) {
|
|
40
|
+
this.refreshTimer = setInterval(() => this.maybeRefresh(), REFRESH_CHECK_MS);
|
|
41
|
+
this.refreshTimer.unref?.();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Idle keepalive: the Stick wedges (sessions succeed, output dark) after
|
|
45
|
+
* hours without traffic. Re-send the current universe state whenever the
|
|
46
|
+
* controller has been idle for refreshMs. Also re-asserts state after an
|
|
47
|
+
* out-of-band power-cycle of the Stick. */
|
|
48
|
+
maybeRefresh() {
|
|
49
|
+
if (this.streaming || this.targets.size === 0)
|
|
50
|
+
return;
|
|
51
|
+
const idleMs = Date.now() - Math.max(this.lastTxEndAt, this.lastChangeAt);
|
|
52
|
+
if (idleMs < this.refreshMs)
|
|
53
|
+
return;
|
|
54
|
+
this.log.info(`keepalive refresh after ${Math.round(idleMs / 60_000)}m idle`);
|
|
55
|
+
this.needsProbe = true; // the Stick may have power-cycled while we were idle
|
|
56
|
+
this.streaming = true;
|
|
57
|
+
this.run().catch((e) => {
|
|
58
|
+
this.log.error('keepalive refresh crashed:', e.message);
|
|
59
|
+
}).finally(() => {
|
|
60
|
+
this.streaming = false;
|
|
61
|
+
});
|
|
35
62
|
}
|
|
36
63
|
/** Stage a fixture's bytes into the universe target buffer and ensure
|
|
37
64
|
* the streaming loop is running. */
|
|
@@ -62,7 +89,9 @@ export class StickController {
|
|
|
62
89
|
return arr;
|
|
63
90
|
}
|
|
64
91
|
/** Build the CLI args from current targets. Only non-zero channels are
|
|
65
|
-
* passed — send_dmx initialises the universe to 0 so omission == 0.
|
|
92
|
+
* passed — send_dmx initialises the universe to 0 so omission == 0.
|
|
93
|
+
* When everything is 0 (all lights off), send an explicit zero so
|
|
94
|
+
* send_dmx still has at least one assignment to transact. */
|
|
66
95
|
buildArgs() {
|
|
67
96
|
const args = [SEND_DMX_PATH, this.ip];
|
|
68
97
|
for (const [u, arr] of this.targets) {
|
|
@@ -71,6 +100,10 @@ export class StickController {
|
|
|
71
100
|
args.push(`${u},${i + 1}=${arr[i]}`);
|
|
72
101
|
}
|
|
73
102
|
}
|
|
103
|
+
if (args.length === 2 && this.targets.size > 0) {
|
|
104
|
+
const firstU = this.targets.keys().next().value;
|
|
105
|
+
args.push(`${firstU},1=0`);
|
|
106
|
+
}
|
|
74
107
|
return args;
|
|
75
108
|
}
|
|
76
109
|
/** Subprocess approach: spawn tools/send_dmx.mjs for each transaction.
|
|
@@ -107,6 +140,7 @@ export class StickController {
|
|
|
107
140
|
});
|
|
108
141
|
});
|
|
109
142
|
const elapsedMs = Date.now() - startedAt;
|
|
143
|
+
this.lastTxEndAt = Date.now();
|
|
110
144
|
if (exitCode === 0) {
|
|
111
145
|
this.needsProbe = false;
|
|
112
146
|
this.log.info(`send_dmx done in ${elapsedMs}ms`);
|
|
@@ -120,9 +154,11 @@ export class StickController {
|
|
|
120
154
|
return;
|
|
121
155
|
}
|
|
122
156
|
}
|
|
123
|
-
/** Shutdown hook.
|
|
157
|
+
/** Shutdown hook. Stop the keepalive timer; the subprocess approach has
|
|
158
|
+
* no other long-lived state. */
|
|
124
159
|
shutdown() {
|
|
125
|
-
|
|
160
|
+
if (this.refreshTimer)
|
|
161
|
+
clearInterval(this.refreshTimer);
|
|
126
162
|
}
|
|
127
163
|
}
|
|
128
164
|
//# sourceMappingURL=controller.js.map
|
package/dist/controller.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,YAAY;AACZ,mEAAmE;AACnE,wEAAwE;AACxE,sEAAsE;AACtE,kCAAkC;AAClC,yEAAyE;AACzE,0EAA0E;AAG1E,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/platform.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class DmxPlatform implements DynamicPlatformPlugin {
|
|
|
8
8
|
readonly accessories: PlatformAccessory[];
|
|
9
9
|
private cfg;
|
|
10
10
|
private controllers;
|
|
11
|
+
private registry;
|
|
11
12
|
constructor(log: Logger, config: PlatformConfig, api: API);
|
|
12
13
|
private makeController;
|
|
13
14
|
/** Called by Homebridge for each cached accessory at startup. */
|
package/dist/platform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,GAAG,EACH,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,OAAO,EACR,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,GAAG,EACH,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,OAAO,EACR,MAAM,YAAY,CAAC;AASpB,qBAAa,WAAY,YAAW,qBAAqB;aAUrC,GAAG,EAAE,MAAM;aACX,MAAM,EAAE,cAAc;aACtB,GAAG,EAAE,GAAG;IAX1B,SAAgB,OAAO,EAAE,OAAO,OAAO,CAAC;IACxC,SAAgB,cAAc,EAAE,OAAO,cAAc,CAAC;IACtD,SAAgB,WAAW,EAAE,iBAAiB,EAAE,CAAM;IAEtD,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,QAAQ,CAAuB;gBAGrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,GAAG;IAgC1B,OAAO,CAAC,cAAc;IAStB,iEAAiE;IACjE,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKtD,OAAO,CAAC,eAAe;CA0DxB"}
|
package/dist/platform.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
import { loadConfig } from './config.js';
|
|
7
7
|
import { StickController } from './controller.js';
|
|
8
8
|
import { StickFixture } from './platformAccessory.js';
|
|
9
|
+
import { StateRegistry } from './stateRegistry.js';
|
|
9
10
|
import { ZoneFixture } from './zoneAccessory.js';
|
|
10
|
-
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
11
|
+
import { DEBOUNCE_MS, PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
11
12
|
export class DmxPlatform {
|
|
12
13
|
log;
|
|
13
14
|
config;
|
|
@@ -17,6 +18,7 @@ export class DmxPlatform {
|
|
|
17
18
|
accessories = [];
|
|
18
19
|
cfg = null;
|
|
19
20
|
controllers = new Map();
|
|
21
|
+
registry = new StateRegistry();
|
|
20
22
|
constructor(log, config, api) {
|
|
21
23
|
this.log = log;
|
|
22
24
|
this.config = config;
|
|
@@ -46,7 +48,7 @@ export class DmxPlatform {
|
|
|
46
48
|
makeController(c) {
|
|
47
49
|
switch (c.type) {
|
|
48
50
|
case 'StickDE3':
|
|
49
|
-
return new StickController(c.ip, this.log);
|
|
51
|
+
return new StickController(c.ip, this.log, DEBOUNCE_MS, c.refreshMinutes * 60_000);
|
|
50
52
|
default:
|
|
51
53
|
throw new Error(`unsupported controller type "${c.type}"`);
|
|
52
54
|
}
|
|
@@ -72,13 +74,13 @@ export class DmxPlatform {
|
|
|
72
74
|
if (existing) {
|
|
73
75
|
this.log.debug(`wiring cached accessory ${fx.id}`);
|
|
74
76
|
existing.context.fixture = { id: fx.id, controllerId: fx.controller.id };
|
|
75
|
-
new StickFixture(this, existing, fx, controller);
|
|
77
|
+
new StickFixture(this, existing, fx, controller, this.registry);
|
|
76
78
|
}
|
|
77
79
|
else {
|
|
78
80
|
this.log.info(`registering new accessory: ${fx.name} (${fx.id})`);
|
|
79
81
|
const accessory = new this.api.platformAccessory(fx.name, uuid);
|
|
80
82
|
accessory.context.fixture = { id: fx.id, controllerId: fx.controller.id };
|
|
81
|
-
new StickFixture(this, accessory, fx, controller);
|
|
83
|
+
new StickFixture(this, accessory, fx, controller, this.registry);
|
|
82
84
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
83
85
|
this.accessories.push(accessory);
|
|
84
86
|
}
|
|
@@ -91,13 +93,13 @@ export class DmxPlatform {
|
|
|
91
93
|
if (existing) {
|
|
92
94
|
this.log.debug(`wiring cached zone ${zone.id}`);
|
|
93
95
|
existing.context.zone = { id: zone.id };
|
|
94
|
-
new ZoneFixture(this, existing, zone, this.controllers);
|
|
96
|
+
new ZoneFixture(this, existing, zone, this.controllers, this.registry);
|
|
95
97
|
}
|
|
96
98
|
else {
|
|
97
99
|
this.log.info(`registering new zone: ${zone.name} (${zone.id}, ${zone.members.length} members)`);
|
|
98
100
|
const accessory = new this.api.platformAccessory(zone.name, uuid);
|
|
99
101
|
accessory.context.zone = { id: zone.id };
|
|
100
|
-
new ZoneFixture(this, accessory, zone, this.controllers);
|
|
102
|
+
new ZoneFixture(this, accessory, zone, this.controllers, this.registry);
|
|
101
103
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
102
104
|
this.accessories.push(accessory);
|
|
103
105
|
}
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,kBAAkB;AAYlB,OAAO,EAA4B,UAAU,EAAa,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,17 +2,17 @@ import type { PlatformAccessory } from 'homebridge';
|
|
|
2
2
|
import { Fixture } from './config.js';
|
|
3
3
|
import { StickController } from './controller.js';
|
|
4
4
|
import type { DmxPlatform } from './platform.js';
|
|
5
|
+
import { StateRegistry } from './stateRegistry.js';
|
|
5
6
|
export declare class StickFixture {
|
|
6
7
|
private readonly platform;
|
|
7
8
|
private readonly accessory;
|
|
8
9
|
private readonly fixture;
|
|
9
10
|
private readonly controller;
|
|
10
|
-
|
|
11
|
-
private state;
|
|
12
|
-
/** Debounce timer for HomeKit's multi-characteristic dispatch
|
|
13
|
-
* (Hue/Saturation/Brightness arrive as 3 separate set calls). */
|
|
11
|
+
private readonly registry;
|
|
14
12
|
private pending;
|
|
15
|
-
|
|
13
|
+
private bulb;
|
|
14
|
+
constructor(platform: DmxPlatform, accessory: PlatformAccessory, fixture: Fixture, controller: StickController, registry: StateRegistry);
|
|
15
|
+
private pushFromRegistry;
|
|
16
16
|
private schedule;
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=platformAccessory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformAccessory.d.ts","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"platformAccessory.d.ts","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,qBAAa,YAAY;IAKrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,IAAI,CAAU;gBAGH,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,eAAe,EAC3B,QAAQ,EAAE,aAAa;IAoE1C,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;CAcjB"}
|
|
@@ -1,80 +1,109 @@
|
|
|
1
1
|
// StickFixture — one HomeKit Lightbulb accessory per patched fixture.
|
|
2
|
+
//
|
|
3
|
+
// State is owned by the platform-wide StateRegistry. We read on .onGet,
|
|
4
|
+
// write on .onSet, and subscribe to changes so the HomeKit-displayed
|
|
5
|
+
// values track the registry (e.g. when a zone updates this fixture).
|
|
2
6
|
import { CHARACTERISTIC_UPDATE_DELAY_MS } from './settings.js';
|
|
3
7
|
export class StickFixture {
|
|
4
8
|
platform;
|
|
5
9
|
accessory;
|
|
6
10
|
fixture;
|
|
7
11
|
controller;
|
|
8
|
-
|
|
9
|
-
state = { on: false, brightness: 100 };
|
|
10
|
-
/** Debounce timer for HomeKit's multi-characteristic dispatch
|
|
11
|
-
* (Hue/Saturation/Brightness arrive as 3 separate set calls). */
|
|
12
|
+
registry;
|
|
12
13
|
pending = null;
|
|
13
|
-
|
|
14
|
+
bulb;
|
|
15
|
+
constructor(platform, accessory, fixture, controller, registry) {
|
|
14
16
|
this.platform = platform;
|
|
15
17
|
this.accessory = accessory;
|
|
16
18
|
this.fixture = fixture;
|
|
17
19
|
this.controller = controller;
|
|
20
|
+
this.registry = registry;
|
|
18
21
|
const C = platform.Characteristic;
|
|
19
22
|
const info = accessory.getService(platform.Service.AccessoryInformation);
|
|
20
23
|
info?.setCharacteristic(C.Manufacturer, 'Stick-DE3 DMX')
|
|
21
24
|
?.setCharacteristic(C.Model, fixture.profile.name)
|
|
22
25
|
?.setCharacteristic(C.SerialNumber, fixture.id);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
this.bulb =
|
|
27
|
+
accessory.getService(platform.Service.Lightbulb)
|
|
28
|
+
?? accessory.addService(platform.Service.Lightbulb, fixture.name);
|
|
29
|
+
this.bulb.setCharacteristic(C.Name, fixture.name);
|
|
26
30
|
const chars = fixture.profile.model.characteristics;
|
|
27
|
-
bulb.getCharacteristic(C.On)
|
|
28
|
-
.onGet(() =>
|
|
29
|
-
.onSet((v) => {
|
|
31
|
+
this.bulb.getCharacteristic(C.On)
|
|
32
|
+
.onGet(() => registry.get(fixture.id).on)
|
|
33
|
+
.onSet((v) => {
|
|
34
|
+
registry.update(fixture.id, { on: Boolean(v) });
|
|
35
|
+
this.schedule();
|
|
36
|
+
});
|
|
30
37
|
if (chars.includes('Brightness')) {
|
|
31
|
-
bulb.getCharacteristic(C.Brightness)
|
|
32
|
-
.onGet(() =>
|
|
38
|
+
this.bulb.getCharacteristic(C.Brightness)
|
|
39
|
+
.onGet(() => registry.get(fixture.id).brightness)
|
|
33
40
|
.onSet((v) => {
|
|
34
|
-
|
|
41
|
+
registry.update(fixture.id, { brightness: Number(v) });
|
|
35
42
|
this.schedule();
|
|
36
43
|
});
|
|
37
44
|
}
|
|
38
45
|
if (chars.includes('Hue')) {
|
|
39
|
-
bulb.getCharacteristic(C.Hue)
|
|
40
|
-
.onGet(() =>
|
|
46
|
+
this.bulb.getCharacteristic(C.Hue)
|
|
47
|
+
.onGet(() => registry.get(fixture.id).hue ?? 0)
|
|
41
48
|
.onSet((v) => {
|
|
42
|
-
|
|
43
|
-
// setting hue implies color mode (sat>0); leave saturation as-is
|
|
49
|
+
registry.update(fixture.id, { hue: Number(v) });
|
|
44
50
|
this.schedule();
|
|
45
51
|
});
|
|
46
52
|
}
|
|
47
53
|
if (chars.includes('Saturation')) {
|
|
48
|
-
bulb.getCharacteristic(C.Saturation)
|
|
49
|
-
.onGet(() =>
|
|
54
|
+
this.bulb.getCharacteristic(C.Saturation)
|
|
55
|
+
.onGet(() => registry.get(fixture.id).saturation ?? 0)
|
|
50
56
|
.onSet((v) => {
|
|
51
|
-
|
|
57
|
+
registry.update(fixture.id, { saturation: Number(v) });
|
|
52
58
|
this.schedule();
|
|
53
59
|
});
|
|
54
60
|
}
|
|
55
61
|
if (chars.includes('ColorTemperature')) {
|
|
56
|
-
bulb.getCharacteristic(C.ColorTemperature)
|
|
57
|
-
.onGet(() =>
|
|
62
|
+
this.bulb.getCharacteristic(C.ColorTemperature)
|
|
63
|
+
.onGet(() => registry.get(fixture.id).colorTemperatureMireds ?? 200)
|
|
58
64
|
.onSet((v) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
// CCT change implies white mode; zero saturation.
|
|
66
|
+
registry.update(fixture.id, {
|
|
67
|
+
colorTemperatureMireds: Number(v),
|
|
68
|
+
saturation: 0,
|
|
69
|
+
});
|
|
63
70
|
this.schedule();
|
|
64
71
|
});
|
|
65
72
|
}
|
|
73
|
+
// Refresh HomeKit characteristics whenever this fixture's state changes
|
|
74
|
+
// in the registry (e.g. via a zone set, or another channel of itself).
|
|
75
|
+
registry.onChange((changedId) => {
|
|
76
|
+
if (changedId !== fixture.id)
|
|
77
|
+
return;
|
|
78
|
+
this.pushFromRegistry();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
pushFromRegistry() {
|
|
82
|
+
const s = this.registry.get(this.fixture.id);
|
|
83
|
+
const C = this.platform.Characteristic;
|
|
84
|
+
const chars = this.fixture.profile.model.characteristics;
|
|
85
|
+
this.bulb.updateCharacteristic(C.On, s.on);
|
|
86
|
+
if (chars.includes('Brightness'))
|
|
87
|
+
this.bulb.updateCharacteristic(C.Brightness, s.brightness);
|
|
88
|
+
if (chars.includes('Hue') && s.hue != null)
|
|
89
|
+
this.bulb.updateCharacteristic(C.Hue, s.hue);
|
|
90
|
+
if (chars.includes('Saturation') && s.saturation != null)
|
|
91
|
+
this.bulb.updateCharacteristic(C.Saturation, s.saturation);
|
|
92
|
+
if (chars.includes('ColorTemperature') && s.colorTemperatureMireds != null) {
|
|
93
|
+
this.bulb.updateCharacteristic(C.ColorTemperature, s.colorTemperatureMireds);
|
|
94
|
+
}
|
|
66
95
|
}
|
|
67
96
|
schedule() {
|
|
68
97
|
if (this.pending)
|
|
69
98
|
clearTimeout(this.pending);
|
|
70
99
|
this.pending = setTimeout(() => {
|
|
71
100
|
this.pending = null;
|
|
72
|
-
const s = this.
|
|
101
|
+
const s = this.registry.get(this.fixture.id);
|
|
73
102
|
this.platform.log.info(`[${this.fixture.id}] HK set: on=${s.on} br=${s.brightness}` +
|
|
74
103
|
(s.hue != null ? ` h=${s.hue}` : '') +
|
|
75
104
|
(s.saturation != null ? ` s=${s.saturation}` : '') +
|
|
76
105
|
(s.colorTemperatureMireds != null ? ` ct=${s.colorTemperatureMireds}` : ''));
|
|
77
|
-
this.controller.setFixture(this.fixture,
|
|
106
|
+
this.controller.setFixture(this.fixture, s);
|
|
78
107
|
}, CHARACTERISTIC_UPDATE_DELAY_MS);
|
|
79
108
|
}
|
|
80
109
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"AAAA,sEAAsE;
|
|
1
|
+
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"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"}
|
package/dist/settings.d.ts
CHANGED
|
@@ -7,6 +7,15 @@ export declare const CHARACTERISTIC_UPDATE_DELAY_MS = 50;
|
|
|
7
7
|
* this long before spawning the send_dmx subprocess. Coalesces a fast
|
|
8
8
|
* slider drag into one transaction. */
|
|
9
9
|
export declare const DEBOUNCE_MS = 750;
|
|
10
|
+
/** Idle keepalive. The Stick-DE3 drifts into a wedged state after hours
|
|
11
|
+
* without traffic: sessions and the crypto handshake still succeed but
|
|
12
|
+
* live DMX output goes dark (observed 2026-08-22..25; only a hard power
|
|
13
|
+
* cycle recovers it). Re-sending the last-known state whenever the
|
|
14
|
+
* controller has been idle this long keeps the device exercised and
|
|
15
|
+
* re-asserts state after an out-of-band power-cycle. 0 disables. */
|
|
16
|
+
export declare const REFRESH_MINUTES_DEFAULT = 30;
|
|
17
|
+
/** How often the controller checks whether a keepalive refresh is due. */
|
|
18
|
+
export declare const REFRESH_CHECK_MS = 60000;
|
|
10
19
|
/** Frame interval (40ms = 25Hz, matches HWM exactly). Unused by the
|
|
11
20
|
* subprocess controller — send_dmx.mjs's own FRAME_HZ env knob is what
|
|
12
21
|
* matters now. */
|
package/dist/settings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,QAAQ,CAAC;AACnC,eAAO,MAAM,WAAW,8BAA8B,CAAC;AAEvD;6EAC6E;AAC7E,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD;;wCAEwC;AACxC,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B;;mBAEmB;AACnB,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,QAAQ,CAAC;AACnC,eAAO,MAAM,WAAW,8BAA8B,CAAC;AAEvD;6EAC6E;AAC7E,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD;;wCAEwC;AACxC,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B;;;;;qEAKqE;AACrE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC;;mBAEmB;AACnB,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
|
package/dist/settings.js
CHANGED
|
@@ -8,6 +8,15 @@ export const CHARACTERISTIC_UPDATE_DELAY_MS = 50;
|
|
|
8
8
|
* this long before spawning the send_dmx subprocess. Coalesces a fast
|
|
9
9
|
* slider drag into one transaction. */
|
|
10
10
|
export const DEBOUNCE_MS = 750;
|
|
11
|
+
/** Idle keepalive. The Stick-DE3 drifts into a wedged state after hours
|
|
12
|
+
* without traffic: sessions and the crypto handshake still succeed but
|
|
13
|
+
* live DMX output goes dark (observed 2026-08-22..25; only a hard power
|
|
14
|
+
* cycle recovers it). Re-sending the last-known state whenever the
|
|
15
|
+
* controller has been idle this long keeps the device exercised and
|
|
16
|
+
* re-asserts state after an out-of-band power-cycle. 0 disables. */
|
|
17
|
+
export const REFRESH_MINUTES_DEFAULT = 30;
|
|
18
|
+
/** How often the controller checks whether a keepalive refresh is due. */
|
|
19
|
+
export const REFRESH_CHECK_MS = 60_000;
|
|
11
20
|
/** Frame interval (40ms = 25Hz, matches HWM exactly). Unused by the
|
|
12
21
|
* subprocess controller — send_dmx.mjs's own FRAME_HZ env knob is what
|
|
13
22
|
* matters now. */
|
package/dist/settings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AACnC,MAAM,CAAC,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEvD;6EAC6E;AAC7E,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAEjD;;wCAEwC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B;;mBAEmB;AACnB,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AACnC,MAAM,CAAC,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEvD;6EAC6E;AAC7E,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAEjD;;wCAEwC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B;;;;;qEAKqE;AACrE,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC;;mBAEmB;AACnB,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HomeKitLightState } from './color/types.js';
|
|
2
|
+
export type StateChangeListener = (fixtureId: string) => void;
|
|
3
|
+
export declare class StateRegistry {
|
|
4
|
+
private states;
|
|
5
|
+
private listeners;
|
|
6
|
+
get(id: string): HomeKitLightState;
|
|
7
|
+
/** Replace a fixture's state and fire change listeners. */
|
|
8
|
+
set(id: string, state: HomeKitLightState): void;
|
|
9
|
+
/** Merge a partial update into a fixture's state, returning the new full
|
|
10
|
+
* state. Fires listeners. */
|
|
11
|
+
update(id: string, patch: Partial<HomeKitLightState>): HomeKitLightState;
|
|
12
|
+
onChange(cb: StateChangeListener): () => void;
|
|
13
|
+
}
|
|
14
|
+
/** Bucket members' values; return the value with the largest bucket. Ties
|
|
15
|
+
* break in favour of the value held by the first member in
|
|
16
|
+
* alphabetical-id order. */
|
|
17
|
+
export declare function pickMajority<T>(items: Array<{
|
|
18
|
+
id: string;
|
|
19
|
+
v: T;
|
|
20
|
+
}>): T | undefined;
|
|
21
|
+
//# sourceMappingURL=stateRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateRegistry.d.ts","sourceRoot":"","sources":["../src/stateRegistry.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AAI9D,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,SAAS,CAAkC;IAEnD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB;IAIlC,2DAA2D;IAC3D,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAK/C;kCAC8B;IAC9B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;IAMxE,QAAQ,CAAC,EAAE,EAAE,mBAAmB,GAAG,MAAM,IAAI;CAI9C;AAED;;6BAE6B;AAC7B,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAoBjF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// StateRegistry — central store of every fixture's HomeKit-visible state.
|
|
2
|
+
//
|
|
3
|
+
// Every StickFixture and ZoneFixture reads/writes through here. Whenever a
|
|
4
|
+
// fixture's state changes, listeners fire so zones can refresh their own
|
|
5
|
+
// HomeKit characteristics (and vice versa: setting a zone updates each
|
|
6
|
+
// member's state in the registry, which fires events, which prompt the
|
|
7
|
+
// per-fixture accessories to update their HomeKit-displayed state).
|
|
8
|
+
//
|
|
9
|
+
// Majority rule for zone state: see pickMajority() — count occurrences of
|
|
10
|
+
// each value across (alphabetically-sorted) members; largest count wins;
|
|
11
|
+
// ties are broken in favour of the value held by the alphabetically-first
|
|
12
|
+
// member.
|
|
13
|
+
const DEFAULT_STATE = { on: false, brightness: 100 };
|
|
14
|
+
export class StateRegistry {
|
|
15
|
+
states = new Map();
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
get(id) {
|
|
18
|
+
return this.states.get(id) ?? { ...DEFAULT_STATE };
|
|
19
|
+
}
|
|
20
|
+
/** Replace a fixture's state and fire change listeners. */
|
|
21
|
+
set(id, state) {
|
|
22
|
+
this.states.set(id, { ...state });
|
|
23
|
+
for (const l of this.listeners)
|
|
24
|
+
l(id);
|
|
25
|
+
}
|
|
26
|
+
/** Merge a partial update into a fixture's state, returning the new full
|
|
27
|
+
* state. Fires listeners. */
|
|
28
|
+
update(id, patch) {
|
|
29
|
+
const next = { ...this.get(id), ...patch };
|
|
30
|
+
this.set(id, next);
|
|
31
|
+
return next;
|
|
32
|
+
}
|
|
33
|
+
onChange(cb) {
|
|
34
|
+
this.listeners.add(cb);
|
|
35
|
+
return () => this.listeners.delete(cb);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Bucket members' values; return the value with the largest bucket. Ties
|
|
39
|
+
* break in favour of the value held by the first member in
|
|
40
|
+
* alphabetical-id order. */
|
|
41
|
+
export function pickMajority(items) {
|
|
42
|
+
if (items.length === 0)
|
|
43
|
+
return undefined;
|
|
44
|
+
const sorted = items.slice().sort((a, b) => a.id.localeCompare(b.id));
|
|
45
|
+
const counts = new Map();
|
|
46
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
47
|
+
const v = sorted[i].v;
|
|
48
|
+
const rec = counts.get(v);
|
|
49
|
+
if (rec)
|
|
50
|
+
rec.count++;
|
|
51
|
+
else
|
|
52
|
+
counts.set(v, { count: 1, firstIdx: i });
|
|
53
|
+
}
|
|
54
|
+
let bestV = sorted[0].v;
|
|
55
|
+
let bestRec = counts.get(bestV);
|
|
56
|
+
for (const [v, rec] of counts) {
|
|
57
|
+
if (rec.count > bestRec.count ||
|
|
58
|
+
(rec.count === bestRec.count && rec.firstIdx < bestRec.firstIdx)) {
|
|
59
|
+
bestV = v;
|
|
60
|
+
bestRec = rec;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return bestV;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=stateRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateRegistry.js","sourceRoot":"","sources":["../src/stateRegistry.ts"],"names":[],"mappings":"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"}
|