node-red-contrib-dmx-for-ha 0.3.5 → 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-dmx.js +49 -1
- 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-dmx.js
CHANGED
|
@@ -243,9 +243,11 @@ module.exports = function (RED) {
|
|
|
243
243
|
|
|
244
244
|
// ── Effects ───────────────────────────────────────────────
|
|
245
245
|
let effectTimer = null;
|
|
246
|
+
let jitterTimer = null;
|
|
246
247
|
let preEffectState = null;
|
|
247
248
|
|
|
248
249
|
function stopEffect() {
|
|
250
|
+
if (jitterTimer) { clearTimeout(jitterTimer); jitterTimer = null; }
|
|
249
251
|
if (effectTimer) {
|
|
250
252
|
clearInterval(effectTimer); clearTimeout(effectTimer);
|
|
251
253
|
effectTimer = null;
|
|
@@ -329,7 +331,7 @@ module.exports = function (RED) {
|
|
|
329
331
|
// Prevents thundering herd when many nodes start transitions simultaneously
|
|
330
332
|
const jitter = Math.random() * intervalMs;
|
|
331
333
|
// Fix 3: Jitter delay spreads load across event loop
|
|
332
|
-
setTimeout(function () {
|
|
334
|
+
jitterTimer = setTimeout(function () {
|
|
333
335
|
effectTimer = setInterval(function () {
|
|
334
336
|
tick++;
|
|
335
337
|
const progress = Math.min(1, tick / totalTicks);
|
|
@@ -521,6 +523,48 @@ module.exports = function (RED) {
|
|
|
521
523
|
return [[vv,t,p],[q,vv,p],[p,vv,t],[p,q,vv],[t,p,vv],[vv,p,q]][i];
|
|
522
524
|
}
|
|
523
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
|
+
|
|
524
568
|
// ── Device add ────────────────────────────────────────────
|
|
525
569
|
function handleDeviceAdd() {
|
|
526
570
|
if (ctxGet('state') === undefined && ctxGet('state', 'disk_values') === undefined) {
|
|
@@ -565,6 +609,8 @@ module.exports = function (RED) {
|
|
|
565
609
|
};
|
|
566
610
|
|
|
567
611
|
pub(cfgTopic, discovery, true);
|
|
612
|
+
// Check for DMX channel conflicts before discovery
|
|
613
|
+
if (!checkChannelConflicts()) return;
|
|
568
614
|
resetLastSent(); // Force full state re-publish after discovery
|
|
569
615
|
|
|
570
616
|
broker.subscribe(cmdTopic, cfg.qos, function (topic, rawPayload) {
|
|
@@ -607,6 +653,7 @@ module.exports = function (RED) {
|
|
|
607
653
|
|
|
608
654
|
// ── Device remove ─────────────────────────────────────────
|
|
609
655
|
function handleDeviceRemove() {
|
|
656
|
+
clearChannelRegistry();
|
|
610
657
|
stopEffect();
|
|
611
658
|
if (diskTimer) { clearTimeout(diskTimer); diskTimer = null; }
|
|
612
659
|
['state','brightness','red','green','blue','white','warmWhite'].forEach(function (k) {
|
|
@@ -656,6 +703,7 @@ module.exports = function (RED) {
|
|
|
656
703
|
|
|
657
704
|
// ── Cleanup ───────────────────────────────────────────────
|
|
658
705
|
node.on('close', function (done) {
|
|
706
|
+
clearChannelRegistry();
|
|
659
707
|
stopEffect();
|
|
660
708
|
if (diskTimer) clearTimeout(diskTimer);
|
|
661
709
|
broker.unsubscribe(cmdTopic, node.id);
|
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",
|