node-red-contrib-dmx-for-ha 0.3.0 → 0.3.5

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.
@@ -284,7 +284,7 @@
284
284
  Debug output to NR debug tab
285
285
  </label>
286
286
  <div style="margin-left:106px; margin-top:4px; color:#e74c3c; font-size:0.8em;">
287
- ⚠ Disable in production — logs every MQTT publish
287
+ ⚠ Disable in production — logs every MQTT publish. Auto-disables after 12hrs.
288
288
  </div>
289
289
  </div>
290
290
 
@@ -37,14 +37,32 @@ module.exports = function (RED) {
37
37
  }, 1500);
38
38
  }
39
39
 
40
- // Track if already discovered to prevent double-fire
40
+ // Track discovery state prevents double-fire and allows reconnect re-discovery
41
41
  let _discovered = false;
42
+ let _lastDiscoveryTime = 0;
43
+ const _DISCOVERY_COOLDOWN_MS = 5000; // 5 second cooldown between discoveries
44
+
42
45
  function _tryDiscover() {
43
46
  if (_discovered) return;
47
+ const now = Date.now();
48
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) return;
44
49
  _discovered = true;
50
+ _lastDiscoveryTime = now;
45
51
  autoDiscover();
46
52
  }
47
53
 
54
+ function _manualDiscover() {
55
+ // Manual trigger (canvas button or input msg) — bypass _discovered flag
56
+ // but still respect cooldown to prevent accidental rapid-fire
57
+ const now = Date.now();
58
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
59
+ node.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
60
+ return;
61
+ }
62
+ _lastDiscoveryTime = now;
63
+ handleDeviceAdd();
64
+ }
65
+
48
66
  // Auto-discover if broker already connected on deploy
49
67
  if (broker.connected) {
50
68
  _tryDiscover();
@@ -54,12 +72,15 @@ module.exports = function (RED) {
54
72
  _tryDiscover();
55
73
  });
56
74
  broker.on('close', function () {
75
+ _discovered = false; // Allow re-discovery on reconnect
57
76
  setStatus('red', 'ring', 'Broker disconnected');
58
77
  });
59
78
  broker.on('error', function (err) {
60
79
  node.warn('MQTT broker error: ' + (err.message || err));
61
80
  setStatus('red', 'dot', 'Broker error — check config');
62
81
  });
82
+
83
+
63
84
  // Fallback — if connect event already fired before listener registered
64
85
  setTimeout(function () {
65
86
  _tryDiscover();
@@ -86,14 +107,18 @@ module.exports = function (RED) {
86
107
  debugMode: config.debugMode === true,
87
108
  };
88
109
 
89
- const fixtureId = `S-${S.uid}${S.uidPostfix}`;
90
- const objectId = `s_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
91
- const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'binary_sensor', fixtureId);
92
- const uiBtnTopic = cfg.buildTopic(cfg.discoveryPrefix, 'button', fixtureId + '-BTN');
93
- const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
94
- const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
95
- const uiBtnCfgTopic = `${uiBtnTopic}/${cfg.configTopic}`;
96
- const uiBtnCmdTopic = `${uiBtnTopic}/${cfg.commandTopic}`;
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
+ }
97
122
 
98
123
  // ── Helpers ───────────────────────────────────────────────
99
124
  function pub(topic, payload, retain) {
@@ -104,7 +129,7 @@ module.exports = function (RED) {
104
129
  qos: cfg.qos,
105
130
  retain: retain !== undefined ? retain : cfg.retain,
106
131
  });
107
- if (S.debugMode) node.debug(`${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
132
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
108
133
  }
109
134
 
110
135
  function setStatus(fill, shape, text) {
@@ -190,7 +215,7 @@ module.exports = function (RED) {
190
215
 
191
216
  if (devReq) {
192
217
  switch (devReq) {
193
- case 'add': handleDeviceAdd(); break;
218
+ case 'add': _manualDiscover(); break;
194
219
  case 'remove': handleDeviceRemove(); break;
195
220
  default: node.warn(`${fixtureId} — unknown device.request: "${devReq}"`);
196
221
  }
@@ -312,7 +312,7 @@
312
312
  Debug output to NR debug tab
313
313
  </label>
314
314
  <div style="margin-left:106px; margin-top:4px; color:#e74c3c; font-size:0.8em;">
315
- ⚠ Disable in production — logs every MQTT publish
315
+ ⚠ Disable in production — logs every MQTT publish. Auto-disables after 12hrs.
316
316
  </div>
317
317
  </div>
318
318
 
@@ -40,14 +40,32 @@ module.exports = function (RED) {
40
40
  }, 1500);
41
41
  }
42
42
 
43
- // Track if already discovered to prevent double-fire
43
+ // Track discovery state prevents double-fire and allows reconnect re-discovery
44
44
  let _discovered = false;
45
+ let _lastDiscoveryTime = 0;
46
+ const _DISCOVERY_COOLDOWN_MS = 5000; // 5 second cooldown between discoveries
47
+
45
48
  function _tryDiscover() {
46
49
  if (_discovered) return;
50
+ const now = Date.now();
51
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) return;
47
52
  _discovered = true;
53
+ _lastDiscoveryTime = now;
48
54
  autoDiscover();
49
55
  }
50
56
 
57
+ function _manualDiscover() {
58
+ // Manual trigger (canvas button or input msg) — bypass _discovered flag
59
+ // but still respect cooldown to prevent accidental rapid-fire
60
+ const now = Date.now();
61
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
62
+ node.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
63
+ return;
64
+ }
65
+ _lastDiscoveryTime = now;
66
+ handleDeviceAdd();
67
+ }
68
+
51
69
  // Auto-discover if broker already connected on deploy
52
70
  if (broker.connected) {
53
71
  _tryDiscover();
@@ -57,12 +75,15 @@ module.exports = function (RED) {
57
75
  _tryDiscover();
58
76
  });
59
77
  broker.on('close', function () {
78
+ _discovered = false; // Allow re-discovery on reconnect
60
79
  setStatus('red', 'ring', 'Broker disconnected');
61
80
  });
62
81
  broker.on('error', function (err) {
63
82
  node.warn('MQTT broker error: ' + (err.message || err));
64
83
  setStatus('red', 'dot', 'Broker error — check config');
65
84
  });
85
+
86
+
66
87
  // Fallback — if connect event already fired before listener registered
67
88
  setTimeout(function () {
68
89
  _tryDiscover();
@@ -93,12 +114,18 @@ module.exports = function (RED) {
93
114
  diskDelay: cfg.diskDelay,
94
115
  };
95
116
 
96
- const groupId = `LG-${S.uid}${S.uidPostfix}`;
97
- const objectId = `lg_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
98
- const groupTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', groupId);
99
- const cfgTopic = `${groupTopic}/${cfg.configTopic}`;
100
- const statTopic = `${groupTopic}/${cfg.stateTopic}`;
101
- const cmdTopic = `${groupTopic}/${cfg.commandTopic}`;
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
+ }
102
129
 
103
130
  // ── Context helpers ───────────────────────────────────────
104
131
  // Check disk store available once on startup
@@ -136,7 +163,7 @@ module.exports = function (RED) {
136
163
  qos: cfg.qos,
137
164
  retain: retain !== undefined ? retain : cfg.retain,
138
165
  });
139
- if (S.debugMode) node.debug(`${groupId} → ${topic} ${strPayload.substring(0, 120)}`);
166
+ if (S.debugMode) node.warn(`[DEBUG] ${groupId} → ${topic} ${strPayload.substring(0, 120)}`);
140
167
  }
141
168
 
142
169
  function pubState(payload) {
@@ -303,7 +330,7 @@ module.exports = function (RED) {
303
330
 
304
331
  if (devReq) {
305
332
  switch (devReq) {
306
- case 'add': handleDeviceAdd(null); break;
333
+ case 'add': _manualDiscover(); break;
307
334
  case 'remove': handleDeviceRemove(null); break;
308
335
  default: node.warn(`${groupId} — unknown device.request: "${devReq}"`);
309
336
  }
@@ -548,7 +548,7 @@
548
548
  Debug output to NR debug tab
549
549
  </label>
550
550
  <div style="margin-left:106px; margin-top:4px; color:#e74c3c; font-size:0.8em;">
551
- ⚠ Disable in production — logs every MQTT publish
551
+ ⚠ Disable in production — logs every MQTT publish. Auto-disables after 12hrs.
552
552
  </div>
553
553
  </div>
554
554
 
@@ -47,14 +47,32 @@ module.exports = function (RED) {
47
47
  }, 1500);
48
48
  }
49
49
 
50
- // Track if already discovered to prevent double-fire
50
+ // Track discovery state prevents double-fire and allows reconnect re-discovery
51
51
  let _discovered = false;
52
+ let _lastDiscoveryTime = 0;
53
+ const _DISCOVERY_COOLDOWN_MS = 5000; // 5 second cooldown between discoveries
54
+
52
55
  function _tryDiscover() {
53
56
  if (_discovered) return;
57
+ const now = Date.now();
58
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) return;
54
59
  _discovered = true;
60
+ _lastDiscoveryTime = now;
55
61
  autoDiscover();
56
62
  }
57
63
 
64
+ function _manualDiscover() {
65
+ // Manual trigger (canvas button or input msg) — bypass _discovered flag
66
+ // but still respect cooldown to prevent accidental rapid-fire
67
+ const now = Date.now();
68
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
69
+ node.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
70
+ return;
71
+ }
72
+ _lastDiscoveryTime = now;
73
+ handleDeviceAdd();
74
+ }
75
+
58
76
  // Auto-discover if broker already connected on deploy
59
77
  if (broker.connected) {
60
78
  _tryDiscover();
@@ -64,12 +82,15 @@ module.exports = function (RED) {
64
82
  _tryDiscover();
65
83
  });
66
84
  broker.on('close', function () {
85
+ _discovered = false; // Allow re-discovery on reconnect
67
86
  setStatus('red', 'ring', 'Broker disconnected');
68
87
  });
69
88
  broker.on('error', function (err) {
70
89
  node.warn('MQTT broker error: ' + (err.message || err));
71
90
  setStatus('red', 'dot', 'Broker error — check config');
72
91
  });
92
+
93
+
73
94
  // Fallback — if connect event already fired before listener registered
74
95
  setTimeout(function () {
75
96
  _tryDiscover();
@@ -117,13 +138,18 @@ module.exports = function (RED) {
117
138
  diskDelay: cfg.diskDelay,
118
139
  };
119
140
 
120
- const fixtureId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
121
- const objectId = `${S.uidPrefix}_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
122
- const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', fixtureId);
123
- const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
124
- const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
125
- const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
126
- const dmxTopic = cfg.buildTopic(cfg.siteId, cfg.zone, 'dmx', S.universe);
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
+ }
127
153
 
128
154
  // ── Context helpers ───────────────────────────────────────
129
155
  // Check disk store available once on startup
@@ -180,12 +206,12 @@ module.exports = function (RED) {
180
206
  if (payload === null) return;
181
207
  // Skip if value unchanged since last publish
182
208
  if (_lastSent[ch] === val) {
183
- if (S.debugMode) node.debug(`${fixtureId} → SKIP ch${ch}=${val} (unchanged)`);
209
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → SKIP ch${ch}=${val} (unchanged)`);
184
210
  return;
185
211
  }
186
212
  _lastSent[ch] = val;
187
213
  broker.publish({ topic: dmxTopic, payload, qos: cfg.qos, retain: false });
188
- if (S.debugMode) node.debug(`${fixtureId} → ${dmxTopic} "${payload}"`);
214
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → ${dmxTopic} "${payload}"`);
189
215
  });
190
216
  }
191
217
 
@@ -204,7 +230,7 @@ module.exports = function (RED) {
204
230
  qos: cfg.qos,
205
231
  retain: retain !== undefined ? retain : cfg.retain,
206
232
  });
207
- if (S.debugMode) node.debug(`${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
233
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
208
234
  }
209
235
 
210
236
  function pubState(payload) {
@@ -617,7 +643,7 @@ module.exports = function (RED) {
617
643
 
618
644
  if (devReq) {
619
645
  switch (devReq) {
620
- case 'add': handleDeviceAdd(); break;
646
+ case 'add': _manualDiscover(); break;
621
647
  case 'remove': handleDeviceRemove(); break;
622
648
  default: node.warn(`${fixtureId} — unknown device.request: "${devReq}"`);
623
649
  }
@@ -289,7 +289,7 @@
289
289
  Debug output to NR debug tab
290
290
  </label>
291
291
  <div style="margin-left:106px; margin-top:4px; color:#e74c3c; font-size:0.8em;">
292
- ⚠ Disable in production — logs every MQTT publish
292
+ ⚠ Disable in production — logs every MQTT publish. Auto-disables after 12hrs.
293
293
  </div>
294
294
  </div>
295
295
 
@@ -37,14 +37,32 @@ module.exports = function (RED) {
37
37
  }, 1500);
38
38
  }
39
39
 
40
- // Track if already discovered to prevent double-fire
40
+ // Track discovery state prevents double-fire and allows reconnect re-discovery
41
41
  let _discovered = false;
42
+ let _lastDiscoveryTime = 0;
43
+ const _DISCOVERY_COOLDOWN_MS = 5000; // 5 second cooldown between discoveries
44
+
42
45
  function _tryDiscover() {
43
46
  if (_discovered) return;
47
+ const now = Date.now();
48
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) return;
44
49
  _discovered = true;
50
+ _lastDiscoveryTime = now;
45
51
  autoDiscover();
46
52
  }
47
53
 
54
+ function _manualDiscover() {
55
+ // Manual trigger (canvas button or input msg) — bypass _discovered flag
56
+ // but still respect cooldown to prevent accidental rapid-fire
57
+ const now = Date.now();
58
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
59
+ node.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
60
+ return;
61
+ }
62
+ _lastDiscoveryTime = now;
63
+ handleDeviceAdd();
64
+ }
65
+
48
66
  // Auto-discover if broker already connected on deploy
49
67
  if (broker.connected) {
50
68
  _tryDiscover();
@@ -54,12 +72,15 @@ module.exports = function (RED) {
54
72
  _tryDiscover();
55
73
  });
56
74
  broker.on('close', function () {
75
+ _discovered = false; // Allow re-discovery on reconnect
57
76
  setStatus('red', 'ring', 'Broker disconnected');
58
77
  });
59
78
  broker.on('error', function (err) {
60
79
  node.warn('MQTT broker error: ' + (err.message || err));
61
80
  setStatus('red', 'dot', 'Broker error — check config');
62
81
  });
82
+
83
+
63
84
  // Fallback — if connect event already fired before listener registered
64
85
  setTimeout(function () {
65
86
  _tryDiscover();
@@ -87,13 +108,18 @@ module.exports = function (RED) {
87
108
  debugMode: config.debugMode === true,
88
109
  };
89
110
 
90
- const fixtureId = `S-${S.uid}${S.uidPostfix}`;
91
- const objectId = `s_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
92
- const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'binary_sensor', fixtureId);
93
- const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
94
- const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
95
- const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
96
- const avtyTopic = `${fixtureTopic}/${cfg.availTopic}`;
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
+ }
97
123
 
98
124
  // ── Helpers ───────────────────────────────────────────────
99
125
  function pub(topic, payload, retain) {
@@ -104,7 +130,7 @@ module.exports = function (RED) {
104
130
  qos: cfg.qos,
105
131
  retain: retain !== undefined ? retain : cfg.retain,
106
132
  });
107
- if (S.debugMode) node.debug(`${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
133
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
108
134
  }
109
135
 
110
136
  function setStatus(fill, shape, text) {
@@ -285,7 +285,7 @@
285
285
  Debug output to NR debug tab
286
286
  </label>
287
287
  <div style="margin-left:106px; margin-top:4px; color:#e74c3c; font-size:0.8em;">
288
- ⚠ Disable in production — logs every MQTT publish
288
+ ⚠ Disable in production — logs every MQTT publish. Auto-disables after 12hrs.
289
289
  </div>
290
290
  </div>
291
291
 
@@ -40,14 +40,32 @@ module.exports = function (RED) {
40
40
  }, 1500);
41
41
  }
42
42
 
43
- // Track if already discovered to prevent double-fire
43
+ // Track discovery state prevents double-fire and allows reconnect re-discovery
44
44
  let _discovered = false;
45
+ let _lastDiscoveryTime = 0;
46
+ const _DISCOVERY_COOLDOWN_MS = 5000; // 5 second cooldown between discoveries
47
+
45
48
  function _tryDiscover() {
46
49
  if (_discovered) return;
50
+ const now = Date.now();
51
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) return;
47
52
  _discovered = true;
53
+ _lastDiscoveryTime = now;
48
54
  autoDiscover();
49
55
  }
50
56
 
57
+ function _manualDiscover() {
58
+ // Manual trigger (canvas button or input msg) — bypass _discovered flag
59
+ // but still respect cooldown to prevent accidental rapid-fire
60
+ const now = Date.now();
61
+ if (now - _lastDiscoveryTime < _DISCOVERY_COOLDOWN_MS) {
62
+ node.warn('Discovery cooldown active — please wait 5 seconds between manual triggers');
63
+ return;
64
+ }
65
+ _lastDiscoveryTime = now;
66
+ handleDeviceAdd();
67
+ }
68
+
51
69
  // Auto-discover if broker already connected on deploy
52
70
  if (broker.connected) {
53
71
  _tryDiscover();
@@ -57,12 +75,15 @@ module.exports = function (RED) {
57
75
  _tryDiscover();
58
76
  });
59
77
  broker.on('close', function () {
78
+ _discovered = false; // Allow re-discovery on reconnect
60
79
  setStatus('red', 'ring', 'Broker disconnected');
61
80
  });
62
81
  broker.on('error', function (err) {
63
82
  node.warn('MQTT broker error: ' + (err.message || err));
64
83
  setStatus('red', 'dot', 'Broker error — check config');
65
84
  });
85
+
86
+
66
87
  // Fallback — if connect event already fired before listener registered
67
88
  setTimeout(function () {
68
89
  _tryDiscover();
@@ -93,13 +114,18 @@ module.exports = function (RED) {
93
114
  diskDelay: cfg.diskDelay,
94
115
  };
95
116
 
96
- const fixtureId = `${S.uidPrefix}-${S.uid}${S.uidPostfix}`;
97
- const objectId = `${S.uidPrefix}_${S.uid}${S.uidPostfix}`.toLowerCase().replace(/[^a-z0-9_]/g, '_');
98
- const fixtureTopic = cfg.buildTopic(cfg.discoveryPrefix, 'light', fixtureId);
99
- const cfgTopic = `${fixtureTopic}/${cfg.configTopic}`;
100
- const statTopic = `${fixtureTopic}/${cfg.stateTopic}`;
101
- const cmdTopic = `${fixtureTopic}/${cfg.commandTopic}`;
102
- const relayTopic = cfg.buildTopic(cfg.siteId, cfg.zone, S.controllerNum, S.mqttSegment, S.relayNum);
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
+ }
103
129
 
104
130
  // ── Context helpers ───────────────────────────────────────
105
131
  // Check disk store available once on startup
@@ -140,7 +166,7 @@ module.exports = function (RED) {
140
166
  qos: cfg.qos,
141
167
  retain: retain !== undefined ? retain : cfg.retain,
142
168
  });
143
- if (S.debugMode) node.debug(`${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
169
+ if (S.debugMode) node.warn(`[DEBUG] ${fixtureId} → ${topic} ${strPayload.substring(0, 120)}`);
144
170
  }
145
171
 
146
172
  function pubRelay(value) {
@@ -325,7 +351,7 @@ module.exports = function (RED) {
325
351
 
326
352
  if (devReq) {
327
353
  switch (devReq) {
328
- case 'add': handleDeviceAdd(); break;
354
+ case 'add': _manualDiscover(); break;
329
355
  case 'remove': handleDeviceRemove(); break;
330
356
  default: node.warn(`${fixtureId} — unknown device.request: "${devReq}"`);
331
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-dmx-for-ha",
3
- "version": "0.3.0",
3
+ "version": "0.3.5",
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",