node-red-contrib-dmx-for-ha 0.1.2 → 0.1.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/nodes/ha-mqtt-button.html +10 -0
- package/nodes/ha-mqtt-button.js +33 -12
- package/nodes/ha-mqtt-config.html +3 -3
- package/nodes/ha-mqtt-dmx-group.html +10 -0
- package/nodes/ha-mqtt-dmx-group.js +42 -14
- package/nodes/ha-mqtt-dmx.html +18 -8
- package/nodes/ha-mqtt-dmx.js +42 -14
- package/nodes/ha-mqtt-pir.html +10 -0
- package/nodes/ha-mqtt-pir.js +33 -12
- package/nodes/ha-mqtt-relay.html +10 -0
- package/nodes/ha-mqtt-relay.js +42 -14
- package/package.json +1 -1
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
16
|
+
button: {
|
|
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
|
+
});
|
|
24
|
+
}
|
|
25
|
+
},
|
|
16
26
|
paletteLabel: 'Button',
|
|
17
27
|
|
|
18
28
|
defaults: {
|
package/nodes/ha-mqtt-button.js
CHANGED
|
@@ -19,21 +19,28 @@ module.exports = function (RED) {
|
|
|
19
19
|
|
|
20
20
|
broker.register(node);
|
|
21
21
|
|
|
22
|
-
// ──
|
|
23
|
-
|
|
22
|
+
// ── Auto-discovery based on Discovery Mode ────────────────────
|
|
23
|
+
const discoveryMode = config.discoveryMode || 'enabled';
|
|
24
|
+
|
|
25
|
+
function autoDiscover() {
|
|
26
|
+
if (discoveryMode === 'disabled') {
|
|
27
|
+
setStatus('grey', 'ring', 'Disabled — not discovered');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Small delay to allow broker connection to stabilise
|
|
31
|
+
setTimeout(function () {
|
|
32
|
+
handleDeviceAdd();
|
|
33
|
+
}, 2000);
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
broker.on('connect', function () {
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
broker.on('close', function () {
|
|
28
|
-
setStatus('red', 'ring', 'Broker disconnected');
|
|
29
|
-
});
|
|
30
|
-
broker.on('error', function (err) {
|
|
31
|
-
node.warn('MQTT broker error: ' + err.message);
|
|
32
|
-
setStatus('red', 'dot', 'Broker error — check config');
|
|
37
|
+
autoDiscover();
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
37
44
|
|
|
38
45
|
// ── Node settings ─────────────────────────────────────────
|
|
39
46
|
const S = {
|
|
@@ -170,5 +177,19 @@ module.exports = function (RED) {
|
|
|
170
177
|
});
|
|
171
178
|
}
|
|
172
179
|
|
|
180
|
+
|
|
181
|
+
// ── HTTP endpoints for canvas buttons — registered once at module level ──
|
|
182
|
+
RED.httpAdmin.post('/ha-mqtt-button/:id/add', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
|
|
183
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
184
|
+
if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
|
|
185
|
+
else res.sendStatus(404);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
RED.httpAdmin.post('/ha-mqtt-button/:id/remove', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
|
|
189
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
190
|
+
if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
|
|
191
|
+
else res.sendStatus(404);
|
|
192
|
+
});
|
|
193
|
+
|
|
173
194
|
RED.nodes.registerType('ha-mqtt-button', HaMqttButtonNode);
|
|
174
195
|
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
RED.nodes.registerType('ha-mqtt-config', {
|
|
9
9
|
category: 'config',
|
|
10
10
|
defaults: {
|
|
11
|
-
|
|
11
|
+
name: { value: '', required: true },
|
|
12
12
|
siteId: { value: '', required: true },
|
|
13
13
|
zone: { value: '', required: true },
|
|
14
14
|
broker: { value: '', type: 'mqtt-broker', required: true },
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
|
|
44
44
|
<!-- NAME -->
|
|
45
45
|
<div class="form-row">
|
|
46
|
-
<label for="node-config-input-
|
|
46
|
+
<label for="node-config-input-name">
|
|
47
47
|
<i class="fa fa-tag"></i> Config Label
|
|
48
48
|
</label>
|
|
49
|
-
<input type="text" id="node-config-input-
|
|
49
|
+
<input type="text" id="node-config-input-name"
|
|
50
50
|
placeholder="e.g. Master Zone, Guest Wing" />
|
|
51
51
|
<div style="margin-left:106px; margin-top:4px; color:#999; font-size:0.85em;">
|
|
52
52
|
Give this config a friendly label so you can find it in the node dropdowns
|
|
@@ -14,6 +14,16 @@
|
|
|
14
14
|
outputs: 1,
|
|
15
15
|
outputLabels: ['Link'],
|
|
16
16
|
inputLabels: ['Input'],
|
|
17
|
+
button: {
|
|
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
|
+
});
|
|
25
|
+
}
|
|
26
|
+
},
|
|
17
27
|
paletteLabel: 'DMX Group',
|
|
18
28
|
|
|
19
29
|
defaults: {
|
|
@@ -22,21 +22,28 @@ module.exports = function (RED) {
|
|
|
22
22
|
|
|
23
23
|
broker.register(node);
|
|
24
24
|
|
|
25
|
-
// ──
|
|
26
|
-
|
|
25
|
+
// ── Auto-discovery based on Discovery Mode ────────────────────
|
|
26
|
+
const discoveryMode = config.discoveryMode || 'enabled';
|
|
27
|
+
|
|
28
|
+
function autoDiscover() {
|
|
29
|
+
if (discoveryMode === 'disabled') {
|
|
30
|
+
setStatus('grey', 'ring', 'Disabled — not discovered');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Small delay to allow broker connection to stabilise
|
|
34
|
+
setTimeout(function () {
|
|
35
|
+
handleDeviceAdd();
|
|
36
|
+
}, 2000);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
broker.on('connect', function () {
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
broker.on('close', function () {
|
|
31
|
-
setStatus('red', 'ring', 'Broker disconnected');
|
|
32
|
-
});
|
|
33
|
-
broker.on('error', function (err) {
|
|
34
|
-
node.warn('MQTT broker error: ' + err.message);
|
|
35
|
-
setStatus('red', 'dot', 'Broker error — check config');
|
|
40
|
+
autoDiscover();
|
|
36
41
|
});
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
40
47
|
|
|
41
48
|
// ── Node settings ─────────────────────────────────────────
|
|
42
49
|
const S = {
|
|
@@ -66,12 +73,19 @@ module.exports = function (RED) {
|
|
|
66
73
|
const cmdTopic = `${groupTopic}/${cfg.commandTopic}`;
|
|
67
74
|
|
|
68
75
|
// ── Context helpers ───────────────────────────────────────
|
|
76
|
+
// Check disk store available once on startup
|
|
77
|
+
let diskAvailable = false;
|
|
78
|
+
try { node.context().get('__test', 'disk'); diskAvailable = true; }
|
|
79
|
+
catch(e) { diskAvailable = false; }
|
|
80
|
+
|
|
69
81
|
function ctxGet(key, store) {
|
|
70
|
-
|
|
82
|
+
if (store === 'disk' && !diskAvailable) return node.context().get(key);
|
|
83
|
+
try { return node.context().get(key, store || undefined); }
|
|
71
84
|
catch(e) { return node.context().get(key); }
|
|
72
85
|
}
|
|
73
86
|
function ctxSet(key, val, store) {
|
|
74
|
-
|
|
87
|
+
if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
|
|
88
|
+
try { node.context().set(key, val, store || undefined); }
|
|
75
89
|
catch(e) { node.context().set(key, val); }
|
|
76
90
|
}
|
|
77
91
|
function recall(ramKey, diskKey, fallback) {
|
|
@@ -277,5 +291,19 @@ module.exports = function (RED) {
|
|
|
277
291
|
});
|
|
278
292
|
}
|
|
279
293
|
|
|
294
|
+
|
|
295
|
+
// ── HTTP endpoints for canvas buttons — registered once at module level ──
|
|
296
|
+
RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/add', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
|
|
297
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
298
|
+
if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
|
|
299
|
+
else res.sendStatus(404);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
RED.httpAdmin.post('/ha-mqtt-dmx-group/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx-group.write'), function(req, res) {
|
|
303
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
304
|
+
if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
|
|
305
|
+
else res.sendStatus(404);
|
|
306
|
+
});
|
|
307
|
+
|
|
280
308
|
RED.nodes.registerType('ha-mqtt-dmx-group', HaMqttDmxGroupNode);
|
|
281
309
|
};
|
package/nodes/ha-mqtt-dmx.html
CHANGED
|
@@ -122,6 +122,16 @@
|
|
|
122
122
|
icon: 'font-awesome/fa-lightbulb-o',
|
|
123
123
|
inputs: 1,
|
|
124
124
|
outputs: 0,
|
|
125
|
+
button: {
|
|
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
|
+
});
|
|
133
|
+
}
|
|
134
|
+
},
|
|
125
135
|
paletteLabel: 'DMX',
|
|
126
136
|
|
|
127
137
|
defaults: {
|
|
@@ -217,14 +227,6 @@
|
|
|
217
227
|
placeholder="Optional — defaults to fixture ID e.g. L-992-A" />
|
|
218
228
|
</div>
|
|
219
229
|
|
|
220
|
-
<!-- CONFIG -->
|
|
221
|
-
<div class="form-row">
|
|
222
|
-
<label for="node-input-config">
|
|
223
|
-
<i class="fa fa-cog"></i> Config
|
|
224
|
-
</label>
|
|
225
|
-
<input type="text" id="node-input-config" />
|
|
226
|
-
</div>
|
|
227
|
-
|
|
228
230
|
<!-- Discovery Mode -->
|
|
229
231
|
<div class="form-row">
|
|
230
232
|
<label for="node-input-discoveryMode">
|
|
@@ -338,6 +340,14 @@
|
|
|
338
340
|
</select>
|
|
339
341
|
</div>
|
|
340
342
|
|
|
343
|
+
<!-- CONFIG -->
|
|
344
|
+
<div class="form-row">
|
|
345
|
+
<label for="node-input-config">
|
|
346
|
+
<i class="fa fa-cog"></i> Config
|
|
347
|
+
</label>
|
|
348
|
+
<input type="text" id="node-input-config" />
|
|
349
|
+
</div>
|
|
350
|
+
|
|
341
351
|
<!-- 5. Area -->
|
|
342
352
|
<div class="form-row">
|
|
343
353
|
<label for="node-input-area">
|
package/nodes/ha-mqtt-dmx.js
CHANGED
|
@@ -29,21 +29,28 @@ module.exports = function (RED) {
|
|
|
29
29
|
|
|
30
30
|
broker.register(node);
|
|
31
31
|
|
|
32
|
-
// ──
|
|
33
|
-
|
|
32
|
+
// ── Auto-discovery based on Discovery Mode ────────────────────
|
|
33
|
+
const discoveryMode = config.discoveryMode || 'enabled';
|
|
34
|
+
|
|
35
|
+
function autoDiscover() {
|
|
36
|
+
if (discoveryMode === 'disabled') {
|
|
37
|
+
setStatus('grey', 'ring', 'Disabled — not discovered');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Small delay to allow broker connection to stabilise
|
|
41
|
+
setTimeout(function () {
|
|
42
|
+
handleDeviceAdd();
|
|
43
|
+
}, 2000);
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
broker.on('connect', function () {
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
broker.on('close', function () {
|
|
38
|
-
setStatus('red', 'ring', 'Broker disconnected');
|
|
39
|
-
});
|
|
40
|
-
broker.on('error', function (err) {
|
|
41
|
-
node.warn('MQTT broker error: ' + err.message);
|
|
42
|
-
setStatus('red', 'dot', 'Broker error — check config');
|
|
47
|
+
autoDiscover();
|
|
43
48
|
});
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
47
54
|
|
|
48
55
|
// ── Node settings ─────────────────────────────────────────
|
|
49
56
|
const S = {
|
|
@@ -87,12 +94,19 @@ module.exports = function (RED) {
|
|
|
87
94
|
const dmxTopic = `${cfg.siteId}/${cfg.zone}/dmx/${S.universe}`;
|
|
88
95
|
|
|
89
96
|
// ── Context helpers ───────────────────────────────────────
|
|
97
|
+
// Check disk store available once on startup
|
|
98
|
+
let diskAvailable = false;
|
|
99
|
+
try { node.context().get('__test', 'disk'); diskAvailable = true; }
|
|
100
|
+
catch(e) { diskAvailable = false; }
|
|
101
|
+
|
|
90
102
|
function ctxGet(key, store) {
|
|
91
|
-
|
|
103
|
+
if (store === 'disk' && !diskAvailable) return node.context().get(key);
|
|
104
|
+
try { return node.context().get(key, store || undefined); }
|
|
92
105
|
catch(e) { return node.context().get(key); }
|
|
93
106
|
}
|
|
94
107
|
function ctxSet(key, val, store) {
|
|
95
|
-
|
|
108
|
+
if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
|
|
109
|
+
try { node.context().set(key, val, store || undefined); }
|
|
96
110
|
catch(e) { node.context().set(key, val); }
|
|
97
111
|
}
|
|
98
112
|
function recall(ramKey, diskKey, fallback) {
|
|
@@ -549,5 +563,19 @@ module.exports = function (RED) {
|
|
|
549
563
|
});
|
|
550
564
|
}
|
|
551
565
|
|
|
566
|
+
|
|
567
|
+
// ── HTTP endpoints for canvas buttons — registered once at module level ──
|
|
568
|
+
RED.httpAdmin.post('/ha-mqtt-dmx/:id/add', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
|
|
569
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
570
|
+
if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
|
|
571
|
+
else res.sendStatus(404);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
RED.httpAdmin.post('/ha-mqtt-dmx/:id/remove', RED.auth.needsPermission('ha-mqtt-dmx.write'), function(req, res) {
|
|
575
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
576
|
+
if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
|
|
577
|
+
else res.sendStatus(404);
|
|
578
|
+
});
|
|
579
|
+
|
|
552
580
|
RED.nodes.registerType('ha-mqtt-dmx', HaMqttDmxNode);
|
|
553
581
|
};
|
package/nodes/ha-mqtt-pir.html
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
16
|
+
button: {
|
|
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
|
+
});
|
|
24
|
+
}
|
|
25
|
+
},
|
|
16
26
|
paletteLabel: 'PIR',
|
|
17
27
|
|
|
18
28
|
defaults: {
|
package/nodes/ha-mqtt-pir.js
CHANGED
|
@@ -19,21 +19,28 @@ module.exports = function (RED) {
|
|
|
19
19
|
|
|
20
20
|
broker.register(node);
|
|
21
21
|
|
|
22
|
-
// ──
|
|
23
|
-
|
|
22
|
+
// ── Auto-discovery based on Discovery Mode ────────────────────
|
|
23
|
+
const discoveryMode = config.discoveryMode || 'enabled';
|
|
24
|
+
|
|
25
|
+
function autoDiscover() {
|
|
26
|
+
if (discoveryMode === 'disabled') {
|
|
27
|
+
setStatus('grey', 'ring', 'Disabled — not discovered');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Small delay to allow broker connection to stabilise
|
|
31
|
+
setTimeout(function () {
|
|
32
|
+
handleDeviceAdd();
|
|
33
|
+
}, 2000);
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
broker.on('connect', function () {
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
broker.on('close', function () {
|
|
28
|
-
setStatus('red', 'ring', 'Broker disconnected');
|
|
29
|
-
});
|
|
30
|
-
broker.on('error', function (err) {
|
|
31
|
-
node.warn('MQTT broker error: ' + err.message);
|
|
32
|
-
setStatus('red', 'dot', 'Broker error — check config');
|
|
37
|
+
autoDiscover();
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
37
44
|
|
|
38
45
|
// ── Node settings ─────────────────────────────────────────
|
|
39
46
|
const S = {
|
|
@@ -195,5 +202,19 @@ module.exports = function (RED) {
|
|
|
195
202
|
});
|
|
196
203
|
}
|
|
197
204
|
|
|
205
|
+
|
|
206
|
+
// ── HTTP endpoints for canvas buttons — registered once at module level ──
|
|
207
|
+
RED.httpAdmin.post('/ha-mqtt-pir/:id/add', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
|
|
208
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
209
|
+
if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
|
|
210
|
+
else res.sendStatus(404);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
RED.httpAdmin.post('/ha-mqtt-pir/:id/remove', RED.auth.needsPermission('ha-mqtt-pir.write'), function(req, res) {
|
|
214
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
215
|
+
if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
|
|
216
|
+
else res.sendStatus(404);
|
|
217
|
+
});
|
|
218
|
+
|
|
198
219
|
RED.nodes.registerType('ha-mqtt-pir', HaMqttPirNode);
|
|
199
220
|
};
|
package/nodes/ha-mqtt-relay.html
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
16
|
+
button: {
|
|
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
|
+
});
|
|
24
|
+
}
|
|
25
|
+
},
|
|
16
26
|
paletteLabel: 'Relay',
|
|
17
27
|
|
|
18
28
|
defaults: {
|
package/nodes/ha-mqtt-relay.js
CHANGED
|
@@ -22,21 +22,28 @@ module.exports = function (RED) {
|
|
|
22
22
|
|
|
23
23
|
broker.register(node);
|
|
24
24
|
|
|
25
|
-
// ──
|
|
26
|
-
|
|
25
|
+
// ── Auto-discovery based on Discovery Mode ────────────────────
|
|
26
|
+
const discoveryMode = config.discoveryMode || 'enabled';
|
|
27
|
+
|
|
28
|
+
function autoDiscover() {
|
|
29
|
+
if (discoveryMode === 'disabled') {
|
|
30
|
+
setStatus('grey', 'ring', 'Disabled — not discovered');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Small delay to allow broker connection to stabilise
|
|
34
|
+
setTimeout(function () {
|
|
35
|
+
handleDeviceAdd();
|
|
36
|
+
}, 2000);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
broker.on('connect', function () {
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
broker.on('close', function () {
|
|
31
|
-
setStatus('red', 'ring', 'Broker disconnected');
|
|
32
|
-
});
|
|
33
|
-
broker.on('error', function (err) {
|
|
34
|
-
node.warn('MQTT broker error: ' + err.message);
|
|
35
|
-
setStatus('red', 'dot', 'Broker error — check config');
|
|
40
|
+
autoDiscover();
|
|
36
41
|
});
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
40
47
|
|
|
41
48
|
// ── Node settings ─────────────────────────────────────────
|
|
42
49
|
const S = {
|
|
@@ -67,12 +74,19 @@ module.exports = function (RED) {
|
|
|
67
74
|
const relayTopic = `${cfg.siteId}/${cfg.zone}/${S.controllerNum}/${S.mqttSegment}/${S.relayNum}`;
|
|
68
75
|
|
|
69
76
|
// ── Context helpers ───────────────────────────────────────
|
|
77
|
+
// Check disk store available once on startup
|
|
78
|
+
let diskAvailable = false;
|
|
79
|
+
try { node.context().get('__test', 'disk'); diskAvailable = true; }
|
|
80
|
+
catch(e) { diskAvailable = false; }
|
|
81
|
+
|
|
70
82
|
function ctxGet(key, store) {
|
|
71
|
-
|
|
83
|
+
if (store === 'disk' && !diskAvailable) return node.context().get(key);
|
|
84
|
+
try { return node.context().get(key, store || undefined); }
|
|
72
85
|
catch(e) { return node.context().get(key); }
|
|
73
86
|
}
|
|
74
87
|
function ctxSet(key, val, store) {
|
|
75
|
-
|
|
88
|
+
if (store === 'disk' && !diskAvailable) { node.context().set(key, val); return; }
|
|
89
|
+
try { node.context().set(key, val, store || undefined); }
|
|
76
90
|
catch(e) { node.context().set(key, val); }
|
|
77
91
|
}
|
|
78
92
|
function recall(ramKey, diskKey, fallback) {
|
|
@@ -301,5 +315,19 @@ module.exports = function (RED) {
|
|
|
301
315
|
});
|
|
302
316
|
}
|
|
303
317
|
|
|
318
|
+
|
|
319
|
+
// ── HTTP endpoints for canvas buttons — registered once at module level ──
|
|
320
|
+
RED.httpAdmin.post('/ha-mqtt-relay/:id/add', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
|
|
321
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
322
|
+
if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
|
|
323
|
+
else res.sendStatus(404);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
RED.httpAdmin.post('/ha-mqtt-relay/:id/remove', RED.auth.needsPermission('ha-mqtt-relay.write'), function(req, res) {
|
|
327
|
+
const n = RED.nodes.getNode(req.params.id);
|
|
328
|
+
if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
|
|
329
|
+
else res.sendStatus(404);
|
|
330
|
+
});
|
|
331
|
+
|
|
304
332
|
RED.nodes.registerType('ha-mqtt-relay', HaMqttRelayNode);
|
|
305
333
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-dmx-for-ha",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.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",
|