node-red-contrib-dmx-for-ha 0.1.4 → 0.1.8

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.
@@ -14,28 +14,13 @@
14
14
  outputs: 0,
15
15
  inputLabels: ['Input'],
16
16
  button: {
17
- cancel: {
18
- label: 'Remove',
19
- onclick: function() {
20
- var self = this;
21
- $.ajax({
22
- url: 'ha-mqtt-button/' + this.id + '/remove',
23
- type: 'POST',
24
- success: function() { RED.notify('device:remove sent', 'success'); },
25
- error: function() { RED.notify('Error sending remove', 'error'); }
26
- });
27
- }
28
- },
29
- confirm: {
30
- label: 'Add',
31
- onclick: function() {
32
- $.ajax({
33
- url: 'ha-mqtt-button/' + this.id + '/add',
34
- type: 'POST',
35
- success: function() { RED.notify('device:add sent', 'success'); },
36
- error: function() { RED.notify('Error sending add', 'error'); }
37
- });
38
- }
17
+ onclick: function() {
18
+ $.ajax({
19
+ url: 'ha-mqtt-button/' + this.id + '/add',
20
+ type: 'POST',
21
+ success: function() { RED.notify('device:add sent','success'); },
22
+ error: function() { RED.notify('Error check NR log','error'); }
23
+ });
39
24
  }
40
25
  },
41
26
  paletteLabel: 'Button',
@@ -232,6 +217,8 @@
232
217
  </span>
233
218
  </div>
234
219
 
220
+ <div class="form-row">
221
+
235
222
  <div class="form-row">
236
223
  <div class="form-row">
237
224
  <label for="node-input-config">
@@ -30,25 +30,18 @@ module.exports = function (RED) {
30
30
  // Small delay to allow broker connection to stabilise
31
31
  setTimeout(function () {
32
32
  handleDeviceAdd();
33
- }, 2000);
33
+ }, 1500);
34
34
  }
35
35
 
36
+ // Auto-discover if broker already connected on deploy
37
+ if (broker.connected) {
38
+ autoDiscover();
39
+ }
40
+ // Or wait for broker to connect
36
41
  broker.on('connect', function () {
37
42
  autoDiscover();
38
43
  });
39
44
 
40
- // ── HTTP endpoints for canvas buttons ─────────────────────────
41
- RED.httpAdmin.post('/ha-mqtt-button/:id/add', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
42
- const n = RED.nodes.getNode(req.params.id);
43
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
44
- else res.sendStatus(404);
45
- });
46
-
47
- RED.httpAdmin.post('/ha-mqtt-button/:id/remove', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
48
- const n = RED.nodes.getNode(req.params.id);
49
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
50
- else res.sendStatus(404);
51
- });
52
45
 
53
46
 
54
47
 
@@ -108,7 +101,7 @@ module.exports = function (RED) {
108
101
  name: `${S.buttonPosition} button ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
109
102
  stat_t: statTopic,
110
103
  off_delay: S.holdTime,
111
- enabled_by_default: cfg.enabledDefault,
104
+ enabled_by_default: (discoveryMode !== 'hidden'),
112
105
  icon: S.haIcon,
113
106
  device: {
114
107
  identifiers: `binary_sensor-${fixtureId}`,
@@ -130,7 +123,7 @@ module.exports = function (RED) {
130
123
  name: `${S.buttonPosition} (UI) ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
131
124
  cmd_t: uiBtnCmdTopic,
132
125
  payload_press: 'PRESS',
133
- enabled_by_default: cfg.enabledDefault,
126
+ enabled_by_default: (discoveryMode !== 'hidden'),
134
127
  icon: S.haIcon,
135
128
  device: { identifiers: `binary_sensor-${fixtureId}` },
136
129
  };
@@ -189,5 +182,19 @@ module.exports = function (RED) {
189
182
  });
190
183
  }
191
184
 
185
+
186
+ // ── HTTP endpoints for canvas buttons — registered once at module level ──
187
+ RED.httpAdmin.post('/ha-mqtt-button/:id/add', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
188
+ const n = RED.nodes.getNode(req.params.id);
189
+ if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
190
+ else res.sendStatus(404);
191
+ });
192
+
193
+ RED.httpAdmin.post('/ha-mqtt-button/:id/remove', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
194
+ const n = RED.nodes.getNode(req.params.id);
195
+ if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
196
+ else res.sendStatus(404);
197
+ });
198
+
192
199
  RED.nodes.registerType('ha-mqtt-button', HaMqttButtonNode);
193
200
  };
@@ -15,28 +15,13 @@
15
15
  outputLabels: ['Link'],
16
16
  inputLabels: ['Input'],
17
17
  button: {
18
- cancel: {
19
- label: 'Remove',
20
- onclick: function() {
21
- var self = this;
22
- $.ajax({
23
- url: 'ha-mqtt-dmx-group/' + this.id + '/remove',
24
- type: 'POST',
25
- success: function() { RED.notify('device:remove sent', 'success'); },
26
- error: function() { RED.notify('Error sending remove', 'error'); }
27
- });
28
- }
29
- },
30
- confirm: {
31
- label: 'Add',
32
- onclick: function() {
33
- $.ajax({
34
- url: 'ha-mqtt-dmx-group/' + this.id + '/add',
35
- type: 'POST',
36
- success: function() { RED.notify('device:add sent', 'success'); },
37
- error: function() { RED.notify('Error sending add', 'error'); }
38
- });
39
- }
18
+ onclick: function() {
19
+ $.ajax({
20
+ url: 'ha-mqtt-dmx-group/' + this.id + '/add',
21
+ type: 'POST',
22
+ success: function() { RED.notify('device:add sent','success'); },
23
+ error: function() { RED.notify('Error check NR log','error'); }
24
+ });
40
25
  }
41
26
  },
42
27
  paletteLabel: 'DMX Group',
@@ -178,14 +163,7 @@
178
163
  </div>
179
164
 
180
165
  <!-- CONFIG -->
181
- <div class="form-row">
182
- <label for="node-input-config">
183
- <i class="fa fa-cog"></i> Config
184
- </label>
185
- <input type="text" id="node-input-config" />
186
- </div>
187
-
188
- <!-- Discovery Mode -->
166
+ <!-- Discovery Mode -->
189
167
  <div class="form-row">
190
168
  <label for="node-input-discoveryMode">
191
169
  <i class="fa fa-toggle-on"></i> Discovery Mode
@@ -201,7 +179,6 @@
201
179
  </div>
202
180
  </div>
203
181
 
204
-
205
182
  <hr/>
206
183
 
207
184
  <!-- ── GROUP (* Required) ────────────────────────────────── -->
@@ -292,6 +269,7 @@
292
269
  </div>
293
270
 
294
271
  <!-- CONFIG -->
272
+ <!-- Area -->
295
273
  <div class="form-row">
296
274
  <label for="node-input-config">
297
275
  <i class="fa fa-cog"></i> Config
@@ -299,7 +277,6 @@
299
277
  <input type="text" id="node-input-config" />
300
278
  </div>
301
279
 
302
- <!-- Area -->
303
280
  <div class="form-row">
304
281
  <label for="node-input-area">
305
282
  <i class="fa fa-map-marker"></i> Area
@@ -33,25 +33,18 @@ module.exports = function (RED) {
33
33
  // Small delay to allow broker connection to stabilise
34
34
  setTimeout(function () {
35
35
  handleDeviceAdd();
36
- }, 2000);
36
+ }, 1500);
37
37
  }
38
38
 
39
+ // Auto-discover if broker already connected on deploy
40
+ if (broker.connected) {
41
+ autoDiscover();
42
+ }
43
+ // Or wait for broker to connect
39
44
  broker.on('connect', function () {
40
45
  autoDiscover();
41
46
  });
42
47
 
43
- // ── HTTP endpoints for canvas buttons ─────────────────────────
44
- RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/add', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
45
- const n = RED.nodes.getNode(req.params.id);
46
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
47
- else res.sendStatus(404);
48
- });
49
-
50
- RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
51
- const n = RED.nodes.getNode(req.params.id);
52
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
53
- else res.sendStatus(404);
54
- });
55
48
 
56
49
 
57
50
 
@@ -85,12 +78,19 @@ module.exports = function (RED) {
85
78
  const cmdTopic = `${groupTopic}/${cfg.commandTopic}`;
86
79
 
87
80
  // ── Context helpers ───────────────────────────────────────
81
+ // Check disk store available once on startup
82
+ let diskAvailable = false;
83
+ try { node.context().get('__test', 'disk'); diskAvailable = true; }
84
+ catch(e) { diskAvailable = false; }
85
+
88
86
  function ctxGet(key, store) {
89
- try { return node.context().get(key, store); }
87
+ if (store === 'disk' && !diskAvailable) return node.context().get(key);
88
+ try { return node.context().get(key, store || undefined); }
90
89
  catch(e) { return node.context().get(key); }
91
90
  }
92
91
  function ctxSet(key, val, store) {
93
- try { node.context().set(key, val, store); }
92
+ if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
93
+ try { node.context().set(key, val, store || undefined); }
94
94
  catch(e) { node.context().set(key, val); }
95
95
  }
96
96
  function recall(ramKey, diskKey, fallback) {
@@ -185,7 +185,7 @@ module.exports = function (RED) {
185
185
  schema: 'json',
186
186
  object_id: objectId,
187
187
  optimistic: false,
188
- enabled_by_default: cfg.enabledDefault,
188
+ enabled_by_default: (discoveryMode !== 'hidden'),
189
189
  icon: S.haIcon,
190
190
  supported_color_modes: [S.colorMode],
191
191
  brightness: true,
@@ -296,5 +296,19 @@ module.exports = function (RED) {
296
296
  });
297
297
  }
298
298
 
299
+
300
+ // ── HTTP endpoints for canvas buttons — registered once at module level ──
301
+ RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/add', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
302
+ const n = RED.nodes.getNode(req.params.id);
303
+ if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
304
+ else res.sendStatus(404);
305
+ });
306
+
307
+ RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
308
+ const n = RED.nodes.getNode(req.params.id);
309
+ if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
310
+ else res.sendStatus(404);
311
+ });
312
+
299
313
  RED.nodes.registerType('ha-mqtt-dmx-group', HaMqttDmxGroupNode);
300
314
  };
@@ -123,28 +123,13 @@
123
123
  inputs: 1,
124
124
  outputs: 0,
125
125
  button: {
126
- cancel: {
127
- label: 'Remove',
128
- onclick: function() {
129
- var self = this;
130
- $.ajax({
131
- url: 'ha-mqtt-dmx/' + this.id + '/remove',
132
- type: 'POST',
133
- success: function() { RED.notify('device:remove sent', 'success'); },
134
- error: function() { RED.notify('Error sending remove', 'error'); }
135
- });
136
- }
137
- },
138
- confirm: {
139
- label: 'Add',
140
- onclick: function() {
141
- $.ajax({
142
- url: 'ha-mqtt-dmx/' + this.id + '/add',
143
- type: 'POST',
144
- success: function() { RED.notify('device:add sent', 'success'); },
145
- error: function() { RED.notify('Error sending add', 'error'); }
146
- });
147
- }
126
+ onclick: function() {
127
+ $.ajax({
128
+ url: 'ha-mqtt-dmx/' + this.id + '/add',
129
+ type: 'POST',
130
+ success: function() { RED.notify('device:add sent','success'); },
131
+ error: function() { RED.notify('Error check NR log','error'); }
132
+ });
148
133
  }
149
134
  },
150
135
  paletteLabel: 'DMX',
@@ -40,25 +40,18 @@ module.exports = function (RED) {
40
40
  // Small delay to allow broker connection to stabilise
41
41
  setTimeout(function () {
42
42
  handleDeviceAdd();
43
- }, 2000);
43
+ }, 1500);
44
44
  }
45
45
 
46
+ // Auto-discover if broker already connected on deploy
47
+ if (broker.connected) {
48
+ autoDiscover();
49
+ }
50
+ // Or wait for broker to connect
46
51
  broker.on('connect', function () {
47
52
  autoDiscover();
48
53
  });
49
54
 
50
- // ── HTTP endpoints for canvas buttons ─────────────────────────
51
- RED.httpAdmin.post('/ha-mqtt-dmx/:id/add', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
52
- const n = RED.nodes.getNode(req.params.id);
53
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
54
- else res.sendStatus(404);
55
- });
56
-
57
- RED.httpAdmin.post('/ha-mqtt-dmx/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
58
- const n = RED.nodes.getNode(req.params.id);
59
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
60
- else res.sendStatus(404);
61
- });
62
55
 
63
56
 
64
57
 
@@ -106,12 +99,19 @@ module.exports = function (RED) {
106
99
  const dmxTopic = `${cfg.siteId}/${cfg.zone}/dmx/${S.universe}`;
107
100
 
108
101
  // ── Context helpers ───────────────────────────────────────
102
+ // Check disk store available once on startup
103
+ let diskAvailable = false;
104
+ try { node.context().get('__test', 'disk'); diskAvailable = true; }
105
+ catch(e) { diskAvailable = false; }
106
+
109
107
  function ctxGet(key, store) {
110
- try { return node.context().get(key, store); }
108
+ if (store === 'disk' && !diskAvailable) return node.context().get(key);
109
+ try { return node.context().get(key, store || undefined); }
111
110
  catch(e) { return node.context().get(key); }
112
111
  }
113
112
  function ctxSet(key, val, store) {
114
- try { node.context().set(key, val, store); }
113
+ if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
114
+ try { node.context().set(key, val, store || undefined); }
115
115
  catch(e) { node.context().set(key, val); }
116
116
  }
117
117
  function recall(ramKey, diskKey, fallback) {
@@ -443,7 +443,7 @@ module.exports = function (RED) {
443
443
  schema: 'json',
444
444
  object_id: objectId,
445
445
  optimistic: false,
446
- enabled_by_default: cfg.enabledDefault,
446
+ enabled_by_default: (discoveryMode !== 'hidden'),
447
447
  icon: S.haIcon,
448
448
  supported_color_modes: [S.colorMode],
449
449
  brightness: true,
@@ -568,5 +568,19 @@ module.exports = function (RED) {
568
568
  });
569
569
  }
570
570
 
571
+
572
+ // ── HTTP endpoints for canvas buttons — registered once at module level ──
573
+ RED.httpAdmin.post('/ha-mqtt-dmx/:id/add', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
574
+ const n = RED.nodes.getNode(req.params.id);
575
+ if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
576
+ else res.sendStatus(404);
577
+ });
578
+
579
+ RED.httpAdmin.post('/ha-mqtt-dmx/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
580
+ const n = RED.nodes.getNode(req.params.id);
581
+ if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
582
+ else res.sendStatus(404);
583
+ });
584
+
571
585
  RED.nodes.registerType('ha-mqtt-dmx', HaMqttDmxNode);
572
586
  };
@@ -14,28 +14,13 @@
14
14
  outputs: 0,
15
15
  inputLabels: ['Input'],
16
16
  button: {
17
- cancel: {
18
- label: 'Remove',
19
- onclick: function() {
20
- var self = this;
21
- $.ajax({
22
- url: 'ha-mqtt-pir/' + this.id + '/remove',
23
- type: 'POST',
24
- success: function() { RED.notify('device:remove sent', 'success'); },
25
- error: function() { RED.notify('Error sending remove', 'error'); }
26
- });
27
- }
28
- },
29
- confirm: {
30
- label: 'Add',
31
- onclick: function() {
32
- $.ajax({
33
- url: 'ha-mqtt-pir/' + this.id + '/add',
34
- type: 'POST',
35
- success: function() { RED.notify('device:add sent', 'success'); },
36
- error: function() { RED.notify('Error sending add', 'error'); }
37
- });
38
- }
17
+ onclick: function() {
18
+ $.ajax({
19
+ url: 'ha-mqtt-pir/' + this.id + '/add',
20
+ type: 'POST',
21
+ success: function() { RED.notify('device:add sent','success'); },
22
+ error: function() { RED.notify('Error check NR log','error'); }
23
+ });
39
24
  }
40
25
  },
41
26
  paletteLabel: 'PIR',
@@ -246,6 +231,8 @@
246
231
  </select>
247
232
  </div>
248
233
 
234
+ <div class="form-row">
235
+
249
236
  <div class="form-row">
250
237
  <div class="form-row">
251
238
  <label for="node-input-config">
@@ -30,25 +30,18 @@ module.exports = function (RED) {
30
30
  // Small delay to allow broker connection to stabilise
31
31
  setTimeout(function () {
32
32
  handleDeviceAdd();
33
- }, 2000);
33
+ }, 1500);
34
34
  }
35
35
 
36
+ // Auto-discover if broker already connected on deploy
37
+ if (broker.connected) {
38
+ autoDiscover();
39
+ }
40
+ // Or wait for broker to connect
36
41
  broker.on('connect', function () {
37
42
  autoDiscover();
38
43
  });
39
44
 
40
- // ── HTTP endpoints for canvas buttons ─────────────────────────
41
- RED.httpAdmin.post('/ha-mqtt-pir/:id/add', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
42
- const n = RED.nodes.getNode(req.params.id);
43
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
44
- else res.sendStatus(404);
45
- });
46
-
47
- RED.httpAdmin.post('/ha-mqtt-pir/:id/remove', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
48
- const n = RED.nodes.getNode(req.params.id);
49
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
50
- else res.sendStatus(404);
51
- });
52
45
 
53
46
 
54
47
 
@@ -149,7 +142,7 @@ module.exports = function (RED) {
149
142
  payload_not_available: 'offline',
150
143
  off_delay: S.holdTime,
151
144
  device_class: 'motion',
152
- enabled_by_default: cfg.enabledDefault,
145
+ enabled_by_default: (discoveryMode !== 'hidden'),
153
146
  icon: S.haIcon,
154
147
  device: {
155
148
  identifiers: `binary_sensor-${fixtureId}`,
@@ -214,5 +207,19 @@ module.exports = function (RED) {
214
207
  });
215
208
  }
216
209
 
210
+
211
+ // ── HTTP endpoints for canvas buttons — registered once at module level ──
212
+ RED.httpAdmin.post('/ha-mqtt-pir/:id/add', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
213
+ const n = RED.nodes.getNode(req.params.id);
214
+ if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
215
+ else res.sendStatus(404);
216
+ });
217
+
218
+ RED.httpAdmin.post('/ha-mqtt-pir/:id/remove', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
219
+ const n = RED.nodes.getNode(req.params.id);
220
+ if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
221
+ else res.sendStatus(404);
222
+ });
223
+
217
224
  RED.nodes.registerType('ha-mqtt-pir', HaMqttPirNode);
218
225
  };
@@ -14,28 +14,13 @@
14
14
  outputs: 0,
15
15
  inputLabels: ['Input'],
16
16
  button: {
17
- cancel: {
18
- label: 'Remove',
19
- onclick: function() {
20
- var self = this;
21
- $.ajax({
22
- url: 'ha-mqtt-relay/' + this.id + '/remove',
23
- type: 'POST',
24
- success: function() { RED.notify('device:remove sent', 'success'); },
25
- error: function() { RED.notify('Error sending remove', 'error'); }
26
- });
27
- }
28
- },
29
- confirm: {
30
- label: 'Add',
31
- onclick: function() {
32
- $.ajax({
33
- url: 'ha-mqtt-relay/' + this.id + '/add',
34
- type: 'POST',
35
- success: function() { RED.notify('device:add sent', 'success'); },
36
- error: function() { RED.notify('Error sending add', 'error'); }
37
- });
38
- }
17
+ onclick: function() {
18
+ $.ajax({
19
+ url: 'ha-mqtt-relay/' + this.id + '/add',
20
+ type: 'POST',
21
+ success: function() { RED.notify('device:add sent','success'); },
22
+ error: function() { RED.notify('Error check NR log','error'); }
23
+ });
39
24
  }
40
25
  },
41
26
  paletteLabel: 'Relay',
@@ -248,6 +233,8 @@
248
233
  </span>
249
234
  </div>
250
235
 
236
+ <div class="form-row">
237
+
251
238
  <div class="form-row">
252
239
  <div class="form-row">
253
240
  <label for="node-input-config">
@@ -33,25 +33,18 @@ module.exports = function (RED) {
33
33
  // Small delay to allow broker connection to stabilise
34
34
  setTimeout(function () {
35
35
  handleDeviceAdd();
36
- }, 2000);
36
+ }, 1500);
37
37
  }
38
38
 
39
+ // Auto-discover if broker already connected on deploy
40
+ if (broker.connected) {
41
+ autoDiscover();
42
+ }
43
+ // Or wait for broker to connect
39
44
  broker.on('connect', function () {
40
45
  autoDiscover();
41
46
  });
42
47
 
43
- // ── HTTP endpoints for canvas buttons ─────────────────────────
44
- RED.httpAdmin.post('/ha-mqtt-relay/:id/add', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
45
- const n = RED.nodes.getNode(req.params.id);
46
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
47
- else res.sendStatus(404);
48
- });
49
-
50
- RED.httpAdmin.post('/ha-mqtt-relay/:id/remove', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
51
- const n = RED.nodes.getNode(req.params.id);
52
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
53
- else res.sendStatus(404);
54
- });
55
48
 
56
49
 
57
50
 
@@ -86,12 +79,19 @@ module.exports = function (RED) {
86
79
  const relayTopic = `${cfg.siteId}/${cfg.zone}/${S.controllerNum}/${S.mqttSegment}/${S.relayNum}`;
87
80
 
88
81
  // ── Context helpers ───────────────────────────────────────
82
+ // Check disk store available once on startup
83
+ let diskAvailable = false;
84
+ try { node.context().get('__test', 'disk'); diskAvailable = true; }
85
+ catch(e) { diskAvailable = false; }
86
+
89
87
  function ctxGet(key, store) {
90
- try { return node.context().get(key, store); }
88
+ if (store === 'disk' && !diskAvailable) return node.context().get(key);
89
+ try { return node.context().get(key, store || undefined); }
91
90
  catch(e) { return node.context().get(key); }
92
91
  }
93
92
  function ctxSet(key, val, store) {
94
- try { node.context().set(key, val, store); }
93
+ if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
94
+ try { node.context().set(key, val, store || undefined); }
95
95
  catch(e) { node.context().set(key, val); }
96
96
  }
97
97
  function recall(ramKey, diskKey, fallback) {
@@ -216,7 +216,7 @@ module.exports = function (RED) {
216
216
  cmd_t: cmdTopic,
217
217
  stat_t: statTopic,
218
218
  optimistic: false,
219
- enabled_by_default: cfg.enabledDefault,
219
+ enabled_by_default: (discoveryMode !== 'hidden'),
220
220
  icon: S.haIcon,
221
221
  supported_color_modes: ['onoff'],
222
222
  brightness: false,
@@ -320,5 +320,19 @@ module.exports = function (RED) {
320
320
  });
321
321
  }
322
322
 
323
+
324
+ // ── HTTP endpoints for canvas buttons — registered once at module level ──
325
+ RED.httpAdmin.post('/ha-mqtt-relay/:id/add', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
326
+ const n = RED.nodes.getNode(req.params.id);
327
+ if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
328
+ else res.sendStatus(404);
329
+ });
330
+
331
+ RED.httpAdmin.post('/ha-mqtt-relay/:id/remove', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
332
+ const n = RED.nodes.getNode(req.params.id);
333
+ if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
334
+ else res.sendStatus(404);
335
+ });
336
+
323
337
  RED.nodes.registerType('ha-mqtt-relay', HaMqttRelayNode);
324
338
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-dmx-for-ha",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
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",