node-red-contrib-knx-ultimate 2.1.40 → 2.1.42

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/.eslintrc.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "commonjs": true,
5
+ "es2021": true
6
+ },
7
+ "extends": "airbnb-base",
8
+ "parserOptions": {
9
+ "ecmaVersion": "latest"
10
+ },
11
+ "rules": {
12
+ }
13
+ }
package/CHANGELOG.md CHANGED
@@ -6,8 +6,19 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 2.1.42</b> - August 2023<br/>
11
+ - Fixed some issues in getting the hue device's names, when using some non LTS versions of node.js.<br/>
12
+ </p>
13
+ <p>
14
+ <b>Version 2.1.41</b> - August 2023<br/>
15
+ - NEW: HUE Light: you can now control ALL GROUPED LIGHT together.<br/>
16
+ - HUE Light: fixed an issue with the "Link brightness to on/off switch" option, when a json color is selected at daylight or nighttime<br/>
17
+ - HUE Light: New: now you can use both DPT 5.001 and 3.007 in the color temperature, at the same time.<br/>
18
+ </p>
19
+ <p>
9
20
  <b>Version 2.1.40</b> - August 2023<br/>
10
- - HUE Light: Buffix: color cycle continues to cycle color, even if a FALSE is sent from the group address.<br/>
21
+ - HUE Light: Bugfix: color cycle continues to cycle color, even if a FALSE is sent from the group address.<br/>
11
22
  </p>
12
23
  <p>
13
24
  <b>Version 2.1.39</b> - August 2023<br/>
@@ -18,7 +18,7 @@
18
18
  }
19
19
  $("#getinfocam").click(function () {
20
20
 
21
- var myNotification = RED.notify("Please press the Link button on the HUE Bridge / Pre favore, premi il pulsante sul bridge HUE",
21
+ var myNotification = RED.notify("Please press the Link button on the HUE Bridge",
22
22
  {
23
23
  modal: true,
24
24
  fixed: true,
@@ -69,13 +69,7 @@
69
69
  }
70
70
  }]
71
71
  });
72
-
73
-
74
-
75
-
76
72
  });
77
-
78
-
79
73
  },
80
74
  oneditsave: function () {
81
75
 
@@ -96,9 +90,7 @@
96
90
 
97
91
  <div class="form-row">
98
92
  <label for="node-config-input-host">
99
- <i class="fa fa-server"></i>
100
- <span data-i18n="hue-config.properties.host" </span>
101
- </label>
93
+ <i class="fa fa-server"></i> IP</label>
102
94
  <input type="text" id="node-config-input-host" placeholder="Write here the HUE bridge's IP, then click CONNECT">
103
95
  </div>
104
96
  <div class="form-row">
@@ -127,11 +119,11 @@
127
119
 
128
120
  <div class="form-row">
129
121
  <label for="node-config-input-username"> Username</label>
130
- <input type="password" id="node-config-input-username" placeholder="">
122
+ <input type="password" id="node-config-input-username" placeholder="" disabled>
131
123
  </div>
132
124
  <div class="form-row">
133
125
  <label for="node-config-input-clientkey"> Bridge Key</label>
134
- <input type="password" id="node-config-input-clientkey" placeholder="">
126
+ <input type="password" id="node-config-input-clientkey" placeholder="" disabled>
135
127
  </div>
136
128
  </div>
137
129
 
@@ -1,149 +1,243 @@
1
-
2
- const dptlib = require('./../KNXEngine/src/dptlib')
3
- const hueClass = require('./utils/hueEngine').classHUE
4
- const loggerEngine = require('./utils/sysLogger.js')
1
+ /* eslint-disable max-len */
2
+ const dptlib = require('../KNXEngine/src/dptlib');
3
+ const HueClass = require('./utils/hueEngine').classHUE;
4
+ const loggerEngine = require('./utils/sysLogger');
5
5
  // Helpers
6
6
  const sortBy = (field) => (a, b) => {
7
- if (a[field] > b[field]) { return 1 } else { return -1 }
8
- }
7
+ if (a[field] > b[field]) { return 1; } return -1;
8
+ };
9
9
 
10
- const onlyDptKeys = (kv) => {
11
- return kv[0].startsWith('DPT')
12
- }
10
+ const onlyDptKeys = (kv) => kv[0].startsWith('DPT');
13
11
 
14
- const extractBaseNo = (kv) => {
15
- return {
16
- subtypes: kv[1].subtypes,
17
- base: parseInt(kv[1].id.replace('DPT', ''))
18
- }
19
- }
12
+ const extractBaseNo = (kv) => ({
13
+ subtypes: kv[1].subtypes,
14
+ base: parseInt(kv[1].id.replace('DPT', '')),
15
+ });
20
16
 
21
17
  const convertSubtype = (baseType) => (kv) => {
22
- const value = `${baseType.base}.${kv[0]}`
18
+ const value = `${baseType.base}.${kv[0]}`;
23
19
  // let sRet = value + " " + kv[1].name + (kv[1].unit === undefined ? "" : " (" + kv[1].unit + ")");
24
- const sRet = value + ' ' + kv[1].name
20
+ const sRet = `${value} ${kv[1].name}`;
25
21
  return {
26
22
  value,
27
- text: sRet
28
- }
29
- }
23
+ text: sRet,
24
+ };
25
+ };
30
26
 
31
27
  const toConcattedSubtypes = (acc, baseType) => {
32
- const subtypes =
33
- Object.entries(baseType.subtypes)
34
- .sort(sortBy(0))
35
- .map(convertSubtype(baseType))
28
+ const subtypes = Object.entries(baseType.subtypes)
29
+ .sort(sortBy(0))
30
+ .map(convertSubtype(baseType));
36
31
 
37
- return acc.concat(subtypes)
38
- }
32
+ return acc.concat(subtypes);
33
+ };
39
34
 
40
35
  module.exports = (RED) => {
41
- 'use strict'
36
+ RED.httpAdmin.get('/knxUltimateDpts', RED.auth.needsPermission('hue-config.read'), (req, res) => {
37
+ const dpts = Object.entries(dptlib)
38
+ .filter(onlyDptKeys)
39
+ .map(extractBaseNo)
40
+ .sort(sortBy('base'))
41
+ .reduce(toConcattedSubtypes, []);
42
42
 
43
- RED.httpAdmin.get('/knxUltimateDpts', RED.auth.needsPermission('hue-config.read'), function (req, res) {
44
- const dpts =
45
- Object.entries(dptlib)
46
- .filter(onlyDptKeys)
47
- .map(extractBaseNo)
48
- .sort(sortBy('base'))
49
- .reduce(toConcattedSubtypes, [])
50
-
51
- res.json(dpts)
52
- })
43
+ res.json(dpts);
44
+ });
53
45
 
54
46
  function hueConfig(config) {
55
- RED.nodes.createNode(this, config)
56
- const node = this
57
- node.host = config.host
58
- node.nodeClients = [] // Stores the registered clients
59
- node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error' // 18/02/2020 Loglevel default error
60
- node.sysLogger = null // 20/03/2022 Default
47
+ RED.nodes.createNode(this, config);
48
+ const node = this;
49
+ node.host = config.host;
50
+ node.nodeClients = []; // Stores the registered clients
51
+ node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error'; // 18/02/2020 Loglevel default error
52
+ node.sysLogger = null;
61
53
  try {
62
- node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
63
- } catch (error) { }
64
- node.name = (config.name === undefined || config.name === '') ? node.host : config.name // 12/08/2021
54
+ node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // New logger to adhere to the loglevel selected in the config-window
55
+ } catch (error) { /* empty */ }
56
+ node.name = (config.name === undefined || config.name === '') ? node.host : config.name;
65
57
 
66
58
  // Init HUE Utility
67
- node.hueManager = new hueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid, node.sysLogger)
68
-
69
- // Event clip V2
70
- node.hueManager.on('event', _event => {
71
- node.nodeClients.forEach(_oClient => {
72
- const oClient = RED.nodes.getNode(_oClient.id)
73
- try {
74
- if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event)
75
- } catch (error) {
76
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Errore node.hueManager.on(event): ' + error.message)
77
- }
78
- })
79
- })
59
+ node.hueManager = new HueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid, node.sysLogger);
80
60
 
81
- RED.httpAdmin.get('/KNXUltimateGetResourcesHUE', RED.auth.needsPermission('hue-config.read'), function (req, res) {
61
+ // Connect to Bridge and get the resources
62
+ node.ConnectToHueBridge = async () => {
63
+ await node.hueManager.Connect();
64
+ // Handle events
82
65
  try {
66
+ node.hueManager.removeAllListeners();
67
+ } catch (error) { /* empty */ }
68
+ node.hueManager.on('event', (_event) => {
69
+ node.nodeClients.forEach((_oClient) => {
70
+ const oClient = _oClient;
71
+ try {
72
+ if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event);
73
+ } catch (error) {
74
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`Errore node.hueManager.on(event): ${error.message}`);
75
+ }
76
+ });
77
+ });
78
+ // Connected
79
+ node.hueManager.on('connected', () => {
80
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('node.hueManager connected event');
81
+ });
82
+ // Initialize the http wrapper, to use the provided key.
83
+ // This http wrapper is used to get the data from HUE brigde
84
+ try {
85
+ // Load all resources, to avoid too many call to the HUE bridge and speed up the showing of the device mnames, during typing in the config window
86
+ node.hueAllResources = await node.hueManager.hueApiV2.get('/resource');
87
+ node.hueAllRooms = await node.hueManager.hueApiV2.get('/resource/room');
88
+ node.hueAllDevices = await node.hueManager.hueApiV2.get('/resource/device');
89
+ } catch (error) {
90
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: getting resources: ${error.message}`);
91
+ }
92
+ };
93
+
94
+ (async () => {
95
+ await node.ConnectToHueBridge();
96
+ })();
97
+
98
+ RED.httpAdmin.get('/KNXUltimateGetResourcesHUE', RED.auth.needsPermission('hue-config.read'), (req, res) => {
99
+ try {
100
+ // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
101
+ const jRet = node.getResources(req.query.rtype);
102
+ res.json(jRet);
103
+ // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
104
+ } catch (error) {
105
+ RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
106
+ res.json({ devices: error.message });
83
107
  (async () => {
108
+ await node.ConnectToHueBridge();
109
+ })();
110
+ }
111
+ });
112
+
113
+ // Get all devices and join it with relative rooms, by adding the room name to the device name
114
+ node.getResources = (_rtype) => {
115
+ try {
116
+ // Api V2
117
+
118
+ // Returns capitalized string
119
+ function capStr(s) {
120
+ if (typeof s !== 'string') return '';
121
+ return s.charAt(0).toUpperCase() + s.slice(1);
122
+ }
123
+
124
+ const retArray = [];
125
+ let allResources;
126
+ if (_rtype === 'light' || _rtype === 'grouped_light') {
127
+ allResources = node.hueAllResources.filter((a) => a.type === 'light' || a.type === 'grouped_light');
128
+ } else {
129
+ allResources = node.hueAllResources.filter((a) => a.type === _rtype);
130
+ }
131
+ for (let index = 0; index < allResources.length; index++) {
132
+ const resource = allResources[index];
133
+ // Get the owner
84
134
  try {
85
- // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
86
- const _node = RED.nodes.getNode(req.query.nodeID)// Retrieve node.id of the config node.
87
- const jRet = await node.hueManager.getResources(req.query.rtype, _node.host, _node.credentials.username)
88
- res.json(jRet)
89
- // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
90
- } catch (err) {
91
- RED.log.error('Errore KNXUltimateGetResourcesHUE non gestito ' + err.message)
92
- res.json({ error: err.message })
135
+ let resourceName = '';
136
+ let sRoom = '';
137
+ if (_rtype === 'light' || _rtype === 'grouped_light') {
138
+ // It's a service, having a owner
139
+ const owners = node.hueAllResources.filter((a) => a.id === resource.owner.rid);
140
+ for (let index = 0; index < owners.length; index++) {
141
+ const owner = owners[index];
142
+ if (owner.type === 'bridge_home') {
143
+ resourceName += 'ALL GROUPS and ';
144
+ } else {
145
+ resourceName += `${owner.metadata.name} and `;
146
+ const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
147
+ sRoom += room !== undefined ? `${room.metadata.name} + ` : ' + ';
148
+ }
149
+ }
150
+ sRoom = sRoom.slice(0, -(' + '.length));
151
+ resourceName = resourceName.slice(0, -(' and '.length));
152
+ resourceName += sRoom !== '' ? ` - Room: ${sRoom}` : '';
153
+ retArray.push({ name: `${capStr(resource.type)}: ${resourceName}`, id: resource.id, deviceObject: resource });
154
+ }
155
+ if (_rtype === 'scene') {
156
+ resourceName = resource.metadata.name || '**Name Not Found**';
157
+ // Get the linked zone
158
+ const zone = node.hueAllResources.find((res) => res.id === resource.group.rid);
159
+ resourceName += ` - ${capStr(resource.group.rtype)}: ${zone.metadata.name}`;
160
+ retArray.push({ name: `${capStr(_rtype)}: ${resourceName}`, id: resource.id });
161
+ }
162
+ if (_rtype === 'button') {
163
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
164
+ const controlID = resource.metadata !== undefined ? (resource.metadata.control_id || '') : '';
165
+ retArray.push({ name: `${capStr(_rtype)}: ${linkedDevName}, button ${controlID}`, id: resource.id });
166
+ }
167
+ if (_rtype === 'motion') {
168
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
169
+ retArray.push({ name: `${capStr(_rtype)}: ${linkedDevName}`, id: resource.id });
170
+ }
171
+ if (_rtype === 'relative_rotary') {
172
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
173
+ retArray.push({ name: `Rotary: ${linkedDevName}`, id: resource.id });
174
+ }
175
+ if (_rtype === 'light_level') {
176
+ const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
177
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
178
+ retArray.push({ name: `Light Level: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
179
+ }
180
+ if (_rtype === 'temperature') {
181
+ const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
182
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
183
+ retArray.push({ name: `Temperature: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
184
+ }
185
+ if (_rtype === 'device_power') {
186
+ const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
187
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
188
+ retArray.push({ name: `Battery: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
189
+ }
190
+ } catch (error) {
191
+ retArray.push({ name: `${_rtype}: ERROR ${error.message}`, id: resource.id });
93
192
  }
94
- })()
95
- } catch (err) {
96
- RED.log.error('Errore KNXUltimateGetResourcesHUE bsonto ' + err.message)
97
- res.json({ error: err.message })
193
+ }
194
+ return { devices: retArray };
195
+ } catch (error) {
196
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: classHUE: getDevices: error ${error.message}`);
197
+ return ({ devices: error.message });
98
198
  }
99
- })
199
+ };
100
200
 
101
201
  node.addClient = (_Node) => {
102
202
  // Check if node already exists
103
- if (node.nodeClients.filter(x => x.id === _Node.id).length === 0) {
203
+ if (node.nodeClients.filter((x) => x.id === _Node.id).length === 0) {
104
204
  // Add _Node to the clients array
105
- _Node.setNodeStatusHue({ fill: 'grey', shape: 'ring', text: 'Hue initialized.' })
106
- // 01/06/2023 Add node to the array
107
- const jNode = {}
108
- jNode.id = _Node.id
109
- jNode.topic = _Node.topic
110
- node.nodeClients.push(jNode)
205
+ _Node.setNodeStatusHue({ fill: 'grey', shape: 'ring', text: 'Hue initialized.' });
206
+ node.nodeClients.push(_Node);
111
207
  }
112
- }
208
+ };
113
209
 
114
210
  node.removeClient = (_Node) => {
115
211
  // Remove the client node from the clients array
116
212
  try {
117
- node.nodeClients = node.nodeClients.filter(x => x.id !== _Node.id)
213
+ node.nodeClients = node.nodeClients.filter((x) => x.id !== _Node.id);
118
214
  } catch (error) { }
119
- }
215
+ };
120
216
 
121
- node.on('close', function (done) {
217
+ node.on('close', (done) => {
122
218
  try {
123
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy()
124
- node.nodeClients = []
219
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy();
220
+ node.nodeClients = [];
125
221
  node.hueManager.removeAllListeners();
126
222
  (async () => {
127
223
  try {
128
- await node.hueManager.close()
129
- node.hueManager = null
130
- delete node.hueManager
131
- } catch (error) {
132
- }
133
- done()
134
- })()
224
+ await node.hueManager.close();
225
+ node.hueManager = null;
226
+ delete node.hueManager;
227
+ } catch (error) { /* empty */ }
228
+ done();
229
+ })();
135
230
  } catch (error) {
136
- done()
137
- console.log(error.message)
231
+ done();
138
232
  }
139
- })
233
+ });
140
234
  }
141
235
 
142
236
  // RED.nodes.registerType("hue-config", hue-config);
143
237
  RED.nodes.registerType('hue-config', hueConfig, {
144
238
  credentials: {
145
239
  username: { type: 'password' },
146
- clientkey: { type: 'password' }
147
- }
148
- })
149
- }
240
+ clientkey: { type: 'password' },
241
+ },
242
+ });
243
+ };