node-red-contrib-dmx-for-ha 0.3.4 → 0.3.6
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 +12 -23
- package/nodes/ha-mqtt-dmx-group.js +12 -21
- package/nodes/ha-mqtt-dmx.js +61 -23
- package/nodes/ha-mqtt-pir.js +12 -22
- package/nodes/ha-mqtt-relay.js +12 -22
- 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
|
@@ -79,21 +79,6 @@ module.exports = function (RED) {
|
|
|
79
79
|
node.warn('MQTT broker error: ' + (err.message || err));
|
|
80
80
|
setStatus('red', 'dot', 'Broker error — check config');
|
|
81
81
|
});
|
|
82
|
-
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
83
|
-
if (S.debugMode) {
|
|
84
|
-
// Permanent canvas warning so debug mode is obvious
|
|
85
|
-
setStatus('red', 'dot', `ha-mqtt-button "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
86
|
-
node.warn(`[DEBUG] ha-mqtt-button "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
87
|
-
|
|
88
|
-
// Auto-disable after 12 hours — safety net for forgotten debug sessions
|
|
89
|
-
setTimeout(function () {
|
|
90
|
-
if (S.debugMode) {
|
|
91
|
-
S.debugMode = false;
|
|
92
|
-
node.warn(`[DEBUG] ha-mqtt-button "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
93
|
-
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
94
|
-
}
|
|
95
|
-
}, 12 * 60 * 60 * 1000);
|
|
96
|
-
}
|
|
97
82
|
|
|
98
83
|
|
|
99
84
|
// Fallback — if connect event already fired before listener registered
|
|
@@ -122,14 +107,18 @@ module.exports = function (RED) {
|
|
|
122
107
|
debugMode: config.debugMode === true,
|
|
123
108
|
};
|
|
124
109
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
110
|
+
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
111
|
+
if (S.debugMode) {
|
|
112
|
+
setStatus('red', 'dot', `ha-mqtt-button "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
113
|
+
node.warn(`[DEBUG] ha-mqtt-button "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
114
|
+
setTimeout(function () {
|
|
115
|
+
if (S.debugMode) {
|
|
116
|
+
S.debugMode = false;
|
|
117
|
+
node.warn(`[DEBUG] ha-mqtt-button "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
118
|
+
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
119
|
+
}
|
|
120
|
+
}, 12 * 60 * 60 * 1000);
|
|
121
|
+
}
|
|
133
122
|
|
|
134
123
|
// ── Helpers ───────────────────────────────────────────────
|
|
135
124
|
function pub(topic, payload, retain) {
|
|
@@ -82,21 +82,6 @@ module.exports = function (RED) {
|
|
|
82
82
|
node.warn('MQTT broker error: ' + (err.message || err));
|
|
83
83
|
setStatus('red', 'dot', 'Broker error — check config');
|
|
84
84
|
});
|
|
85
|
-
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
86
|
-
if (S.debugMode) {
|
|
87
|
-
// Permanent canvas warning so debug mode is obvious
|
|
88
|
-
setStatus('red', 'dot', `ha-mqtt-dmx-group "${groupId}" ⚠ DEBUG MODE ON`);
|
|
89
|
-
node.warn(`[DEBUG] ha-mqtt-dmx-group "${groupId}" — debug mode is enabled. Disable in production.`);
|
|
90
|
-
|
|
91
|
-
// Auto-disable after 12 hours — safety net for forgotten debug sessions
|
|
92
|
-
setTimeout(function () {
|
|
93
|
-
if (S.debugMode) {
|
|
94
|
-
S.debugMode = false;
|
|
95
|
-
node.warn(`[DEBUG] ha-mqtt-dmx-group "${groupId}" — debug mode auto-disabled after 12 hours`);
|
|
96
|
-
setStatus('yellow', 'ring', `${groupId} ready — awaiting HA`);
|
|
97
|
-
}
|
|
98
|
-
}, 12 * 60 * 60 * 1000);
|
|
99
|
-
}
|
|
100
85
|
|
|
101
86
|
|
|
102
87
|
// Fallback — if connect event already fired before listener registered
|
|
@@ -129,12 +114,18 @@ module.exports = function (RED) {
|
|
|
129
114
|
diskDelay: cfg.diskDelay,
|
|
130
115
|
};
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
117
|
+
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
118
|
+
if (S.debugMode) {
|
|
119
|
+
setStatus('red', 'dot', `ha-mqtt-dmx-group "${groupId}" ⚠ DEBUG MODE ON`);
|
|
120
|
+
node.warn(`[DEBUG] ha-mqtt-dmx-group "${groupId}" — debug mode is enabled. Disable in production.`);
|
|
121
|
+
setTimeout(function () {
|
|
122
|
+
if (S.debugMode) {
|
|
123
|
+
S.debugMode = false;
|
|
124
|
+
node.warn(`[DEBUG] ha-mqtt-dmx-group "${groupId}" — debug mode auto-disabled after 12 hours`);
|
|
125
|
+
setStatus('yellow', 'ring', `${groupId} ready — awaiting HA`);
|
|
126
|
+
}
|
|
127
|
+
}, 12 * 60 * 60 * 1000);
|
|
128
|
+
}
|
|
138
129
|
|
|
139
130
|
// ── Context helpers ───────────────────────────────────────
|
|
140
131
|
// Check disk store available once on startup
|
package/nodes/ha-mqtt-dmx.js
CHANGED
|
@@ -89,21 +89,6 @@ module.exports = function (RED) {
|
|
|
89
89
|
node.warn('MQTT broker error: ' + (err.message || err));
|
|
90
90
|
setStatus('red', 'dot', 'Broker error — check config');
|
|
91
91
|
});
|
|
92
|
-
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
93
|
-
if (S.debugMode) {
|
|
94
|
-
// Permanent canvas warning so debug mode is obvious
|
|
95
|
-
setStatus('red', 'dot', `ha-mqtt-dmx "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
96
|
-
node.warn(`[DEBUG] ha-mqtt-dmx "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
97
|
-
|
|
98
|
-
// Auto-disable after 12 hours — safety net for forgotten debug sessions
|
|
99
|
-
setTimeout(function () {
|
|
100
|
-
if (S.debugMode) {
|
|
101
|
-
S.debugMode = false;
|
|
102
|
-
node.warn(`[DEBUG] ha-mqtt-dmx "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
103
|
-
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
104
|
-
}
|
|
105
|
-
}, 12 * 60 * 60 * 1000);
|
|
106
|
-
}
|
|
107
92
|
|
|
108
93
|
|
|
109
94
|
// Fallback — if connect event already fired before listener registered
|
|
@@ -153,13 +138,18 @@ module.exports = function (RED) {
|
|
|
153
138
|
diskDelay: cfg.diskDelay,
|
|
154
139
|
};
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
141
|
+
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
142
|
+
if (S.debugMode) {
|
|
143
|
+
setStatus('red', 'dot', `ha-mqtt-dmx "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
144
|
+
node.warn(`[DEBUG] ha-mqtt-dmx "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
145
|
+
setTimeout(function () {
|
|
146
|
+
if (S.debugMode) {
|
|
147
|
+
S.debugMode = false;
|
|
148
|
+
node.warn(`[DEBUG] ha-mqtt-dmx "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
149
|
+
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
150
|
+
}
|
|
151
|
+
}, 12 * 60 * 60 * 1000);
|
|
152
|
+
}
|
|
163
153
|
|
|
164
154
|
// ── Context helpers ───────────────────────────────────────
|
|
165
155
|
// Check disk store available once on startup
|
|
@@ -253,9 +243,11 @@ module.exports = function (RED) {
|
|
|
253
243
|
|
|
254
244
|
// ── Effects ───────────────────────────────────────────────
|
|
255
245
|
let effectTimer = null;
|
|
246
|
+
let jitterTimer = null;
|
|
256
247
|
let preEffectState = null;
|
|
257
248
|
|
|
258
249
|
function stopEffect() {
|
|
250
|
+
if (jitterTimer) { clearTimeout(jitterTimer); jitterTimer = null; }
|
|
259
251
|
if (effectTimer) {
|
|
260
252
|
clearInterval(effectTimer); clearTimeout(effectTimer);
|
|
261
253
|
effectTimer = null;
|
|
@@ -339,7 +331,7 @@ module.exports = function (RED) {
|
|
|
339
331
|
// Prevents thundering herd when many nodes start transitions simultaneously
|
|
340
332
|
const jitter = Math.random() * intervalMs;
|
|
341
333
|
// Fix 3: Jitter delay spreads load across event loop
|
|
342
|
-
setTimeout(function () {
|
|
334
|
+
jitterTimer = setTimeout(function () {
|
|
343
335
|
effectTimer = setInterval(function () {
|
|
344
336
|
tick++;
|
|
345
337
|
const progress = Math.min(1, tick / totalTicks);
|
|
@@ -531,6 +523,48 @@ module.exports = function (RED) {
|
|
|
531
523
|
return [[vv,t,p],[q,vv,p],[p,vv,t],[p,q,vv],[t,p,vv],[vv,p,q]][i];
|
|
532
524
|
}
|
|
533
525
|
|
|
526
|
+
|
|
527
|
+
// ── DMX channel conflict detection ───────────────────────────────────
|
|
528
|
+
function checkChannelConflicts() {
|
|
529
|
+
const globalCtx = node.context().global;
|
|
530
|
+
const registryKey = `dmx_channels_${cfg.siteId}_${cfg.zone}_${S.universe}`;
|
|
531
|
+
let registry = {};
|
|
532
|
+
try { registry = globalCtx.get(registryKey) || {}; } catch(e) {}
|
|
533
|
+
|
|
534
|
+
const myChannels = [S.ch.red, S.ch.green, S.ch.blue, S.ch.white, S.ch.warmWhite]
|
|
535
|
+
.filter(ch => ch && ch > 0);
|
|
536
|
+
|
|
537
|
+
let conflicts = [];
|
|
538
|
+
myChannels.forEach(function(ch) {
|
|
539
|
+
if (registry[ch] && registry[ch] !== fixtureId) {
|
|
540
|
+
conflicts.push(`ch${ch} already used by ${registry[ch]}`);
|
|
541
|
+
}
|
|
542
|
+
registry[ch] = fixtureId;
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
try { globalCtx.set(registryKey, registry); } catch(e) {}
|
|
546
|
+
|
|
547
|
+
if (conflicts.length > 0) {
|
|
548
|
+
const msg = `${fixtureId} — DMX CHANNEL CONFLICT: ${conflicts.join(', ')}`;
|
|
549
|
+
node.warn(msg);
|
|
550
|
+
setStatus('red', 'dot', `${fixtureId} ⚠ CHANNEL CONFLICT`);
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function clearChannelRegistry() {
|
|
557
|
+
try {
|
|
558
|
+
const globalCtx = node.context().global;
|
|
559
|
+
const registryKey = `dmx_channels_${cfg.siteId}_${cfg.zone}_${S.universe}`;
|
|
560
|
+
const registry = globalCtx.get(registryKey) || {};
|
|
561
|
+
[S.ch.red, S.ch.green, S.ch.blue, S.ch.white, S.ch.warmWhite]
|
|
562
|
+
.filter(ch => ch && ch > 0)
|
|
563
|
+
.forEach(ch => { if (registry[ch] === fixtureId) delete registry[ch]; });
|
|
564
|
+
globalCtx.set(registryKey, registry);
|
|
565
|
+
} catch(e) {}
|
|
566
|
+
}
|
|
567
|
+
|
|
534
568
|
// ── Device add ────────────────────────────────────────────
|
|
535
569
|
function handleDeviceAdd() {
|
|
536
570
|
if (ctxGet('state') === undefined && ctxGet('state', 'disk_values') === undefined) {
|
|
@@ -575,6 +609,8 @@ module.exports = function (RED) {
|
|
|
575
609
|
};
|
|
576
610
|
|
|
577
611
|
pub(cfgTopic, discovery, true);
|
|
612
|
+
// Check for DMX channel conflicts before discovery
|
|
613
|
+
if (!checkChannelConflicts()) return;
|
|
578
614
|
resetLastSent(); // Force full state re-publish after discovery
|
|
579
615
|
|
|
580
616
|
broker.subscribe(cmdTopic, cfg.qos, function (topic, rawPayload) {
|
|
@@ -617,6 +653,7 @@ module.exports = function (RED) {
|
|
|
617
653
|
|
|
618
654
|
// ── Device remove ─────────────────────────────────────────
|
|
619
655
|
function handleDeviceRemove() {
|
|
656
|
+
clearChannelRegistry();
|
|
620
657
|
stopEffect();
|
|
621
658
|
if (diskTimer) { clearTimeout(diskTimer); diskTimer = null; }
|
|
622
659
|
['state','brightness','red','green','blue','white','warmWhite'].forEach(function (k) {
|
|
@@ -666,6 +703,7 @@ module.exports = function (RED) {
|
|
|
666
703
|
|
|
667
704
|
// ── Cleanup ───────────────────────────────────────────────
|
|
668
705
|
node.on('close', function (done) {
|
|
706
|
+
clearChannelRegistry();
|
|
669
707
|
stopEffect();
|
|
670
708
|
if (diskTimer) clearTimeout(diskTimer);
|
|
671
709
|
broker.unsubscribe(cmdTopic, node.id);
|
package/nodes/ha-mqtt-pir.js
CHANGED
|
@@ -79,21 +79,6 @@ module.exports = function (RED) {
|
|
|
79
79
|
node.warn('MQTT broker error: ' + (err.message || err));
|
|
80
80
|
setStatus('red', 'dot', 'Broker error — check config');
|
|
81
81
|
});
|
|
82
|
-
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
83
|
-
if (S.debugMode) {
|
|
84
|
-
// Permanent canvas warning so debug mode is obvious
|
|
85
|
-
setStatus('red', 'dot', `ha-mqtt-pir "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
86
|
-
node.warn(`[DEBUG] ha-mqtt-pir "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
87
|
-
|
|
88
|
-
// Auto-disable after 12 hours — safety net for forgotten debug sessions
|
|
89
|
-
setTimeout(function () {
|
|
90
|
-
if (S.debugMode) {
|
|
91
|
-
S.debugMode = false;
|
|
92
|
-
node.warn(`[DEBUG] ha-mqtt-pir "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
93
|
-
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
94
|
-
}
|
|
95
|
-
}, 12 * 60 * 60 * 1000);
|
|
96
|
-
}
|
|
97
82
|
|
|
98
83
|
|
|
99
84
|
// Fallback — if connect event already fired before listener registered
|
|
@@ -123,13 +108,18 @@ module.exports = function (RED) {
|
|
|
123
108
|
debugMode: config.debugMode === true,
|
|
124
109
|
};
|
|
125
110
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
111
|
+
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
112
|
+
if (S.debugMode) {
|
|
113
|
+
setStatus('red', 'dot', `ha-mqtt-pir "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
114
|
+
node.warn(`[DEBUG] ha-mqtt-pir "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
115
|
+
setTimeout(function () {
|
|
116
|
+
if (S.debugMode) {
|
|
117
|
+
S.debugMode = false;
|
|
118
|
+
node.warn(`[DEBUG] ha-mqtt-pir "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
119
|
+
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
120
|
+
}
|
|
121
|
+
}, 12 * 60 * 60 * 1000);
|
|
122
|
+
}
|
|
133
123
|
|
|
134
124
|
// ── Helpers ───────────────────────────────────────────────
|
|
135
125
|
function pub(topic, payload, retain) {
|
package/nodes/ha-mqtt-relay.js
CHANGED
|
@@ -82,21 +82,6 @@ module.exports = function (RED) {
|
|
|
82
82
|
node.warn('MQTT broker error: ' + (err.message || err));
|
|
83
83
|
setStatus('red', 'dot', 'Broker error — check config');
|
|
84
84
|
});
|
|
85
|
-
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
86
|
-
if (S.debugMode) {
|
|
87
|
-
// Permanent canvas warning so debug mode is obvious
|
|
88
|
-
setStatus('red', 'dot', `ha-mqtt-relay "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
89
|
-
node.warn(`[DEBUG] ha-mqtt-relay "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
90
|
-
|
|
91
|
-
// Auto-disable after 12 hours — safety net for forgotten debug sessions
|
|
92
|
-
setTimeout(function () {
|
|
93
|
-
if (S.debugMode) {
|
|
94
|
-
S.debugMode = false;
|
|
95
|
-
node.warn(`[DEBUG] ha-mqtt-relay "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
96
|
-
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
97
|
-
}
|
|
98
|
-
}, 12 * 60 * 60 * 1000);
|
|
99
|
-
}
|
|
100
85
|
|
|
101
86
|
|
|
102
87
|
// Fallback — if connect event already fired before listener registered
|
|
@@ -129,13 +114,18 @@ module.exports = function (RED) {
|
|
|
129
114
|
diskDelay: cfg.diskDelay,
|
|
130
115
|
};
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
117
|
+
// ── Debug mode safeguards ─────────────────────────────────────────
|
|
118
|
+
if (S.debugMode) {
|
|
119
|
+
setStatus('red', 'dot', `ha-mqtt-relay "${fixtureId}" ⚠ DEBUG MODE ON`);
|
|
120
|
+
node.warn(`[DEBUG] ha-mqtt-relay "${fixtureId}" — debug mode is enabled. Disable in production.`);
|
|
121
|
+
setTimeout(function () {
|
|
122
|
+
if (S.debugMode) {
|
|
123
|
+
S.debugMode = false;
|
|
124
|
+
node.warn(`[DEBUG] ha-mqtt-relay "${fixtureId}" — debug mode auto-disabled after 12 hours`);
|
|
125
|
+
setStatus('yellow', 'ring', `${fixtureId} ready — awaiting HA`);
|
|
126
|
+
}
|
|
127
|
+
}, 12 * 60 * 60 * 1000);
|
|
128
|
+
}
|
|
139
129
|
|
|
140
130
|
// ── Context helpers ───────────────────────────────────────
|
|
141
131
|
// Check disk store available once on startup
|
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.6",
|
|
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",
|