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.
@@ -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.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
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.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
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
- setStatus('green', 'dot', `${groupId} → ${_lbl}`);
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
- forwardToChildren({ state, brightness, color }, null);
304
- setStatus('yellow', 'ring', `${groupId} ready`);
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
- setStatus('green', 'dot', `${groupId} → cascade:${msg.payload.state}`);
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
- setStatus('green', 'dot', `${groupId} → ${msg.payload.state}`);
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
  }
@@ -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.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
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) { clearTimeout(diskTimer); diskTimer = null; }
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) { clearTimeout(diskTimer); diskTimer = null; }
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
  });
@@ -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.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
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.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
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.8",
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",