node-red-contrib-knx-ultimate 2.3.4 → 2.4.1
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/.github/ISSUE_TEMPLATE/bug_report.md +7 -7
- package/CHANGELOG.md +29 -0
- package/KNXEngine/CHANGELOG.md +5 -1
- package/KNXEngine/package.json +1 -1
- package/KNXEngine/src/dptlib/dpt275.js +60 -0
- package/KNXEngine/src/dptlib/index.js +79 -65
- package/nodes/commonFunctions.js +419 -0
- package/nodes/hue-config.html +56 -40
- package/nodes/hue-config.js +0 -108
- package/nodes/knxUltimate-config.html +1 -1
- package/nodes/knxUltimate-config.js +13 -232
- package/nodes/knxUltimate.html +2 -2
- package/nodes/knxUltimateHueBattery.html +2 -2
- package/nodes/knxUltimateHueButton.html +4 -4
- package/nodes/knxUltimateHueLight.html +989 -947
- package/nodes/knxUltimateHueLight.js +50 -26
- package/nodes/knxUltimateHueLightSensor.html +2 -2
- package/nodes/knxUltimateHueMotion.html +2 -2
- package/nodes/knxUltimateHueScene.html +4 -4
- package/nodes/knxUltimateHueTapDial.html +2 -2
- package/nodes/knxUltimateHueTemperatureSensor.html +2 -2
- package/nodes/knxUltimateLoadControl.html +1 -1
- package/nodes/knxUltimateSceneController.html +6 -6
- package/nodes/knxUltimateSceneController.js +1 -9
- package/package.json +2 -2
- package/nodes/utils/commonFunctions.js +0 -21
package/nodes/hue-config.js
CHANGED
|
@@ -468,114 +468,6 @@ module.exports = (RED) => {
|
|
|
468
468
|
done();
|
|
469
469
|
}
|
|
470
470
|
});
|
|
471
|
-
|
|
472
|
-
RED.httpAdmin.get("/knxUltimateGetHueColor", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
473
|
-
try {
|
|
474
|
-
// find wether the light is a light or is grouped_light
|
|
475
|
-
let hexColor;
|
|
476
|
-
const _oDevice = node.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
477
|
-
if (_oDevice.type === "light") {
|
|
478
|
-
hexColor = node.getColorFromHueLight(req.query.id);
|
|
479
|
-
} else {
|
|
480
|
-
// grouped_light, get the first light in the group
|
|
481
|
-
const oLight = node.getFirstLightInGroup(_oDevice.id);
|
|
482
|
-
hexColor = node.getColorFromHueLight(oLight.id);
|
|
483
|
-
}
|
|
484
|
-
res.json(hexColor !== undefined ? hexColor : "Select the device first!");
|
|
485
|
-
} catch (error) {
|
|
486
|
-
res.json("Select the device first!");
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
RED.httpAdmin.get("/knxUltimateGetKelvinColor", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
490
|
-
try {
|
|
491
|
-
// find wether the light is a light or is grouped_light
|
|
492
|
-
let kelvinValue;
|
|
493
|
-
const _oDevice = node.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
494
|
-
if (_oDevice.type === "light") {
|
|
495
|
-
kelvinValue = node.getKelvinFromHueLight(req.query.id);
|
|
496
|
-
} else {
|
|
497
|
-
// grouped_light, get the first light in the group
|
|
498
|
-
const oLight = node.getFirstLightInGroup(_oDevice.id);
|
|
499
|
-
kelvinValue = node.getKelvinFromHueLight(oLight.id);
|
|
500
|
-
}
|
|
501
|
-
res.json(kelvinValue !== undefined ? kelvinValue : "Select the device first!");
|
|
502
|
-
} catch (error) {
|
|
503
|
-
res.json("Select the device first!");
|
|
504
|
-
};
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
RED.httpAdmin.get("/knxUltimateGetLightObject", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
508
|
-
try {
|
|
509
|
-
if (node.hueAllResources === undefined) {
|
|
510
|
-
throw (new Error("Resource not yet loaded"));
|
|
511
|
-
}
|
|
512
|
-
const _lightId = req.query.id;
|
|
513
|
-
const oLight = node.hueAllResources.filter((a) => a.id === _lightId)[0];
|
|
514
|
-
// Infer some useful info, so the HTML part can avoid to query the server
|
|
515
|
-
// Kelvin
|
|
516
|
-
try {
|
|
517
|
-
if (oLight.color_temperature !== undefined && oLight.color_temperature.mirek !== undefined) {
|
|
518
|
-
oLight.calculatedKelvin = hueColorConverter.ColorConverter.mirekToKelvin(oLight.color_temperature.mirek);
|
|
519
|
-
}
|
|
520
|
-
} catch (error) {
|
|
521
|
-
oLight.calculatedKelvin = undefined;
|
|
522
|
-
}
|
|
523
|
-
// HEX value from XYBri
|
|
524
|
-
try {
|
|
525
|
-
const retRGB = hueColorConverter.ColorConverter.xyBriToRgb(oLight.color.xy.x, oLight.color.xy.y, oLight.dimming.brightness);
|
|
526
|
-
const ret = "#" + hueColorConverter.ColorConverter.rgbHex(retRGB.r, retRGB.g, retRGB.b).toString();
|
|
527
|
-
oLight.calculatedHEXColor = ret;
|
|
528
|
-
} catch (error) {
|
|
529
|
-
oLight.calculatedHEXColor = undefined;
|
|
530
|
-
}
|
|
531
|
-
res.json(oLight);
|
|
532
|
-
} catch (error) {
|
|
533
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}.`);
|
|
534
|
-
res.json({});
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
RED.httpAdmin.get("/KNXUltimateGetResourcesHUE", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
539
|
-
try {
|
|
540
|
-
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
541
|
-
const serverNode = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
542
|
-
if (serverNode === null) {
|
|
543
|
-
RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
|
|
544
|
-
res.json({ devices: `serverNode not set` });
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
const jRet = serverNode.getResources(req.query.rtype);
|
|
548
|
-
if (jRet !== undefined) {
|
|
549
|
-
res.json(jRet);
|
|
550
|
-
} else {
|
|
551
|
-
res.json({ devices: [{ name: "I'm still connecting...Try in some seconds" }] });
|
|
552
|
-
}
|
|
553
|
-
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
554
|
-
} catch (error) {
|
|
555
|
-
//RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
556
|
-
res.json({ devices: error.message });
|
|
557
|
-
RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
|
|
558
|
-
// (async () => {
|
|
559
|
-
// await node.initHUEConnection();
|
|
560
|
-
// })();
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
RED.httpAdmin.get("/knxUltimateGetFirstLightInGroup", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
565
|
-
try {
|
|
566
|
-
res.json(node.getFirstLightInGroup(req.query.id));
|
|
567
|
-
} catch (error) {
|
|
568
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetFirstLightInGroup: error ${error.message}`);
|
|
569
|
-
res.json({});
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
RED.httpAdmin.get("/knxUltimateDpts", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
574
|
-
try {
|
|
575
|
-
const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base")).reduce(toConcattedSubtypes, []);
|
|
576
|
-
res.json(dpts);
|
|
577
|
-
} catch (error) { }
|
|
578
|
-
});
|
|
579
471
|
}
|
|
580
472
|
RED.nodes.registerType("hue-config", hueConfig, {
|
|
581
473
|
credentials: {
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
// 14/08/2021 Elimino il file delle persistenze di questo nodo
|
|
114
|
-
$.getJSON("deletePersistGAFile?
|
|
114
|
+
$.getJSON("deletePersistGAFile?serverId=" + node.id, (data) => { });
|
|
115
115
|
|
|
116
116
|
// 06/07/2023 Tabs
|
|
117
117
|
// *****************************
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/* eslint-disable prefer-arrow-callback */
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const path = require("path");
|
|
8
|
-
const oOS = require("os");
|
|
9
8
|
const net = require("net");
|
|
10
9
|
const _ = require("lodash");
|
|
11
10
|
const knx = require("../KNXEngine/src");
|
|
@@ -16,7 +15,8 @@ const payloadRounder = require("./utils/payloadManipulation");
|
|
|
16
15
|
const loggerEngine = require("./utils/sysLogger.js");
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
//
|
|
18
|
+
// DATAPONT MANIPULATION HELPERS
|
|
19
|
+
// ####################
|
|
20
20
|
const sortBy = (field) => (a, b) => {
|
|
21
21
|
if (a[field] > b[field]) {
|
|
22
22
|
return 1;
|
|
@@ -51,57 +51,12 @@ const toConcattedSubtypes = (acc, baseType) => {
|
|
|
51
51
|
|
|
52
52
|
return acc.concat(subtypes);
|
|
53
53
|
};
|
|
54
|
+
// ####################
|
|
55
|
+
|
|
56
|
+
|
|
54
57
|
|
|
55
58
|
module.exports = (RED) => {
|
|
56
|
-
RED.httpAdmin.get("/knxUltimateDpts", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
57
|
-
const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base")).reduce(toConcattedSubtypes, []);
|
|
58
|
-
res.json(dpts);
|
|
59
|
-
// Utilità per visualizzare i datapoints, da copiare in README
|
|
60
|
-
// var stringa = "";
|
|
61
|
-
// for (let index = 0; index < dpts.length; index++) {
|
|
62
|
-
// const element = dpts[index];
|
|
63
|
-
// stringa += element.text + "<br/>\n";
|
|
64
|
-
// }
|
|
65
|
-
// if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn(stringa)
|
|
66
|
-
});
|
|
67
59
|
|
|
68
|
-
// 15/09/2020 Supergiovane, read datapoint help usage
|
|
69
|
-
RED.httpAdmin.get("/knxUltimateDptsGetHelp", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
70
|
-
const sDPT = req.query.dpt.split(".")[0]; // Takes only the main type
|
|
71
|
-
let jRet;
|
|
72
|
-
if (sDPT === "0") {
|
|
73
|
-
// Special fake datapoint, meaning "Universal Mode"
|
|
74
|
-
jRet = {
|
|
75
|
-
help: `// KNX-Ultimate set as UNIVERSAL NODE
|
|
76
|
-
// Example of a function that sends a message to the KNX-Ultimate
|
|
77
|
-
msg.destination = "0/0/1"; // Set the destination
|
|
78
|
-
msg.payload = false; // issues a write or response (based on the options Telegram type above) to the KNX bus
|
|
79
|
-
msg.event = "GroupValue_Write"; // "GroupValue_Write" or "GroupValue_Response", overrides the option Telegram type above.
|
|
80
|
-
msg.dpt = "1.001"; // for example "1.001", overrides the Datapoint option. (Datapoints can be sent as 9 , "9" , "9.001" or "DPT9.001")
|
|
81
|
-
return msg;`,
|
|
82
|
-
helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki",
|
|
83
|
-
};
|
|
84
|
-
res.json(jRet);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
jRet = {
|
|
88
|
-
help: "NO",
|
|
89
|
-
helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
90
|
-
};
|
|
91
|
-
const dpts = Object.entries(dptlib).filter(onlyDptKeys);
|
|
92
|
-
for (let index = 0; index < dpts.length; index++) {
|
|
93
|
-
if (dpts[index][0].toUpperCase() === "DPT" + sDPT) {
|
|
94
|
-
jRet = {
|
|
95
|
-
help: dpts[index][1].basetype.hasOwnProperty("help") ? dpts[index][1].basetype.help : "NO",
|
|
96
|
-
helplink: dpts[index][1].basetype.hasOwnProperty("helplink")
|
|
97
|
-
? dpts[index][1].basetype.helplink
|
|
98
|
-
: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
99
|
-
};
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
res.json(jRet);
|
|
104
|
-
});
|
|
105
60
|
|
|
106
61
|
function knxUltimateConfigNode(config) {
|
|
107
62
|
RED.nodes.createNode(this, config);
|
|
@@ -286,186 +241,7 @@ return msg;`,
|
|
|
286
241
|
|
|
287
242
|
// ************************
|
|
288
243
|
|
|
289
|
-
// Endpoint for connecting to HUE Bridge
|
|
290
|
-
RED.httpAdmin.get("/KNXUltimateRegisterToHueBridge", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
291
|
-
try {
|
|
292
|
-
if (typeof req.query.nodeID !== "undefined" && req.query.nodeID !== null && req.query.nodeID !== "") {
|
|
293
|
-
const _node = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
294
|
-
if (_node !== null) res.json(RED.nodes.getNode(_node.id).csv);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
(async () => {
|
|
298
|
-
try {
|
|
299
|
-
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
300
|
-
const discovery = require("node-hue-api").discovery;
|
|
301
|
-
// If using this code outside of the examples directory, you will want to use the line below and remove the
|
|
302
|
-
// const discovery = require('node-hue-api').discovery
|
|
303
|
-
const hueApi = require("node-hue-api").api;
|
|
304
|
-
const appName = "KNXUltimate";
|
|
305
|
-
const deviceName = "Node-Red";
|
|
306
|
-
|
|
307
|
-
// async function discoverBridge() {
|
|
308
|
-
// const discoveryResults = await discovery.nupnpSearch()
|
|
309
|
-
|
|
310
|
-
// if (discoveryResults.length === 0) {
|
|
311
|
-
// if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Failed to resolve any Hue Bridges')
|
|
312
|
-
// return null
|
|
313
|
-
// } else {
|
|
314
|
-
// // Ignoring that you could have more than one Hue Bridge on a network as this is unlikely in 99.9% of users situations
|
|
315
|
-
// return discoveryResults[0].ipaddress
|
|
316
|
-
// }
|
|
317
|
-
// }
|
|
318
|
-
async function discoverAndCreateUser() {
|
|
319
|
-
// const ipAddress = await discoverBridge()
|
|
320
|
-
const ipAddress = req.query.IP;
|
|
321
|
-
|
|
322
|
-
// Create an unauthenticated instance of the Hue API so that we can create a new user
|
|
323
|
-
const unauthenticatedApi = await hueApi.createLocal(ipAddress).connect();
|
|
324
|
-
let createdUser;
|
|
325
|
-
try {
|
|
326
|
-
createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
|
|
327
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
328
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) {
|
|
329
|
-
node.sysLogger.info(
|
|
330
|
-
"User has been created on the Hue Bridge. The following username can be used to\n" +
|
|
331
|
-
"authenticate with the Bridge and provide full local access to the Hue Bridge.\n" +
|
|
332
|
-
"YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
336
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
337
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
338
|
-
|
|
339
|
-
// Create a new API instance that is authenticated with the new user we created
|
|
340
|
-
const authenticatedApi = await hueApi.createLocal(ipAddress).connect(createdUser.username);
|
|
341
|
-
// Do something with the authenticated user/api
|
|
342
|
-
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
343
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
344
|
-
return { bridge: bridgeConfig, user: createdUser };
|
|
345
|
-
} catch (err) {
|
|
346
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error("The Link button on the bridge was not pressed. " + err.message);
|
|
347
|
-
throw err;
|
|
348
|
-
// return {
|
|
349
|
-
// error:
|
|
350
|
-
// "The Link button on the bridge was not pressed or an error has occurred. " +
|
|
351
|
-
// err.message,
|
|
352
|
-
// };
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
async function discoverAndCreateUserInsecure() {
|
|
356
|
-
// const ipAddress = await discoverBridge()
|
|
357
|
-
const ipAddress = req.query.IP;
|
|
358
244
|
|
|
359
|
-
// Create an unauthenticated instance of the Hue API so that we can create a new user
|
|
360
|
-
const unauthenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect();
|
|
361
|
-
let createdUser;
|
|
362
|
-
try {
|
|
363
|
-
createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
|
|
364
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
365
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) {
|
|
366
|
-
node.sysLogger.info(
|
|
367
|
-
"User has been created on the Hue Bridge. The following username can be used to\n" +
|
|
368
|
-
"authenticate with the Bridge and provide full local access to the Hue Bridge.\n" +
|
|
369
|
-
"YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
373
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
374
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
375
|
-
|
|
376
|
-
// Create a new API instance that is authenticated with the new user we created
|
|
377
|
-
const authenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect(createdUser.username);
|
|
378
|
-
// Do something with the authenticated user/api
|
|
379
|
-
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
380
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
381
|
-
return { bridge: bridgeConfig, user: createdUser };
|
|
382
|
-
} catch (err) {
|
|
383
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error("The Link button on the bridge was not pressed. " + err.message);
|
|
384
|
-
return {
|
|
385
|
-
error: "The Link button on the bridge was not pressed or an error has occurred. " + err.message,
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Invoke the discovery and create user code
|
|
391
|
-
try {
|
|
392
|
-
const jRet = await discoverAndCreateUser();
|
|
393
|
-
res.json(jRet);
|
|
394
|
-
} catch (error) {
|
|
395
|
-
RED.log.error("Errore KNXUltimateRegisterToHueBridge non gestito Secure " + error.message + ". Try with insecure http connection...");
|
|
396
|
-
// Try with insecureClient (avoid problems with expired https certificates)
|
|
397
|
-
try {
|
|
398
|
-
const jRet = await discoverAndCreateUserInsecure();
|
|
399
|
-
res.json(jRet);
|
|
400
|
-
} catch (error) {
|
|
401
|
-
RED.log.error("Errore KNXUltimateRegisterToHueBridge non gestito Insecure " + error.message + ". I give up.");
|
|
402
|
-
res.json({ error: error.message });
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
} catch (err) {
|
|
406
|
-
RED.log.error("Errore KNXUltimateRegisterToHueBridge non gestito " + err.message);
|
|
407
|
-
}
|
|
408
|
-
})();
|
|
409
|
-
} catch (err) {
|
|
410
|
-
RED.log.error("Errore KNXUltimateRegisterToHueBridge bsonto " + err.message);
|
|
411
|
-
res.json({ error: err.message });
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// Endpoint for reading csv/esf by the other nodes
|
|
416
|
-
RED.httpAdmin.get("/knxUltimatecsv", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
417
|
-
if (typeof req.query.nodeID !== "undefined" && req.query.nodeID !== null && req.query.nodeID !== "") {
|
|
418
|
-
const _node = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
419
|
-
if (_node !== null) res.json(RED.nodes.getNode(_node.id).csv);
|
|
420
|
-
} else {
|
|
421
|
-
// Get the first knxultimate-config having a valid csv
|
|
422
|
-
try {
|
|
423
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("KNXUltimate-config: Requested csv maybe from visu-ultimate?");
|
|
424
|
-
RED.nodes.eachNode(function (_node) {
|
|
425
|
-
if (_node.hasOwnProperty("csv") && _node.type == "knxUltimate-config" && _node.csv !== "") {
|
|
426
|
-
res.json(RED.nodes.getNode(_node.id).csv);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
} catch (error) { }
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
// 14/08/2019 Endpoint for retrieving the ethernet interfaces
|
|
434
|
-
RED.httpAdmin.get("/knxUltimateETHInterfaces", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
435
|
-
const oiFaces = oOS.networkInterfaces();
|
|
436
|
-
const jListInterfaces = [];
|
|
437
|
-
try {
|
|
438
|
-
Object.keys(oiFaces).forEach((ifname) => {
|
|
439
|
-
// Interface with single IP
|
|
440
|
-
if (Object.keys(oiFaces[ifname]).length === 1) {
|
|
441
|
-
if (Object.keys(oiFaces[ifname])[0].internal === false) jListInterfaces.push({
|
|
442
|
-
name: ifname,
|
|
443
|
-
address: Object.keys(oiFaces[ifname])[0].address,
|
|
444
|
-
});
|
|
445
|
-
} else {
|
|
446
|
-
let sAddresses = "";
|
|
447
|
-
oiFaces[ifname].forEach(function (iface) {
|
|
448
|
-
if (iface.internal === false) sAddresses += "+" + iface.address;
|
|
449
|
-
});
|
|
450
|
-
if (sAddresses !== "") jListInterfaces.push({ name: ifname, address: sAddresses });
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
} catch (error) { }
|
|
454
|
-
res.json(jListInterfaces);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// 12/08/2021 Endpoint for deleting the GA persistent file for the current gateway
|
|
458
|
-
RED.httpAdmin.get("/deletePersistGAFile", RED.auth.needsPermission("knxUltimate-config.read"), function (req, res) {
|
|
459
|
-
if (typeof req.query.nodeID !== "undefined" && req.query.nodeID !== null && req.query.nodeID !== "") {
|
|
460
|
-
const sFile = path.join(node.userDir, "knxpersistvalues", "knxpersist" + req.query.nodeID + ".json");
|
|
461
|
-
try {
|
|
462
|
-
fs.unlinkSync(sFile);
|
|
463
|
-
} catch (error) { }
|
|
464
|
-
res.json({ error: "No error" });
|
|
465
|
-
} else {
|
|
466
|
-
res.json({ error: "No NodeID specified" });
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
245
|
|
|
470
246
|
// 16/02/2020 KNX-Ultimate nodes calls this function, then this funcion calls the same function on the Watchdog
|
|
471
247
|
node.reportToWatchdogCalledByKNXUltimateNode = (_oError) => {
|
|
@@ -890,7 +666,8 @@ return msg;`,
|
|
|
890
666
|
node.knxConnection.on(knx.KNXClient.KNXClientEvents.error, (err) => {
|
|
891
667
|
try {
|
|
892
668
|
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error("knxUltimate-config: received KNXClientEvents.error: " + (err.message === undefined ? err : err.message));
|
|
893
|
-
} catch (error) {
|
|
669
|
+
} catch (error) {
|
|
670
|
+
}
|
|
894
671
|
// 31/03/2022 Don't care about some errors
|
|
895
672
|
if (err.message !== undefined && (err.message === "ROUTING_LOST_MESSAGE" || err.message === "ROUTING_BUSY")) {
|
|
896
673
|
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(
|
|
@@ -932,7 +709,11 @@ return msg;`,
|
|
|
932
709
|
// Start the timer to do initial read.
|
|
933
710
|
if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
|
|
934
711
|
node.timerDoInitialRead = setTimeout(() => {
|
|
935
|
-
|
|
712
|
+
try {
|
|
713
|
+
DoInitialReadFromKNXBusOrFile();
|
|
714
|
+
} catch (error) {
|
|
715
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error("knxUltimate-config: DoInitialReadFromKNXBusOrFile " + error.stack);
|
|
716
|
+
}
|
|
936
717
|
}, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
|
|
937
718
|
const t = setTimeout(() => {
|
|
938
719
|
// 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ".
|
|
@@ -1764,7 +1545,7 @@ return msg;`,
|
|
|
1764
1545
|
" NodeID " +
|
|
1765
1546
|
_oNode.id || "",
|
|
1766
1547
|
);
|
|
1767
|
-
errorMessage.payload = "UNKNOWN: ERROR dptlib.fromBuffer:" + error.
|
|
1548
|
+
errorMessage.payload = "UNKNOWN: ERROR dptlib.fromBuffer:" + error.stack;
|
|
1768
1549
|
return errorMessage;
|
|
1769
1550
|
}
|
|
1770
1551
|
}
|
package/nodes/knxUltimate.html
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
|
|
79
79
|
// 15/09/2020 Supergiovane, set the help sample based on Datapoint
|
|
80
80
|
function knxUltimateDptsGetHelp(_dpt, _forceClose) {
|
|
81
|
-
$.getJSON("knxUltimateDptsGetHelp?dpt=" + _dpt, (data) => {
|
|
81
|
+
$.getJSON("knxUltimateDptsGetHelp?dpt=" + _dpt + "&serverId=" + $("#node-input-server").val(), (data) => {
|
|
82
82
|
try {
|
|
83
83
|
$("#example-editor").html("");
|
|
84
84
|
$("#sampleCodeEditor").html();
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
// ###########################
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
$.getJSON("knxUltimateDpts", (data) => {
|
|
136
|
+
$.getJSON("knxUltimateDpts?serverId=" + $("#node-input-server").val(), (data) => {
|
|
137
137
|
data.forEach(dpt => {
|
|
138
138
|
$("#node-input-dpt").append($("<option></option>")
|
|
139
139
|
.attr("value", dpt.value)
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
|
|
71
71
|
// DPT
|
|
72
72
|
// ########################
|
|
73
|
-
$.getJSON('knxUltimateDpts', (data) => {
|
|
73
|
+
$.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
|
|
74
74
|
data.forEach(dpt => {
|
|
75
75
|
if (dpt.value.startsWith("5.001")) {
|
|
76
76
|
$("#node-input-dptbatterysensor").append($("<option></option>")
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
$("#node-input-name").autocomplete({
|
|
122
122
|
minLength: 1,
|
|
123
123
|
source: function (request, response) {
|
|
124
|
-
$.getJSON("KNXUltimateGetResourcesHUE?rtype=device_power&
|
|
124
|
+
$.getJSON("KNXUltimateGetResourcesHUE?rtype=device_power&serverId=" + oNodeServerHue.id, (data) => {
|
|
125
125
|
response($.map(data.devices, function (value, key) {
|
|
126
126
|
//alert(JSON.stringify(value) + " "+ key)
|
|
127
127
|
var sSearch = (value.name);
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
// DPT Dim
|
|
79
79
|
// ########################
|
|
80
|
-
$.getJSON('knxUltimateDpts', (data) => {
|
|
80
|
+
$.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
|
|
81
81
|
data.forEach(dpt => {
|
|
82
82
|
if (dpt.value.startsWith("3.007")) {
|
|
83
83
|
$("#node-input-dptrepeat").append($("<option></option>")
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
|
|
125
125
|
// DPT dptshort_release
|
|
126
126
|
// ########################
|
|
127
|
-
$.getJSON('knxUltimateDpts', (data) => {
|
|
127
|
+
$.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
|
|
128
128
|
data.forEach(dpt => {
|
|
129
129
|
if (dpt.value.startsWith('1.')) {
|
|
130
130
|
$("#node-input-dptshort_release").append($("<option></option>")
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
$("#node-input-name").autocomplete({
|
|
268
268
|
minLength: 1,
|
|
269
269
|
source: function (request, response) {
|
|
270
|
-
$.getJSON("KNXUltimateGetResourcesHUE?rtype=button&
|
|
270
|
+
$.getJSON("KNXUltimateGetResourcesHUE?rtype=button&serverId=" + oNodeServerHue.id, (data) => {
|
|
271
271
|
response($.map(data.devices, function (value, key) {
|
|
272
272
|
//alert(JSON.stringify(value) + " "+ key)
|
|
273
273
|
var sSearch = (value.name);
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
<input type="text" id="node-input-nameshort_release" style="width:200px;margin-left: 5px; text-align: left;">
|
|
362
362
|
</div>
|
|
363
363
|
<div class="form-row">
|
|
364
|
-
<label style="width:100px;"><i class="fa fa-
|
|
364
|
+
<label style="width:100px;"><i class="fa fa-question-circle"></i> Switch Status</label>
|
|
365
365
|
|
|
366
366
|
<label for="node-input-GAshort_releaseStatus" style="width:20px;">GA</label>
|
|
367
367
|
<input type="text" id="node-input-GAshort_releaseStatus" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|