node-red-contrib-knx-ultimate 2.4.4 → 2.4.5-beta.0
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 +6 -3
- package/nodes/commonFunctions.js +339 -321
- package/nodes/knxUltimate.html +177 -176
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
-
**Version 2.4.
|
|
9
|
+
**Version 2.4.5-beta.0** - Jan 2024<br/>
|
|
10
|
+
- WARNING: this version uses the Node-Red plugin system; the Node-Red version must be **equals or major than 3.1.1**<br/>
|
|
10
11
|
- NEW: Added KNX Datapoint 275.100<br/>
|
|
11
12
|
- HUE Light: fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/317<br/>
|
|
12
13
|
- HUE Light: corrected the 7.600 kelvin range https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/316<br/>
|
|
13
14
|
- HUE Light: blinking effect and color cyle are now stopped, whenever an FALSE KNX telegram is received by the light switching group address.<br/>
|
|
14
15
|
- HUE Light: when the light is off, the dim up sequence starts now with initial brightness = zero.<br/>
|
|
15
|
-
- KNX Engine: moved all HTTP calls to a single js file, loaded at startup, to avoid multi KNX gateway or multi HUE bridges issues.<br/>
|
|
16
|
-
|
|
16
|
+
- KNX Engine: moved all HTTP calls to a single js file plugin, loaded at startup, to avoid multi KNX gateway or multi HUE bridges issues.<br/>
|
|
17
|
+
|
|
18
|
+
**Version 2.4.4** - Jan 2024<br/>
|
|
19
|
+
- Transitional version.<br/>
|
|
17
20
|
|
|
18
21
|
**Version 2.4.0-beta.1** - Jan 2024<br/>
|
|
19
22
|
- This is a public installable beta.<br/>
|
package/nodes/commonFunctions.js
CHANGED
|
@@ -48,377 +48,395 @@ const toConcattedSubtypes = (acc, baseType) => {
|
|
|
48
48
|
// ####################
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
module.exports =
|
|
52
|
-
|
|
51
|
+
module.exports = (RED) => {
|
|
52
|
+
RED.plugins.registerPlugin("commonFunctions", {
|
|
53
|
+
type: "foo",
|
|
54
|
+
onadd: function () {
|
|
55
|
+
RED.events.on("registry:plugin-added", function (id) {
|
|
56
|
+
console.log(`my-test-plugin: plugin-added event "${id}"`)
|
|
57
|
+
commonFunctions();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
})
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
node
|
|
56
|
-
} catch (error) { }
|
|
62
|
+
function commonFunctions() {
|
|
63
|
+
var node = this;
|
|
57
64
|
|
|
58
|
-
// 11/03/2020 Delete scene saved file, from html
|
|
59
|
-
RED.httpAdmin.get('/knxultimateCheckHueConnected', (req, res) => {
|
|
60
65
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
67
|
+
} catch (error) { }
|
|
68
|
+
|
|
69
|
+
// 11/03/2020 Delete scene saved file, from html
|
|
70
|
+
RED.httpAdmin.get('/knxultimateCheckHueConnected', (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
73
|
+
if (serverId.hueAllResources === null || serverId.hueAllResources === undefined) {
|
|
74
|
+
res.json({ ready: false });
|
|
75
|
+
} else {
|
|
76
|
+
res.json({ ready: true });
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
63
79
|
res.json({ ready: false });
|
|
64
|
-
} else {
|
|
65
|
-
res.json({ ready: true });
|
|
66
80
|
}
|
|
67
|
-
}
|
|
68
|
-
res.json({ ready: false });
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
81
|
+
});
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
// 11/03/2020 Delete scene saved file, from html
|
|
84
|
+
RED.httpAdmin.get('/knxultimatescenecontrollerdelete'), (req, res) => {
|
|
85
|
+
// Delete the file
|
|
86
|
+
try {
|
|
87
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
88
|
+
const newPath = `${serverId.userDir}/scenecontroller/SceneController_${req.query.FileName}`;
|
|
89
|
+
fs.unlinkSync(newPath);
|
|
90
|
+
} catch (error) { if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn(`e ${error}`); }
|
|
91
|
+
res.json({ status: 220 });
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Endpoint for connecting to HUE Bridge
|
|
95
|
+
RED.httpAdmin.get("/KNXUltimateRegisterToHueBridge", (req, res) => {
|
|
96
|
+
try {
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
98
|
+
(async () => {
|
|
99
|
+
try {
|
|
100
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
101
|
+
|
|
102
|
+
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
103
|
+
// If using this code outside of the examples directory, you will want to use the line below and remove the
|
|
104
|
+
// const discovery = require('node-hue-api').discovery
|
|
105
|
+
const hueApi = require("node-hue-api").api;
|
|
106
|
+
const appName = "KNXUltimate";
|
|
107
|
+
const deviceName = "Node-Red";
|
|
108
|
+
|
|
109
|
+
// async function discoverBridge() {
|
|
110
|
+
// const discoveryResults = await discovery.nupnpSearch()
|
|
111
|
+
|
|
112
|
+
// if (discoveryResults.length === 0) {
|
|
113
|
+
// if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Failed to resolve any Hue Bridges')
|
|
114
|
+
// return null
|
|
115
|
+
// } else {
|
|
116
|
+
// // Ignoring that you could have more than one Hue Bridge on a network as this is unlikely in 99.9% of users situations
|
|
117
|
+
// return discoveryResults[0].ipaddress
|
|
118
|
+
// }
|
|
119
|
+
// }
|
|
120
|
+
async function discoverAndCreateUser() {
|
|
121
|
+
// const ipAddress = await discoverBridge()
|
|
122
|
+
const ipAddress = req.query.IP;
|
|
123
|
+
// Create an unauthenticated instance of the Hue API so that we can create a new user
|
|
124
|
+
try {
|
|
125
|
+
const unauthenticatedApi = await hueApi.createLocal(ipAddress).connect();
|
|
126
|
+
let createdUser;
|
|
127
|
+
createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
|
|
128
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
129
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) {
|
|
130
|
+
node.sysLogger.info(
|
|
131
|
+
"User has been created on the Hue Bridge. The following username can be used to\n"
|
|
132
|
+
+ "authenticate with the Bridge and provide full local access to the Hue Bridge.\n"
|
|
133
|
+
+ "YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
137
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
138
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
139
|
+
|
|
140
|
+
// Create a new API instance that is authenticated with the new user we created
|
|
141
|
+
const authenticatedApi = await hueApi.createLocal(ipAddress).connect(createdUser.username);
|
|
142
|
+
// Do something with the authenticated user/api
|
|
143
|
+
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
144
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
145
|
+
return { bridge: bridgeConfig, user: createdUser };
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`The Link button on the bridge was not pressed. ${err.message}`);
|
|
148
|
+
throw err;
|
|
149
|
+
// return {
|
|
150
|
+
// error:
|
|
151
|
+
// "The Link button on the bridge was not pressed or an error has occurred. " +
|
|
152
|
+
// err.message,
|
|
153
|
+
// };
|
|
125
154
|
}
|
|
126
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
127
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
128
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
129
|
-
|
|
130
|
-
// Create a new API instance that is authenticated with the new user we created
|
|
131
|
-
const authenticatedApi = await hueApi.createLocal(ipAddress).connect(createdUser.username);
|
|
132
|
-
// Do something with the authenticated user/api
|
|
133
|
-
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
134
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
135
|
-
return { bridge: bridgeConfig, user: createdUser };
|
|
136
|
-
} catch (err) {
|
|
137
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`The Link button on the bridge was not pressed. ${err.message}`);
|
|
138
|
-
throw err;
|
|
139
|
-
// return {
|
|
140
|
-
// error:
|
|
141
|
-
// "The Link button on the bridge was not pressed or an error has occurred. " +
|
|
142
|
-
// err.message,
|
|
143
|
-
// };
|
|
144
155
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
async function discoverAndCreateUserInsecure() {
|
|
157
|
+
// const ipAddress = await discoverBridge()
|
|
158
|
+
const ipAddress = req.query.IP;
|
|
159
|
+
|
|
160
|
+
// Create an unauthenticated instance of the Hue API so that we can create a new user
|
|
161
|
+
try {
|
|
162
|
+
const unauthenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect();
|
|
163
|
+
let createdUser;
|
|
164
|
+
createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
|
|
165
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
166
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) {
|
|
167
|
+
node.sysLogger.info(
|
|
168
|
+
"User has been created on the Hue Bridge. The following username can be used to\n"
|
|
169
|
+
+ "authenticate with the Bridge and provide full local access to the Hue Bridge.\n"
|
|
170
|
+
+ "YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
174
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
175
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
176
|
+
|
|
177
|
+
// Create a new API instance that is authenticated with the new user we created
|
|
178
|
+
const authenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect(createdUser.username);
|
|
179
|
+
// Do something with the authenticated user/api
|
|
180
|
+
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
181
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
182
|
+
return { bridge: bridgeConfig, user: createdUser };
|
|
183
|
+
} catch (err) {
|
|
184
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`The Link button on the bridge was not pressed. ` + err.stack);
|
|
185
|
+
return {
|
|
186
|
+
error: `The Link button on the bridge was not pressed or an error has occurred.`,
|
|
187
|
+
};
|
|
162
188
|
}
|
|
163
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User: ${createdUser.username}`);
|
|
164
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Hue Bridge User Client Key: ${createdUser.clientkey}`);
|
|
165
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
|
|
166
|
-
|
|
167
|
-
// Create a new API instance that is authenticated with the new user we created
|
|
168
|
-
const authenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect(createdUser.username);
|
|
169
|
-
// Do something with the authenticated user/api
|
|
170
|
-
const bridgeConfig = await authenticatedApi.configuration.getConfiguration();
|
|
171
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Connected to Hue Bridge: ${bridgeConfig.name} :: ${bridgeConfig.ipaddress}`);
|
|
172
|
-
return { bridge: bridgeConfig, user: createdUser };
|
|
173
|
-
} catch (err) {
|
|
174
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`The Link button on the bridge was not pressed. ` + err.stack);
|
|
175
|
-
return {
|
|
176
|
-
error: `The Link button on the bridge was not pressed or an error has occurred.`,
|
|
177
|
-
};
|
|
178
189
|
}
|
|
179
|
-
}
|
|
180
190
|
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const jRet = await discoverAndCreateUser();
|
|
184
|
-
res.json(jRet);
|
|
185
|
-
} catch (error) {
|
|
186
|
-
RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito Secure ${error.message}. Try with insecure http connection...`);
|
|
187
|
-
// Try with insecureClient (avoid problems with expired https certificates)
|
|
191
|
+
// Invoke the discovery and create user code
|
|
188
192
|
try {
|
|
189
|
-
const jRet = await
|
|
193
|
+
const jRet = await discoverAndCreateUser();
|
|
190
194
|
res.json(jRet);
|
|
191
195
|
} catch (error) {
|
|
192
|
-
RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito
|
|
193
|
-
|
|
196
|
+
RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito Secure ${error.message}. Try with insecure http connection...`);
|
|
197
|
+
// Try with insecureClient (avoid problems with expired https certificates)
|
|
198
|
+
try {
|
|
199
|
+
const jRet = await discoverAndCreateUserInsecure();
|
|
200
|
+
res.json(jRet);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito Insecure ${error.message}. I give up.`);
|
|
203
|
+
res.json({ error: error.message });
|
|
204
|
+
}
|
|
194
205
|
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito ${err.message}`);
|
|
195
208
|
}
|
|
196
|
-
}
|
|
197
|
-
|
|
209
|
+
})();
|
|
210
|
+
} catch (err) {
|
|
211
|
+
RED.log.error(`Errore KNXUltimateRegisterToHueBridge bsonto ${err.message}`);
|
|
212
|
+
res.json({ error: err.message });
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Endpoint for reading csv/esf by the other nodes
|
|
217
|
+
RED.httpAdmin.get("/knxUltimatecsv", RED.auth.needsPermission("knxUltimate-config.read"), (req, res) => {
|
|
218
|
+
try {
|
|
219
|
+
if (typeof req.query.nodeID !== "undefined" && req.query.nodeID !== null && req.query.nodeID !== "") {
|
|
220
|
+
const _node = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
221
|
+
if (_node !== null) res.json(RED.nodes.getNode(_node.id).csv);
|
|
222
|
+
} else {
|
|
223
|
+
// Get the first knxultimate-config having a valid csv
|
|
224
|
+
try {
|
|
225
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("KNXUltimate-config: Requested csv maybe from visu-ultimate?");
|
|
226
|
+
RED.nodes.eachNode((_node) => {
|
|
227
|
+
if (_node.hasOwnProperty("csv") && _node.type === "knxUltimate-config" && _node.csv !== "") {
|
|
228
|
+
res.json(RED.nodes.getNode(_node.id).csv);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
} catch (error) { }
|
|
198
232
|
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// Endpoint for reading csv/esf by the other nodes
|
|
207
|
-
RED.httpAdmin.get("/knxUltimatecsv", RED.auth.needsPermission("knxUltimate-config.read"), (req, res) => {
|
|
208
|
-
if (typeof req.query.nodeID !== "undefined" && req.query.nodeID !== null && req.query.nodeID !== "") {
|
|
209
|
-
const _node = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
|
|
210
|
-
if (_node !== null) res.json(RED.nodes.getNode(_node.id).csv);
|
|
211
|
-
} else {
|
|
212
|
-
// Get the first knxultimate-config having a valid csv
|
|
233
|
+
} catch (error) {
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// 14/08/2019 Endpoint for retrieving the ethernet interfaces
|
|
239
|
+
RED.httpAdmin.get("/knxUltimateETHInterfaces", (req, res) => {
|
|
213
240
|
try {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
const oiFaces = oOS.networkInterfaces();
|
|
242
|
+
const jListInterfaces = [];
|
|
243
|
+
Object.keys(oiFaces).forEach((ifname) => {
|
|
244
|
+
// Interface with single IP
|
|
245
|
+
if (Object.keys(oiFaces[ifname]).length === 1) {
|
|
246
|
+
if (Object.keys(oiFaces[ifname])[0].internal === false) {
|
|
247
|
+
jListInterfaces.push({
|
|
248
|
+
name: ifname,
|
|
249
|
+
address: Object.keys(oiFaces[ifname])[0].address,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
let sAddresses = "";
|
|
254
|
+
oiFaces[ifname].forEach((iface) => {
|
|
255
|
+
if (iface.internal === false) sAddresses += `+${iface.address}`;
|
|
256
|
+
});
|
|
257
|
+
if (sAddresses !== "") jListInterfaces.push({ name: ifname, address: sAddresses });
|
|
218
258
|
}
|
|
219
259
|
});
|
|
220
260
|
} catch (error) { }
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// 14/08/2019 Endpoint for retrieving the ethernet interfaces
|
|
225
|
-
RED.httpAdmin.get("/knxUltimateETHInterfaces", RED.auth.needsPermission("knxUltimate-config.read"), (req, res) => {
|
|
261
|
+
res.json(jListInterfaces);
|
|
262
|
+
});
|
|
226
263
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
238
|
-
}
|
|
264
|
+
// 12/08/2021 Endpoint for deleting the GA persistent file for the current gateway
|
|
265
|
+
RED.httpAdmin.get("/deletePersistGAFile", RED.auth.needsPermission("knxUltimate-config.read"), (req, res) => {
|
|
266
|
+
try {
|
|
267
|
+
if (typeof req.query.serverId !== "undefined" && req.query.serverId !== null && req.query.serverId !== "") {
|
|
268
|
+
try {
|
|
269
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
270
|
+
const sFile = path.join(serverId.userDir, "knxpersistvalues", `knxpersist${req.query.serverId}.json`);
|
|
271
|
+
fs.unlinkSync(sFile);
|
|
272
|
+
} catch (error) { res.json({ error: error.stack }); }
|
|
273
|
+
res.json({ error: "No error" });
|
|
239
274
|
} else {
|
|
240
|
-
|
|
241
|
-
oiFaces[ifname].forEach((iface) => {
|
|
242
|
-
if (iface.internal === false) sAddresses += `+${iface.address}`;
|
|
243
|
-
});
|
|
244
|
-
if (sAddresses !== "") jListInterfaces.push({ name: ifname, address: sAddresses });
|
|
275
|
+
res.json({ error: "No serverId specified" });
|
|
245
276
|
}
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
});
|
|
277
|
+
} catch (error) {
|
|
278
|
+
}
|
|
279
|
+
});
|
|
250
280
|
|
|
251
|
-
|
|
252
|
-
RED.httpAdmin.get("/deletePersistGAFile", RED.auth.needsPermission("knxUltimate-config.read"), (req, res) => {
|
|
253
|
-
if (typeof req.query.serverId !== "undefined" && req.query.serverId !== null && req.query.serverId !== "") {
|
|
281
|
+
RED.httpAdmin.get("/knxUltimateGetHueColor", (req, res) => {
|
|
254
282
|
try {
|
|
255
283
|
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
let hexColor;
|
|
270
|
-
const _oDevice = serverId.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
271
|
-
if (_oDevice.type === "light") {
|
|
272
|
-
hexColor = serverId.getColorFromHueLight(req.query.id);
|
|
273
|
-
} else {
|
|
274
|
-
// grouped_light, get the first light in the group
|
|
275
|
-
const oLight = serverId.getFirstLightInGroup(_oDevice.id);
|
|
276
|
-
hexColor = serverId.getColorFromHueLight(oLight.id);
|
|
277
|
-
}
|
|
278
|
-
res.json(hexColor !== undefined ? hexColor : "Select the device first!");
|
|
279
|
-
} catch (error) {
|
|
280
|
-
res.json("Select the device first!");
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
RED.httpAdmin.get("/knxUltimateGetKelvinColor", RED.auth.needsPermission("hue-config.read"), (req, res) => {
|
|
284
|
-
try {
|
|
285
|
-
// find wether the light is a light or is grouped_light
|
|
286
|
-
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
287
|
-
let kelvinValue;
|
|
288
|
-
const _oDevice = serverId.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
289
|
-
if (_oDevice.type === "light") {
|
|
290
|
-
kelvinValue = serverId.getKelvinFromHueLight(req.query.id);
|
|
291
|
-
} else {
|
|
292
|
-
// grouped_light, get the first light in the group
|
|
293
|
-
const oLight = serverId.getFirstLightInGroup(_oDevice.id);
|
|
294
|
-
kelvinValue = serverId.getKelvinFromHueLight(oLight.id);
|
|
284
|
+
// find wether the light is a light or is grouped_light
|
|
285
|
+
let hexColor;
|
|
286
|
+
const _oDevice = serverId.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
287
|
+
if (_oDevice.type === "light") {
|
|
288
|
+
hexColor = serverId.getColorFromHueLight(req.query.id);
|
|
289
|
+
} else {
|
|
290
|
+
// grouped_light, get the first light in the group
|
|
291
|
+
const oLight = serverId.getFirstLightInGroup(_oDevice.id);
|
|
292
|
+
hexColor = serverId.getColorFromHueLight(oLight.id);
|
|
293
|
+
}
|
|
294
|
+
res.json(hexColor !== undefined ? hexColor : "Select the device first!");
|
|
295
|
+
} catch (error) {
|
|
296
|
+
res.json("Select the device first!");
|
|
295
297
|
}
|
|
296
|
-
|
|
297
|
-
} catch (error) {
|
|
298
|
-
res.json("Select the device first!");
|
|
299
|
-
}
|
|
300
|
-
});
|
|
298
|
+
});
|
|
301
299
|
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
305
|
-
if (serverId.hueAllResources === null || serverId.hueAllResources === undefined) {
|
|
306
|
-
throw (new Error("Resource not yet loaded"));
|
|
307
|
-
}
|
|
308
|
-
const _lightId = req.query.id;
|
|
309
|
-
const oLight = serverId.hueAllResources.filter((a) => a.id === _lightId)[0];
|
|
310
|
-
// Infer some useful info, so the HTML part can avoid to query the server
|
|
311
|
-
// Kelvin
|
|
300
|
+
RED.httpAdmin.get("/knxUltimateGetKelvinColor", (req, res) => {
|
|
312
301
|
try {
|
|
313
|
-
|
|
314
|
-
|
|
302
|
+
// find wether the light is a light or is grouped_light
|
|
303
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
304
|
+
let kelvinValue;
|
|
305
|
+
const _oDevice = serverId.hueAllResources.filter((a) => a.id === req.query.id)[0];
|
|
306
|
+
if (_oDevice.type === "light") {
|
|
307
|
+
kelvinValue = serverId.getKelvinFromHueLight(req.query.id);
|
|
308
|
+
} else {
|
|
309
|
+
// grouped_light, get the first light in the group
|
|
310
|
+
const oLight = serverId.getFirstLightInGroup(_oDevice.id);
|
|
311
|
+
kelvinValue = serverId.getKelvinFromHueLight(oLight.id);
|
|
315
312
|
}
|
|
313
|
+
res.json(kelvinValue !== undefined ? kelvinValue : "Select the device first!");
|
|
316
314
|
} catch (error) {
|
|
317
|
-
|
|
315
|
+
res.json("Select the device first!");
|
|
318
316
|
}
|
|
319
|
-
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
RED.httpAdmin.get("/knxUltimateGetLightObject", (req, res) => {
|
|
320
320
|
try {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
322
|
+
if (serverId.hueAllResources === null || serverId.hueAllResources === undefined) {
|
|
323
|
+
throw (new Error("Resource not yet loaded"));
|
|
324
|
+
}
|
|
325
|
+
const _lightId = req.query.id;
|
|
326
|
+
const oLight = serverId.hueAllResources.filter((a) => a.id === _lightId)[0];
|
|
327
|
+
// Infer some useful info, so the HTML part can avoid to query the server
|
|
328
|
+
// Kelvin
|
|
329
|
+
try {
|
|
330
|
+
if (oLight.color_temperature !== undefined && oLight.color_temperature.mirek !== undefined) {
|
|
331
|
+
oLight.calculatedKelvin = hueColorConverter.ColorConverter.mirekToKelvin(oLight.color_temperature.mirek);
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
oLight.calculatedKelvin = undefined;
|
|
335
|
+
}
|
|
336
|
+
// HEX value from XYBri
|
|
337
|
+
try {
|
|
338
|
+
const retRGB = hueColorConverter.ColorConverter.xyBriToRgb(oLight.color.xy.x, oLight.color.xy.y, oLight.dimming.brightness);
|
|
339
|
+
const ret = `#${hueColorConverter.ColorConverter.rgbHex(retRGB.r, retRGB.g, retRGB.b).toString()}`;
|
|
340
|
+
oLight.calculatedHEXColor = ret;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
oLight.calculatedHEXColor = undefined;
|
|
343
|
+
}
|
|
344
|
+
res.json(oLight);
|
|
324
345
|
} catch (error) {
|
|
325
|
-
|
|
346
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}.`);
|
|
347
|
+
res.json({});
|
|
326
348
|
}
|
|
327
|
-
|
|
328
|
-
} catch (error) {
|
|
329
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}.`);
|
|
330
|
-
res.json({});
|
|
331
|
-
}
|
|
332
|
-
});
|
|
349
|
+
});
|
|
333
350
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
RED.httpAdmin.get("/KNXUltimateGetResourcesHUE", (req, res) => {
|
|
352
|
+
try {
|
|
353
|
+
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
354
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
355
|
+
if (serverId === null) {
|
|
356
|
+
RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverId is null`);
|
|
357
|
+
res.json({ devices: `serverId not set` });
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const jRet = serverId.getResources(req.query.rtype);
|
|
361
|
+
if (jRet !== undefined) {
|
|
362
|
+
res.json(jRet);
|
|
363
|
+
} else {
|
|
364
|
+
res.json({ devices: [{ name: "I'm still connecting...Try in some seconds" }] });
|
|
365
|
+
}
|
|
366
|
+
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
367
|
+
} catch (error) {
|
|
368
|
+
// RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
369
|
+
res.json({ devices: error.message });
|
|
370
|
+
RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
|
|
371
|
+
// (async () => {
|
|
372
|
+
// await node.initHUEConnection();
|
|
373
|
+
// })();
|
|
348
374
|
}
|
|
349
|
-
|
|
350
|
-
} catch (error) {
|
|
351
|
-
// RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
352
|
-
res.json({ devices: error.message });
|
|
353
|
-
RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
|
|
354
|
-
// (async () => {
|
|
355
|
-
// await node.initHUEConnection();
|
|
356
|
-
// })();
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
RED.httpAdmin.get("/knxUltimateDpts", (req, res) => {
|
|
361
|
-
try {
|
|
362
|
-
const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base"))
|
|
363
|
-
.reduce(toConcattedSubtypes, []);
|
|
364
|
-
res.json(dpts);
|
|
365
|
-
} catch (error) { }
|
|
366
|
-
});
|
|
375
|
+
});
|
|
367
376
|
|
|
377
|
+
RED.httpAdmin.get("/knxUltimateDpts", (req, res) => {
|
|
378
|
+
try {
|
|
379
|
+
const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base"))
|
|
380
|
+
.reduce(toConcattedSubtypes, []);
|
|
381
|
+
res.json(dpts);
|
|
382
|
+
} catch (error) { }
|
|
383
|
+
});
|
|
368
384
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
385
|
+
// 15/09/2020 Supergiovane, read datapoint help usage
|
|
386
|
+
RED.httpAdmin.get("/knxUltimateDptsGetHelp", (req, res) => {
|
|
387
|
+
try {
|
|
372
388
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
389
|
+
const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
|
|
390
|
+
const sDPT = req.query.dpt.split(".")[0]; // Takes only the main type
|
|
391
|
+
let jRet;
|
|
392
|
+
if (sDPT === "0") {
|
|
393
|
+
// Special fake datapoint, meaning "Universal Mode"
|
|
394
|
+
jRet = {
|
|
395
|
+
help: `// KNX-Ultimate set as UNIVERSAL NODE
|
|
380
396
|
// Example of a function that sends a message to the KNX-Ultimate
|
|
381
397
|
msg.destination = "0/0/1"; // Set the destination
|
|
382
398
|
msg.payload = false; // issues a write or response (based on the options Telegram type above) to the KNX bus
|
|
383
399
|
msg.event = "GroupValue_Write"; // "GroupValue_Write" or "GroupValue_Response", overrides the option Telegram type above.
|
|
384
400
|
msg.dpt = "1.001"; // for example "1.001", overrides the Datapoint option. (Datapoints can be sent as 9 , "9" , "9.001" or "DPT9.001")
|
|
385
401
|
return msg;`,
|
|
386
|
-
|
|
387
|
-
};
|
|
388
|
-
res.json(jRet);
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
jRet = {
|
|
392
|
-
help: "NO",
|
|
393
|
-
helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
394
|
-
};
|
|
395
|
-
const dpts = Object.entries(dptlib).filter(onlyDptKeys);
|
|
396
|
-
for (let index = 0; index < dpts.length; index++) {
|
|
397
|
-
if (dpts[index][0].toUpperCase() === `DPT${sDPT}`) {
|
|
398
|
-
jRet = {
|
|
399
|
-
help: dpts[index][1].basetype.hasOwnProperty("help") ? dpts[index][1].basetype.help : "NO",
|
|
400
|
-
helplink: dpts[index][1].basetype.hasOwnProperty("helplink")
|
|
401
|
-
? dpts[index][1].basetype.helplink
|
|
402
|
-
: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
402
|
+
helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki",
|
|
403
403
|
};
|
|
404
|
-
|
|
404
|
+
res.json(jRet);
|
|
405
|
+
return;
|
|
405
406
|
}
|
|
407
|
+
jRet = {
|
|
408
|
+
help: "NO",
|
|
409
|
+
helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
410
|
+
};
|
|
411
|
+
const dpts = Object.entries(dptlib).filter(onlyDptKeys);
|
|
412
|
+
for (let index = 0; index < dpts.length; index++) {
|
|
413
|
+
if (dpts[index][0].toUpperCase() === `DPT${sDPT}`) {
|
|
414
|
+
jRet = {
|
|
415
|
+
help: dpts[index][1].basetype.hasOwnProperty("help") ? dpts[index][1].basetype.help : "NO",
|
|
416
|
+
helplink: dpts[index][1].basetype.hasOwnProperty("helplink")
|
|
417
|
+
? dpts[index][1].basetype.helplink
|
|
418
|
+
: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome",
|
|
419
|
+
};
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
res.json(jRet);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
res.json({ error: error.stack });
|
|
406
426
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// RED.httpAdmin.post("/banana", RED.auth.needsPermission("write"), (req, res) => {
|
|
430
|
+
// const node = RED.nodes.getNode(req.params.id);
|
|
431
|
+
// if (node != null) {
|
|
432
|
+
// try {
|
|
433
|
+
// if (req.body) {
|
|
434
|
+
// console.log(body);
|
|
435
|
+
// }
|
|
436
|
+
// } catch (err) { }
|
|
437
|
+
// }
|
|
438
|
+
// res.json(req.body);
|
|
439
|
+
// });
|
|
412
440
|
|
|
413
|
-
|
|
414
|
-
const node = RED.nodes.getNode(req.params.id);
|
|
415
|
-
if (node != null) {
|
|
416
|
-
try {
|
|
417
|
-
if (req.body) {
|
|
418
|
-
console.log(body);
|
|
419
|
-
}
|
|
420
|
-
} catch (err) { }
|
|
421
|
-
}
|
|
422
|
-
res.json(req.body);
|
|
423
|
-
});
|
|
441
|
+
}
|
|
424
442
|
};
|
package/nodes/knxUltimate.html
CHANGED
|
@@ -63,53 +63,38 @@
|
|
|
63
63
|
|
|
64
64
|
// 15/09/2020 Supergiovane, set the help sample based on Datapoint
|
|
65
65
|
function knxUltimateDptsGetHelp(_dpt, _forceClose) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
if (data.help !== "NO") {
|
|
76
|
-
node.sampleEditor = RED.editor.createEditor({
|
|
77
|
-
id: 'example-editor',
|
|
78
|
-
mode: 'ace/mode/javascript',
|
|
79
|
-
value: data.help//this.exampleText
|
|
80
|
-
}).renderer.setShowGutter(false);//A.setReadOnly(true).setShowPrintMargin(false);
|
|
81
|
-
if (data.helplink !== "") $("#sampleCodeEditor").html(" <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"" + data.helplink + "\"><u>Link example of " + _dpt + "</u></a>");
|
|
82
|
-
} else {
|
|
83
|
-
// No help avaiable
|
|
84
|
-
node.sampleEditor = RED.editor.createEditor({
|
|
85
|
-
id: 'example-editor',
|
|
86
|
-
mode: 'ace/mode/javascript',
|
|
87
|
-
value: "Currently, no sample payload is avaiable, sorry."
|
|
88
|
-
}).renderer.setShowGutter(false);//B.setReadOnly(true).setShowPrintMargin(false);
|
|
89
|
-
if (data.helplink !== "") $("#sampleCodeEditor").html(" <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"" + data.helplink + "\"><u>Link to wiki</u></a>");
|
|
66
|
+
if ($("#node-input-server").val() !== "_ADD_" && $("#node-input-server").val() !== '' && _dpt !== null && _dpt !== '') {
|
|
67
|
+
$.getJSON("knxUltimateDptsGetHelp?dpt=" + _dpt + "&serverId=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => {
|
|
68
|
+
try {
|
|
69
|
+
$("#example-editor").html("");
|
|
70
|
+
$("#sampleCodeEditor").html();
|
|
71
|
+
node.sampleEditor.destroy();
|
|
72
|
+
delete node.sampleEditor;
|
|
73
|
+
} catch (error) {
|
|
90
74
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
try {
|
|
76
|
+
if (data.help !== "NO") {
|
|
77
|
+
node.sampleEditor = RED.editor.createEditor({
|
|
78
|
+
id: 'example-editor',
|
|
79
|
+
mode: 'ace/mode/javascript',
|
|
80
|
+
value: data.help//this.exampleText
|
|
81
|
+
}).renderer.setShowGutter(false);//A.setReadOnly(true).setShowPrintMargin(false);
|
|
82
|
+
if (data.helplink !== "") $("#sampleCodeEditor").html(" <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"" + data.helplink + "\"><u>Link example of " + _dpt + "</u></a>");
|
|
83
|
+
} else {
|
|
84
|
+
// No help avaiable
|
|
85
|
+
node.sampleEditor = RED.editor.createEditor({
|
|
86
|
+
id: 'example-editor',
|
|
87
|
+
mode: 'ace/mode/javascript',
|
|
88
|
+
value: "Currently, no sample payload is avaiable, sorry."
|
|
89
|
+
}).renderer.setShowGutter(false);//B.setReadOnly(true).setShowPrintMargin(false);
|
|
90
|
+
if (data.helplink !== "") $("#sampleCodeEditor").html(" <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"" + data.helplink + "\"><u>Link to wiki</u></a>");
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
// 02/04/2020 Alert user about data type
|
|
98
|
-
// ###########################
|
|
99
|
-
$("#node-input-dpt").on("change", function () {
|
|
100
|
-
|
|
101
|
-
// Load help sample
|
|
102
|
-
knxUltimateDptsGetHelp($(this).val(), false);
|
|
103
|
-
|
|
104
|
-
});
|
|
105
|
-
// ###########################
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// 19/02/2020 Used to alert the user if the CSV file has not been loaded and to get the server sooner als deploy
|
|
109
|
-
// ###########################
|
|
110
|
-
$("#node-input-server").change(function () {
|
|
111
|
-
checkUI();
|
|
112
|
-
});
|
|
113
98
|
function checkUI() {
|
|
114
99
|
oNodeServer = RED.nodes.node($("#node-input-server").val());
|
|
115
100
|
if (oNodeServer === undefined) {
|
|
@@ -133,156 +118,172 @@
|
|
|
133
118
|
} else {
|
|
134
119
|
$("#divknxsecure").hide();
|
|
135
120
|
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
checkUI();
|
|
139
|
-
// ###########################
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
$.getJSON("knxUltimateDpts?serverId=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => {
|
|
143
|
-
data.forEach(dpt => {
|
|
144
|
-
$("#node-input-dpt").append($("<option></option>")
|
|
145
|
-
.attr("value", dpt.value)
|
|
146
|
-
.text(dpt.text))
|
|
147
|
-
});
|
|
148
|
-
$("#node-input-dpt").val(this.dpt);
|
|
149
|
-
// Load help sample
|
|
150
|
-
knxUltimateDptsGetHelp(this.dpt, true);
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// Add write and response as default for existing nodes like was default before
|
|
154
|
-
if (typeof this.notifywrite === 'undefined') {
|
|
155
|
-
this.notifywrite = true
|
|
156
|
-
this.notifyresponse = true
|
|
157
|
-
$("#node-input-notifywrite").prop("checked", true)
|
|
158
|
-
$("#node-input-notifyresponse").prop("checked", true)
|
|
159
|
-
}
|
|
160
121
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
122
|
+
if ($("#node-input-server").val() !== "_ADD_" && $("#node-input-server").val() !== '') {
|
|
123
|
+
$.getJSON("knxUltimateDpts?serverId=" + $("#node-input-server").val() + "&_=" + new Date().getTime(), (data) => {
|
|
124
|
+
data.forEach(dpt => {
|
|
125
|
+
$("#node-input-dpt").append($("<option></option>")
|
|
126
|
+
.attr("value", dpt.value)
|
|
127
|
+
.text(dpt.text))
|
|
128
|
+
});
|
|
129
|
+
$("#node-input-dpt").val(node.dpt);
|
|
130
|
+
// Load help sample
|
|
131
|
+
knxUltimateDptsGetHelp(node.dpt, true);
|
|
132
|
+
})
|
|
133
|
+
}
|
|
166
134
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
$("#
|
|
135
|
+
// Add write and response as default for existing nodes like was default before
|
|
136
|
+
if (node.notifywrite === undefined) {
|
|
137
|
+
node.notifywrite = true
|
|
138
|
+
node.notifyresponse = true
|
|
139
|
+
$("#node-input-notifywrite").prop("checked", true)
|
|
140
|
+
$("#node-input-notifyresponse").prop("checked", true)
|
|
172
141
|
}
|
|
173
|
-
} else {
|
|
174
|
-
$("#divnotifyreadrequestautoreact").hide();
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
142
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
$("#node-input-outputRBE").prop('checked', false)
|
|
183
|
-
$("#divInputRBE").hide()
|
|
184
|
-
$("#node-input-inputRBE").prop('checked', false)
|
|
185
|
-
$("#divnotifyreadrequestautoreact").hide();
|
|
186
|
-
$("#helpallga").show();
|
|
187
|
-
$("#divTopic").hide()
|
|
188
|
-
|
|
189
|
-
// Call a fake datapoint to load a sample "Universal Node"
|
|
190
|
-
knxUltimateDptsGetHelp("0.000", true); // 15/09/2020 Supergiovane, load sample help
|
|
191
|
-
|
|
192
|
-
if ($("#helpallga").html == "") {
|
|
193
|
-
// There is a ETS csv file, show the init read option
|
|
194
|
-
$("#divNode-input-initialread").show()
|
|
195
|
-
} else {
|
|
196
|
-
// There isn't a ETS csv file, hide and deselect the init read option
|
|
197
|
-
$("#divNode-input-initialread").hide();
|
|
198
|
-
$("#node-input-initialread").val(0);
|
|
143
|
+
// Add Write as default for existing clients output
|
|
144
|
+
if (node.outputtype === undefined) {
|
|
145
|
+
node.outputtype = "write"
|
|
146
|
+
$("#node-input-outputtype").val("write")
|
|
199
147
|
}
|
|
200
|
-
} else {
|
|
201
148
|
|
|
202
|
-
|
|
203
|
-
|
|
149
|
+
$("#node-input-notifyreadrequest").on('change', function () {
|
|
150
|
+
if ($("#node-input-notifyreadrequest").is(":checked")) {
|
|
151
|
+
if ($("#node-input-listenallga").is(":checked")) {
|
|
152
|
+
} else {
|
|
153
|
+
$("#divnotifyreadrequestautoreact").show();
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
$("#divnotifyreadrequestautoreact").hide();
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
$("#node-input-listenallga").on('change', function () {
|
|
161
|
+
if ($("#node-input-listenallga").is(":checked")) {
|
|
162
|
+
$("#GAandDPT").hide()
|
|
163
|
+
$("#divOutputRBE").hide()
|
|
164
|
+
$("#node-input-outputRBE").prop('checked', false)
|
|
165
|
+
$("#divInputRBE").hide()
|
|
166
|
+
$("#node-input-inputRBE").prop('checked', false)
|
|
167
|
+
$("#divnotifyreadrequestautoreact").hide();
|
|
168
|
+
$("#helpallga").show();
|
|
169
|
+
$("#divTopic").hide()
|
|
170
|
+
|
|
171
|
+
// Call a fake datapoint to load a sample "Universal Node"
|
|
172
|
+
knxUltimateDptsGetHelp("0.000", true); // 15/09/2020 Supergiovane, load sample help
|
|
173
|
+
|
|
174
|
+
if ($("#helpallga").html == "") {
|
|
175
|
+
// There is a ETS csv file, show the init read option
|
|
176
|
+
$("#divNode-input-initialread").show()
|
|
177
|
+
} else {
|
|
178
|
+
// There isn't a ETS csv file, hide and deselect the init read option
|
|
179
|
+
$("#divNode-input-initialread").hide();
|
|
180
|
+
$("#node-input-initialread").val(0);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
204
183
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
$("#helpallga").hide()
|
|
215
|
-
$("#divNode-input-initialread").show();
|
|
216
|
-
}
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
// Autocomplete suggestion with ETS csv File
|
|
220
|
-
$("#node-input-topic").autocomplete({
|
|
221
|
-
minLength: 1,
|
|
222
|
-
source: function (request, response) {
|
|
223
|
-
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id + "&" + { _: new Date().getTime() }, (data) => {
|
|
224
|
-
response($.map(data, function (value, key) {
|
|
225
|
-
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
226
|
-
if (fullSearch(sSearch, request.term)) {
|
|
227
|
-
return {
|
|
228
|
-
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
229
|
-
value: value.ga // Value
|
|
230
|
-
}
|
|
184
|
+
// 15/09/2020 Supergiovane, load the help sample of the current datapoint
|
|
185
|
+
knxUltimateDptsGetHelp($("#node-input-dpt").val(), false); // 15/09/2020 Supergiovane, load sample help
|
|
186
|
+
|
|
187
|
+
$("#GAandDPT").show()
|
|
188
|
+
$("#divOutputRBE").show()
|
|
189
|
+
$("#divInputRBE").show()
|
|
190
|
+
$("#divTopic").show()
|
|
191
|
+
if ($("#node-input-notifyreadrequest").is(":checked")) {
|
|
192
|
+
$("#divnotifyreadrequestautoreact").show();
|
|
231
193
|
} else {
|
|
232
|
-
|
|
194
|
+
$("#divnotifyreadrequestautoreact").hide();
|
|
233
195
|
}
|
|
234
|
-
|
|
196
|
+
$("#helpallga").hide()
|
|
197
|
+
$("#divNode-input-initialread").show();
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Autocomplete suggestion with ETS csv File
|
|
202
|
+
$("#node-input-topic").autocomplete({
|
|
203
|
+
minLength: 1,
|
|
204
|
+
source: function (request, response) {
|
|
205
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id + "&" + { _: new Date().getTime() }, (data) => {
|
|
206
|
+
response($.map(data, function (value, key) {
|
|
207
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
208
|
+
if (fullSearch(sSearch, request.term)) {
|
|
209
|
+
return {
|
|
210
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
211
|
+
value: value.ga // Value
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}));
|
|
217
|
+
});
|
|
218
|
+
}, select: function (event, ui) {
|
|
219
|
+
// Sets Datapoint and device name automatically
|
|
220
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
221
|
+
try {
|
|
222
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
}
|
|
225
|
+
$('#node-input-name').val(sDevName);
|
|
226
|
+
var optVal = $("#node-input-dpt option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
227
|
+
// Select the option value
|
|
228
|
+
$("#node-input-dpt").val(optVal);
|
|
229
|
+
$("#node-input-dpt").trigger("change");
|
|
230
|
+
}
|
|
235
231
|
});
|
|
236
|
-
}, select: function (event, ui) {
|
|
237
|
-
// Sets Datapoint and device name automatically
|
|
238
|
-
var sDevName = ui.item.label.split("#")[1].trim();
|
|
239
|
-
try {
|
|
240
|
-
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
241
|
-
} catch (error) {
|
|
242
|
-
}
|
|
243
|
-
$('#node-input-name').val(sDevName);
|
|
244
|
-
var optVal = $("#node-input-dpt option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
245
|
-
// Select the option value
|
|
246
|
-
$("#node-input-dpt").val(optVal);
|
|
247
|
-
$("#node-input-dpt").trigger("change");
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
232
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
233
|
+
// Hide or show the GA and DPT fields if Notify on all Group Addresses is checked
|
|
234
|
+
if (oNodeServer !== undefined && oNodeServer !== null) {
|
|
235
|
+
if (oNodeServer.csv !== undefined && oNodeServer.csv !== "") {
|
|
236
|
+
$("#helpallga").html("");
|
|
237
|
+
// There is a ETS csv file, show the init read option
|
|
238
|
+
$("#divNode-input-initialread").show()
|
|
239
|
+
} else {
|
|
240
|
+
// 25/10/2019 Warn user that the node will node encode/decode datagram, if Listen All GA's if the config node doesn't contain the csv
|
|
241
|
+
$("#helpallga").html("<i> " + node._("knxUltimate.advanced.noETSWarning") + " </i>");
|
|
242
|
+
if ($("#node-input-listenallga").is(":checked")) {
|
|
243
|
+
// There isn't a ETS csv file, hide and deselect the init read option
|
|
244
|
+
$("#divNode-input-initialread").hide();
|
|
245
|
+
$("#node-input-initialread").val(0);
|
|
246
|
+
} else {
|
|
247
|
+
$("#divNode-input-initialread").show()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
264
250
|
} else {
|
|
251
|
+
$("#node-input-listenallga").prop("checked", false)
|
|
252
|
+
$("#divTopic").show()
|
|
253
|
+
$("#GAandDPT").show()
|
|
254
|
+
$("#divOutputRBE").show()
|
|
255
|
+
$("#divInputRBE").show()
|
|
256
|
+
$("#helpallga").hide()
|
|
265
257
|
$("#divNode-input-initialread").show()
|
|
266
258
|
}
|
|
259
|
+
|
|
260
|
+
// *****************************
|
|
261
|
+
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
if (node.listenallga === true) {
|
|
264
|
+
// Call a fake datapoint to load a sample "Universal Node"
|
|
265
|
+
knxUltimateDptsGetHelp("0.000", true); // 15/09/2020 Supergiovane, load sample help
|
|
266
|
+
}
|
|
267
|
+
}, 300);
|
|
268
|
+
|
|
267
269
|
}
|
|
268
|
-
} else {
|
|
269
|
-
$("#node-input-listenallga").prop("checked", false)
|
|
270
|
-
$("#divTopic").show()
|
|
271
|
-
$("#GAandDPT").show()
|
|
272
|
-
$("#divOutputRBE").show()
|
|
273
|
-
$("#divInputRBE").show()
|
|
274
|
-
$("#helpallga").hide()
|
|
275
|
-
$("#divNode-input-initialread").show()
|
|
276
270
|
}
|
|
277
271
|
|
|
278
|
-
//
|
|
272
|
+
// 02/04/2020 Alert user about data type
|
|
273
|
+
// ###########################
|
|
274
|
+
$("#node-input-dpt").on("change", function (event) {
|
|
275
|
+
// Load help sample
|
|
276
|
+
knxUltimateDptsGetHelp(event.target.value, false);
|
|
277
|
+
});
|
|
278
|
+
// ###########################
|
|
279
279
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
280
|
+
|
|
281
|
+
// 19/02/2020 Used to alert the user if the CSV file has not been loaded and to get the server sooner als deploy
|
|
282
|
+
// ###########################
|
|
283
|
+
$("#node-input-server").change(function () {
|
|
284
|
+
checkUI();
|
|
285
|
+
});
|
|
286
|
+
// ###########################
|
|
286
287
|
|
|
287
288
|
},
|
|
288
289
|
oneditsave: function () {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.4.
|
|
6
|
+
"version": "2.4.5-beta.0",
|
|
7
7
|
"description": "Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"binary-parser": "2.2.1",
|
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
"js-yaml": "4.1.0"
|
|
22
22
|
},
|
|
23
23
|
"node-red": {
|
|
24
|
-
"version": ">=
|
|
24
|
+
"version": ">=3.1.1",
|
|
25
|
+
"plugins": {
|
|
26
|
+
"commonFunctions": "/nodes/commonFunctions.js"
|
|
27
|
+
},
|
|
25
28
|
"nodes": {
|
|
26
|
-
"commonFunctions": "/nodes/commonFunctions.js",
|
|
27
29
|
"knxUltimate": "/nodes/knxUltimate.js",
|
|
28
30
|
"knxUltimateSceneController": "/nodes/knxUltimateSceneController.js",
|
|
29
31
|
"knxUltimateWatchDog": "/nodes/knxUltimateWatchDog.js",
|