node-red-contrib-dmx-for-ha 0.3.8 → 0.4.1
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 +1 -1
- package/nodes/ha-mqtt-dmx-group.js +18 -11
- package/nodes/ha-mqtt-dmx.js +29 -3
- package/nodes/ha-mqtt-pir.js +1 -1
- package/nodes/ha-mqtt-relay.js +1 -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;
|
|
@@ -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;
|
|
@@ -236,6 +236,15 @@ module.exports = function (RED) {
|
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
|
|
240
|
+
function groupStatus(state, label) {
|
|
241
|
+
if (state === 'ON') {
|
|
242
|
+
setStatus('green', 'dot', label || `${groupId} ON`);
|
|
243
|
+
} else {
|
|
244
|
+
setStatus('grey', 'ring', label || `${groupId} OFF`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
239
248
|
// ── Device add ────────────────────────────────────────────
|
|
240
249
|
function handleDeviceAdd(incomingTrace) {
|
|
241
250
|
if (!ctxGet('state') && !ctxGet('state', 'disk_values')) {
|
|
@@ -284,15 +293,12 @@ module.exports = function (RED) {
|
|
|
284
293
|
pubState({ state: payload.state, color_mode: S.colorMode, brightness: payload.brightness });
|
|
285
294
|
forwardToChildren(payload, null);
|
|
286
295
|
const _lbl = payload.effect ? `effect:${payload.effect}` : `${payload.state}`;
|
|
287
|
-
|
|
288
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
296
|
+
groupStatus(payload.state, `${groupId} → ${_lbl}`);
|
|
289
297
|
}, node.id);
|
|
290
298
|
|
|
291
299
|
setStatus('green', 'ring', `${groupId} discovery sent`);
|
|
292
300
|
node.log(`Group device added: "${S.groupName || groupId}"`);
|
|
293
301
|
|
|
294
|
-
// Forward device:add to children via Link so they self-discover
|
|
295
|
-
node.send([{ device: 'add' }]);
|
|
296
302
|
|
|
297
303
|
// Recovery
|
|
298
304
|
setTimeout(() => {
|
|
@@ -300,8 +306,10 @@ module.exports = function (RED) {
|
|
|
300
306
|
const brightness = recall('brightness', 'brightness_disk', 255);
|
|
301
307
|
const color = recall('color', 'color_disk', null);
|
|
302
308
|
pubState({ state, color_mode: S.colorMode, brightness, color });
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
// NOTE: do NOT forward to children on recovery — each child node
|
|
310
|
+
// recovers its own state independently from its own disk store.
|
|
311
|
+
// Forwarding here would overwrite children with the group's state.
|
|
312
|
+
groupStatus(state, `${groupId} → ${state}`);
|
|
305
313
|
node.log(`${groupId} recovery — state:${state}`);
|
|
306
314
|
}, 2000);
|
|
307
315
|
}
|
|
@@ -309,6 +317,7 @@ module.exports = function (RED) {
|
|
|
309
317
|
// ── Device remove ─────────────────────────────────────────
|
|
310
318
|
function handleDeviceRemove(incomingTrace) {
|
|
311
319
|
if (diskTimer) { clearTimeout(diskTimer); diskTimer = null; }
|
|
320
|
+
// No flush needed on remove — state is cleared below
|
|
312
321
|
ctxSet('state', null); ctxSet('state', null, 'disk_values');
|
|
313
322
|
pub(cfgTopic, '', true);
|
|
314
323
|
broker.unsubscribe(cmdTopic, node.id);
|
|
@@ -330,8 +339,7 @@ module.exports = function (RED) {
|
|
|
330
339
|
saveGroupState(msg.payload);
|
|
331
340
|
pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
|
|
332
341
|
forwardToChildren(msg.payload, msg.dmx_trace);
|
|
333
|
-
|
|
334
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
342
|
+
groupStatus(msg.payload.state, `${groupId} → cascade:${msg.payload.state}`);
|
|
335
343
|
}
|
|
336
344
|
} else {
|
|
337
345
|
const devReq = typeof msg.device === 'string'
|
|
@@ -348,8 +356,7 @@ module.exports = function (RED) {
|
|
|
348
356
|
saveGroupState(msg.payload);
|
|
349
357
|
pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
|
|
350
358
|
forwardToChildren(msg.payload, null);
|
|
351
|
-
|
|
352
|
-
setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
|
|
359
|
+
groupStatus(msg.payload.state, `${groupId} → ${msg.payload.state}`);
|
|
353
360
|
} else {
|
|
354
361
|
node.warn(`${groupId} — unrecognised message received and dropped. See node documentation.`);
|
|
355
362
|
}
|
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
|
|
|
@@ -666,7 +679,20 @@ module.exports = function (RED) {
|
|
|
666
679
|
function handleDeviceRemove() {
|
|
667
680
|
clearChannelRegistry();
|
|
668
681
|
stopEffect();
|
|
669
|
-
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
|
+
}
|
|
670
696
|
['state','brightness','red','green','blue','white','warmWhite'].forEach(function (k) {
|
|
671
697
|
ctxSet(k, null); ctxSet(k, null, 'disk_values');
|
|
672
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;
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-dmx-for-ha",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|