node-red-contrib-dmx-for-ha 0.3.5 → 0.3.7
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/README.md +14 -0
- package/nodes/ha-mqtt-button.js +5 -4
- package/nodes/ha-mqtt-dmx-group.js +5 -4
- package/nodes/ha-mqtt-dmx.js +54 -5
- package/nodes/ha-mqtt-pir.js +5 -4
- package/nodes/ha-mqtt-relay.js +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -290,6 +290,20 @@ Set to `0` to make all un-timed commands instant.
|
|
|
290
290
|
|
|
291
291
|
| Version | Changes |
|
|
292
292
|
|---|---|
|
|
293
|
+
| 0.3.6 | DMX channel conflict detection, jitter timer cancellable on teardown |
|
|
294
|
+
| 0.3.5 | Fix debug mode block initialisation order (S before debug) |
|
|
295
|
+
| 0.3.4 | Debug hint text updated — notes 12hr auto-disable |
|
|
296
|
+
| 0.3.3 | Debug mode canvas warning + 12hr auto-disable safety net |
|
|
297
|
+
| 0.3.2 | Debug output to NR debug tab via node.warn with [DEBUG] prefix |
|
|
298
|
+
| 0.3.1 | Double-fire fix — 5s cooldown + _discovered resets on broker close |
|
|
299
|
+
| 0.3.0 | Debug mode toggle in Advanced section on all nodes |
|
|
300
|
+
| 0.2.9 | _lastSent cache — skip redundant MQTT publishes (unchanged values) |
|
|
301
|
+
| 0.2.8 | Transition jitter, micro-transition threshold, unconfigured ch skip |
|
|
302
|
+
| 0.2.7 | transitionRateLimit + transitionHaUiTime wired from config node |
|
|
303
|
+
| 0.2.6 | Zone optional — omitted from topic when blank |
|
|
304
|
+
| 0.2.5 | buildLocation helper — strips parens, skips duplicates, uses " - " separator |
|
|
305
|
+
| 0.2.4 | sw_version reads from package.json, unique_id = fixtureId only, MW3D removed |
|
|
306
|
+
| 0.2.3 | Canvas label format: L-991-E · Downlight in Bedroom 1 |
|
|
293
307
|
| 0.2.2 | Group Node status updates, broker feedback, version in editor panel |
|
|
294
308
|
| 0.2.1 | Hidden mode — entity not auto-placed in dashboard |
|
|
295
309
|
| 0.2.0 | Canvas button toggles remove/add, auto-discovery on deploy |
|
package/nodes/ha-mqtt-button.js
CHANGED
|
@@ -108,14 +108,15 @@ module.exports = function (RED) {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
111
|
+
const _debugId = `S-${S.uid}${S.uidPostfix}`;
|
|
111
112
|
if (S.debugMode) {
|
|
112
|
-
setStatus('red', 'dot', `ha-mqtt-button "${
|
|
113
|
-
node.warn(`[DEBUG] ha-mqtt-button "${
|
|
113
|
+
setStatus('red', 'dot', `ha-mqtt-button "${_debugId}" ⚠ DEBUG MODE ON`);
|
|
114
|
+
node.warn(`[DEBUG] ha-mqtt-button "${_debugId}" — debug mode is enabled. Disable in production.`);
|
|
114
115
|
setTimeout(function () {
|
|
115
116
|
if (S.debugMode) {
|
|
116
117
|
S.debugMode = false;
|
|
117
|
-
node.warn(`[DEBUG] ha-mqtt-button "${
|
|
118
|
-
setStatus('yellow', 'ring', `${
|
|
118
|
+
node.warn(`[DEBUG] ha-mqtt-button "${_debugId}" — debug mode auto-disabled after 12 hours`);
|
|
119
|
+
setStatus('yellow', 'ring', `${_debugId} ready — awaiting HA`);
|
|
119
120
|
}
|
|
120
121
|
}, 12 * 60 * 60 * 1000);
|
|
121
122
|
}
|
|
@@ -115,14 +115,15 @@ module.exports = function (RED) {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
118
|
+
const _debugId = `LG-${S.uid}${S.uidPostfix}`;
|
|
118
119
|
if (S.debugMode) {
|
|
119
|
-
setStatus('red', 'dot', `ha-mqtt-dmx-group "${
|
|
120
|
-
node.warn(`[DEBUG] ha-mqtt-dmx-group "${
|
|
120
|
+
setStatus('red', 'dot', `ha-mqtt-dmx-group "${_debugId}" ⚠ DEBUG MODE ON`);
|
|
121
|
+
node.warn(`[DEBUG] ha-mqtt-dmx-group "${_debugId}" — debug mode is enabled. Disable in production.`);
|
|
121
122
|
setTimeout(function () {
|
|
122
123
|
if (S.debugMode) {
|
|
123
124
|
S.debugMode = false;
|
|
124
|
-
node.warn(`[DEBUG] ha-mqtt-dmx-group "${
|
|
125
|
-
setStatus('yellow', 'ring', `${
|
|
125
|
+
node.warn(`[DEBUG] ha-mqtt-dmx-group "${_debugId}" — debug mode auto-disabled after 12 hours`);
|
|
126
|
+
setStatus('yellow', 'ring', `${_debugId} ready — awaiting HA`);
|
|
126
127
|
}
|
|
127
128
|
}, 12 * 60 * 60 * 1000);
|
|
128
129
|
}
|
package/nodes/ha-mqtt-dmx.js
CHANGED
|
@@ -139,14 +139,15 @@ module.exports = function (RED) {
|
|
|
139
139
|
};
|
|
140
140
|
|
|
141
141
|
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
142
|
+
const _debugId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
|
|
142
143
|
if (S.debugMode) {
|
|
143
|
-
setStatus('red', 'dot', `ha-mqtt-dmx "${
|
|
144
|
-
node.warn(`[DEBUG] ha-mqtt-dmx "${
|
|
144
|
+
setStatus('red', 'dot', `ha-mqtt-dmx "${_debugId}" ⚠ DEBUG MODE ON`);
|
|
145
|
+
node.warn(`[DEBUG] ha-mqtt-dmx "${_debugId}" — debug mode is enabled. Disable in production.`);
|
|
145
146
|
setTimeout(function () {
|
|
146
147
|
if (S.debugMode) {
|
|
147
148
|
S.debugMode = false;
|
|
148
|
-
node.warn(`[DEBUG] ha-mqtt-dmx "${
|
|
149
|
-
setStatus('yellow', 'ring', `${
|
|
149
|
+
node.warn(`[DEBUG] ha-mqtt-dmx "${_debugId}" — debug mode auto-disabled after 12 hours`);
|
|
150
|
+
setStatus('yellow', 'ring', `${_debugId} ready — awaiting HA`);
|
|
150
151
|
}
|
|
151
152
|
}, 12 * 60 * 60 * 1000);
|
|
152
153
|
}
|
|
@@ -243,9 +244,11 @@ module.exports = function (RED) {
|
|
|
243
244
|
|
|
244
245
|
// ── Effects ───────────────────────────────────────────────
|
|
245
246
|
let effectTimer = null;
|
|
247
|
+
let jitterTimer = null;
|
|
246
248
|
let preEffectState = null;
|
|
247
249
|
|
|
248
250
|
function stopEffect() {
|
|
251
|
+
if (jitterTimer) { clearTimeout(jitterTimer); jitterTimer = null; }
|
|
249
252
|
if (effectTimer) {
|
|
250
253
|
clearInterval(effectTimer); clearTimeout(effectTimer);
|
|
251
254
|
effectTimer = null;
|
|
@@ -329,7 +332,7 @@ module.exports = function (RED) {
|
|
|
329
332
|
// Prevents thundering herd when many nodes start transitions simultaneously
|
|
330
333
|
const jitter = Math.random() * intervalMs;
|
|
331
334
|
// Fix 3: Jitter delay spreads load across event loop
|
|
332
|
-
setTimeout(function () {
|
|
335
|
+
jitterTimer = setTimeout(function () {
|
|
333
336
|
effectTimer = setInterval(function () {
|
|
334
337
|
tick++;
|
|
335
338
|
const progress = Math.min(1, tick / totalTicks);
|
|
@@ -521,6 +524,48 @@ module.exports = function (RED) {
|
|
|
521
524
|
return [[vv,t,p],[q,vv,p],[p,vv,t],[p,q,vv],[t,p,vv],[vv,p,q]][i];
|
|
522
525
|
}
|
|
523
526
|
|
|
527
|
+
|
|
528
|
+
// ── DMX channel conflict detection ───────────────────────────────────
|
|
529
|
+
function checkChannelConflicts() {
|
|
530
|
+
const globalCtx = node.context().global;
|
|
531
|
+
const registryKey = `dmx_channels_${cfg.siteId}_${cfg.zone}_${S.universe}`;
|
|
532
|
+
let registry = {};
|
|
533
|
+
try { registry = globalCtx.get(registryKey) || {}; } catch(e) {}
|
|
534
|
+
|
|
535
|
+
const myChannels = [S.ch.red, S.ch.green, S.ch.blue, S.ch.white, S.ch.warmWhite]
|
|
536
|
+
.filter(ch => ch && ch > 0);
|
|
537
|
+
|
|
538
|
+
let conflicts = [];
|
|
539
|
+
myChannels.forEach(function(ch) {
|
|
540
|
+
if (registry[ch] && registry[ch] !== fixtureId) {
|
|
541
|
+
conflicts.push(`ch${ch} already used by ${registry[ch]}`);
|
|
542
|
+
}
|
|
543
|
+
registry[ch] = fixtureId;
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
try { globalCtx.set(registryKey, registry); } catch(e) {}
|
|
547
|
+
|
|
548
|
+
if (conflicts.length > 0) {
|
|
549
|
+
const msg = `${fixtureId} — DMX CHANNEL CONFLICT: ${conflicts.join(', ')}`;
|
|
550
|
+
node.warn(msg);
|
|
551
|
+
setStatus('red', 'dot', `${fixtureId} ⚠ CHANNEL CONFLICT`);
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function clearChannelRegistry() {
|
|
558
|
+
try {
|
|
559
|
+
const globalCtx = node.context().global;
|
|
560
|
+
const registryKey = `dmx_channels_${cfg.siteId}_${cfg.zone}_${S.universe}`;
|
|
561
|
+
const registry = globalCtx.get(registryKey) || {};
|
|
562
|
+
[S.ch.red, S.ch.green, S.ch.blue, S.ch.white, S.ch.warmWhite]
|
|
563
|
+
.filter(ch => ch && ch > 0)
|
|
564
|
+
.forEach(ch => { if (registry[ch] === fixtureId) delete registry[ch]; });
|
|
565
|
+
globalCtx.set(registryKey, registry);
|
|
566
|
+
} catch(e) {}
|
|
567
|
+
}
|
|
568
|
+
|
|
524
569
|
// ── Device add ────────────────────────────────────────────
|
|
525
570
|
function handleDeviceAdd() {
|
|
526
571
|
if (ctxGet('state') === undefined && ctxGet('state', 'disk_values') === undefined) {
|
|
@@ -565,6 +610,8 @@ module.exports = function (RED) {
|
|
|
565
610
|
};
|
|
566
611
|
|
|
567
612
|
pub(cfgTopic, discovery, true);
|
|
613
|
+
// Check for DMX channel conflicts before discovery
|
|
614
|
+
if (!checkChannelConflicts()) return;
|
|
568
615
|
resetLastSent(); // Force full state re-publish after discovery
|
|
569
616
|
|
|
570
617
|
broker.subscribe(cmdTopic, cfg.qos, function (topic, rawPayload) {
|
|
@@ -607,6 +654,7 @@ module.exports = function (RED) {
|
|
|
607
654
|
|
|
608
655
|
// ── Device remove ─────────────────────────────────────────
|
|
609
656
|
function handleDeviceRemove() {
|
|
657
|
+
clearChannelRegistry();
|
|
610
658
|
stopEffect();
|
|
611
659
|
if (diskTimer) { clearTimeout(diskTimer); diskTimer = null; }
|
|
612
660
|
['state','brightness','red','green','blue','white','warmWhite'].forEach(function (k) {
|
|
@@ -656,6 +704,7 @@ module.exports = function (RED) {
|
|
|
656
704
|
|
|
657
705
|
// ── Cleanup ───────────────────────────────────────────────
|
|
658
706
|
node.on('close', function (done) {
|
|
707
|
+
clearChannelRegistry();
|
|
659
708
|
stopEffect();
|
|
660
709
|
if (diskTimer) clearTimeout(diskTimer);
|
|
661
710
|
broker.unsubscribe(cmdTopic, node.id);
|
package/nodes/ha-mqtt-pir.js
CHANGED
|
@@ -109,14 +109,15 @@ module.exports = function (RED) {
|
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
112
|
+
const _debugId = `S-${S.uid}${S.uidPostfix}`;
|
|
112
113
|
if (S.debugMode) {
|
|
113
|
-
setStatus('red', 'dot', `ha-mqtt-pir "${
|
|
114
|
-
node.warn(`[DEBUG] ha-mqtt-pir "${
|
|
114
|
+
setStatus('red', 'dot', `ha-mqtt-pir "${_debugId}" ⚠ DEBUG MODE ON`);
|
|
115
|
+
node.warn(`[DEBUG] ha-mqtt-pir "${_debugId}" — debug mode is enabled. Disable in production.`);
|
|
115
116
|
setTimeout(function () {
|
|
116
117
|
if (S.debugMode) {
|
|
117
118
|
S.debugMode = false;
|
|
118
|
-
node.warn(`[DEBUG] ha-mqtt-pir "${
|
|
119
|
-
setStatus('yellow', 'ring', `${
|
|
119
|
+
node.warn(`[DEBUG] ha-mqtt-pir "${_debugId}" — debug mode auto-disabled after 12 hours`);
|
|
120
|
+
setStatus('yellow', 'ring', `${_debugId} ready — awaiting HA`);
|
|
120
121
|
}
|
|
121
122
|
}, 12 * 60 * 60 * 1000);
|
|
122
123
|
}
|
package/nodes/ha-mqtt-relay.js
CHANGED
|
@@ -115,14 +115,15 @@ module.exports = function (RED) {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
118
|
+
const _debugId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
|
|
118
119
|
if (S.debugMode) {
|
|
119
|
-
setStatus('red', 'dot', `ha-mqtt-relay "${
|
|
120
|
-
node.warn(`[DEBUG] ha-mqtt-relay "${
|
|
120
|
+
setStatus('red', 'dot', `ha-mqtt-relay "${_debugId}" ⚠ DEBUG MODE ON`);
|
|
121
|
+
node.warn(`[DEBUG] ha-mqtt-relay "${_debugId}" — debug mode is enabled. Disable in production.`);
|
|
121
122
|
setTimeout(function () {
|
|
122
123
|
if (S.debugMode) {
|
|
123
124
|
S.debugMode = false;
|
|
124
|
-
node.warn(`[DEBUG] ha-mqtt-relay "${
|
|
125
|
-
setStatus('yellow', 'ring', `${
|
|
125
|
+
node.warn(`[DEBUG] ha-mqtt-relay "${_debugId}" — debug mode auto-disabled after 12 hours`);
|
|
126
|
+
setStatus('yellow', 'ring', `${_debugId} ready — awaiting HA`);
|
|
126
127
|
}
|
|
127
128
|
}, 12 * 60 * 60 * 1000);
|
|
128
129
|
}
|
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.7",
|
|
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",
|