node-red-contrib-knx-ultimate 2.2.25 → 2.2.26
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 +9 -0
- package/nodes/hue-config.html +7 -9
- package/nodes/hue-config.js +37 -14
- package/nodes/knxUltimate-config.html +1 -1
- package/nodes/knxUltimateHueBattery.html +2 -2
- package/nodes/knxUltimateHueBattery.js +31 -5
- package/nodes/knxUltimateHueButton.js +0 -2
- package/nodes/knxUltimateHueLight.html +123 -69
- package/nodes/knxUltimateHueLight.js +41 -30
- package/nodes/knxUltimateHueLightSensor.html +1 -1
- package/nodes/knxUltimateHueLightSensor.js +32 -2
- package/nodes/knxUltimateHueTemperatureSensor.html +1 -1
- package/nodes/knxUltimateHueTemperatureSensor.js +32 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.2.26** - November 2023<br/>
|
|
10
|
+
This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
|
|
11
|
+
- HUE Light: fixed some spurious node status errors.<br/>
|
|
12
|
+
- HUE Light: now the brightness status is ever transmitted over the KNX bus, whenever the light is switched on.<br/>
|
|
13
|
+
- HUE Battery: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
|
|
14
|
+
- HUE light level: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
|
|
15
|
+
- HUE temperature sensor: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
|
|
16
|
+
- WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
|
|
17
|
+
|
|
9
18
|
**Version 2.2.25** - November 2023<br/>
|
|
10
19
|
- HUE Light: fixed settings of some behaviour options.<br/>
|
|
11
20
|
- You can now query the HUE Light node stati, via a "read" telegram sent to the KNX stati group address. ("Status" is Latin, not English, so the plural is "stati" and not "statuses" nor "status"). Other HUE nodes will follow asap.<br/>
|
package/nodes/hue-config.html
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
$("#getinfocam").click(function () {
|
|
20
20
|
|
|
21
|
-
var myNotification = RED.notify("Please press the Link button on the HUE Bridge",
|
|
21
|
+
var myNotification = RED.notify("Please press the Link button on the HUE Bridge. Once pressed, click OK.",
|
|
22
22
|
{
|
|
23
23
|
modal: true,
|
|
24
24
|
fixed: true,
|
|
@@ -125,17 +125,15 @@
|
|
|
125
125
|
<label for="node-config-input-clientkey"> Bridge Key</label>
|
|
126
126
|
<input type="password" id="node-config-input-clientkey" placeholder="" disabled>
|
|
127
127
|
</div>
|
|
128
|
+
<div class="form-row">
|
|
129
|
+
<label for="node-config-input-debug"> Bridge Key</label>
|
|
130
|
+
<input rows="20" style="width:100%" type="textarea" id="node-config-input-debug" placeholder="" disabled>
|
|
131
|
+
</div>
|
|
128
132
|
</div>
|
|
129
133
|
|
|
130
134
|
|
|
131
135
|
</script>
|
|
132
|
-
<script type="text/markdown" data-help-name="hue-config"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Just set the Bridge's IP and click **CONNECT** button.
|
|
136
|
-
|
|
137
|
-
[Find it useful?](https://www.paypal.me/techtoday)
|
|
138
|
-
|
|
139
|
-
<br/>
|
|
136
|
+
<script type="text/markdown" data-help-name="hue-config" This node registers to the Hue Bridge. Just set the Bridge's IP
|
|
137
|
+
and click **CONNECT** button. [Find it useful?](https://www.paypal.me/techtoday) <br />
|
|
140
138
|
|
|
141
139
|
</script>
|
package/nodes/hue-config.js
CHANGED
|
@@ -174,6 +174,24 @@ module.exports = (RED) => {
|
|
|
174
174
|
// °°°°°° Load ALL resources
|
|
175
175
|
try {
|
|
176
176
|
node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
|
|
177
|
+
|
|
178
|
+
// // DEBUG
|
|
179
|
+
// try {
|
|
180
|
+
// const fs = require('fs');
|
|
181
|
+
// const { resolve } = require('path');
|
|
182
|
+
// const content = JSON.stringify(node.hueAllResources);
|
|
183
|
+
// try {
|
|
184
|
+
// fs.writeFileSync('resources.json', content);
|
|
185
|
+
// RED.log.info("******************************* FILE WROTE IN resources.json " + resolve("resources.json"))
|
|
186
|
+
// // file written successfully
|
|
187
|
+
// } catch (error) {
|
|
188
|
+
// RED.log.error("********************************************* const content = JSON.stringify(node.hueAllResources)2222: " + error.message)
|
|
189
|
+
// }
|
|
190
|
+
// } catch (error) {
|
|
191
|
+
// RED.log.error("********************************************* const content = JSON.stringify(node.hueAllResources): " + error.message)
|
|
192
|
+
// }
|
|
193
|
+
|
|
194
|
+
|
|
177
195
|
if (node.hueAllResources !== undefined) {
|
|
178
196
|
node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
|
|
179
197
|
// Update all KNX State of the nodes with the new hue device values
|
|
@@ -206,21 +224,25 @@ module.exports = (RED) => {
|
|
|
206
224
|
};
|
|
207
225
|
|
|
208
226
|
node.getFirstLightInGroup = function getFirstLightInGroup(_groupID) {
|
|
209
|
-
if (node.hueAllResources === undefined) return;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
227
|
+
if (node.hueAllResources === undefined || node.hueAllResources === null) return;
|
|
228
|
+
try {
|
|
229
|
+
// Find the group
|
|
230
|
+
const group = node.hueAllResources.filter((a) => a.id === _groupID)[0];
|
|
231
|
+
if (group === null || group === undefined) return;
|
|
232
|
+
const owner = node.hueAllResources.filter((a) => a.id === group.owner.rid)[0];
|
|
233
|
+
if (owner.children !== undefined && owner.children.length > 0) {
|
|
234
|
+
const firstLightId = owner.children.find((a) => a.rtype === "light").rid;
|
|
235
|
+
if (firstLightId !== undefined && firstLightId !== null) {
|
|
236
|
+
const firstLight = node.hueAllResources.find((a) => a.id === firstLightId);
|
|
237
|
+
if (firstLight !== null && firstLight !== undefined) {
|
|
238
|
+
return firstLight;
|
|
239
|
+
} else {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
222
242
|
}
|
|
223
243
|
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
|
|
224
246
|
}
|
|
225
247
|
};
|
|
226
248
|
|
|
@@ -443,7 +465,7 @@ module.exports = (RED) => {
|
|
|
443
465
|
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
444
466
|
const serverNode = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
445
467
|
if (serverNode === null) {
|
|
446
|
-
RED.log.
|
|
468
|
+
RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
|
|
447
469
|
res.json({ devices: `serverNode not set` });
|
|
448
470
|
return;
|
|
449
471
|
}
|
|
@@ -457,6 +479,7 @@ module.exports = (RED) => {
|
|
|
457
479
|
} catch (error) {
|
|
458
480
|
//RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
459
481
|
res.json({ devices: error.message });
|
|
482
|
+
RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
|
|
460
483
|
// (async () => {
|
|
461
484
|
// await node.initHUEConnection();
|
|
462
485
|
// })();
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
ignoreTelegramsWithRepeatedFlag: { value: false, required: false },
|
|
22
22
|
keyringFileXML: { value: "" },
|
|
23
23
|
knxSecureSelected: { value: false },
|
|
24
|
-
autoReconnect: { value:
|
|
24
|
+
autoReconnect: { value: "yes" }
|
|
25
25
|
},
|
|
26
26
|
credentials: {
|
|
27
27
|
keyringFilePassword: { type: "password" }
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
namebatterysensor: { value: "" },
|
|
14
14
|
GAbatterysensor: { value: "" },
|
|
15
15
|
dptbatterysensor: { value: "" },
|
|
16
|
-
readStatusAtStartup: { value: "
|
|
16
|
+
readStatusAtStartup: { value: "yes" },
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
hueDevice: { value: "" }
|
|
20
20
|
},
|
|
21
21
|
inputs: 0,
|
|
22
|
-
outputs:
|
|
22
|
+
outputs: 1,
|
|
23
23
|
icon: "node-hue-icon.svg",
|
|
24
24
|
label: function () {
|
|
25
25
|
return (this.name);
|
|
@@ -7,7 +7,7 @@ module.exports = function (RED) {
|
|
|
7
7
|
node.topic = node.name;
|
|
8
8
|
node.name = config.name === undefined ? 'Hue' : config.name;
|
|
9
9
|
node.dpt = '';
|
|
10
|
-
node.notifyreadrequest =
|
|
10
|
+
node.notifyreadrequest = true;
|
|
11
11
|
node.notifyreadrequestalsorespondtobus = 'false';
|
|
12
12
|
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
13
13
|
node.notifyresponse = false;
|
|
@@ -23,7 +23,9 @@ module.exports = function (RED) {
|
|
|
23
23
|
node.formatnegativevalue = 'leave';
|
|
24
24
|
node.formatdecimalsvalue = 2;
|
|
25
25
|
node.hueDevice = config.hueDevice;
|
|
26
|
-
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "
|
|
26
|
+
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
|
|
27
|
+
node.currentDeviceValue = 0;
|
|
28
|
+
|
|
27
29
|
// Used to call the status update from the config node.
|
|
28
30
|
node.setNodeStatus = ({
|
|
29
31
|
fill, shape, text, payload,
|
|
@@ -42,13 +44,22 @@ module.exports = function (RED) {
|
|
|
42
44
|
|
|
43
45
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
44
46
|
node.handleSend = (msg) => {
|
|
47
|
+
// Respond to KNX read telegram, by sending the current value as response telegram.
|
|
48
|
+
if (msg.knx.event === "GroupValue_Read") {
|
|
49
|
+
switch (msg.knx.destination) {
|
|
50
|
+
case config.GAbatterysensor:
|
|
51
|
+
// To the KNX bus wires
|
|
52
|
+
node.sendResponseToKNX(node.currentDeviceValue);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
45
58
|
};
|
|
46
59
|
|
|
47
60
|
node.handleSendHUE = (_event) => {
|
|
48
61
|
try {
|
|
49
62
|
if (_event.id === config.hueDevice) {
|
|
50
|
-
|
|
51
|
-
// IMPORTANT: exit if no event presen.
|
|
52
63
|
if (!_event.hasOwnProperty("power_state") || _event.power_state.battery_level === undefined) return;
|
|
53
64
|
|
|
54
65
|
const knxMsgPayload = {};
|
|
@@ -62,6 +73,7 @@ module.exports = function (RED) {
|
|
|
62
73
|
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
63
74
|
});
|
|
64
75
|
}
|
|
76
|
+
node.currentDeviceValue = knxMsgPayload.payload;
|
|
65
77
|
|
|
66
78
|
// Setup the output msg
|
|
67
79
|
knxMsgPayload.name = node.name;
|
|
@@ -73,13 +85,27 @@ module.exports = function (RED) {
|
|
|
73
85
|
node.setNodeStatusHue({
|
|
74
86
|
fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
|
|
75
87
|
});
|
|
76
|
-
|
|
77
88
|
}
|
|
78
89
|
} catch (error) {
|
|
79
90
|
node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
|
|
80
91
|
}
|
|
81
92
|
};
|
|
82
93
|
|
|
94
|
+
|
|
95
|
+
node.sendResponseToKNX = (_level) => {
|
|
96
|
+
const knxMsgPayload = {};
|
|
97
|
+
knxMsgPayload.topic = config.GAbatterysensor;
|
|
98
|
+
knxMsgPayload.dpt = config.dptbatterysensor;
|
|
99
|
+
|
|
100
|
+
knxMsgPayload.payload = _level;
|
|
101
|
+
// Send to KNX bus
|
|
102
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
103
|
+
node.server.writeQueueAdd({
|
|
104
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
83
109
|
// On each deploy, unsubscribe+resubscribe
|
|
84
110
|
if (node.server) {
|
|
85
111
|
node.server.removeClient(node);
|
|
@@ -194,8 +194,6 @@ module.exports = function (RED) {
|
|
|
194
194
|
node.server.writeQueueAdd({
|
|
195
195
|
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
196
196
|
});
|
|
197
|
-
}
|
|
198
|
-
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
199
197
|
node.setNodeStatusHue({
|
|
200
198
|
fill: 'grey', shape: 'ring', text: 'HUE->KNX STOP DIM', payload: knxMsgPayload.payload,
|
|
201
199
|
});
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
inputs: { value: 0 },
|
|
93
93
|
|
|
94
94
|
hueDevice: { value: "" },
|
|
95
|
+
hueDeviceObject: { value: {} },
|
|
95
96
|
},
|
|
96
97
|
inputs: 0,
|
|
97
98
|
outputs: 0,
|
|
@@ -99,7 +100,7 @@
|
|
|
99
100
|
label: function () {
|
|
100
101
|
return this.name;
|
|
101
102
|
},
|
|
102
|
-
paletteLabel: "Hue Light",
|
|
103
|
+
paletteLabel: "Hue Light (beta)",
|
|
103
104
|
oneditprepare: function () {
|
|
104
105
|
var node = this;
|
|
105
106
|
var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
|
|
@@ -126,8 +127,10 @@
|
|
|
126
127
|
}
|
|
127
128
|
// ################################################################
|
|
128
129
|
|
|
130
|
+
|
|
129
131
|
$("#tabs").tabs(); // Tabs gestione KNX
|
|
130
132
|
|
|
133
|
+
|
|
131
134
|
// 19/02/2020 Used to get the server sooner als deploy.
|
|
132
135
|
$("#node-input-server").change(function () {
|
|
133
136
|
try {
|
|
@@ -169,7 +172,7 @@
|
|
|
169
172
|
}
|
|
170
173
|
});
|
|
171
174
|
// Eval
|
|
172
|
-
const format = "
|
|
175
|
+
const format = "this." + _destinationWidget.replace("#node-input-", "");
|
|
173
176
|
try {
|
|
174
177
|
if (format !== undefined) $(_destinationWidget).val(eval(format).toString());
|
|
175
178
|
} catch (error) { }
|
|
@@ -264,6 +267,52 @@
|
|
|
264
267
|
getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", " 9.002");
|
|
265
268
|
|
|
266
269
|
|
|
270
|
+
|
|
271
|
+
// Autocomplete suggestion with HUE Lights
|
|
272
|
+
$("#node-input-name").autocomplete({
|
|
273
|
+
minLength: 1,
|
|
274
|
+
source: function (request, response) {
|
|
275
|
+
$.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
|
|
276
|
+
response(
|
|
277
|
+
$.map(data.devices, function (value, key) {
|
|
278
|
+
//alert(JSON.stringify(value) + " "+ key)
|
|
279
|
+
var sSearch = value.name;
|
|
280
|
+
if (!value.name.includes("I'm still connecting")) {
|
|
281
|
+
if (fullSearch(sSearch, request.term)) {
|
|
282
|
+
return {
|
|
283
|
+
hueDevice: value.id,
|
|
284
|
+
value: value.name,
|
|
285
|
+
};
|
|
286
|
+
} else {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
return {
|
|
291
|
+
hueDevice: value.id,
|
|
292
|
+
value: value.name,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
select: function (event, ui) {
|
|
300
|
+
// Distinguish between group of lights an single light.
|
|
301
|
+
if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
|
|
302
|
+
$("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
|
|
303
|
+
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
304
|
+
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
305
|
+
} else {
|
|
306
|
+
$("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
|
|
307
|
+
$("#getColorAtSwitchOnDayTimeButton").show();
|
|
308
|
+
$("#getColorAtSwitchOnNightTimeButton").show();
|
|
309
|
+
}
|
|
310
|
+
$("#tabs").show();
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
267
316
|
// Show/Hide the div of the color at swich on
|
|
268
317
|
if (this.specifySwitchOnBrightness === "yes") {
|
|
269
318
|
$("#divColorsAtSwitchOn").show();
|
|
@@ -313,7 +362,7 @@
|
|
|
313
362
|
$("#divColorsAtSwitchOnNightTime").show();
|
|
314
363
|
$("#divTemperatureAtSwitchOnNightTime").hide();
|
|
315
364
|
blinkBackground("#colorPickerNight")
|
|
316
|
-
$("#getColorAtSwitchOnDayTimeButton").text("Get
|
|
365
|
+
$("#getColorAtSwitchOnDayTimeButton").text("Get current");
|
|
317
366
|
} else if ($("#node-input-enableDayNightLighting").val() === "temperature") {
|
|
318
367
|
$("#divEnableDayNightLighting").show();
|
|
319
368
|
$("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
|
|
@@ -326,67 +375,65 @@
|
|
|
326
375
|
});
|
|
327
376
|
|
|
328
377
|
|
|
329
|
-
|
|
330
|
-
// Autocomplete suggestion with HUE Lights
|
|
331
|
-
$("#node-input-name").autocomplete({
|
|
332
|
-
minLength: 1,
|
|
333
|
-
source: function (request, response) {
|
|
334
|
-
$.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
|
|
335
|
-
response(
|
|
336
|
-
$.map(data.devices, function (value, key) {
|
|
337
|
-
//alert(JSON.stringify(value) + " "+ key)
|
|
338
|
-
var sSearch = value.name;
|
|
339
|
-
if (!value.name.includes("I'm still connecting")) {
|
|
340
|
-
if (fullSearch(sSearch, request.term)) {
|
|
341
|
-
return {
|
|
342
|
-
hueDevice: value.id,
|
|
343
|
-
value: value.name,
|
|
344
|
-
};
|
|
345
|
-
} else {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
} else {
|
|
349
|
-
return {
|
|
350
|
-
hueDevice: value.id,
|
|
351
|
-
value: value.name,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
})
|
|
355
|
-
);
|
|
356
|
-
});
|
|
357
|
-
},
|
|
358
|
-
select: function (event, ui) {
|
|
359
|
-
// Distinguish between group of lights an single light.
|
|
360
|
-
if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
|
|
361
|
-
$("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
|
|
362
|
-
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
363
|
-
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
364
|
-
} else {
|
|
365
|
-
$("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
|
|
366
|
-
$("#getColorAtSwitchOnDayTimeButton").show();
|
|
367
|
-
$("#getColorAtSwitchOnNightTimeButton").show();
|
|
368
|
-
}
|
|
369
|
-
$("#tabs").show();
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
|
|
373
378
|
// Get the HUE capabilities to enable/disable UI parts
|
|
374
379
|
$.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
|
|
375
380
|
data.devices.forEach((element) => {
|
|
376
381
|
if (element.id === this.hueDevice.split("#")[0] && element.deviceObject !== undefined) {
|
|
377
|
-
|
|
378
|
-
if (element.deviceObject.dimming === undefined) {
|
|
379
|
-
$("#tabs").tabs("disable", "#tabs-2");
|
|
380
|
-
$("#divColorsAtSwitchOn").hide();
|
|
381
|
-
$("#divColorCycle").hide();
|
|
382
|
-
}
|
|
382
|
+
|
|
383
383
|
// Check color
|
|
384
384
|
if (element.deviceObject.color_temperature === undefined) $("#tabs").tabs("disable", "#tabs-3");
|
|
385
|
-
if (element.deviceObject.color === undefined) {
|
|
385
|
+
if (element.deviceObject.color === undefined || JSON.stringify(deviceObject.color) === "{}") {
|
|
386
386
|
$("#tabs").tabs("disable", "#tabs-4");
|
|
387
387
|
$("#divColorsAtSwitchOn").hide();
|
|
388
388
|
$("#divColorCycle").hide();
|
|
389
|
+
$("#node-input-specifySwitchOnBrightness").empty().append(
|
|
390
|
+
$("<option>")
|
|
391
|
+
.val("no")
|
|
392
|
+
.text("Last status")
|
|
393
|
+
).append(
|
|
394
|
+
$("<option>")
|
|
395
|
+
.val("temperature")
|
|
396
|
+
.text("Select temperature and brightness")
|
|
397
|
+
);
|
|
398
|
+
$("#node-input-specifySwitchOnBrightness").val(this.specifySwitchOnBrightness).trigger('change');
|
|
399
|
+
|
|
400
|
+
$("#node-input-enableDayNightLighting").empty().append(
|
|
401
|
+
$("<option>")
|
|
402
|
+
.val("no")
|
|
403
|
+
.text("Last status")
|
|
404
|
+
).append(
|
|
405
|
+
$("<option>")
|
|
406
|
+
.val("temperature")
|
|
407
|
+
.text("Select temperature and brightness")
|
|
408
|
+
);
|
|
409
|
+
$("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Check dimming
|
|
413
|
+
if (element.deviceObject.dimming === undefined) {
|
|
414
|
+
$("#tabs").tabs("disable", "#tabs-2");
|
|
415
|
+
$("#divColorsAtSwitchOn").hide();
|
|
416
|
+
$("#divColorCycle").hide();
|
|
417
|
+
$("#divUpdateKNXBrightnessStatusOnHUEOnOff").hide();
|
|
418
|
+
$("#divCCSBoxAtNightLighting").hide();
|
|
419
|
+
$("#node-input-specifySwitchOnBrightness").val("no");
|
|
420
|
+
$("#node-input-enableDayNightLighting").val("no");
|
|
421
|
+
$("#divMinMaxBrightness").hide();
|
|
422
|
+
$("#node-input-specifySwitchOnBrightness").empty().append(
|
|
423
|
+
$("<option>")
|
|
424
|
+
.val("no")
|
|
425
|
+
.text("Last status")
|
|
426
|
+
)
|
|
427
|
+
$("#node-input-specifySwitchOnBrightness").val(this.specifySwitchOnBrightness).trigger('change');
|
|
428
|
+
|
|
429
|
+
$("#node-input-enableDayNightLighting").empty().append(
|
|
430
|
+
$("<option>")
|
|
431
|
+
.val("no")
|
|
432
|
+
.text("Last status")
|
|
433
|
+
)
|
|
389
434
|
}
|
|
435
|
+
$("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
|
|
436
|
+
|
|
390
437
|
// Check if grouped, to hide/show the "Get current" buttons
|
|
391
438
|
if (element.deviceObject.type === "grouped_light") {
|
|
392
439
|
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
@@ -476,7 +523,9 @@
|
|
|
476
523
|
this.colorAtSwitchOnNightTime = this.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property
|
|
477
524
|
try {
|
|
478
525
|
json = JSON.parse(this.colorAtSwitchOnDayTime);
|
|
479
|
-
} catch (error) {
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.log("json = JSON.parse(this.colorAtSwitchOnDayTime) in HTML: " + error.message)
|
|
528
|
+
}
|
|
480
529
|
if (json !== undefined && json.kelvin !== undefined) {
|
|
481
530
|
// Kelvin
|
|
482
531
|
$("#comboTemperatureAtSwitchOn").val(json.kelvin);
|
|
@@ -566,6 +615,9 @@
|
|
|
566
615
|
}
|
|
567
616
|
});
|
|
568
617
|
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
|
|
569
621
|
function rgbHex(red, green, blue, alpha) {
|
|
570
622
|
const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
|
|
571
623
|
const parseCssRgbString = (input) => {
|
|
@@ -937,7 +989,7 @@
|
|
|
937
989
|
<option value="yes">Yes, and emit KNX telegrams.</option>
|
|
938
990
|
</select>
|
|
939
991
|
</div>
|
|
940
|
-
<div class="form-row">
|
|
992
|
+
<div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
|
|
941
993
|
<label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
|
|
942
994
|
<i class="fa fa-tag"></i> KNX Brightness Status
|
|
943
995
|
</label>
|
|
@@ -1027,19 +1079,21 @@
|
|
|
1027
1079
|
</label>
|
|
1028
1080
|
<input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:260px">
|
|
1029
1081
|
</div>
|
|
1030
|
-
<div
|
|
1031
|
-
<
|
|
1032
|
-
<
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
<
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
<
|
|
1040
|
-
<
|
|
1041
|
-
|
|
1042
|
-
|
|
1082
|
+
<div id ="divMinMaxBrightness">
|
|
1083
|
+
<div class="form-row">
|
|
1084
|
+
<label for="node-input-minDimLevelLight" style="width:260px;">
|
|
1085
|
+
<i class="fa fa-clone"></i> Min Dim Brightness
|
|
1086
|
+
</label>
|
|
1087
|
+
<select id="node-input-minDimLevelLight">
|
|
1088
|
+
<option value="useHueLightLevel">Use minimum brightness specified in the HUE light</option>
|
|
1089
|
+
</select>
|
|
1090
|
+
</div>
|
|
1091
|
+
<div class="form-row">
|
|
1092
|
+
<label for="node-input-maxDimLevelLight" style="width:260px;">
|
|
1093
|
+
<i class="fa fa-clone"></i> Max Dim Brightness
|
|
1094
|
+
</label>
|
|
1095
|
+
<select id="node-input-maxDimLevelLight"></select>
|
|
1096
|
+
</div>
|
|
1043
1097
|
</div>
|
|
1044
1098
|
<div class="form-row">
|
|
1045
1099
|
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
@@ -481,7 +481,7 @@ module.exports = function (RED) {
|
|
|
481
481
|
node.handleSendHUE = (_event) => {
|
|
482
482
|
try {
|
|
483
483
|
if (_event.id === node.hueDevice) {
|
|
484
|
-
if (node.currentHUEDevice === undefined) {
|
|
484
|
+
if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
485
485
|
node.setNodeStatusHue({
|
|
486
486
|
fill: "red",
|
|
487
487
|
shape: "ring",
|
|
@@ -494,9 +494,15 @@ module.exports = function (RED) {
|
|
|
494
494
|
// Output the msg to the flow
|
|
495
495
|
node.send(_event);
|
|
496
496
|
|
|
497
|
+
// // DEBUG
|
|
498
|
+
//delete _event.dimming;
|
|
499
|
+
//delete _event.color;
|
|
500
|
+
//delete _event.color_temperature;
|
|
501
|
+
//delete _event.color_temperature_delta;
|
|
502
|
+
|
|
497
503
|
// As grouped_light doesn't contain all requested properties, i find the first light in the group, and use this below in the code
|
|
498
504
|
// If the event type is grouped light, and there are missing properties, i infer these missing properties from the first light in the group!
|
|
499
|
-
if ((_event.
|
|
505
|
+
if ((_event.color !== undefined || _event.dimming !== undefined || _event.color_temperature !== undefined) && _event.type === 'grouped_light') {
|
|
500
506
|
try {
|
|
501
507
|
const firstLightInGroup = node.serverHue.getFirstLightInGroup(_event.id);
|
|
502
508
|
if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
|
|
@@ -513,17 +519,19 @@ module.exports = function (RED) {
|
|
|
513
519
|
if (_event.hasOwnProperty("on")) {
|
|
514
520
|
node.updateKNXLightState(_event.on.on);
|
|
515
521
|
// In case of switch off, set the dim to zero
|
|
516
|
-
if (
|
|
517
|
-
_event.on.on === false
|
|
518
|
-
&& (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
|
|
519
|
-
) {
|
|
522
|
+
if (_event.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
520
523
|
node.updateKNXBrightnessState(0);
|
|
521
524
|
//node.currentHUEDevice.dimming.brightness = 0;
|
|
525
|
+
} else if (_event.on.on === true && node.currentHUEDevice.on.on === false) {
|
|
526
|
+
// Turn on always update the dimming KNX Status value as well.
|
|
527
|
+
let brightVal = 100;
|
|
528
|
+
if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) brightVal = node.currentHUEDevice.dimming.brightness;
|
|
529
|
+
node.updateKNXBrightnessState(brightVal);
|
|
522
530
|
}
|
|
523
531
|
node.currentHUEDevice.on.on = _event.on.on;
|
|
524
532
|
}
|
|
525
533
|
|
|
526
|
-
if (_event.
|
|
534
|
+
if (_event.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
527
535
|
node.updateKNXLightColorState(_event.color);
|
|
528
536
|
node.currentHUEDevice.color = _event.color;
|
|
529
537
|
}
|
|
@@ -658,33 +666,36 @@ module.exports = function (RED) {
|
|
|
658
666
|
|
|
659
667
|
node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
|
|
660
668
|
if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
|
|
669
|
+
if (!_value.hasOwnProperty('xy') || _value.xy.x === undefined) return;
|
|
661
670
|
const knxMsgPayload = {};
|
|
662
671
|
knxMsgPayload.topic = config.GALightColorState;
|
|
663
672
|
knxMsgPayload.dpt = config.dptLightColorState;
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (
|
|
673
|
-
node.server.
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
673
|
+
try {
|
|
674
|
+
knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
|
|
675
|
+
_value.xy.x,
|
|
676
|
+
_value.xy.y,
|
|
677
|
+
node.currentHUEDevice !== undefined && node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness === undefined ? node.currentHUEDevice.dimming.brightness : 100,
|
|
678
|
+
);
|
|
679
|
+
knxMsgPayload.payload = { red: knxMsgPayload.payload.r, green: knxMsgPayload.payload.g, blue: knxMsgPayload.payload.b };
|
|
680
|
+
// Send to KNX bus
|
|
681
|
+
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
682
|
+
if (node.server !== null && node.server !== undefined) {
|
|
683
|
+
node.server.writeQueueAdd({
|
|
684
|
+
grpaddr: knxMsgPayload.topic,
|
|
685
|
+
payload: knxMsgPayload.payload,
|
|
686
|
+
dpt: knxMsgPayload.dpt,
|
|
687
|
+
outputtype: _outputtype,
|
|
688
|
+
nodecallerid: node.id,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
680
691
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
})
|
|
692
|
+
node.setNodeStatusHue({
|
|
693
|
+
fill: "blue",
|
|
694
|
+
shape: "ring",
|
|
695
|
+
text: "HUE->KNX Color",
|
|
696
|
+
payload: knxMsgPayload.payload,
|
|
697
|
+
});
|
|
698
|
+
} catch (error) { }
|
|
688
699
|
}
|
|
689
700
|
};
|
|
690
701
|
|
|
@@ -7,7 +7,7 @@ module.exports = function (RED) {
|
|
|
7
7
|
node.topic = node.name;
|
|
8
8
|
node.name = config.name === undefined ? 'Hue' : config.name;
|
|
9
9
|
node.dpt = '';
|
|
10
|
-
node.notifyreadrequest =
|
|
10
|
+
node.notifyreadrequest = true;
|
|
11
11
|
node.notifyreadrequestalsorespondtobus = 'false';
|
|
12
12
|
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
13
13
|
node.notifyresponse = false;
|
|
@@ -23,7 +23,8 @@ module.exports = function (RED) {
|
|
|
23
23
|
node.formatnegativevalue = 'leave';
|
|
24
24
|
node.formatdecimalsvalue = 2;
|
|
25
25
|
node.hueDevice = config.hueDevice;
|
|
26
|
-
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "
|
|
26
|
+
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
|
|
27
|
+
node.currentDeviceValue = 0;
|
|
27
28
|
|
|
28
29
|
// Used to call the status update from the config node.
|
|
29
30
|
node.setNodeStatus = ({ fill, shape, text, payload }) => {
|
|
@@ -39,6 +40,19 @@ module.exports = function (RED) {
|
|
|
39
40
|
|
|
40
41
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
41
42
|
node.handleSend = msg => {
|
|
43
|
+
|
|
44
|
+
// Respond to KNX read telegram, by sending the current value as response telegram.
|
|
45
|
+
if (msg.knx.event === "GroupValue_Read") {
|
|
46
|
+
switch (msg.knx.destination) {
|
|
47
|
+
case config.GAlightsensor:
|
|
48
|
+
// To the KNX bus wires
|
|
49
|
+
node.sendResponseToKNX(node.currentDeviceValue);
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
};
|
|
43
57
|
|
|
44
58
|
node.handleSendHUE = _event => {
|
|
@@ -57,6 +71,8 @@ module.exports = function (RED) {
|
|
|
57
71
|
knxMsgPayload.payload = _event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000));
|
|
58
72
|
// Send to KNX bus
|
|
59
73
|
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id });
|
|
74
|
+
node.currentDeviceValue = knxMsgPayload.payload;
|
|
75
|
+
|
|
60
76
|
node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
61
77
|
|
|
62
78
|
// Setup the output msg
|
|
@@ -74,6 +90,20 @@ module.exports = function (RED) {
|
|
|
74
90
|
}
|
|
75
91
|
};
|
|
76
92
|
|
|
93
|
+
node.sendResponseToKNX = (_level) => {
|
|
94
|
+
const knxMsgPayload = {};
|
|
95
|
+
knxMsgPayload.topic = config.GAlightsensor;
|
|
96
|
+
knxMsgPayload.dpt = config.dptlightsensor;
|
|
97
|
+
|
|
98
|
+
knxMsgPayload.payload = _level;
|
|
99
|
+
// Send to KNX bus
|
|
100
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
101
|
+
node.server.writeQueueAdd({
|
|
102
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
77
107
|
// On each deploy, unsubscribe+resubscribe
|
|
78
108
|
if (node.server) {
|
|
79
109
|
node.server.removeClient(node);
|
|
@@ -7,7 +7,7 @@ module.exports = function (RED) {
|
|
|
7
7
|
node.topic = node.name;
|
|
8
8
|
node.name = config.name === undefined ? 'Hue' : config.name;
|
|
9
9
|
node.dpt = '';
|
|
10
|
-
node.notifyreadrequest =
|
|
10
|
+
node.notifyreadrequest = true;
|
|
11
11
|
node.notifyreadrequestalsorespondtobus = 'false';
|
|
12
12
|
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
13
13
|
node.notifyresponse = false;
|
|
@@ -23,7 +23,8 @@ module.exports = function (RED) {
|
|
|
23
23
|
node.formatnegativevalue = 'leave';
|
|
24
24
|
node.formatdecimalsvalue = 2;
|
|
25
25
|
node.hueDevice = config.hueDevice;
|
|
26
|
-
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "
|
|
26
|
+
node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
|
|
27
|
+
node.currentDeviceValue = 0;
|
|
27
28
|
|
|
28
29
|
// Used to call the status update from the config node.
|
|
29
30
|
node.setNodeStatus = ({
|
|
@@ -43,6 +44,17 @@ module.exports = function (RED) {
|
|
|
43
44
|
|
|
44
45
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
45
46
|
node.handleSend = (msg) => {
|
|
47
|
+
// Respond to KNX read telegram, by sending the current value as response telegram.
|
|
48
|
+
if (msg.knx.event === "GroupValue_Read") {
|
|
49
|
+
switch (msg.knx.destination) {
|
|
50
|
+
case config.GAtemperaturesensor:
|
|
51
|
+
// To the KNX bus wires
|
|
52
|
+
node.sendResponseToKNX(node.currentDeviceValue);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
};
|
|
47
59
|
|
|
48
60
|
node.handleSendHUE = (_event) => {
|
|
@@ -60,6 +72,8 @@ module.exports = function (RED) {
|
|
|
60
72
|
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
61
73
|
});
|
|
62
74
|
}
|
|
75
|
+
node.currentDeviceValue = knxMsgPayload.payload;
|
|
76
|
+
|
|
63
77
|
node.status({ fill: 'green', shape: 'dot', text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
|
|
64
78
|
|
|
65
79
|
// Setup the output msg
|
|
@@ -79,6 +93,22 @@ module.exports = function (RED) {
|
|
|
79
93
|
}
|
|
80
94
|
};
|
|
81
95
|
|
|
96
|
+
|
|
97
|
+
node.sendResponseToKNX = (_level) => {
|
|
98
|
+
const knxMsgPayload = {};
|
|
99
|
+
knxMsgPayload.topic = config.GAtemperaturesensor;
|
|
100
|
+
knxMsgPayload.dpt = config.dpttemperaturesensor;
|
|
101
|
+
|
|
102
|
+
knxMsgPayload.payload = _level;
|
|
103
|
+
// Send to KNX bus
|
|
104
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
105
|
+
node.server.writeQueueAdd({
|
|
106
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
|
|
82
112
|
// On each deploy, unsubscribe+resubscribe
|
|
83
113
|
if (node.server) {
|
|
84
114
|
node.server.removeClient(node);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.2.
|
|
6
|
+
"version": "2.2.26",
|
|
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 control.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"binary-parser": "2.2.1",
|