node-red-contrib-knx-ultimate 2.2.24 → 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 +16 -0
- package/nodes/hue-config.html +7 -9
- package/nodes/hue-config.js +68 -24
- package/nodes/knxUltimate-config.html +1 -1
- package/nodes/knxUltimate-config.js +2 -2
- package/nodes/knxUltimateHueBattery.html +2 -2
- package/nodes/knxUltimateHueBattery.js +31 -5
- package/nodes/knxUltimateHueButton.js +0 -2
- package/nodes/knxUltimateHueLight.html +152 -80
- package/nodes/knxUltimateHueLight.js +132 -65
- 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/nodes/utils/hueEngine.js +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@
|
|
|
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
|
+
|
|
18
|
+
**Version 2.2.25** - November 2023<br/>
|
|
19
|
+
- HUE Light: fixed settings of some behaviour options.<br/>
|
|
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/>
|
|
21
|
+
- Fixed some little bugs.<br/>
|
|
22
|
+
- Fixed an issue where in some circumstances, the HUE Light turn off by itself.<br/>
|
|
23
|
+
- WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
|
|
24
|
+
|
|
9
25
|
**Version 2.2.24** - November 2023<br/>
|
|
10
26
|
- HUE Light: added DPT 9.002 for direct kelvin selection, with HUE range (2000K-6535K). There is another DPT 7.600 with the KNX scale (0K-6553K) avaiable.<br/>
|
|
11
27
|
- NEW: HUE Light: now you can choose the "switch on" behaviour between color and temperature (in Kelvin) + brightness.<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
|
@@ -111,29 +111,30 @@ module.exports = (RED) => {
|
|
|
111
111
|
});
|
|
112
112
|
// Connected
|
|
113
113
|
node.hueManager.on("connected", () => {
|
|
114
|
-
node.linkStatus
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
114
|
+
if (node.linkStatus === "disconnected") {
|
|
115
|
+
node.linkStatus = "connected";
|
|
116
|
+
// Start the timer to do initial read.
|
|
117
|
+
if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
|
|
118
|
+
node.timerDoInitialRead = setTimeout(() => {
|
|
119
|
+
(async () => {
|
|
120
|
+
try {
|
|
121
|
+
await node.loadResourcesFromHUEBridge();
|
|
122
|
+
} catch (error) {
|
|
123
|
+
node.linkStatus = "disconnected";
|
|
124
|
+
node.nodeClients.forEach((_oClient) => {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
_oClient.setNodeStatusHue({
|
|
127
|
+
fill: "red",
|
|
128
|
+
shape: "ring",
|
|
129
|
+
text: "HUE",
|
|
130
|
+
payload: error.message,
|
|
131
|
+
});
|
|
132
|
+
}, 1000);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
}, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
|
|
137
|
+
}
|
|
137
138
|
});
|
|
138
139
|
|
|
139
140
|
node.hueManager.on("disconnected", () => {
|
|
@@ -173,6 +174,24 @@ module.exports = (RED) => {
|
|
|
173
174
|
// °°°°°° Load ALL resources
|
|
174
175
|
try {
|
|
175
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
|
+
|
|
176
195
|
if (node.hueAllResources !== undefined) {
|
|
177
196
|
node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
|
|
178
197
|
// Update all KNX State of the nodes with the new hue device values
|
|
@@ -204,6 +223,29 @@ module.exports = (RED) => {
|
|
|
204
223
|
//})();
|
|
205
224
|
};
|
|
206
225
|
|
|
226
|
+
node.getFirstLightInGroup = function getFirstLightInGroup(_groupID) {
|
|
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
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
207
249
|
// Returns the cached devices (node.hueAllResources) by type.
|
|
208
250
|
node.getResources = function getResources(_rtype) {
|
|
209
251
|
try {
|
|
@@ -423,8 +465,9 @@ module.exports = (RED) => {
|
|
|
423
465
|
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
424
466
|
const serverNode = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
425
467
|
if (serverNode === null) {
|
|
426
|
-
RED.log.
|
|
468
|
+
RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
|
|
427
469
|
res.json({ devices: `serverNode not set` });
|
|
470
|
+
return;
|
|
428
471
|
}
|
|
429
472
|
const jRet = serverNode.getResources(req.query.rtype);
|
|
430
473
|
if (jRet !== undefined) {
|
|
@@ -436,6 +479,7 @@ module.exports = (RED) => {
|
|
|
436
479
|
} catch (error) {
|
|
437
480
|
//RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
438
481
|
res.json({ devices: error.message });
|
|
482
|
+
RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
|
|
439
483
|
// (async () => {
|
|
440
484
|
// await node.initHUEConnection();
|
|
441
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" }
|
|
@@ -1244,7 +1244,7 @@ return msg;`,
|
|
|
1244
1244
|
devicename: msg.devicename,
|
|
1245
1245
|
});
|
|
1246
1246
|
input.handleSend(msg);
|
|
1247
|
-
} else if (input.topic
|
|
1247
|
+
} else if (input.topic === _dest) {
|
|
1248
1248
|
// 04/02/2020 Watchdog implementation
|
|
1249
1249
|
if (input.hasOwnProperty("isWatchDog")) {
|
|
1250
1250
|
// Is a watchdog node
|
|
@@ -1333,7 +1333,7 @@ return msg;`,
|
|
|
1333
1333
|
devicename: msg.devicename,
|
|
1334
1334
|
});
|
|
1335
1335
|
input.handleSend(msg);
|
|
1336
|
-
} else if (input.topic
|
|
1336
|
+
} else if (input.topic === _dest) {
|
|
1337
1337
|
// 04/02/2020 Watchdog implementation
|
|
1338
1338
|
if (input.hasOwnProperty("isWatchDog")) {
|
|
1339
1339
|
// Is a watchdog node
|
|
@@ -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,14 +100,37 @@
|
|
|
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
|
|
106
107
|
var oNodeServerHue = RED.nodes.node($("#node-input-serverHue").val()); // Store the config-node
|
|
107
108
|
|
|
109
|
+
// TIMER BLINK ####################################################
|
|
110
|
+
let blinkStatus = 2;
|
|
111
|
+
let timerBlinkBackground;
|
|
112
|
+
function blinkBackground(_elementIDwithHashAtTheBeginning) {
|
|
113
|
+
if (timerBlinkBackground !== undefined) clearInterval(timerBlinkBackground);
|
|
114
|
+
timerBlinkBackground = setInterval(() => {
|
|
115
|
+
if (isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "lightgreen");
|
|
116
|
+
if (!isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "");
|
|
117
|
+
blinkStatus += 1;
|
|
118
|
+
if (blinkStatus >= 14) {
|
|
119
|
+
clearInterval(timerBlinkBackground);
|
|
120
|
+
blinkStatus = 2;
|
|
121
|
+
$(_elementIDwithHashAtTheBeginning).css("background-color", "");
|
|
122
|
+
}
|
|
123
|
+
}, 100);
|
|
124
|
+
}
|
|
125
|
+
function isEven(n) {
|
|
126
|
+
return (n % 2 == 0);
|
|
127
|
+
}
|
|
128
|
+
// ################################################################
|
|
129
|
+
|
|
130
|
+
|
|
108
131
|
$("#tabs").tabs(); // Tabs gestione KNX
|
|
109
132
|
|
|
133
|
+
|
|
110
134
|
// 19/02/2020 Used to get the server sooner als deploy.
|
|
111
135
|
$("#node-input-server").change(function () {
|
|
112
136
|
try {
|
|
@@ -148,7 +172,7 @@
|
|
|
148
172
|
}
|
|
149
173
|
});
|
|
150
174
|
// Eval
|
|
151
|
-
const format = "
|
|
175
|
+
const format = "this." + _destinationWidget.replace("#node-input-", "");
|
|
152
176
|
try {
|
|
153
177
|
if (format !== undefined) $(_destinationWidget).val(eval(format).toString());
|
|
154
178
|
} catch (error) { }
|
|
@@ -243,6 +267,52 @@
|
|
|
243
267
|
getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", " 9.002");
|
|
244
268
|
|
|
245
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
|
+
|
|
246
316
|
// Show/Hide the div of the color at swich on
|
|
247
317
|
if (this.specifySwitchOnBrightness === "yes") {
|
|
248
318
|
$("#divColorsAtSwitchOn").show();
|
|
@@ -259,6 +329,7 @@
|
|
|
259
329
|
if ($("#node-input-specifySwitchOnBrightness").val() === "yes") {
|
|
260
330
|
$("#divColorsAtSwitchOn").show();
|
|
261
331
|
$("#divTemperatureAtSwitchOn").hide();
|
|
332
|
+
blinkBackground("#colorPickerDay");
|
|
262
333
|
} else if ($("#node-input-specifySwitchOnBrightness").val() === "temperature") {
|
|
263
334
|
$("#divColorsAtSwitchOn").hide();
|
|
264
335
|
$("#divTemperatureAtSwitchOn").show();
|
|
@@ -290,6 +361,8 @@
|
|
|
290
361
|
$("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
|
|
291
362
|
$("#divColorsAtSwitchOnNightTime").show();
|
|
292
363
|
$("#divTemperatureAtSwitchOnNightTime").hide();
|
|
364
|
+
blinkBackground("#colorPickerNight")
|
|
365
|
+
$("#getColorAtSwitchOnDayTimeButton").text("Get current");
|
|
293
366
|
} else if ($("#node-input-enableDayNightLighting").val() === "temperature") {
|
|
294
367
|
$("#divEnableDayNightLighting").show();
|
|
295
368
|
$("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
|
|
@@ -302,67 +375,65 @@
|
|
|
302
375
|
});
|
|
303
376
|
|
|
304
377
|
|
|
305
|
-
|
|
306
|
-
// Autocomplete suggestion with HUE Lights
|
|
307
|
-
$("#node-input-name").autocomplete({
|
|
308
|
-
minLength: 1,
|
|
309
|
-
source: function (request, response) {
|
|
310
|
-
$.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
|
|
311
|
-
response(
|
|
312
|
-
$.map(data.devices, function (value, key) {
|
|
313
|
-
//alert(JSON.stringify(value) + " "+ key)
|
|
314
|
-
var sSearch = value.name;
|
|
315
|
-
if (!value.name.includes("I'm still connecting")) {
|
|
316
|
-
if (fullSearch(sSearch, request.term)) {
|
|
317
|
-
return {
|
|
318
|
-
hueDevice: value.id,
|
|
319
|
-
value: value.name,
|
|
320
|
-
};
|
|
321
|
-
} else {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
return {
|
|
326
|
-
hueDevice: value.id,
|
|
327
|
-
value: value.name,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
})
|
|
331
|
-
);
|
|
332
|
-
});
|
|
333
|
-
},
|
|
334
|
-
select: function (event, ui) {
|
|
335
|
-
// Distinguish between group of lights an single light.
|
|
336
|
-
if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
|
|
337
|
-
$("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
|
|
338
|
-
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
339
|
-
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
340
|
-
} else {
|
|
341
|
-
$("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
|
|
342
|
-
$("#getColorAtSwitchOnDayTimeButton").show();
|
|
343
|
-
$("#getColorAtSwitchOnNightTimeButton").show();
|
|
344
|
-
}
|
|
345
|
-
$("#tabs").show();
|
|
346
|
-
},
|
|
347
|
-
});
|
|
348
|
-
|
|
349
378
|
// Get the HUE capabilities to enable/disable UI parts
|
|
350
379
|
$.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
|
|
351
380
|
data.devices.forEach((element) => {
|
|
352
381
|
if (element.id === this.hueDevice.split("#")[0] && element.deviceObject !== undefined) {
|
|
353
|
-
|
|
354
|
-
if (element.deviceObject.dimming === undefined) {
|
|
355
|
-
$("#tabs").tabs("disable", "#tabs-2");
|
|
356
|
-
$("#divColorsAtSwitchOn").hide();
|
|
357
|
-
$("#divColorCycle").hide();
|
|
358
|
-
}
|
|
382
|
+
|
|
359
383
|
// Check color
|
|
360
384
|
if (element.deviceObject.color_temperature === undefined) $("#tabs").tabs("disable", "#tabs-3");
|
|
361
|
-
if (element.deviceObject.color === undefined) {
|
|
385
|
+
if (element.deviceObject.color === undefined || JSON.stringify(deviceObject.color) === "{}") {
|
|
362
386
|
$("#tabs").tabs("disable", "#tabs-4");
|
|
363
387
|
$("#divColorsAtSwitchOn").hide();
|
|
364
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');
|
|
365
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
|
+
)
|
|
434
|
+
}
|
|
435
|
+
$("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
|
|
436
|
+
|
|
366
437
|
// Check if grouped, to hide/show the "Get current" buttons
|
|
367
438
|
if (element.deviceObject.type === "grouped_light") {
|
|
368
439
|
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
@@ -385,10 +456,7 @@
|
|
|
385
456
|
$.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
|
|
386
457
|
$("#node-input-colorAtSwitchOnDayTime").val(data);
|
|
387
458
|
$("#colorPickerDay").val(data);
|
|
388
|
-
|
|
389
|
-
setTimeout(() => {
|
|
390
|
-
$("#colorPickerDay").css("background-color", "");
|
|
391
|
-
}, 500);
|
|
459
|
+
blinkBackground("#colorPickerDay")
|
|
392
460
|
$("#getColorAtSwitchOnDayTimeButton").text("Get again");
|
|
393
461
|
});
|
|
394
462
|
});
|
|
@@ -402,10 +470,7 @@
|
|
|
402
470
|
$.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
|
|
403
471
|
$("#node-input-colorAtSwitchOnNightTime").val(data);
|
|
404
472
|
$("#colorPickerNight").val(data);
|
|
405
|
-
|
|
406
|
-
setTimeout(() => {
|
|
407
|
-
$("#colorPickerNight").css("background-color", "");
|
|
408
|
-
}, 500);
|
|
473
|
+
blinkBackground("#colorPickerNight")
|
|
409
474
|
$("#getColorAtSwitchOnNightTimeButton").text("Get again");
|
|
410
475
|
});
|
|
411
476
|
});
|
|
@@ -458,7 +523,9 @@
|
|
|
458
523
|
this.colorAtSwitchOnNightTime = this.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property
|
|
459
524
|
try {
|
|
460
525
|
json = JSON.parse(this.colorAtSwitchOnDayTime);
|
|
461
|
-
} catch (error) {
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.log("json = JSON.parse(this.colorAtSwitchOnDayTime) in HTML: " + error.message)
|
|
528
|
+
}
|
|
462
529
|
if (json !== undefined && json.kelvin !== undefined) {
|
|
463
530
|
// Kelvin
|
|
464
531
|
$("#comboTemperatureAtSwitchOn").val(json.kelvin);
|
|
@@ -548,6 +615,9 @@
|
|
|
548
615
|
}
|
|
549
616
|
});
|
|
550
617
|
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
|
|
551
621
|
function rgbHex(red, green, blue, alpha) {
|
|
552
622
|
const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
|
|
553
623
|
const parseCssRgbString = (input) => {
|
|
@@ -919,12 +989,12 @@
|
|
|
919
989
|
<option value="yes">Yes, and emit KNX telegrams.</option>
|
|
920
990
|
</select>
|
|
921
991
|
</div>
|
|
922
|
-
<div class="form-row">
|
|
992
|
+
<div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
|
|
923
993
|
<label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
|
|
924
994
|
<i class="fa fa-tag"></i> KNX Brightness Status
|
|
925
995
|
</label>
|
|
926
996
|
<select id="node-input-updateKNXBrightnessStatusOnHUEOnOff">
|
|
927
|
-
<option value="onhueoff">When HUE light is Off
|
|
997
|
+
<option value="onhueoff">When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)</option>
|
|
928
998
|
<option value="no">Leave as is (default HUE behaviour)</option>
|
|
929
999
|
</select>
|
|
930
1000
|
</div>
|
|
@@ -1009,19 +1079,21 @@
|
|
|
1009
1079
|
</label>
|
|
1010
1080
|
<input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:260px">
|
|
1011
1081
|
</div>
|
|
1012
|
-
<div
|
|
1013
|
-
<
|
|
1014
|
-
<
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
<
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
<
|
|
1022
|
-
<
|
|
1023
|
-
|
|
1024
|
-
|
|
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>
|
|
1025
1097
|
</div>
|
|
1026
1098
|
<div class="form-row">
|
|
1027
1099
|
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
@@ -1103,9 +1175,9 @@ Start typing in the GA field, the name or group address of your KNX device, the
|
|
|
1103
1175
|
|
|
1104
1176
|
| Property | Description |
|
|
1105
1177
|
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1106
|
-
| KNX Brightness Status | Updates the KNX brightness group address status, whenever the HUE lamp is switched ON/OFF. The options are **When HUE light is Off
|
|
1107
|
-
| Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/>**Select color:** the light will be switched on with the color of your choice.<br/>**Select temperature and brightness:** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br>**Last status:** the light will be switched on in the last status. |
|
|
1108
|
-
| Night Lighting | It allows to set a particular light color/brightness at nighttime.
|
|
1178
|
+
| KNX Brightness Status | Updates the KNX brightness group address status, whenever the HUE lamp is switched ON/OFF. The options are **When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)** and **Leave as is (default HUE behaviour)**. If you have KNX dimmer with brightness status, like MDT, the suggested option is ***When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)*** |
|
|
1179
|
+
| Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/>**Select color:** the light will be switched on with the color of your choice. To change color, just CLICK on the color selector (under the *Select color* control).<br/>**Select temperature and brightness:** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br>**Last status:** the light will be switched on in the last status. |
|
|
1180
|
+
| Night Lighting | It allows to set a particular light color/brightness at nighttime. The options are the same as the daytime. You could select either a temperature/brightness or color. A cozy temperature of 2700 Kelvin, with a brightness of 10% or 20%, is a good choice for bathroom's night light.|
|
|
1109
1181
|
| Day/Night | Select the group address used to set the day/night behaviour. The group address value is _true_ if daytime, _false_ if nighttime. |
|
|
1110
1182
|
| Invert day/night values | Invert the values of _Day/Night_ group address. Default value is **unchecked**. |
|
|
1111
1183
|
| Dim Speed (ms) | The dimming speed, in Milliseconds. This applies to the **light** and also to the **tunable white** dimming datapoints. It' calculated from 0% to 100%. |
|
|
@@ -15,7 +15,7 @@ module.exports = function (RED) {
|
|
|
15
15
|
node.name = config.name === undefined ? "Hue" : config.name;
|
|
16
16
|
node.outputtopic = node.name;
|
|
17
17
|
node.dpt = "";
|
|
18
|
-
node.notifyreadrequest =
|
|
18
|
+
node.notifyreadrequest = true;
|
|
19
19
|
node.notifyreadrequestalsorespondtobus = "false";
|
|
20
20
|
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = "";
|
|
21
21
|
node.notifyresponse = false;
|
|
@@ -335,6 +335,36 @@ module.exports = function (RED) {
|
|
|
335
335
|
default:
|
|
336
336
|
break;
|
|
337
337
|
}
|
|
338
|
+
|
|
339
|
+
// I must respond to query requests (read request) sent from the KNX BUS
|
|
340
|
+
|
|
341
|
+
if (msg.knx.event === "GroupValue_Read" && node.currentHUEDevice !== undefined) {
|
|
342
|
+
let ret;
|
|
343
|
+
switch (msg.knx.destination) {
|
|
344
|
+
case config.GALightState:
|
|
345
|
+
ret = node.currentHUEDevice.on.on;
|
|
346
|
+
if (ret !== undefined) node.updateKNXLightState(ret, "response");
|
|
347
|
+
break;
|
|
348
|
+
case config.GALightColorState:
|
|
349
|
+
ret = node.currentHUEDevice.color.xy;
|
|
350
|
+
if (ret !== undefined) node.updateKNXLightColorState(node.currentHUEDevice.color, "response");
|
|
351
|
+
break;
|
|
352
|
+
case config.GALightHSVState:
|
|
353
|
+
ret = node.currentHUEDevice.color_temperature.mirek;
|
|
354
|
+
if (ret !== undefined) node.updateKNXLightHSVState(ret, "response");
|
|
355
|
+
break;
|
|
356
|
+
case config.GALightBrightnessState:
|
|
357
|
+
ret = node.currentHUEDevice.dimming.brightness;
|
|
358
|
+
if (ret !== undefined) node.updateKNXBrightnessState(ret, "response");
|
|
359
|
+
break;
|
|
360
|
+
case config.GALightKelvinState:
|
|
361
|
+
ret = node.currentHUEDevice.color_temperature.mirek;
|
|
362
|
+
if (ret !== undefined) node.updateKNXLightKelvinState(ret, "response");
|
|
363
|
+
break;
|
|
364
|
+
default:
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
338
368
|
} catch (error) {
|
|
339
369
|
node.status({
|
|
340
370
|
fill: "red",
|
|
@@ -451,7 +481,7 @@ module.exports = function (RED) {
|
|
|
451
481
|
node.handleSendHUE = (_event) => {
|
|
452
482
|
try {
|
|
453
483
|
if (_event.id === node.hueDevice) {
|
|
454
|
-
if (node.currentHUEDevice === undefined) {
|
|
484
|
+
if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
455
485
|
node.setNodeStatusHue({
|
|
456
486
|
fill: "red",
|
|
457
487
|
shape: "ring",
|
|
@@ -464,25 +494,48 @@ module.exports = function (RED) {
|
|
|
464
494
|
// Output the msg to the flow
|
|
465
495
|
node.send(_event);
|
|
466
496
|
|
|
497
|
+
// // DEBUG
|
|
498
|
+
//delete _event.dimming;
|
|
499
|
+
//delete _event.color;
|
|
500
|
+
//delete _event.color_temperature;
|
|
501
|
+
//delete _event.color_temperature_delta;
|
|
502
|
+
|
|
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
|
|
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!
|
|
505
|
+
if ((_event.color !== undefined || _event.dimming !== undefined || _event.color_temperature !== undefined) && _event.type === 'grouped_light') {
|
|
506
|
+
try {
|
|
507
|
+
const firstLightInGroup = node.serverHue.getFirstLightInGroup(_event.id);
|
|
508
|
+
if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
|
|
509
|
+
if (_event.color === undefined || Object.keys(_event.color).length === 0) {
|
|
510
|
+
_event.color = firstLightInGroup.color;
|
|
511
|
+
}
|
|
512
|
+
if (_event.color_temperature === undefined || Object.keys(_event.color_temperature).length === 0) {
|
|
513
|
+
_event.color_temperature = firstLightInGroup.color_temperature;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
} catch (error) { }
|
|
517
|
+
}
|
|
518
|
+
|
|
467
519
|
if (_event.hasOwnProperty("on")) {
|
|
468
520
|
node.updateKNXLightState(_event.on.on);
|
|
469
521
|
// In case of switch off, set the dim to zero
|
|
470
|
-
if (
|
|
471
|
-
_event.on.on === false
|
|
472
|
-
&& (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
|
|
473
|
-
) {
|
|
522
|
+
if (_event.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
474
523
|
node.updateKNXBrightnessState(0);
|
|
475
|
-
node.currentHUEDevice.dimming.brightness = 0;
|
|
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);
|
|
476
530
|
}
|
|
477
531
|
node.currentHUEDevice.on.on = _event.on.on;
|
|
478
532
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
node.currentHUEDevice.color = _event.color;
|
|
484
|
-
}
|
|
533
|
+
|
|
534
|
+
if (_event.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
535
|
+
node.updateKNXLightColorState(_event.color);
|
|
536
|
+
node.currentHUEDevice.color = _event.color;
|
|
485
537
|
}
|
|
538
|
+
|
|
486
539
|
if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
|
|
487
540
|
// Every once on a time, the light transmit the brightness value of 0.39.
|
|
488
541
|
// To avoid wrongly turn light state on, exit
|
|
@@ -521,7 +574,7 @@ module.exports = function (RED) {
|
|
|
521
574
|
};
|
|
522
575
|
|
|
523
576
|
// Leave the name after "function", to avoid <anonymous function> in the stack trace, in caso of errors.
|
|
524
|
-
node.updateKNXBrightnessState = function updateKNXBrightnessState(_value) {
|
|
577
|
+
node.updateKNXBrightnessState = function updateKNXBrightnessState(_value, _outputtype = "write") {
|
|
525
578
|
if (config.GALightBrightnessState !== undefined && config.GALightBrightnessState !== "") {
|
|
526
579
|
const knxMsgPayload = {};
|
|
527
580
|
knxMsgPayload.topic = config.GALightBrightnessState;
|
|
@@ -529,13 +582,15 @@ module.exports = function (RED) {
|
|
|
529
582
|
knxMsgPayload.payload = _value;
|
|
530
583
|
// Send to KNX bus
|
|
531
584
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
532
|
-
node.server.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
585
|
+
if (node.server !== null && node.server !== undefined) {
|
|
586
|
+
node.server.writeQueueAdd({
|
|
587
|
+
grpaddr: knxMsgPayload.topic,
|
|
588
|
+
payload: knxMsgPayload.payload,
|
|
589
|
+
dpt: knxMsgPayload.dpt,
|
|
590
|
+
outputtype: _outputtype,
|
|
591
|
+
nodecallerid: node.id,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
539
594
|
}
|
|
540
595
|
node.setNodeStatusHue({
|
|
541
596
|
fill: "blue",
|
|
@@ -546,7 +601,7 @@ module.exports = function (RED) {
|
|
|
546
601
|
}
|
|
547
602
|
};
|
|
548
603
|
|
|
549
|
-
node.updateKNXLightState = function
|
|
604
|
+
node.updateKNXLightState = function updateKNXLightState(_value, _outputtype = "write") {
|
|
550
605
|
try {
|
|
551
606
|
const knxMsgPayload = {};
|
|
552
607
|
knxMsgPayload.topic = config.GALightState;
|
|
@@ -557,13 +612,15 @@ module.exports = function (RED) {
|
|
|
557
612
|
// Check not to have already sent the value
|
|
558
613
|
// Send to KNX bus
|
|
559
614
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
560
|
-
node.server.
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
615
|
+
if (node.server !== null && node.server !== undefined) {
|
|
616
|
+
node.server.writeQueueAdd({
|
|
617
|
+
grpaddr: knxMsgPayload.topic,
|
|
618
|
+
payload: knxMsgPayload.payload,
|
|
619
|
+
dpt: knxMsgPayload.dpt,
|
|
620
|
+
outputtype: _outputtype,
|
|
621
|
+
nodecallerid: node.id,
|
|
622
|
+
});
|
|
623
|
+
}
|
|
567
624
|
}
|
|
568
625
|
node.setNodeStatusHue({
|
|
569
626
|
fill: "blue",
|
|
@@ -577,7 +634,7 @@ module.exports = function (RED) {
|
|
|
577
634
|
}
|
|
578
635
|
};
|
|
579
636
|
|
|
580
|
-
node.updateKNXLightHSVState = function
|
|
637
|
+
node.updateKNXLightHSVState = function updateKNXLightHSVState(_value, _outputtype = "write") {
|
|
581
638
|
if (config.GALightHSVState !== undefined && config.GALightHSVState !== "") {
|
|
582
639
|
const knxMsgPayload = {};
|
|
583
640
|
knxMsgPayload.topic = config.GALightHSVState;
|
|
@@ -588,13 +645,15 @@ module.exports = function (RED) {
|
|
|
588
645
|
}
|
|
589
646
|
// Send to KNX bus
|
|
590
647
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
591
|
-
node.server.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
648
|
+
if (node.server !== null && node.server !== undefined) {
|
|
649
|
+
node.server.writeQueueAdd({
|
|
650
|
+
grpaddr: knxMsgPayload.topic,
|
|
651
|
+
payload: knxMsgPayload.payload,
|
|
652
|
+
dpt: knxMsgPayload.dpt,
|
|
653
|
+
outputtype: _outputtype,
|
|
654
|
+
nodecallerid: node.id,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
598
657
|
}
|
|
599
658
|
node.setNodeStatusHue({
|
|
600
659
|
fill: "blue",
|
|
@@ -605,36 +664,42 @@ module.exports = function (RED) {
|
|
|
605
664
|
}
|
|
606
665
|
};
|
|
607
666
|
|
|
608
|
-
node.updateKNXLightColorState = function updateKNXLightColorState(_value) {
|
|
667
|
+
node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
|
|
609
668
|
if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
|
|
669
|
+
if (!_value.hasOwnProperty('xy') || _value.xy.x === undefined) return;
|
|
610
670
|
const knxMsgPayload = {};
|
|
611
671
|
knxMsgPayload.topic = config.GALightColorState;
|
|
612
672
|
knxMsgPayload.dpt = config.dptLightColorState;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
+
}
|
|
691
|
+
}
|
|
692
|
+
node.setNodeStatusHue({
|
|
693
|
+
fill: "blue",
|
|
694
|
+
shape: "ring",
|
|
695
|
+
text: "HUE->KNX Color",
|
|
622
696
|
payload: knxMsgPayload.payload,
|
|
623
|
-
dpt: knxMsgPayload.dpt,
|
|
624
|
-
outputtype: "write",
|
|
625
|
-
nodecallerid: node.id,
|
|
626
697
|
});
|
|
627
|
-
}
|
|
628
|
-
node.setNodeStatusHue({
|
|
629
|
-
fill: "blue",
|
|
630
|
-
shape: "ring",
|
|
631
|
-
text: "HUE->KNX Color",
|
|
632
|
-
payload: knxMsgPayload.payload,
|
|
633
|
-
});
|
|
698
|
+
} catch (error) { }
|
|
634
699
|
}
|
|
635
700
|
};
|
|
636
701
|
|
|
637
|
-
node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value) {
|
|
702
|
+
node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value, _outputtype = "write") {
|
|
638
703
|
if (config.GALightKelvinState !== undefined && config.GALightKelvinState !== "") {
|
|
639
704
|
const knxMsgPayload = {};
|
|
640
705
|
knxMsgPayload.topic = config.GALightKelvinState;
|
|
@@ -646,13 +711,15 @@ module.exports = function (RED) {
|
|
|
646
711
|
}
|
|
647
712
|
// Send to KNX bus
|
|
648
713
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
649
|
-
node.server.
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
714
|
+
if (node.server !== null && node.server !== undefined) {
|
|
715
|
+
node.server.writeQueueAdd({
|
|
716
|
+
grpaddr: knxMsgPayload.topic,
|
|
717
|
+
payload: knxMsgPayload.payload,
|
|
718
|
+
dpt: knxMsgPayload.dpt,
|
|
719
|
+
outputtype: _outputtype,
|
|
720
|
+
nodecallerid: node.id,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
656
723
|
|
|
657
724
|
node.setNodeStatusHue({
|
|
658
725
|
fill: "blue",
|
|
@@ -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/nodes/utils/hueEngine.js
CHANGED
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",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"simple-get": "4.0.1",
|
|
20
20
|
"xml2js": "0.6.0"
|
|
21
21
|
},
|
|
22
|
+
|
|
22
23
|
"node-red": {
|
|
23
24
|
"version": ">=2.0.0",
|
|
24
25
|
"nodes": {
|