node-red-contrib-dmx-for-ha 0.3.7 → 0.3.9
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/nodes/ha-mqtt-button.js +12 -1
- package/nodes/ha-mqtt-dmx-group.js +10 -6
- package/nodes/ha-mqtt-dmx.js +39 -3
- package/nodes/ha-mqtt-pir.js +11 -1
- package/nodes/ha-mqtt-relay.js +11 -1
- package/package.json +1 -1
package/nodes/ha-mqtt-button.js
CHANGED
|
@@ -56,7 +56,7 @@ module.exports = function (RED) {
|
|
|
56
56
|
// but still respect cooldown to prevent accidental rapid-fire
|
|
57
57
|
const now = Date.now();
|
|
58
58
|
if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
|
|
59
|
-
node.
|
|
59
|
+
if (S.debugMode) node.debug('Discovery cooldown active — skipping duplicate auto-discover');
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
_lastDiscoveryTime = now;
|
|
@@ -121,6 +121,17 @@ module.exports = function (RED) {
|
|
|
121
121
|
}, 12 * 60 * 60 * 1000);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
|
|
125
|
+
// ── Fixture identity and topics ───────────────────────────────────────
|
|
126
|
+
const fixtureId = `S-${S.uid}${S.uidPostfix}`;
|
|
127
|
+
const objectId = `s_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
128
|
+
const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'binary_sensor', fixtureId);
|
|
129
|
+
const uiBtnTopic = cfg.buildTopic(cfg.discoveryPrefix, 'button', fixtureId + '-BTN');
|
|
130
|
+
const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
|
|
131
|
+
const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
|
|
132
|
+
const uiBtnCfgTopic = `${uiBtnTopic}/${cfg.configTopic}`;
|
|
133
|
+
const uiBtnCmdTopic = `${uiBtnTopic}/${cfg.commandTopic}`;
|
|
134
|
+
|
|
124
135
|
// ── Helpers ───────────────────────────────────────────────
|
|
125
136
|
function pub(topic, payload, retain) {
|
|
126
137
|
const strPayload = typeof payload === 'object' ? JSON.stringify(payload) : String(payload);
|
|
@@ -59,7 +59,7 @@ module.exports = function (RED) {
|
|
|
59
59
|
// but still respect cooldown to prevent accidental rapid-fire
|
|
60
60
|
const now = Date.now();
|
|
61
61
|
if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
|
|
62
|
-
node.
|
|
62
|
+
if (S.debugMode) node.debug('Discovery cooldown active — skipping duplicate auto-discover');
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
_lastDiscoveryTime = now;
|
|
@@ -128,6 +128,15 @@ module.exports = function (RED) {
|
|
|
128
128
|
}, 12 * 60 * 60 * 1000);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
|
|
132
|
+
// ── Group identity and topics ─────────────────────────────────────────
|
|
133
|
+
const groupId = `LG-${S.uid}${S.uidPostfix}`;
|
|
134
|
+
const objectId = `lg_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
135
|
+
const groupTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', groupId);
|
|
136
|
+
const cfgTopic = `${groupTopic}/${cfg.configTopic}`;
|
|
137
|
+
const statTopic = `${groupTopic}/${cfg.stateTopic}`;
|
|
138
|
+
const cmdTopic = `${groupTopic}/${cfg.commandTopic}`;
|
|
139
|
+
|
|
131
140
|
// ── Context helpers ───────────────────────────────────────
|
|
132
141
|
// Check disk store available once on startup
|
|
133
142
|
let diskAvailable = false;
|
|
@@ -276,14 +285,11 @@ module.exports = function (RED) {
|
|
|
276
285
|
forwardToChildren(payload, null);
|
|
277
286
|
const _lbl = payload.effect ? `effect:${payload.effect}` : `${payload.state}`;
|
|
278
287
|
setStatus('green', 'dot', `${groupId} → ${_lbl}`);
|
|
279
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
280
288
|
}, node.id);
|
|
281
289
|
|
|
282
290
|
setStatus('green', 'ring', `${groupId} discovery sent`);
|
|
283
291
|
node.log(`Group device added: "${S.groupName || groupId}"`);
|
|
284
292
|
|
|
285
|
-
// Forward device:add to children via Link so they self-discover
|
|
286
|
-
node.send([{ device: 'add' }]);
|
|
287
293
|
|
|
288
294
|
// Recovery
|
|
289
295
|
setTimeout(() => {
|
|
@@ -322,7 +328,6 @@ module.exports = function (RED) {
|
|
|
322
328
|
pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
|
|
323
329
|
forwardToChildren(msg.payload, msg.dmx_trace);
|
|
324
330
|
setStatus('green', 'dot', `${groupId} → cascade:${msg.payload.state}`);
|
|
325
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
326
331
|
}
|
|
327
332
|
} else {
|
|
328
333
|
const devReq = typeof msg.device === 'string'
|
|
@@ -340,7 +345,6 @@ module.exports = function (RED) {
|
|
|
340
345
|
pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
|
|
341
346
|
forwardToChildren(msg.payload, null);
|
|
342
347
|
setStatus('green', 'dot', `${groupId} → ${msg.payload.state}`);
|
|
343
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
344
348
|
} else {
|
|
345
349
|
node.warn(`${groupId} — unrecognised message received and dropped. See node documentation.`);
|
|
346
350
|
}
|
package/nodes/ha-mqtt-dmx.js
CHANGED
|
@@ -66,7 +66,7 @@ module.exports = function (RED) {
|
|
|
66
66
|
// but still respect cooldown to prevent accidental rapid-fire
|
|
67
67
|
const now = Date.now();
|
|
68
68
|
if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
|
|
69
|
-
node.
|
|
69
|
+
if (S.debugMode) node.debug('Discovery cooldown active — skipping duplicate auto-discover');
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
_lastDiscoveryTime = now;
|
|
@@ -176,7 +176,20 @@ module.exports = function (RED) {
|
|
|
176
176
|
// ── Disk save timer ───────────────────────────────────────
|
|
177
177
|
let diskTimer = null;
|
|
178
178
|
function startDiskSave(onComplete) {
|
|
179
|
-
if (diskTimer) {
|
|
179
|
+
if (diskTimer) {
|
|
180
|
+
clearTimeout(diskTimer);
|
|
181
|
+
diskTimer = null;
|
|
182
|
+
// Flush pending disk save immediately on close
|
|
183
|
+
const s=ctxGet('state'); const br=ctxGet('brightness');
|
|
184
|
+
const r=ctxGet('red'); const g=ctxGet('green'); const b=ctxGet('blue');
|
|
185
|
+
const w=ctxGet('white'); const ww=ctxGet('warmWhite');
|
|
186
|
+
if (s !== undefined) {
|
|
187
|
+
ctxSet('state',s,'disk_values'); ctxSet('brightness',br,'disk_values');
|
|
188
|
+
ctxSet('red',r,'disk_values'); ctxSet('green',g,'disk_values');
|
|
189
|
+
ctxSet('blue',b,'disk_values'); ctxSet('white',w,'disk_values');
|
|
190
|
+
ctxSet('warmWhite',ww,'disk_values');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
180
193
|
diskTimer = setTimeout(() => { diskTimer = null; onComplete(); }, S.diskDelay * 1000);
|
|
181
194
|
}
|
|
182
195
|
|
|
@@ -525,6 +538,16 @@ module.exports = function (RED) {
|
|
|
525
538
|
}
|
|
526
539
|
|
|
527
540
|
|
|
541
|
+
|
|
542
|
+
// ── Fixture identity and topics ───────────────────────────────────────
|
|
543
|
+
const fixtureId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
|
|
544
|
+
const objectId = `${S.uidPrefix}_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
545
|
+
const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', fixtureId);
|
|
546
|
+
const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
|
|
547
|
+
const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
|
|
548
|
+
const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
|
|
549
|
+
const dmxTopic = cfg.buildTopic(cfg.siteId, cfg.zone, 'dmx', S.universe);
|
|
550
|
+
|
|
528
551
|
// ── DMX channel conflict detection ───────────────────────────────────
|
|
529
552
|
function checkChannelConflicts() {
|
|
530
553
|
const globalCtx = node.context().global;
|
|
@@ -656,7 +679,20 @@ module.exports = function (RED) {
|
|
|
656
679
|
function handleDeviceRemove() {
|
|
657
680
|
clearChannelRegistry();
|
|
658
681
|
stopEffect();
|
|
659
|
-
if (diskTimer) {
|
|
682
|
+
if (diskTimer) {
|
|
683
|
+
clearTimeout(diskTimer);
|
|
684
|
+
diskTimer = null;
|
|
685
|
+
// Flush pending disk save immediately on close
|
|
686
|
+
const s=ctxGet('state'); const br=ctxGet('brightness');
|
|
687
|
+
const r=ctxGet('red'); const g=ctxGet('green'); const b=ctxGet('blue');
|
|
688
|
+
const w=ctxGet('white'); const ww=ctxGet('warmWhite');
|
|
689
|
+
if (s !== undefined) {
|
|
690
|
+
ctxSet('state',s,'disk_values'); ctxSet('brightness',br,'disk_values');
|
|
691
|
+
ctxSet('red',r,'disk_values'); ctxSet('green',g,'disk_values');
|
|
692
|
+
ctxSet('blue',b,'disk_values'); ctxSet('white',w,'disk_values');
|
|
693
|
+
ctxSet('warmWhite',ww,'disk_values');
|
|
694
|
+
}
|
|
695
|
+
}
|
|
660
696
|
['state','brightness','red','green','blue','white','warmWhite'].forEach(function (k) {
|
|
661
697
|
ctxSet(k, null); ctxSet(k, null, 'disk_values');
|
|
662
698
|
});
|
package/nodes/ha-mqtt-pir.js
CHANGED
|
@@ -56,7 +56,7 @@ module.exports = function (RED) {
|
|
|
56
56
|
// but still respect cooldown to prevent accidental rapid-fire
|
|
57
57
|
const now = Date.now();
|
|
58
58
|
if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
|
|
59
|
-
node.
|
|
59
|
+
if (S.debugMode) node.debug('Discovery cooldown active — skipping duplicate auto-discover');
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
_lastDiscoveryTime = now;
|
|
@@ -122,6 +122,16 @@ module.exports = function (RED) {
|
|
|
122
122
|
}, 12 * 60 * 60 * 1000);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
|
|
126
|
+
// ── Fixture identity and topics ───────────────────────────────────────
|
|
127
|
+
const fixtureId = `S-${S.uid}${S.uidPostfix}`;
|
|
128
|
+
const objectId = `s_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
129
|
+
const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'binary_sensor', fixtureId);
|
|
130
|
+
const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
|
|
131
|
+
const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
|
|
132
|
+
const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
|
|
133
|
+
const avtyTopic = `${fixtureTopic}/${cfg.availTopic}`;
|
|
134
|
+
|
|
125
135
|
// ── Helpers ───────────────────────────────────────────────
|
|
126
136
|
function pub(topic, payload, retain) {
|
|
127
137
|
const strPayload = typeof payload === 'object' ? JSON.stringify(payload) : String(payload);
|
package/nodes/ha-mqtt-relay.js
CHANGED
|
@@ -59,7 +59,7 @@ module.exports = function (RED) {
|
|
|
59
59
|
// but still respect cooldown to prevent accidental rapid-fire
|
|
60
60
|
const now = Date.now();
|
|
61
61
|
if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
|
|
62
|
-
node.
|
|
62
|
+
if (S.debugMode) node.debug('Discovery cooldown active — skipping duplicate auto-discover');
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
_lastDiscoveryTime = now;
|
|
@@ -128,6 +128,16 @@ module.exports = function (RED) {
|
|
|
128
128
|
}, 12 * 60 * 60 * 1000);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
|
|
132
|
+
// ── Fixture identity and topics ───────────────────────────────────────
|
|
133
|
+
const fixtureId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
|
|
134
|
+
const objectId = `${S.uidPrefix}_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
135
|
+
const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', fixtureId);
|
|
136
|
+
const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
|
|
137
|
+
const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
|
|
138
|
+
const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
|
|
139
|
+
const relayTopic = cfg.buildTopic(cfg.siteId, cfg.zone, S.controllerNum, S.mqttSegment, S.relayNum);
|
|
140
|
+
|
|
131
141
|
// ── Context helpers ───────────────────────────────────────
|
|
132
142
|
// Check disk store available once on startup
|
|
133
143
|
let diskAvailable = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-dmx-for-ha",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "DMX lighting control for Home Assistant via Node-RED and MQTT. Place a node, fill in the settings, deploy. Full HA device registry integration with RGBW/RGBWW/CCT/brightness colour modes, transitions, effects, and group control.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red",
|