node-red-contrib-knx-ultimate 2.2.16 → 2.2.19
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.js +67 -42
- package/nodes/knxUltimateHueBattery.js +0 -1
- package/nodes/knxUltimateHueButton.js +0 -1
- package/nodes/knxUltimateHueLight.js +17 -22
- package/nodes/knxUltimateHueLightSensor.js +0 -2
- package/nodes/knxUltimateHueMotion.js +0 -2
- package/nodes/knxUltimateHueScene.js +0 -1
- package/nodes/knxUltimateHueTapDial.js +1 -2
- package/nodes/utils/hueEngine.js +18 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
<b>Version 2.2.19</b> - November 2023<br/>
|
|
10
|
+
<p>
|
|
11
|
+
- HUE Bugfix.<br/>
|
|
12
|
+
</p>
|
|
13
|
+
<b>Version 2.2.18</b> - November 2023<br/>
|
|
14
|
+
<p>
|
|
15
|
+
- HUE Bugfix.<br/>
|
|
16
|
+
- New connection check for HUE bridge.<br/>
|
|
17
|
+
</p>
|
|
9
18
|
<b>Version 2.2.16</b> - November 2023<br/>
|
|
10
19
|
<p>
|
|
11
20
|
- NEW: Hue Light: you can now enable the input/output PINs and send/receive commands to/from the light, via the msg flow, like msg.on={"on":true}. The option is "Node Input/Output PINs".<br/>
|
package/nodes/hue-config.js
CHANGED
|
@@ -94,9 +94,10 @@ module.exports = (RED) => {
|
|
|
94
94
|
// Init HUE Utility
|
|
95
95
|
node.hueManager = new HueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid, node.sysLogger);
|
|
96
96
|
} catch (error) { }
|
|
97
|
-
|
|
97
|
+
node.hueManager.Connect();
|
|
98
98
|
} catch (error) {
|
|
99
|
-
|
|
99
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`Errore hue-config: node.initHUEConnection: ${error.message}`);
|
|
100
|
+
node.linkStatus = "disconnected";
|
|
100
101
|
}
|
|
101
102
|
node.hueManager.on("event", (_event) => {
|
|
102
103
|
node.nodeClients.forEach((_oClient) => {
|
|
@@ -114,12 +115,42 @@ module.exports = (RED) => {
|
|
|
114
115
|
// Start the timer to do initial read.
|
|
115
116
|
if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
|
|
116
117
|
node.timerDoInitialRead = setTimeout(() => {
|
|
117
|
-
|
|
118
|
+
(async () => {
|
|
119
|
+
try {
|
|
120
|
+
await node.loadResourcesFromHUEBridge();
|
|
121
|
+
} catch (error) {
|
|
122
|
+
node.linkStatus = "disconnected";
|
|
123
|
+
node.nodeClients.forEach((_oClient) => {
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
_oClient.setNodeStatusHue({
|
|
126
|
+
fill: "red",
|
|
127
|
+
shape: "ring",
|
|
128
|
+
text: "HUE",
|
|
129
|
+
payload: error.message,
|
|
130
|
+
});
|
|
131
|
+
}, 1000);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
node.startWatchdogTimer();
|
|
135
|
+
})();
|
|
118
136
|
}, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
|
|
119
137
|
});
|
|
138
|
+
|
|
139
|
+
node.hueManager.on("disconnected", () => {
|
|
140
|
+
node.linkStatus = "disconnected";
|
|
141
|
+
node.nodeClients.forEach((_oClient) => {
|
|
142
|
+
_oClient.setNodeStatusHue({
|
|
143
|
+
fill: "red",
|
|
144
|
+
shape: "ring",
|
|
145
|
+
text: "HUE Disconnected",
|
|
146
|
+
payload: "",
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
120
150
|
};
|
|
121
151
|
|
|
122
152
|
node.startWatchdogTimer = () => {
|
|
153
|
+
if (node.timerHUEConfigCheckState !== undefined) clearTimeout(node.timerHUEConfigCheckState);
|
|
123
154
|
node.timerHUEConfigCheckState = setTimeout(() => {
|
|
124
155
|
(async () => {
|
|
125
156
|
if (node.linkStatus === "disconnected") {
|
|
@@ -128,12 +159,6 @@ module.exports = (RED) => {
|
|
|
128
159
|
} catch (error) {
|
|
129
160
|
node.linkStatus = "disconnected";
|
|
130
161
|
}
|
|
131
|
-
} else {
|
|
132
|
-
try {
|
|
133
|
-
// Check wether the hue connection is still alive
|
|
134
|
-
const ret = await node.hueManager.isConnected();
|
|
135
|
-
if (!ret) node.linkStatus = "disconnected";
|
|
136
|
-
} catch (error) { node.linkStatus = "disconnected"; }
|
|
137
162
|
}
|
|
138
163
|
node.startWatchdogTimer();
|
|
139
164
|
})();
|
|
@@ -142,40 +167,41 @@ module.exports = (RED) => {
|
|
|
142
167
|
node.startWatchdogTimer();
|
|
143
168
|
|
|
144
169
|
// Query the HUE Bridge to return the resources
|
|
145
|
-
node.loadResourcesFromHUEBridge = () => {
|
|
146
|
-
(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
node.loadResourcesFromHUEBridge = async () => {
|
|
171
|
+
if (node.linkStatus === "disconnected") return;
|
|
172
|
+
//(async () => {
|
|
173
|
+
// °°°°°° Load ALL resources
|
|
174
|
+
try {
|
|
175
|
+
node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
|
|
176
|
+
if (node.hueAllResources !== undefined) {
|
|
177
|
+
node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
|
|
178
|
+
// Update all KNX State of the nodes with the new hue device values
|
|
179
|
+
node.nodeClients.forEach((_node) => {
|
|
180
|
+
if (_node.hueDevice !== undefined && node.hueAllResources !== undefined) {
|
|
181
|
+
const oHUEDevice = node.hueAllResources.filter((a) => a.id === _node.hueDevice)[0];
|
|
182
|
+
if (oHUEDevice !== undefined) {
|
|
183
|
+
// Add _Node to the clients array
|
|
184
|
+
_node.setNodeStatusHue({
|
|
185
|
+
fill: "green",
|
|
186
|
+
shape: "ring",
|
|
187
|
+
text: "Ready :-)",
|
|
188
|
+
});
|
|
189
|
+
_node.currentHUEDevice = oHUEDevice;
|
|
190
|
+
if (_node.initializingAtStart === true) _node.handleSendHUE(oHUEDevice);
|
|
166
191
|
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
} catch (error) {
|
|
172
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null) {
|
|
173
|
-
this.sysLogger.error(`KNXUltimatehueEngine: loadResourcesFromHUEBridge: ${error.message}`);
|
|
174
|
-
return (error.message);
|
|
175
|
-
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
// The config node cannot read the resources. Signalling disconnected
|
|
176
196
|
}
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) {
|
|
199
|
+
this.sysLogger.error(`KNXUltimatehueEngine: loadResourcesFromHUEBridge: ${error.message}`);
|
|
200
|
+
throw (error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
//})();
|
|
179
205
|
};
|
|
180
206
|
|
|
181
207
|
// Returns the cached devices (node.hueAllResources) by type.
|
|
@@ -296,7 +322,6 @@ module.exports = (RED) => {
|
|
|
296
322
|
// Query HUE Bridge and get the updated color.
|
|
297
323
|
node.getColorFromHueLight = (_lightId) => {
|
|
298
324
|
try {
|
|
299
|
-
// await node.loadResourcesFromHUEBridge();
|
|
300
325
|
const oLight = node.hueAllResources.filter((a) => a.id === _lightId)[0];
|
|
301
326
|
const ret = hueColorConverter.ColorConverter.xyBriToRgb(oLight.color.xy.x, oLight.color.xy.y, oLight.dimming.brightness);
|
|
302
327
|
return JSON.stringify(ret);
|
|
@@ -49,7 +49,6 @@ module.exports = function (RED) {
|
|
|
49
49
|
if (_event.id === config.hueDevice) {
|
|
50
50
|
|
|
51
51
|
// IMPORTANT: exit if no event presen.
|
|
52
|
-
if (!node.initializingAtStart) return;
|
|
53
52
|
if (!_event.hasOwnProperty("power_state") || _event.power_state.battery_level === undefined) return;
|
|
54
53
|
|
|
55
54
|
const knxMsgPayload = {};
|
|
@@ -79,7 +79,6 @@ module.exports = function (RED) {
|
|
|
79
79
|
if (_event.id === config.hueDevice) {
|
|
80
80
|
|
|
81
81
|
// IMPORTANT: exit if no button last_event present.
|
|
82
|
-
if (!node.initializingAtStart) return;
|
|
83
82
|
if (!_event.hasOwnProperty("button") || _event.button.last_event === undefined) return;
|
|
84
83
|
|
|
85
84
|
const knxMsgPayload = {};
|
|
@@ -31,7 +31,6 @@ module.exports = function (RED) {
|
|
|
31
31
|
node.formatnegativevalue = "leave";
|
|
32
32
|
node.formatdecimalsvalue = 2;
|
|
33
33
|
node.currentHUEDevice = undefined; // At start, this value is filled by a call to HUE api. It stores a value representing the current light status.
|
|
34
|
-
node.currentKNXGALightState = false; // Stores the current KNX value for the GA
|
|
35
34
|
node.DayTime = true;
|
|
36
35
|
node.isGrouped_light = config.hueDevice.split("#")[1] === "grouped_light";
|
|
37
36
|
node.hueDevice = config.hueDevice.split("#")[0];
|
|
@@ -57,7 +56,7 @@ module.exports = function (RED) {
|
|
|
57
56
|
|
|
58
57
|
// This function is called by the hue-config.js
|
|
59
58
|
node.handleSend = (msg) => {
|
|
60
|
-
if (node.currentHUEDevice === undefined) {
|
|
59
|
+
if (node.currentHUEDevice === undefined && node.serverHue.linkStatus === "connected") {
|
|
61
60
|
node.setNodeStatusHue({
|
|
62
61
|
fill: "yellow",
|
|
63
62
|
shape: "ring",
|
|
@@ -426,8 +425,6 @@ module.exports = function (RED) {
|
|
|
426
425
|
});
|
|
427
426
|
return;
|
|
428
427
|
}
|
|
429
|
-
// IMPORTANT: exit if no button last_event present.
|
|
430
|
-
if (!node.initializingAtStart) return;
|
|
431
428
|
|
|
432
429
|
// Output the msg to the flow
|
|
433
430
|
node.send(_event);
|
|
@@ -458,7 +455,7 @@ module.exports = function (RED) {
|
|
|
458
455
|
if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
|
|
459
456
|
// Do nothing, because the light is off and the dimming also is 0
|
|
460
457
|
} else {
|
|
461
|
-
if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
|
|
458
|
+
if (node.currentHUEDevice.on.on === false && (!_event.hasOwnProperty("on") || (_event.hasOwnProperty("on") && _event.on.on === true))) node.updateKNXLightState(_event.dimming.brightness > 0);
|
|
462
459
|
node.updateKNXBrightnessState(_event.dimming.brightness);
|
|
463
460
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
464
461
|
if (_event.dimming.brightness === 0) {
|
|
@@ -520,27 +517,25 @@ module.exports = function (RED) {
|
|
|
520
517
|
knxMsgPayload.dpt = config.dptLightState;
|
|
521
518
|
knxMsgPayload.payload = _value;
|
|
522
519
|
if (config.GALightState !== undefined && config.GALightState !== "") {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
payload: knxMsgPayload.payload,
|
|
530
|
-
dpt: knxMsgPayload.dpt,
|
|
531
|
-
outputtype: "write",
|
|
532
|
-
nodecallerid: node.id,
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
node.setNodeStatusHue({
|
|
536
|
-
fill: "blue",
|
|
537
|
-
shape: "ring",
|
|
538
|
-
text: "HUE->KNX On/Off",
|
|
520
|
+
|
|
521
|
+
// Check not to have already sent the value
|
|
522
|
+
// Send to KNX bus
|
|
523
|
+
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
524
|
+
node.server.writeQueueAdd({
|
|
525
|
+
grpaddr: knxMsgPayload.topic,
|
|
539
526
|
payload: knxMsgPayload.payload,
|
|
527
|
+
dpt: knxMsgPayload.dpt,
|
|
528
|
+
outputtype: "write",
|
|
529
|
+
nodecallerid: node.id,
|
|
540
530
|
});
|
|
541
531
|
}
|
|
532
|
+
node.setNodeStatusHue({
|
|
533
|
+
fill: "blue",
|
|
534
|
+
shape: "ring",
|
|
535
|
+
text: "HUE->KNX On/Off",
|
|
536
|
+
payload: knxMsgPayload.payload,
|
|
537
|
+
});
|
|
542
538
|
}
|
|
543
|
-
node.currentKNXGALightState = knxMsgPayload.payload; // Stores the current value
|
|
544
539
|
} catch (error) {
|
|
545
540
|
/* empty */
|
|
546
541
|
}
|
|
@@ -45,8 +45,6 @@ module.exports = function (RED) {
|
|
|
45
45
|
try {
|
|
46
46
|
if (_event.id === config.hueDevice) {
|
|
47
47
|
|
|
48
|
-
// IMPORTANT: exit if no event presen.
|
|
49
|
-
if (!node.initializingAtStart) return;
|
|
50
48
|
if (!_event.hasOwnProperty('light') || _event.light.light_level === undefined) return;
|
|
51
49
|
|
|
52
50
|
const knxMsgPayload = {};
|
|
@@ -42,8 +42,6 @@ module.exports = function (RED) {
|
|
|
42
42
|
try {
|
|
43
43
|
if (_event.id === config.hueDevice) {
|
|
44
44
|
|
|
45
|
-
// IMPORTANT: exit if no event presen.
|
|
46
|
-
if (!node.initializingAtStart) return;
|
|
47
45
|
if (!_event.hasOwnProperty("motion") || _event.motion.motion === undefined) return;
|
|
48
46
|
|
|
49
47
|
|
|
@@ -51,8 +51,7 @@ module.exports = function (RED) {
|
|
|
51
51
|
try {
|
|
52
52
|
if (_event.id === config.hueDevice) {
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
if (!node.initializingAtStart) return;
|
|
54
|
+
|
|
56
55
|
if (!_event.hasOwnProperty("relative_rotary")
|
|
57
56
|
|| !_event.relative_rotary.hasOwnProperty("last_event")
|
|
58
57
|
|| _event.relative_rotary.last_event === undefined
|
package/nodes/utils/hueEngine.js
CHANGED
|
@@ -13,11 +13,14 @@ class classHUE extends EventEmitter {
|
|
|
13
13
|
this.commandQueue = [];
|
|
14
14
|
this.closePushEventStream = false;
|
|
15
15
|
// eslint-disable-next-line max-len
|
|
16
|
-
this.timerwriteQueueAdd = setTimeout(this.handleQueue,
|
|
16
|
+
this.timerwriteQueueAdd = setTimeout(this.handleQueue, 10000); // First start. Allow the KNX to connect
|
|
17
17
|
this.sysLogger = _sysLogger;
|
|
18
|
+
this.timerCheckConnected = undefined;
|
|
19
|
+
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
Connect =
|
|
22
|
+
Connect = () => {
|
|
23
|
+
|
|
21
24
|
const options = {
|
|
22
25
|
headers: {
|
|
23
26
|
"hue-application-key": this.username,
|
|
@@ -70,6 +73,12 @@ class classHUE extends EventEmitter {
|
|
|
70
73
|
this.es.onopen = () => {
|
|
71
74
|
// if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: SSE-Connected')
|
|
72
75
|
this.emit("connected");
|
|
76
|
+
|
|
77
|
+
// Check wether the hue bridge is connected or not
|
|
78
|
+
if (this.timerCheckConnected !== undefined) clearInterval(this.timerCheckConnected);
|
|
79
|
+
this.timerCheckConnected = setInterval(() => {
|
|
80
|
+
this.writeHueQueueAdd(null, null, "Ping");
|
|
81
|
+
}, 30000);
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
// this.es.onerror = (error) => {
|
|
@@ -146,28 +155,14 @@ class classHUE extends EventEmitter {
|
|
|
146
155
|
this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: stopScene: ${error.message}`);
|
|
147
156
|
}
|
|
148
157
|
break;
|
|
149
|
-
case "
|
|
150
|
-
try {
|
|
151
|
-
const jReturn = await this.hueApiV2.get(`/resource/device_power/${jRet._lightID}`);
|
|
152
|
-
} catch (error) {
|
|
153
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null)
|
|
154
|
-
this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: getBattery: ${error.message}`);
|
|
155
|
-
}
|
|
156
|
-
break;
|
|
157
|
-
case "getLightLevel":
|
|
158
|
+
case "Ping":
|
|
158
159
|
try {
|
|
159
|
-
const jReturn = await this.hueApiV2.get(
|
|
160
|
+
const jReturn = await this.hueApiV2.get('/resource/bridge');
|
|
160
161
|
} catch (error) {
|
|
161
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
case "getTemperature":
|
|
166
|
-
try {
|
|
167
|
-
const jReturn = await this.hueApiV2.get(`/resource/temperature/${jRet._lightID}`);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null)
|
|
170
|
-
this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: getTemperature: ${error.message}`);
|
|
162
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: Ping: ${error.message}`);
|
|
163
|
+
if (this.timerCheckConnected !== undefined) clearInterval(this.timerCheckConnected);
|
|
164
|
+
this.commandQueue.length = [];
|
|
165
|
+
this.emit("disconnected");
|
|
171
166
|
}
|
|
172
167
|
break;
|
|
173
168
|
default:
|
|
@@ -175,6 +170,7 @@ class classHUE extends EventEmitter {
|
|
|
175
170
|
}
|
|
176
171
|
}
|
|
177
172
|
// The Hue bridge allows about 10 telegram per second, so i need to make a queue manager
|
|
173
|
+
//await new Promise(resolve => setTimeout(resolve, 2000));
|
|
178
174
|
this.timerwriteQueueAdd = setTimeout(this.handleQueue, 200);
|
|
179
175
|
};
|
|
180
176
|
|
|
@@ -184,16 +180,6 @@ class classHUE extends EventEmitter {
|
|
|
184
180
|
};
|
|
185
181
|
// ######################################
|
|
186
182
|
|
|
187
|
-
isConnected = async () => {
|
|
188
|
-
try {
|
|
189
|
-
await this.hueApiV2.get('/resource/bridge');
|
|
190
|
-
return true;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.log("hueEngine: isConnected: " + error.message)
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
183
|
close = async () =>
|
|
198
184
|
new Promise((resolve, reject) => {
|
|
199
185
|
try {
|
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.19",
|
|
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",
|