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 CHANGED
@@ -6,14 +6,17 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
- **Version 2.4.4** - Jan 2024<br/>
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
- - Minor fixes.<br/>
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/>
@@ -48,377 +48,395 @@ const toConcattedSubtypes = (acc, baseType) => {
48
48
  // ####################
49
49
 
50
50
 
51
- module.exports = function (RED) {
52
- const node = this;
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
- try {
55
- node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
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
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
62
- if (serverId.hueAllResources === null || serverId.hueAllResources === undefined) {
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
- } catch (error) {
68
- res.json({ ready: false });
69
- }
70
- });
71
-
81
+ });
72
82
 
73
- // 11/03/2020 Delete scene saved file, from html
74
- RED.httpAdmin.get('/knxultimatescenecontrollerdelete', RED.auth.needsPermission('knxUltimateSceneController.read'), (req, res) => {
75
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
76
- // Delete the file
77
- try {
78
- const newPath = `${serverId.userDir}/scenecontroller/SceneController_${req.query.FileName}`;
79
- fs.unlinkSync(newPath);
80
- } catch (error) { if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn(`e ${error}`); }
81
- res.json({ status: 220 });
82
- });
83
-
84
- // Endpoint for connecting to HUE Bridge
85
- RED.httpAdmin.get("/KNXUltimateRegisterToHueBridge", (req, res) => {
86
- try {
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
- (async () => {
89
- try {
90
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
91
-
92
- // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
93
- // If using this code outside of the examples directory, you will want to use the line below and remove the
94
- // const discovery = require('node-hue-api').discovery
95
- const hueApi = require("node-hue-api").api;
96
- const appName = "KNXUltimate";
97
- const deviceName = "Node-Red";
98
-
99
- // async function discoverBridge() {
100
- // const discoveryResults = await discovery.nupnpSearch()
101
-
102
- // if (discoveryResults.length === 0) {
103
- // if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Failed to resolve any Hue Bridges')
104
- // return null
105
- // } else {
106
- // // Ignoring that you could have more than one Hue Bridge on a network as this is unlikely in 99.9% of users situations
107
- // return discoveryResults[0].ipaddress
108
- // }
109
- // }
110
- async function discoverAndCreateUser() {
111
- // const ipAddress = await discoverBridge()
112
- const ipAddress = req.query.IP;
113
- // Create an unauthenticated instance of the Hue API so that we can create a new user
114
- try {
115
- const unauthenticatedApi = await hueApi.createLocal(ipAddress).connect();
116
- let createdUser;
117
- createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
118
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
119
- if (node.sysLogger !== undefined && node.sysLogger !== null) {
120
- node.sysLogger.info(
121
- "User has been created on the Hue Bridge. The following username can be used to\n"
122
- + "authenticate with the Bridge and provide full local access to the Hue Bridge.\n"
123
- + "YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
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
- async function discoverAndCreateUserInsecure() {
147
- // const ipAddress = await discoverBridge()
148
- const ipAddress = req.query.IP;
149
-
150
- // Create an unauthenticated instance of the Hue API so that we can create a new user
151
- try {
152
- const unauthenticatedApi = await hueApi.createInsecureLocal(ipAddress).connect();
153
- let createdUser;
154
- createdUser = await unauthenticatedApi.users.createUser(appName, deviceName);
155
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("*******************************************************************************\n");
156
- if (node.sysLogger !== undefined && node.sysLogger !== null) {
157
- node.sysLogger.info(
158
- "User has been created on the Hue Bridge. The following username can be used to\n"
159
- + "authenticate with the Bridge and provide full local access to the Hue Bridge.\n"
160
- + "YOU SHOULD TREAT THIS LIKE A PASSWORD\n",
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
- // Invoke the discovery and create user code
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 discoverAndCreateUserInsecure();
193
+ const jRet = await discoverAndCreateUser();
190
194
  res.json(jRet);
191
195
  } catch (error) {
192
- RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito Insecure ${error.message}. I give up.`);
193
- res.json({ error: error.message });
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
- } catch (err) {
197
- RED.log.error(`Errore KNXUltimateRegisterToHueBridge non gestito ${err.message}`);
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
- } catch (err) {
201
- RED.log.error(`Errore KNXUltimateRegisterToHueBridge bsonto ${err.message}`);
202
- res.json({ error: err.message });
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
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("KNXUltimate-config: Requested csv maybe from visu-ultimate?");
215
- RED.nodes.eachNode((_node) => {
216
- if (_node.hasOwnProperty("csv") && _node.type === "knxUltimate-config" && _node.csv !== "") {
217
- res.json(RED.nodes.getNode(_node.id).csv);
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
- const oiFaces = oOS.networkInterfaces();
228
- const jListInterfaces = [];
229
- try {
230
- Object.keys(oiFaces).forEach((ifname) => {
231
- // Interface with single IP
232
- if (Object.keys(oiFaces[ifname]).length === 1) {
233
- if (Object.keys(oiFaces[ifname])[0].internal === false) {
234
- jListInterfaces.push({
235
- name: ifname,
236
- address: Object.keys(oiFaces[ifname])[0].address,
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
- let sAddresses = "";
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
- } catch (error) { }
248
- res.json(jListInterfaces);
249
- });
277
+ } catch (error) {
278
+ }
279
+ });
250
280
 
251
- // 12/08/2021 Endpoint for deleting the GA persistent file for the current gateway
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
- const sFile = path.join(serverId.userDir, "knxpersistvalues", `knxpersist${req.query.serverId}.json`);
257
- fs.unlinkSync(sFile);
258
- } catch (error) { res.json({ error: error.stack }); }
259
- res.json({ error: "No error" });
260
- } else {
261
- res.json({ error: "No serverId specified" });
262
- }
263
- });
264
-
265
- RED.httpAdmin.get("/knxUltimateGetHueColor", RED.auth.needsPermission("hue-config.read"), (req, res) => {
266
- try {
267
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
268
- // find wether the light is a light or is grouped_light
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
- res.json(kelvinValue !== undefined ? kelvinValue : "Select the device first!");
297
- } catch (error) {
298
- res.json("Select the device first!");
299
- }
300
- });
298
+ });
301
299
 
302
- RED.httpAdmin.get("/knxUltimateGetLightObject", RED.auth.needsPermission("hue-config.read"), (req, res) => {
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
- if (oLight.color_temperature !== undefined && oLight.color_temperature.mirek !== undefined) {
314
- oLight.calculatedKelvin = hueColorConverter.ColorConverter.mirekToKelvin(oLight.color_temperature.mirek);
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
- oLight.calculatedKelvin = undefined;
315
+ res.json("Select the device first!");
318
316
  }
319
- // HEX value from XYBri
317
+ });
318
+
319
+ RED.httpAdmin.get("/knxUltimateGetLightObject", (req, res) => {
320
320
  try {
321
- const retRGB = hueColorConverter.ColorConverter.xyBriToRgb(oLight.color.xy.x, oLight.color.xy.y, oLight.dimming.brightness);
322
- const ret = `#${hueColorConverter.ColorConverter.rgbHex(retRGB.r, retRGB.g, retRGB.b).toString()}`;
323
- oLight.calculatedHEXColor = ret;
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
- oLight.calculatedHEXColor = undefined;
346
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}.`);
347
+ res.json({});
326
348
  }
327
- res.json(oLight);
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
- RED.httpAdmin.get("/KNXUltimateGetResourcesHUE", (req, res) => {
335
- try {
336
- // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
337
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
338
- if (serverId === null) {
339
- RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverId is null`);
340
- res.json({ devices: `serverId not set` });
341
- return;
342
- }
343
- const jRet = serverId.getResources(req.query.rtype);
344
- if (jRet !== undefined) {
345
- res.json(jRet);
346
- } else {
347
- res.json({ devices: [{ name: "I'm still connecting...Try in some seconds" }] });
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
- // 15/09/2020 Supergiovane, read datapoint help usage
370
- RED.httpAdmin.get("/knxUltimateDptsGetHelp", (req, res) => {
371
- try {
385
+ // 15/09/2020 Supergiovane, read datapoint help usage
386
+ RED.httpAdmin.get("/knxUltimateDptsGetHelp", (req, res) => {
387
+ try {
372
388
 
373
- const serverId = RED.nodes.getNode(req.query.serverId); // Retrieve node.id of the config node.
374
- const sDPT = req.query.dpt.split(".")[0]; // Takes only the main type
375
- let jRet;
376
- if (sDPT === "0") {
377
- // Special fake datapoint, meaning "Universal Mode"
378
- jRet = {
379
- help: `// KNX-Ultimate set as UNIVERSAL NODE
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
- helplink: "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki",
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
- break;
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
- res.json(jRet);
408
- } catch (error) {
409
- res.json({ error: error.stack });
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
- RED.httpAdmin.post("/banana", RED.auth.needsPermission("write"), (req, res) => {
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
  };
@@ -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
- $.getJSON("knxUltimateDptsGetHelp?dpt=" + _dpt + "&serverId=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => {
67
- try {
68
- $("#example-editor").html("");
69
- $("#sampleCodeEditor").html();
70
- node.sampleEditor.destroy();
71
- delete node.sampleEditor;
72
- } catch (error) {
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("&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<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("&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<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
- } catch (error) {
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("&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<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("&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<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
- // Add Write as default for existing clients output
162
- if (typeof this.outputtype === 'undefined') {
163
- this.outputtype = "write"
164
- $("#node-input-outputtype").val("write")
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
- $("#node-input-notifyreadrequest").on('change', function () {
168
- if ($("#node-input-notifyreadrequest").is(":checked")) {
169
- if ($("#node-input-listenallga").is(":checked")) {
170
- } else {
171
- $("#divnotifyreadrequestautoreact").show();
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
- $("#node-input-listenallga").on('change', function () {
179
- if ($("#node-input-listenallga").is(":checked")) {
180
- $("#GAandDPT").hide()
181
- $("#divOutputRBE").hide()
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
- // 15/09/2020 Supergiovane, load the help sample of the current datapoint
203
- knxUltimateDptsGetHelp($("#node-input-dpt").val(), false); // 15/09/2020 Supergiovane, load sample help
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
- $("#GAandDPT").show()
206
- $("#divOutputRBE").show()
207
- $("#divInputRBE").show()
208
- $("#divTopic").show()
209
- if ($("#node-input-notifyreadrequest").is(":checked")) {
210
- $("#divnotifyreadrequestautoreact").show();
211
- } else {
212
- $("#divnotifyreadrequestautoreact").hide();
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
- return null;
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
- // Hide or show the GA and DPT fields if Notify on all Group Addresses is checked
252
- if (this.server) {
253
- if (typeof RED.nodes.node(this.server).csv !== 'undefined' && RED.nodes.node(this.server).csv != "") {
254
- $("#helpallga").html("");
255
- // There is a ETS csv file, show the init read option
256
- $("#divNode-input-initialread").show()
257
- } else {
258
- // 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
259
- $("#helpallga").html("<i> " + node._("knxUltimate.advanced.noETSWarning") + " </i>");
260
- if ($("#node-input-listenallga").is(":checked")) {
261
- // There isn't a ETS csv file, hide and deselect the init read option
262
- $("#divNode-input-initialread").hide();
263
- $("#node-input-initialread").val(0);
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
- setTimeout(() => {
281
- if (this.listenallga === true) {
282
- // Call a fake datapoint to load a sample "Universal Node"
283
- knxUltimateDptsGetHelp("0.000", true); // 15/09/2020 Supergiovane, load sample help
284
- }
285
- }, 300);
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.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": ">=2.0.0",
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",