node-red-contrib-dmx-for-ha 0.1.2 → 0.1.4
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 +25 -0
- package/nodes/ha-mqtt-button.js +29 -10
- package/nodes/ha-mqtt-config.html +3 -3
- package/nodes/ha-mqtt-dmx-group.html +25 -0
- package/nodes/ha-mqtt-dmx-group.js +29 -10
- package/nodes/ha-mqtt-dmx.html +33 -8
- package/nodes/ha-mqtt-dmx.js +29 -10
- package/nodes/ha-mqtt-pir.html +25 -0
- package/nodes/ha-mqtt-pir.js +29 -10
- package/nodes/ha-mqtt-relay.html +25 -0
- package/nodes/ha-mqtt-relay.js +29 -10
- package/package.json +1 -1
|
@@ -13,6 +13,31 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
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
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
16
41
|
paletteLabel: 'Button',
|
|
17
42
|
|
|
18
43
|
defaults: {
|
package/nodes/ha-mqtt-button.js
CHANGED
|
@@ -19,21 +19,40 @@ 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
|
-
|
|
37
|
+
autoDiscover();
|
|
26
38
|
});
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
|
|
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);
|
|
29
45
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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);
|
|
33
51
|
});
|
|
34
52
|
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
37
56
|
|
|
38
57
|
// ── Node settings ─────────────────────────────────────────
|
|
39
58
|
const S = {
|
|
@@ -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,31 @@
|
|
|
14
14
|
outputs: 1,
|
|
15
15
|
outputLabels: ['Link'],
|
|
16
16
|
inputLabels: ['Input'],
|
|
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
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
17
42
|
paletteLabel: 'DMX Group',
|
|
18
43
|
|
|
19
44
|
defaults: {
|
|
@@ -22,21 +22,40 @@ 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
|
-
|
|
40
|
+
autoDiscover();
|
|
29
41
|
});
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
|
|
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);
|
|
32
48
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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);
|
|
36
54
|
});
|
|
37
55
|
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
40
59
|
|
|
41
60
|
// ── Node settings ─────────────────────────────────────────
|
|
42
61
|
const S = {
|
package/nodes/ha-mqtt-dmx.html
CHANGED
|
@@ -122,6 +122,31 @@
|
|
|
122
122
|
icon: 'font-awesome/fa-lightbulb-o',
|
|
123
123
|
inputs: 1,
|
|
124
124
|
outputs: 0,
|
|
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
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
125
150
|
paletteLabel: 'DMX',
|
|
126
151
|
|
|
127
152
|
defaults: {
|
|
@@ -217,14 +242,6 @@
|
|
|
217
242
|
placeholder="Optional — defaults to fixture ID e.g. L-992-A" />
|
|
218
243
|
</div>
|
|
219
244
|
|
|
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
245
|
<!-- Discovery Mode -->
|
|
229
246
|
<div class="form-row">
|
|
230
247
|
<label for="node-input-discoveryMode">
|
|
@@ -338,6 +355,14 @@
|
|
|
338
355
|
</select>
|
|
339
356
|
</div>
|
|
340
357
|
|
|
358
|
+
<!-- CONFIG -->
|
|
359
|
+
<div class="form-row">
|
|
360
|
+
<label for="node-input-config">
|
|
361
|
+
<i class="fa fa-cog"></i> Config
|
|
362
|
+
</label>
|
|
363
|
+
<input type="text" id="node-input-config" />
|
|
364
|
+
</div>
|
|
365
|
+
|
|
341
366
|
<!-- 5. Area -->
|
|
342
367
|
<div class="form-row">
|
|
343
368
|
<label for="node-input-area">
|
package/nodes/ha-mqtt-dmx.js
CHANGED
|
@@ -29,21 +29,40 @@ 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
|
-
|
|
47
|
+
autoDiscover();
|
|
36
48
|
});
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
|
|
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);
|
|
39
55
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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);
|
|
43
61
|
});
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
47
66
|
|
|
48
67
|
// ── Node settings ─────────────────────────────────────────
|
|
49
68
|
const S = {
|
package/nodes/ha-mqtt-pir.html
CHANGED
|
@@ -13,6 +13,31 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
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
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
16
41
|
paletteLabel: 'PIR',
|
|
17
42
|
|
|
18
43
|
defaults: {
|
package/nodes/ha-mqtt-pir.js
CHANGED
|
@@ -19,21 +19,40 @@ 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
|
-
|
|
37
|
+
autoDiscover();
|
|
26
38
|
});
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
|
|
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);
|
|
29
45
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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);
|
|
33
51
|
});
|
|
34
52
|
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
37
56
|
|
|
38
57
|
// ── Node settings ─────────────────────────────────────────
|
|
39
58
|
const S = {
|
package/nodes/ha-mqtt-relay.html
CHANGED
|
@@ -13,6 +13,31 @@
|
|
|
13
13
|
inputs: 1,
|
|
14
14
|
outputs: 0,
|
|
15
15
|
inputLabels: ['Input'],
|
|
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
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
16
41
|
paletteLabel: 'Relay',
|
|
17
42
|
|
|
18
43
|
defaults: {
|
package/nodes/ha-mqtt-relay.js
CHANGED
|
@@ -22,21 +22,40 @@ 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
|
-
|
|
40
|
+
autoDiscover();
|
|
29
41
|
});
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
|
|
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);
|
|
32
48
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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);
|
|
36
54
|
});
|
|
37
55
|
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
40
59
|
|
|
41
60
|
// ── Node settings ─────────────────────────────────────────
|
|
42
61
|
const S = {
|
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.4",
|
|
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",
|