node-red-contrib-knx-ultimate 2.1.10 → 2.1.12
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/CHANGELOG.md +8 -0
- package/KNXEngine/package-lock.json +36 -33
- package/KNXEngine/package.json +1 -1
- package/nodes/knxUltimateGlobalContext.html +8 -1
- package/nodes/knxUltimateGlobalContext.js +7 -7
- package/nodes/knxUltimateHueLight.html +150 -20
- package/nodes/knxUltimateHueLight.js +51 -2
- package/nodes/utils/hueUtils.js +86 -15
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 2.1.12</b> - June 2023<br/>
|
|
11
|
+
- Hue Light node: added tunable white.<br/>
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
<b>Version 2.1.11</b> - June 2023<br/>
|
|
15
|
+
- KNX Global Context node: added the optional datastore to choose from.<br/>
|
|
16
|
+
</p>
|
|
9
17
|
<p>
|
|
10
18
|
<b>Version 2.1.10</b> - June 2023<br/>
|
|
11
19
|
- KNX Gateway Node: Migrated documentation to the standard node-red documentation box.<br/>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knxultimate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.36",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "knxultimate",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.36",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@types/node": "^6.14.4",
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
"binary-parser": "1.1.5",
|
|
15
15
|
"binary-protocol": "0.0.0",
|
|
16
16
|
"caller-id": "^0.1.0",
|
|
17
|
-
"crypto-js": "
|
|
17
|
+
"crypto-js": "4.1.1",
|
|
18
18
|
"fs": "0.0.1-security",
|
|
19
|
-
"ipaddr.js": "
|
|
20
|
-
"lodash": "
|
|
19
|
+
"ipaddr.js": "2.0.0",
|
|
20
|
+
"lodash": "4.17.21",
|
|
21
21
|
"log-driver": "1.2.7",
|
|
22
22
|
"mkdirp": "^0.5.1",
|
|
23
|
-
"os": "
|
|
24
|
-
"path": "
|
|
25
|
-
"ping": "
|
|
26
|
-
"xml2js": "
|
|
23
|
+
"os": "0.1.1",
|
|
24
|
+
"path": "0.12.7",
|
|
25
|
+
"ping": "0.4.1",
|
|
26
|
+
"xml2js": "0.5.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {},
|
|
29
29
|
"engines": {
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
|
86
86
|
},
|
|
87
87
|
"node_modules/ipaddr.js": {
|
|
88
|
-
"version": "2.0.
|
|
89
|
-
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.
|
|
90
|
-
"integrity": "sha512-
|
|
88
|
+
"version": "2.0.0",
|
|
89
|
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz",
|
|
90
|
+
"integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==",
|
|
91
91
|
"engines": {
|
|
92
92
|
"node": ">= 10"
|
|
93
93
|
}
|
|
@@ -106,9 +106,12 @@
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
"node_modules/minimist": {
|
|
109
|
-
"version": "1.2.
|
|
110
|
-
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.
|
|
111
|
-
"integrity": "sha512-
|
|
109
|
+
"version": "1.2.8",
|
|
110
|
+
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
|
111
|
+
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
|
112
|
+
"funding": {
|
|
113
|
+
"url": "https://github.com/sponsors/ljharb"
|
|
114
|
+
}
|
|
112
115
|
},
|
|
113
116
|
"node_modules/mkdirp": {
|
|
114
117
|
"version": "0.5.5",
|
|
@@ -122,9 +125,9 @@
|
|
|
122
125
|
}
|
|
123
126
|
},
|
|
124
127
|
"node_modules/os": {
|
|
125
|
-
"version": "0.1.
|
|
126
|
-
"resolved": "https://registry.npmjs.org/os/-/os-0.1.
|
|
127
|
-
"integrity": "sha512-
|
|
128
|
+
"version": "0.1.1",
|
|
129
|
+
"resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
|
|
130
|
+
"integrity": "sha512-jg06S2xr5De63mLjZVJDf3/k37tpjppr2LR7MUOsxv8XuUCVpCnvbCksXCBcB5gQqQf/K0+87WGTRlAj5q7r1A=="
|
|
128
131
|
},
|
|
129
132
|
"node_modules/path": {
|
|
130
133
|
"version": "0.12.7",
|
|
@@ -191,9 +194,9 @@
|
|
|
191
194
|
}
|
|
192
195
|
},
|
|
193
196
|
"node_modules/xml2js": {
|
|
194
|
-
"version": "0.
|
|
195
|
-
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.
|
|
196
|
-
"integrity": "sha512-
|
|
197
|
+
"version": "0.5.0",
|
|
198
|
+
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
|
199
|
+
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
|
197
200
|
"dependencies": {
|
|
198
201
|
"sax": ">=0.6.0",
|
|
199
202
|
"xmlbuilder": "~11.0.0"
|
|
@@ -264,9 +267,9 @@
|
|
|
264
267
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
|
265
268
|
},
|
|
266
269
|
"ipaddr.js": {
|
|
267
|
-
"version": "2.0.
|
|
268
|
-
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.
|
|
269
|
-
"integrity": "sha512-
|
|
270
|
+
"version": "2.0.0",
|
|
271
|
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz",
|
|
272
|
+
"integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w=="
|
|
270
273
|
},
|
|
271
274
|
"lodash": {
|
|
272
275
|
"version": "4.17.21",
|
|
@@ -279,9 +282,9 @@
|
|
|
279
282
|
"integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg=="
|
|
280
283
|
},
|
|
281
284
|
"minimist": {
|
|
282
|
-
"version": "1.2.
|
|
283
|
-
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.
|
|
284
|
-
"integrity": "sha512-
|
|
285
|
+
"version": "1.2.8",
|
|
286
|
+
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
|
287
|
+
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
|
|
285
288
|
},
|
|
286
289
|
"mkdirp": {
|
|
287
290
|
"version": "0.5.5",
|
|
@@ -292,9 +295,9 @@
|
|
|
292
295
|
}
|
|
293
296
|
},
|
|
294
297
|
"os": {
|
|
295
|
-
"version": "0.1.
|
|
296
|
-
"resolved": "https://registry.npmjs.org/os/-/os-0.1.
|
|
297
|
-
"integrity": "sha512-
|
|
298
|
+
"version": "0.1.1",
|
|
299
|
+
"resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
|
|
300
|
+
"integrity": "sha512-jg06S2xr5De63mLjZVJDf3/k37tpjppr2LR7MUOsxv8XuUCVpCnvbCksXCBcB5gQqQf/K0+87WGTRlAj5q7r1A=="
|
|
298
301
|
},
|
|
299
302
|
"path": {
|
|
300
303
|
"version": "0.12.7",
|
|
@@ -348,9 +351,9 @@
|
|
|
348
351
|
}
|
|
349
352
|
},
|
|
350
353
|
"xml2js": {
|
|
351
|
-
"version": "0.
|
|
352
|
-
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.
|
|
353
|
-
"integrity": "sha512-
|
|
354
|
+
"version": "0.5.0",
|
|
355
|
+
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
|
356
|
+
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
|
354
357
|
"requires": {
|
|
355
358
|
"sax": ">=0.6.0",
|
|
356
359
|
"xmlbuilder": "~11.0.0"
|
package/KNXEngine/package.json
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
server: { type: "knxUltimate-config", required: true },
|
|
8
8
|
name: { value: "KNXGlobalContext", validate: RED.validators.regex(/^[a-zA-Z]+$/) },
|
|
9
9
|
exposeAsVariable: { value: "exposeAsVariableREADWRITE", required: false },
|
|
10
|
-
writeExecutionInterval: { value: 1000 }
|
|
10
|
+
writeExecutionInterval: { value: 1000 },
|
|
11
|
+
contextStorage: { value: "" }
|
|
11
12
|
},
|
|
12
13
|
inputs: 0,
|
|
13
14
|
outputs: 0,
|
|
@@ -98,5 +99,11 @@
|
|
|
98
99
|
</select>
|
|
99
100
|
</div>
|
|
100
101
|
|
|
102
|
+
<div class="form-row">
|
|
103
|
+
<label for="node-input-contextStorage" style="width:60%;">
|
|
104
|
+
<i class="fa fa-tag"></i> Context storage
|
|
105
|
+
</label>
|
|
106
|
+
<input style="width:35%;" type="text" id="node-input-contextStorage" placeholder="Optional context storage name" />
|
|
107
|
+
</div>
|
|
101
108
|
|
|
102
109
|
</script>
|
|
@@ -26,7 +26,7 @@ module.exports = function (RED) {
|
|
|
26
26
|
// payload
|
|
27
27
|
// }
|
|
28
28
|
|
|
29
|
-
function knxUltimateGlobalContext
|
|
29
|
+
function knxUltimateGlobalContext(config) {
|
|
30
30
|
RED.nodes.createNode(this, config)
|
|
31
31
|
const node = this
|
|
32
32
|
node.server = RED.nodes.getNode(config.server)
|
|
@@ -50,7 +50,7 @@ module.exports = function (RED) {
|
|
|
50
50
|
node.formatnegativevalue = 'leave'
|
|
51
51
|
node.formatdecimalsvalue = 999
|
|
52
52
|
node.writeExecutionInterval = config.writeExecutionInterval === undefined ? 1000 : config.writeExecutionInterval
|
|
53
|
-
|
|
53
|
+
node.contextStorage = config.contextStorage !== undefined ? config.contextStorage : ''
|
|
54
54
|
node.exposeAsVariable = config.exposeAsVariable !== undefined ? config.exposeAsVariable : 'exposeAsVariableREADONLY' // Should expose the Group Addresses to the Global Context?
|
|
55
55
|
node.exposedGAs = []
|
|
56
56
|
node.timerExposedGAs = null
|
|
@@ -83,8 +83,8 @@ module.exports = function (RED) {
|
|
|
83
83
|
node.goTimerGo = function () {
|
|
84
84
|
if (node.timerExposedGAs !== null) clearTimeout(node.timerExposedGAs) // 21/03/2021
|
|
85
85
|
node.timerExposedGAs = setTimeout(() => {
|
|
86
|
-
let oContext = node.context().global.get(node.name + '_WRITE') || []
|
|
87
|
-
node.context().global.set(node.name + '_WRITE', []) // Delete the var
|
|
86
|
+
let oContext = node.context().global.get(node.name + '_WRITE', node.contextStorage) || []
|
|
87
|
+
node.context().global.set(node.name + '_WRITE', [], node.contextStorage) // Delete the var
|
|
88
88
|
for (let index = 0; index < oContext.length; index++) {
|
|
89
89
|
const element = oContext[index]
|
|
90
90
|
if (!element.hasOwnProperty('address')) {
|
|
@@ -129,7 +129,7 @@ module.exports = function (RED) {
|
|
|
129
129
|
node.setNodeStatus({ fill: 'green', shape: 'dot', text: 'Start Writing', payload: '', GA: '', dpt: '', devicename: '' })
|
|
130
130
|
} else {
|
|
131
131
|
if (node.timerExposedGAs !== null) clearTimeout(node.timerExposedGAs)
|
|
132
|
-
node.context().global.set(node.name + '_WRITE', []) // Delete the var
|
|
132
|
+
node.context().global.set(node.name + '_WRITE', [], node.contextStorage) // Delete the var
|
|
133
133
|
}
|
|
134
134
|
// #endregion
|
|
135
135
|
|
|
@@ -149,14 +149,14 @@ module.exports = function (RED) {
|
|
|
149
149
|
}
|
|
150
150
|
// Save into the global Context
|
|
151
151
|
try {
|
|
152
|
-
node.context().global.set(node.name + '_READ', node.exposedGAs)
|
|
152
|
+
node.context().global.set(node.name + '_READ', node.exposedGAs, node.contextStorage)
|
|
153
153
|
} catch (error) {
|
|
154
154
|
console.log(error)
|
|
155
155
|
}
|
|
156
156
|
oGa = null // 21/03/2022
|
|
157
157
|
} else {
|
|
158
158
|
node.exposedGAs = []
|
|
159
|
-
node.context().global.set(node.name + '_READ', node.exposedGAs)
|
|
159
|
+
node.context().global.set(node.name + '_READ', node.exposedGAs, node.contextStorage)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -28,6 +28,14 @@
|
|
|
28
28
|
GALightColorState: { value: "" },
|
|
29
29
|
dptLightColorState: { value: "" },
|
|
30
30
|
|
|
31
|
+
nameLightHSV: { value: "" },
|
|
32
|
+
GALightHSV: { value: "" },
|
|
33
|
+
dptLightHSV: { value: "" },
|
|
34
|
+
|
|
35
|
+
nameLightHSVState: { value: "" },
|
|
36
|
+
GALightHSVState: { value: "" },
|
|
37
|
+
dptLightHSVState: { value: "" },
|
|
38
|
+
|
|
31
39
|
nameLightBrightness: { value: "" },
|
|
32
40
|
GALightBrightness: { value: "" },
|
|
33
41
|
dptLightBrightness: { value: "" },
|
|
@@ -237,7 +245,7 @@
|
|
|
237
245
|
});
|
|
238
246
|
|
|
239
247
|
|
|
240
|
-
// DPT dptLightColor and
|
|
248
|
+
// DPT dptLightColor and dptLightColorState
|
|
241
249
|
// ########################
|
|
242
250
|
$.getJSON('knxUltimateDpts', (data) => {
|
|
243
251
|
data.forEach(dpt => {
|
|
@@ -320,6 +328,105 @@
|
|
|
320
328
|
|
|
321
329
|
|
|
322
330
|
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
// DPT dptLightTunableWhite
|
|
335
|
+
// ########################
|
|
336
|
+
$.getJSON('knxUltimateDpts', (data) => {
|
|
337
|
+
data.forEach(dpt => {
|
|
338
|
+
if (dpt.value === "3.007") {
|
|
339
|
+
$("#node-input-dptLightHSV").append($("<option></option>")
|
|
340
|
+
.attr("value", dpt.value)
|
|
341
|
+
.text(dpt.text))
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
$("#node-input-dptLightHSV").val(this.dptLightHSV)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
// DPT dptLightTunableWhiteState
|
|
349
|
+
$.getJSON('knxUltimateDpts', (data) => {
|
|
350
|
+
data.forEach(dpt => {
|
|
351
|
+
if (dpt.value === "5.001") {
|
|
352
|
+
$("#node-input-dptLightHSVState").append($("<option></option>")
|
|
353
|
+
.attr("value", dpt.value)
|
|
354
|
+
.text(dpt.text))
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
$("#node-input-dptLightHSVState").val(this.dptLightHSVState)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
// Autocomplete suggestion with ETS csv File
|
|
361
|
+
$("#node-input-GALightHSV").autocomplete({
|
|
362
|
+
minLength: 1,
|
|
363
|
+
source: function (request, response) {
|
|
364
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
|
|
365
|
+
response($.map(data, function (value, key) {
|
|
366
|
+
if (value.dpt === "3.007") {
|
|
367
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
368
|
+
if (fullSearch(sSearch, request.term)) {
|
|
369
|
+
return {
|
|
370
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
371
|
+
value: value.ga // Value
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}));
|
|
378
|
+
});
|
|
379
|
+
}, select: function (event, ui) {
|
|
380
|
+
// Sets Datapoint and device name automatically
|
|
381
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
382
|
+
try {
|
|
383
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
384
|
+
} catch (error) {
|
|
385
|
+
}
|
|
386
|
+
$('#node-input-nameLightHSV').val(sDevName);
|
|
387
|
+
var optVal = $("#node-input-dptLightHSV option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
388
|
+
// Select the option value
|
|
389
|
+
$("#node-input-dptLightHSV").val(optVal);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Autocomplete suggestion with ETS csv File
|
|
394
|
+
$("#node-input-GALightHSVState").autocomplete({
|
|
395
|
+
minLength: 1,
|
|
396
|
+
source: function (request, response) {
|
|
397
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
|
|
398
|
+
response($.map(data, function (value, key) {
|
|
399
|
+
if (value.dpt === "5.001") {
|
|
400
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
401
|
+
if (fullSearch(sSearch, request.term)) {
|
|
402
|
+
return {
|
|
403
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
404
|
+
value: value.ga // Value
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}));
|
|
411
|
+
});
|
|
412
|
+
}, select: function (event, ui) {
|
|
413
|
+
// Sets Datapoint and device name automatically
|
|
414
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
415
|
+
try {
|
|
416
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
417
|
+
} catch (error) {
|
|
418
|
+
}
|
|
419
|
+
$('#node-input-nameLightHSVState').val(sDevName);
|
|
420
|
+
var optVal = $("#node-input-dptLightHSVState option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
421
|
+
// Select the option value
|
|
422
|
+
$("#node-input-dptLightHSVState").val(optVal);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
// ########################
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
323
430
|
// DPT dptLightBrightness and dptLightBrightnessState
|
|
324
431
|
// ########################
|
|
325
432
|
$.getJSON('knxUltimateDpts', (data) => {
|
|
@@ -583,8 +690,6 @@
|
|
|
583
690
|
|
|
584
691
|
<br/>
|
|
585
692
|
|
|
586
|
-
<!-- <p> <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/knx.png' width='32px'> -> <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/hue.png' width='32px'></p> -->
|
|
587
|
-
|
|
588
693
|
<p>
|
|
589
694
|
<b>KNX LINK</b>
|
|
590
695
|
</p>
|
|
@@ -612,8 +717,8 @@
|
|
|
612
717
|
<label for="node-input-nameLightState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
613
718
|
<input type="text" id="node-input-nameLightState" style="width:200px;margin-left: 5px; text-align: left;">
|
|
614
719
|
</div>
|
|
615
|
-
|
|
616
|
-
|
|
720
|
+
|
|
721
|
+
|
|
617
722
|
<div class="form-row">
|
|
618
723
|
<label for="node-input-nameLightDIM" style="width:100px;"><i class="fa fa-play-circle-o"></i> Dimming</label>
|
|
619
724
|
|
|
@@ -625,9 +730,8 @@
|
|
|
625
730
|
|
|
626
731
|
<label for="node-input-nameLightDIM" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
627
732
|
<input type="text" id="node-input-nameLightDIM" style="width:200px;margin-left: 5px; text-align: left;">
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
</div>
|
|
733
|
+
</div>
|
|
734
|
+
|
|
631
735
|
<div class="form-row">
|
|
632
736
|
<label for="node-input-nameLightColor" style="width:100px;"><i class="fa fa-play-circle-o"></i> Color</label>
|
|
633
737
|
|
|
@@ -639,7 +743,7 @@
|
|
|
639
743
|
|
|
640
744
|
<label for="node-input-nameLightColor" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
641
745
|
<input type="text" id="node-input-nameLightColor" style="width:200px;margin-left: 5px; text-align: left;">
|
|
642
|
-
|
|
746
|
+
</div>
|
|
643
747
|
<div class="form-row">
|
|
644
748
|
<label for="node-input-nameLightColorState" style="width:100px;"><i class="fa fa-play-circle-o"></i> Color Status</label>
|
|
645
749
|
|
|
@@ -651,10 +755,35 @@
|
|
|
651
755
|
|
|
652
756
|
<label for="node-input-nameLightColorState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
653
757
|
<input type="text" id="node-input-nameLightColorState" style="width:200px;margin-left: 5px; text-align: left;">
|
|
758
|
+
</div>
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
<div class="form-row">
|
|
762
|
+
<label for="node-input-nameLightHSV" style="width:100px;"><i class="fa fa-play-circle-o"></i> Tunable white</label>
|
|
763
|
+
|
|
764
|
+
<label for="node-input-GALightHSV" style="width:20px;"><span data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
|
|
765
|
+
<input type="text" id="node-input-GALightHSV" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
766
|
+
|
|
767
|
+
<label for="node-input-dptLightHSV" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
768
|
+
<select id="node-input-dptLightHSV" style="width:140px;"></select>
|
|
769
|
+
|
|
770
|
+
<label for="node-input-nameLightHSV" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
771
|
+
<input type="text" id="node-input-nameLightHSV" style="width:200px;margin-left: 5px; text-align: left;">
|
|
654
772
|
</div>
|
|
655
|
-
|
|
773
|
+
<div class="form-row">
|
|
774
|
+
<label for="node-input-nameLightHSVState" style="width:100px;"><i class="fa fa-play-circle-o"></i> Tunable white Status</label>
|
|
775
|
+
|
|
776
|
+
<label for="node-input-GALightHSVState" style="width:20px;"><span data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
|
|
777
|
+
<input type="text" id="node-input-GALightHSVState" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
778
|
+
|
|
779
|
+
<label for="node-input-dptLightHSVState" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
780
|
+
<select id="node-input-dptLightHSVState" style="width:140px;"></select>
|
|
781
|
+
|
|
782
|
+
<label for="node-input-nameLightHSVState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
783
|
+
<input type="text" id="node-input-nameLightHSVState" style="width:200px;margin-left: 5px; text-align: left;">
|
|
656
784
|
</div>
|
|
657
|
-
|
|
785
|
+
|
|
786
|
+
|
|
658
787
|
<div class="form-row">
|
|
659
788
|
<label for="node-input-nameLightBrightness" style="width:100px;"><i class="fa fa-play-circle-o"></i> Brightness</label>
|
|
660
789
|
|
|
@@ -666,8 +795,8 @@
|
|
|
666
795
|
|
|
667
796
|
<label for="node-input-nameLightBrightness" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
668
797
|
<input type="text" id="node-input-nameLightBrightness" style="width:200px;margin-left: 5px; text-align: left;">
|
|
669
|
-
|
|
670
|
-
|
|
798
|
+
</div>
|
|
799
|
+
|
|
671
800
|
<div class="form-row">
|
|
672
801
|
<label for="node-input-nameLightBrightnessState" style="width:100px;"><i class="fa fa-play-circle-o"></i> Brightness Status</label>
|
|
673
802
|
|
|
@@ -679,10 +808,9 @@
|
|
|
679
808
|
|
|
680
809
|
<label for="node-input-nameLightBrightnessState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
681
810
|
<input type="text" id="node-input-nameLightBrightnessState" style="width:200px;margin-left: 5px; text-align: left;">
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
</div>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
|
|
686
814
|
<div class="form-row">
|
|
687
815
|
<label for="node-input-nameLightBlink" style="width:100px;"><i class="fa fa-play-circle-o"></i> Blink</label>
|
|
688
816
|
|
|
@@ -694,8 +822,8 @@
|
|
|
694
822
|
|
|
695
823
|
<label for="node-input-nameLightBlink" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
696
824
|
<input type="text" id="node-input-nameLightBlink" style="width:200px;margin-left: 5px; text-align: left;">
|
|
697
|
-
|
|
698
|
-
|
|
825
|
+
</div>
|
|
826
|
+
|
|
699
827
|
<div class="form-row">
|
|
700
828
|
<label for="node-input-nameLightColorCycle" style="width:100px;"><i class="fa fa-play-circle-o"></i> Color Cycle</label>
|
|
701
829
|
|
|
@@ -707,7 +835,7 @@
|
|
|
707
835
|
|
|
708
836
|
<label for="node-input-nameLightColorCycle" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
|
|
709
837
|
<input type="text" id="node-input-nameLightColorCycle" style="width:200px;margin-left: 5px; text-align: left;">
|
|
710
|
-
|
|
838
|
+
</div>
|
|
711
839
|
<br/>
|
|
712
840
|
<br/>
|
|
713
841
|
<br/>
|
|
@@ -738,6 +866,8 @@ Start typing in the GA field, the name or group address of your KNX device, the
|
|
|
738
866
|
| Dimming | Relative DIM the HUE light |
|
|
739
867
|
| Color | This command is used to change the HUE light's color. Accepted datapoint is RGB triplet (r,g,b). The node handles the gamut color correction. As soon as you send a color KNX telegran, the light turns on and sets color and brightness, derived from the brightness human perception. As soon as you send a KNX telegram with r,g,b set to zero, the light turns off |
|
|
740
868
|
| Color Status | Link this to the light's color status group address. Accepted datapoint is RGB triplet (r,g,b)|
|
|
869
|
+
| Tunable white | This command is used to change the HUE light's white temperature. Datapoint is 3.007 dimming. |
|
|
870
|
+
| Tunable white Status | Link this to the light temperature status group address. Datapoint is 5.001 absolute value|
|
|
741
871
|
| Brightness | This command is used to change the absolute HUE light's brightness |
|
|
742
872
|
| Brightness Status| Link this to the light's brightness status group address |
|
|
743
873
|
| Blink| *true* Blink the light, *false* Stop blinking. Blinks the light on and off. Useful for signalling. Works with all HUE lights. |
|
|
@@ -2,7 +2,7 @@ module.exports = function (RED) {
|
|
|
2
2
|
const dptlib = require('./../KNXEngine/src/dptlib')
|
|
3
3
|
const hueColorConverter = require('./utils/hueColorConverter')
|
|
4
4
|
|
|
5
|
-
function knxUltimateHueLight
|
|
5
|
+
function knxUltimateHueLight(config) {
|
|
6
6
|
RED.nodes.createNode(this, config)
|
|
7
7
|
const node = this
|
|
8
8
|
node.server = RED.nodes.getNode(config.server)
|
|
@@ -60,6 +60,21 @@ module.exports = function (RED) {
|
|
|
60
60
|
node.startDimStopper('stop')
|
|
61
61
|
}
|
|
62
62
|
break
|
|
63
|
+
case config.GALightHSV:
|
|
64
|
+
if (config.dptLightHSV === '3.007') {
|
|
65
|
+
// MDT smartbutton will dim the color temperature
|
|
66
|
+
// { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
|
|
67
|
+
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
68
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV))
|
|
69
|
+
if (msg.payload.data > 0) {
|
|
70
|
+
let dimDirectionTunableWhite = 'down'
|
|
71
|
+
dimDirectionTunableWhite = msg.payload.decr_incr === 1 ? 'up' : 'down'
|
|
72
|
+
node.startDimStopperTunableWhite(dimDirectionTunableWhite)
|
|
73
|
+
} else {
|
|
74
|
+
node.startDimStopperTunableWhite('stop')
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break
|
|
63
78
|
case config.GALightBrightness:
|
|
64
79
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness))
|
|
65
80
|
state = { dimming: { brightness: msg.payload } }
|
|
@@ -97,7 +112,7 @@ module.exports = function (RED) {
|
|
|
97
112
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, 'setLight')
|
|
98
113
|
node.timerColorCycle = setInterval(() => {
|
|
99
114
|
try {
|
|
100
|
-
function getRandomIntInclusive
|
|
115
|
+
function getRandomIntInclusive(min, max) {
|
|
101
116
|
min = Math.ceil(min)
|
|
102
117
|
max = Math.floor(max)
|
|
103
118
|
return Math.floor(Math.random() * (max - min + 1) + min) // The maximum is inclusive and the minimum is inclusive
|
|
@@ -150,6 +165,29 @@ module.exports = function (RED) {
|
|
|
150
165
|
}, 300)
|
|
151
166
|
}
|
|
152
167
|
|
|
168
|
+
// Start dimming tunable white
|
|
169
|
+
// mirek: required(integer – minimum: 153 – maximum: 500)
|
|
170
|
+
node.timerDimTunableWhite = undefined
|
|
171
|
+
node.dimDirectionTunableWhite = {}
|
|
172
|
+
node.startDimStopperTunableWhite = function (_direction) {
|
|
173
|
+
if (node.timerDimTunableWhite !== undefined) clearInterval(node.timerDimTunableWhite)
|
|
174
|
+
if (_direction === 'stop') return
|
|
175
|
+
switch (_direction) {
|
|
176
|
+
case 'up':
|
|
177
|
+
node.dimDirectionTunableWhite = { color_temperature_delta: { action: 'up', mirek_delta: 10 } }
|
|
178
|
+
break
|
|
179
|
+
case 'down':
|
|
180
|
+
node.dimDirectionTunableWhite = { color_temperature_delta: { action: 'down', mirek_delta: 10 } }
|
|
181
|
+
break
|
|
182
|
+
default:
|
|
183
|
+
break
|
|
184
|
+
}
|
|
185
|
+
node.timerDimTunableWhite = setInterval(() => {
|
|
186
|
+
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirectionTunableWhite, 'setLight')
|
|
187
|
+
}, 300)
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
153
191
|
node.handleSendHUE = _event => {
|
|
154
192
|
try {
|
|
155
193
|
if (_event.id === config.hueDevice) {
|
|
@@ -187,6 +225,17 @@ module.exports = function (RED) {
|
|
|
187
225
|
// Send to KNX bus
|
|
188
226
|
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
|
|
189
227
|
}
|
|
228
|
+
if (_event.hasOwnProperty('color_temperature')) {
|
|
229
|
+
knxMsgPayload.topic = config.GALightHSVState
|
|
230
|
+
knxMsgPayload.dpt = config.dptLightHSVState
|
|
231
|
+
if (config.dptLightHSVState === '5.001') {
|
|
232
|
+
//NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
|
|
233
|
+
let NewValue = (((_event.color_temperature.mirek - 153) * (100 - 0)) / (500 - 153)) + 0
|
|
234
|
+
knxMsgPayload.payload = NewValue
|
|
235
|
+
}
|
|
236
|
+
// Send to KNX bus
|
|
237
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
|
|
238
|
+
}
|
|
190
239
|
node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX State ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
|
|
191
240
|
}
|
|
192
241
|
} catch (error) {
|
package/nodes/utils/hueUtils.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
const hueApiV2 = require('node-hue')
|
|
5
5
|
const { EventEmitter } = require('events')
|
|
6
6
|
const https = require('https')
|
|
7
|
+
const EventSource = require('eventsource');
|
|
7
8
|
|
|
8
9
|
class classHUE extends EventEmitter {
|
|
9
|
-
constructor
|
|
10
|
+
constructor(_hueBridgeIP, _username, _clientkey, _bridgeid) {
|
|
10
11
|
super()
|
|
11
12
|
this.setup(_hueBridgeIP, _username, _clientkey, _bridgeid)
|
|
12
13
|
}
|
|
@@ -19,6 +20,8 @@ class classHUE extends EventEmitter {
|
|
|
19
20
|
this.commandQueue = []
|
|
20
21
|
this.closePushEventStream = false
|
|
21
22
|
this.timerwriteQueueAdd = setTimeout(this.handleQueue, 3000) // First start
|
|
23
|
+
this.connect()
|
|
24
|
+
|
|
22
25
|
// this.run()
|
|
23
26
|
// start the SSE Stream Receiver
|
|
24
27
|
// #############################################
|
|
@@ -80,28 +83,84 @@ class classHUE extends EventEmitter {
|
|
|
80
83
|
// // Starts the connection for the first time
|
|
81
84
|
// req();
|
|
82
85
|
|
|
86
|
+
|
|
83
87
|
// Eventstream Reader using hueApiV2
|
|
84
|
-
const runStreamReader = async () => {
|
|
88
|
+
// const runStreamReader = async () => {
|
|
89
|
+
// try {
|
|
90
|
+
// const listener = (event) => {
|
|
91
|
+
// // console.log(event)
|
|
92
|
+
// event.data.forEach(element => {
|
|
93
|
+
// if (event.type === 'update') this.emit('event', element)
|
|
94
|
+
// })
|
|
95
|
+
// }
|
|
96
|
+
// const hueEventStream = hueApiV2.connect({
|
|
97
|
+
// host: this.hueBridgeIP,
|
|
98
|
+
// key: this.username,
|
|
99
|
+
// eventListener: listener
|
|
100
|
+
// })
|
|
101
|
+
// } catch (error) {
|
|
102
|
+
// console.log('KNXUltimateHUEConfig: classHUE: const run = async: ' + error.message)
|
|
103
|
+
// }
|
|
104
|
+
// }
|
|
105
|
+
// runStreamReader()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
// #############################################
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
connect() {
|
|
114
|
+
// #############################################
|
|
115
|
+
const options = {
|
|
116
|
+
headers: {
|
|
117
|
+
'hue-application-key': this.username,
|
|
118
|
+
},
|
|
119
|
+
https: {
|
|
120
|
+
rejectUnauthorized: false,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this.es = new EventSource('https://' + this.hueBridgeIP + '/eventstream/clip/v2', options);
|
|
125
|
+
|
|
126
|
+
this.es.onmessage = (event) => {
|
|
85
127
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
128
|
+
if (event && event.type === 'message' && event.data) {
|
|
129
|
+
const data = JSON.parse(event.data);
|
|
130
|
+
data.forEach(element => {
|
|
131
|
+
if (element.type === 'update') {
|
|
132
|
+
element.data.forEach(ev => {
|
|
133
|
+
this.emit('event', ev)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
90
136
|
})
|
|
91
137
|
}
|
|
92
|
-
const hueEventStream = hueApiV2.connect({
|
|
93
|
-
host: this.hueBridgeIP,
|
|
94
|
-
key: this.username,
|
|
95
|
-
eventListener: listener
|
|
96
|
-
})
|
|
97
138
|
} catch (error) {
|
|
98
|
-
console.log('KNXUltimateHUEConfig: classHUE:
|
|
139
|
+
console.log('KNXUltimateHUEConfig: classHUE: this.es.onmessage: ' + error.message)
|
|
99
140
|
}
|
|
141
|
+
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
this.es.onopen = () => {
|
|
145
|
+
//console.log('KNXUltimateHUEConfig: classHUE: SSE-Connected')
|
|
146
|
+
//this.emit('connected');
|
|
100
147
|
}
|
|
101
|
-
|
|
148
|
+
this.es.onerror = (error) => {
|
|
149
|
+
try {
|
|
150
|
+
this.es.close()
|
|
151
|
+
this.es = null
|
|
152
|
+
console.log('KNXUltimateHUEConfig: classHUE: request.on(error): ' + error.message)
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
this.connect()
|
|
155
|
+
}, 5000);
|
|
156
|
+
} catch (error) {
|
|
102
157
|
|
|
103
|
-
|
|
158
|
+
}
|
|
159
|
+
//this.emit('error', err)
|
|
160
|
+
};
|
|
104
161
|
}
|
|
162
|
+
// #############################################
|
|
163
|
+
|
|
105
164
|
|
|
106
165
|
// Handle the send queue
|
|
107
166
|
// ######################################
|
|
@@ -131,7 +190,7 @@ class classHUE extends EventEmitter {
|
|
|
131
190
|
}
|
|
132
191
|
}
|
|
133
192
|
// The Hue bridge allows about 10 telegram per second, so i need to make a queue manager
|
|
134
|
-
setTimeout(this.handleQueue,
|
|
193
|
+
setTimeout(this.handleQueue, 150)
|
|
135
194
|
}
|
|
136
195
|
|
|
137
196
|
writeHueQueueAdd = async (_lightID, _state, _operation, _callback) => {
|
|
@@ -185,10 +244,22 @@ class classHUE extends EventEmitter {
|
|
|
185
244
|
}
|
|
186
245
|
}
|
|
187
246
|
|
|
247
|
+
// Get the light details
|
|
248
|
+
getLightStatus = async (_rid) => {
|
|
249
|
+
try {
|
|
250
|
+
const hue = hueApiV2.connect({ host: this.hueBridgeIP, key: this.username })
|
|
251
|
+
const oLight = await hue.getLight(_rid)
|
|
252
|
+
return oLight[0]
|
|
253
|
+
} catch (error) {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
188
257
|
close = async () => {
|
|
189
258
|
return new Promise((resolve, reject) => {
|
|
190
259
|
try {
|
|
191
260
|
this.closePushEventStream = true
|
|
261
|
+
this.es.close();
|
|
262
|
+
this.es = null;
|
|
192
263
|
setTimeout(() => {
|
|
193
264
|
resolve(true)
|
|
194
265
|
}, 1000)
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.1.
|
|
6
|
+
"version": "2.1.12",
|
|
7
7
|
"description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices handling.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"mkdirp": "1.0.4",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"dns-sync": "0.2.1",
|
|
19
19
|
"node-hue-api": "5.0.0-beta.16",
|
|
20
20
|
"node-hue": "1.0.4",
|
|
21
|
-
"color-convert": "2.0.1"
|
|
21
|
+
"color-convert": "2.0.1",
|
|
22
|
+
"eventsource": "2.0.2"
|
|
22
23
|
},
|
|
23
24
|
"node-red": {
|
|
24
25
|
"version": ">=2.0.0",
|