node-red-contrib-knx-ultimate 2.3.4 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: Bug report
3
- about: Create a report to help us improve
3
+ about: Signal a bug
4
4
  title: ''
5
5
  labels: ''
6
6
  assignees: Supergiovane
@@ -10,26 +10,26 @@ assignees: Supergiovane
10
10
  **Salutation (i'll not respond to users that doesn’t wrote at least "Hello")**
11
11
 
12
12
  **Describe the bug**
13
- A clear and concise description of what the bug is.
13
+ Write here a clear and concise description of what the bug is.
14
14
 
15
15
  **To Reproduce**
16
- Steps to reproduce the behavior:
16
+ Write here the steps to reproduce the behavior, for example:
17
17
  1. Go to '...'
18
18
  2. Click on '....'
19
19
  3. Scroll down to '....'
20
20
  4. See error
21
21
 
22
22
  **Expected behavior**
23
- A clear and concise description of what you expected to happen.
23
+ Write here a clear and concise description of what you expected to happen.
24
24
 
25
25
  **Screenshots**
26
- If applicable, add screenshots to help explain your problem.
26
+ Here, if applicable, add screenshots to help explain your problem.
27
27
 
28
28
  **Knx-Ultimate Version**
29
- - Eg. 1.1.95
29
+ - Eg. 2.3.5
30
30
 
31
31
  **Are you running node-red behind homematic, docker or anything similar?**
32
32
  - Eg. Node-Red running alone in RaspberryPi
33
33
 
34
34
  **Additional context**
35
- Add any other context about the problem here.
35
+ Write here any other context about the problem here.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,35 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ **Version 2.4.1** - Jan 2024<br/>
10
+ - NEW: Added KNX Datapoint 275.100<br/>
11
+ - HUE Light: fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/317<br/>
12
+ - HUE Light: corrected the 7.600 kelvin range https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/316<br/>
13
+ - 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
+ - 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/>
17
+
18
+ **Version 2.4.0-beta.1** - Jan 2024<br/>
19
+ - This is a public installable beta.<br/>
20
+ - HUE Light: fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/317<br/>
21
+ - HUE Light: corrected the 7.600 kelvin range https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/316<br/>
22
+ - HUE Light: blinking effect and color cyle are now stopped, whenever an FALSE KNX telegram is received by the light switching group address.<br/>
23
+ - HUE Light: when the light is off, the dim up sequence starts now with initial brightness = zero.<br/>
24
+ - 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/>
25
+ - Minor fixes.<br/>
26
+
27
+ **Version 2.4.0-beta.0** - Jan 2024<br/>
28
+ - THIS IS A BETA VERSION. INSTALL ONLY IF YOU HAVE MANY HUE BRIDGES.<br/>
29
+ - HUE Light: fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/317<br/>
30
+ - HUE Light: corrected the 7.600 kelvin range https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/316<br/>
31
+ - HUE Light: blinking effect and color cyle are now stopped, whenever an FALSE KNX telegram is received by the light switching group address.<br/>
32
+ - HUE Light: when the light is off, the dim up sequence starts now with initial brightness = zero.<br/>
33
+ - 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/>
34
+
35
+ **Version 2.3.5** - Jan 2024<br/>
36
+ - HUE Light: fixed multi HUE Bridge GUI issue.<br/>
37
+
9
38
  **Version 2.3.4** - Jan 2024<br/>
10
39
  - HUE Light: fixex tab "DIM/Brightness" inaccessible, when "KNX Brightness Status" was set to use the default knx behaviour.<br/>
11
40
 
@@ -7,7 +7,11 @@
7
7
  # CHANGELOG
8
8
 
9
9
  <p>
10
- <b>Version 1.0.43</b> - December 2023<br/>
10
+ <b>Version 1.0.46</b> - January 2024<br/>
11
+ - NEW: added DPT 275.100.<br/>
12
+ </p>
13
+ <p>
14
+ <b>Version 1.0.44</b> - December 2023<br/>
11
15
  - Fixed DPT 9.001 issue when sending numberso having > 2 decimals.<br/>
12
16
  </p>
13
17
  <p>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "knxultimate",
3
3
  "description": "KNX IP protocol implementation for Node. This is the ENGINE of Node-Red KNX-Ultimate node.",
4
- "version": "1.0.44",
4
+ "version": "1.0.46",
5
5
  "engines": {
6
6
  "node": ">=14"
7
7
  },
@@ -0,0 +1,60 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ /**
3
+ * KNXEngine - a KNX protocol stack in Javascript
4
+ * (C) 2020-2024 Supergiovane
5
+ */
6
+
7
+ const knxLog = require("../KnxLog");
8
+ const dpt9 = require("./dpt9");
9
+
10
+ //
11
+ // 4x DPT9.* 2-byte floating point value
12
+ //
13
+
14
+ exports.formatAPDU = function (value) {
15
+ // Get the javascript object and create a telegram for the KNX bus.
16
+ if (typeof value === 'object' && value.hasOwnProperty('comfort') && value.hasOwnProperty('standby') && value.hasOwnProperty('economy') && value.hasOwnProperty('buildingProtection')) {
17
+ const comfort = dpt9.formatAPDU(value.comfort);
18
+ const standby = dpt9.formatAPDU(value.standby);
19
+ const economy = dpt9.formatAPDU(value.economy);
20
+ const buildingProtection = dpt9.formatAPDU(value.buildingProtection);
21
+ return Buffer.concat([comfort, standby, economy, buildingProtection]);
22
+ } else {
23
+ knxLog.get().error('DPT275.formatAPDU: Must supply all values, for example {comfort:22, standby:21.5, economy:21, buildingProtection:15}');
24
+ }
25
+ }
26
+
27
+ exports.fromBuffer = function (buf) {
28
+ // Get the telegram from the KNX bus and create a javascript object.
29
+ if (buf.length !== 8) {
30
+ knxLog.get().warn('DPT275.fromBuffer: buf should be 8 bytes long (got %d bytes)', buf.length);
31
+ return null;
32
+ }
33
+ const comfort = dpt9.fromBuffer(buf.slice(0, 2));
34
+ const standby = dpt9.fromBuffer(buf.slice(2, 4));
35
+ const economy = dpt9.fromBuffer(buf.slice(4, 6));
36
+ const buildingProtection = dpt9.fromBuffer(buf.slice(6, 8));
37
+ return { comfort: comfort, standby: standby, economy: economy, buildingProtection: buildingProtection };
38
+ }
39
+
40
+ // DPT275 basetype info
41
+ exports.basetype = {
42
+ bitlength: 64,
43
+ valuetype: 'basic',
44
+ desc: 'Quadruple setpoints (comfort,standby,economy,buildingProtection) (4 float with 16 Bit)',
45
+ help:
46
+ `// Send comfort, standby, economy mode and buildingProtection temperatures, as n.4 DPT9.001.
47
+ msg.payload = {comfort:22, standby:21.5, economy:21, buildingProtection:15};
48
+ return msg;`
49
+ }
50
+
51
+ // DPT9 subtypes
52
+ exports.subtypes = {
53
+ // 9.001 temperature (oC)
54
+ '100': {
55
+ name: 'Quadruple setpoints (comfort,standby,economy,buildingProtection) (4 float with 16 Bit)',
56
+ desc: 'DPT_TempRoomSetpSetF16[4]',
57
+ unit: '°C',
58
+ range: [-273, 670760]
59
+ }
60
+ }
@@ -3,24 +3,24 @@
3
3
  * (C) 2020-2022 Supergiovane
4
4
  */
5
5
 
6
- const fs = require('fs')
7
- const path = require('path')
8
- const util = require('util')
9
- const knxLog = require('./../KnxLog')
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const util = require('util');
9
+ const knxLog = require("../KnxLog");
10
10
 
11
- let matches
12
- const dirEntries = fs.readdirSync(__dirname)
13
- const dpts = {}
11
+ let matches;
12
+ const dirEntries = fs.readdirSync(__dirname);
13
+ const dpts = {};
14
14
  for (let i = 0; i < dirEntries.length; i++) {
15
15
  if (matches = dirEntries[i].match(/(dpt.*)\.js/)) {
16
- const dptid = matches[1].toUpperCase() // DPT1..DPTxxx
17
- const mod = require(__dirname + path.sep + dirEntries[i])
18
- if (!mod.hasOwnProperty('basetype') ||
19
- !mod.basetype.hasOwnProperty('bitlength')) {
20
- throw 'incomplete ' + dptid + ', missing basetype and/or bitlength!'
16
+ const dptid = matches[1].toUpperCase(); // DPT1..DPTxxx
17
+ const mod = require(__dirname + path.sep + dirEntries[i]);
18
+ if (!mod.hasOwnProperty('basetype')
19
+ || !mod.basetype.hasOwnProperty('bitlength')) {
20
+ throw `incomplete ${dptid}, missing basetype and/or bitlength!`;
21
21
  }
22
- mod.id = dptid
23
- dpts[dptid] = mod
22
+ mod.id = dptid;
23
+ dpts[dptid] = mod;
24
24
  // knxLog.get().trace('DPT library: loaded %s (%s)', dptid, dpts[dptid].basetype.desc);
25
25
  }
26
26
  }
@@ -28,20 +28,20 @@ for (let i = 0; i < dirEntries.length; i++) {
28
28
  // a generic DPT resolution function
29
29
  // DPTs might come in as 9/"9"/"9.001"/"DPT9.001"
30
30
  dpts.resolve = function (dptid) {
31
- const m = dptid.toString().toUpperCase().match(/^(?:DPT)?(\d+)(\.(\d+))?$/)
32
- if (m === null) { throw new Error('Invalid DPT format: ' + dptid) }
31
+ const m = dptid.toString().toUpperCase().match(/^(?:DPT)?(\d+)(\.(\d+))?$/);
32
+ if (m === null) { throw new Error(`Invalid DPT format: ${dptid}`); }
33
33
 
34
- const dpt = dpts[util.format('DPT%s', m[1])]
35
- if (!dpt) { throw new Error('Unsupported DPT: ' + dptid) }
34
+ const dpt = dpts[util.format('DPT%s', m[1])];
35
+ if (!dpt) { throw new Error(`Unsupported DPT: ${dptid}`); }
36
36
 
37
- const cloned_dpt = cloneDpt(dpt)
37
+ const cloned_dpt = cloneDpt(dpt);
38
38
  if (m[3]) {
39
- cloned_dpt.subtypeid = m[3]
40
- cloned_dpt.subtype = cloned_dpt.subtypes[m[3]]
39
+ cloned_dpt.subtypeid = m[3];
40
+ cloned_dpt.subtype = cloned_dpt.subtypes[m[3]];
41
41
  }
42
42
 
43
- return cloned_dpt
44
- }
43
+ return cloned_dpt;
44
+ };
45
45
 
46
46
  /* POPULATE an APDU object from a given Javascript value for the given DPT
47
47
  * - either by a custom DPT formatAPDU function
@@ -51,59 +51,72 @@ dpts.resolve = function (dptid) {
51
51
  */
52
52
  dpts.populateAPDU = function (value, apdu, dptid) {
53
53
  // console.log ("BANANA " + dptid)
54
- const dpt = dpts.resolve(dptid || 'DPT1')
55
- const nbytes = Math.ceil(dpt.basetype.bitlength / 8)
54
+ const dpt = dpts.resolve(dptid || 'DPT1');
55
+ const nbytes = Math.ceil(dpt.basetype.bitlength / 8);
56
56
  // apdu.data = new Buffer(nbytes); // 14/09/2020 Supregiovane: Deprecated. Replaced with below.
57
- apdu.data = Buffer.alloc(nbytes)
58
- apdu.bitlength = dpt.basetype && dpt.basetype.bitlength || 1
59
- let tgtvalue = value
57
+ apdu.data = Buffer.alloc(nbytes);
58
+ apdu.bitlength = dpt.basetype && dpt.basetype.bitlength || 1;
59
+ let tgtvalue = value;
60
60
  // get the raw APDU data for the given JS value
61
61
  if (typeof dpt.formatAPDU === 'function') {
62
62
  // nothing to do here, DPT-specific formatAPDU implementation will handle everything
63
63
  // knxLog.get().trace('>>> custom formatAPDU(%s): %j', dptid, value);
64
- apdu.data = dpt.formatAPDU(value)
64
+ apdu.data = dpt.formatAPDU(value);
65
65
  // knxLog.get().trace('<<< custom formatAPDU(%s): %j', dptid, apdu.data);
66
66
  } else {
67
67
  if (!isFinite(value)) {
68
- throw new Error(util.format('Invalid value, expected a %s',
69
- dpt.desc))
68
+ throw new Error(util.format(
69
+ 'Invalid value, expected a %s',
70
+ dpt.desc,
71
+ ));
70
72
  }
71
73
  // check if value is in range, be it explicitly defined or implied from bitlength
72
74
  const range = (dpt.basetype.hasOwnProperty('range'))
73
75
  ? dpt.basetype.range
74
- : [0, Math.pow(2, dpt.basetype.bitlength) - 1]
76
+ : [0, 2 ** dpt.basetype.bitlength - 1];
75
77
  // is there a scalar range? eg. DPT5.003 angle degrees (0=0, ff=360)
76
78
  if (dpt.hasOwnProperty('subtype') && dpt.subtype.hasOwnProperty(
77
- 'scalar_range')) {
78
- const scalar = dpt.subtype.scalar_range
79
+ 'scalar_range',
80
+ )) {
81
+ const scalar = dpt.subtype.scalar_range;
79
82
  if (value < scalar[0] || value > scalar[1]) {
80
83
  knxLog.get().trace(
81
84
  'Value %j(%s) out of scalar range(%j) for %s',
82
- value, (typeof value), scalar, dpt.id)
85
+ value, (
86
+ typeof value),
87
+ scalar,
88
+ dpt.id,
89
+ );
83
90
  } else {
84
91
  // convert value from its scalar representation
85
92
  // e.g. in DPT5.001, 50(%) => 0x7F , 100(%) => 0xFF
86
- const a = (scalar[1] - scalar[0]) / (range[1] - range[0])
87
- const b = (scalar[0] - range[0])
88
- tgtvalue = Math.round((value - b) / a)
93
+ const a = (scalar[1] - scalar[0]) / (range[1] - range[0]);
94
+ const b = (scalar[0] - range[0]);
95
+ tgtvalue = Math.round((value - b) / a);
89
96
  }
90
97
  } else {
91
98
  // just a plain numeric value, only check if within bounds
92
99
  if (value < range[0] || value > range[1]) {
93
- knxLog.get().trace('Value %j(%s) out of bounds(%j) for %s.%s',
94
- value, (typeof value), range, dpt.id, dpt.subtypeid)
100
+ knxLog.get().trace(
101
+ 'Value %j(%s) out of bounds(%j) for %s.%s',
102
+ value, (
103
+ typeof value),
104
+ range,
105
+ dpt.id,
106
+ dpt.subtypeid,
107
+ );
95
108
  }
96
109
  }
97
110
  // generic APDU is assumed to convey an unsigned integer of arbitrary bitlength
98
111
  if (dpt.basetype.hasOwnProperty('signedness') && dpt.basetype.signedness === 'signed') {
99
- apdu.data.writeIntBE(tgtvalue, 0, nbytes)
112
+ apdu.data.writeIntBE(tgtvalue, 0, nbytes);
100
113
  } else {
101
- apdu.data.writeUIntBE(tgtvalue, 0, nbytes)
114
+ apdu.data.writeUIntBE(tgtvalue, 0, nbytes);
102
115
  }
103
116
  }
104
117
  // knxLog.get().trace('generic populateAPDU tgtvalue=%j(%s) nbytes=%d => apdu=%j', tgtvalue, typeof tgtvalue, nbytes, apdu);
105
- return apdu
106
- }
118
+ return apdu;
119
+ };
107
120
 
108
121
  /* get the correct Javascript value from a APDU buffer for the given DPT
109
122
  * - either by a custom DPT formatAPDU function
@@ -112,48 +125,49 @@ dpts.populateAPDU = function (value, apdu, dptid) {
112
125
  */
113
126
  dpts.fromBuffer = function (buf, dpt) {
114
127
  // sanity check
115
- if (!dpt) throw util.format('DPT %s not found', dpt)
116
- let value = 0
128
+ if (!dpt) throw util.format('DPT %s not found', dpt);
129
+ let value = 0;
117
130
  // get the raw APDU data for the given JS value
118
131
  if (typeof dpt.fromBuffer === 'function') {
119
132
  // nothing to do here, DPT-specific fromBuffer implementation will handle everything
120
- value = dpt.fromBuffer(buf)
133
+ value = dpt.fromBuffer(buf);
121
134
  } else {
122
135
  // knxLog.get().trace('%s buflength == %d => %j', typeof buf, buf.length, JSON.stringify(buf) );
123
136
  // get a raw unsigned integer from the buffer
124
137
  if (buf.length > 6) {
125
- throw 'cannot handle unsigned integers more then 6 bytes in length'
138
+ throw 'cannot handle unsigned integers more then 6 bytes in length';
126
139
  }
127
140
  if (dpt.basetype.hasOwnProperty('signedness') && dpt.basetype.signedness == 'signed') {
128
- value = buf.readIntBE(0, buf.length)
141
+ value = buf.readIntBE(0, buf.length);
129
142
  } else {
130
- value = buf.readUIntBE(0, buf.length)
143
+ value = buf.readUIntBE(0, buf.length);
131
144
  }
132
145
  // knxLog.get().trace(' ../knx/src/index.js : DPT : ' + JSON.stringify(dpt)); // for exploring dpt and implementing description
133
146
  if (dpt.hasOwnProperty('subtype') && dpt.subtype.hasOwnProperty(
134
- 'scalar_range')) {
147
+ 'scalar_range',
148
+ )) {
135
149
  const range = (dpt.basetype.hasOwnProperty('range'))
136
150
  ? dpt.basetype.range
137
- : [0, Math.pow(2, dpt.basetype.bitlength) - 1]
138
- const scalar = dpt.subtype.scalar_range
151
+ : [0, 2 ** dpt.basetype.bitlength - 1];
152
+ const scalar = dpt.subtype.scalar_range;
139
153
  // convert value from its scalar representation
140
154
  // e.g. in DPT5.001, 50(%) => 0x7F , 100(%) => 0xFF
141
- const a = (scalar[1] - scalar[0]) / (range[1] - range[0])
142
- const b = (scalar[0] - range[0])
143
- value = Math.round(a * value + b)
155
+ const a = (scalar[1] - scalar[0]) / (range[1] - range[0]);
156
+ const b = (scalar[0] - range[0]);
157
+ value = Math.round(a * value + b);
144
158
  // knxLog.get().trace('fromBuffer scalar a=%j b=%j %j', a,b, value);
145
159
  }
146
160
  }
147
161
  // knxLog.get().trace('generic fromBuffer buf=%j, value=%j', buf, value);
148
- return value
149
- }
162
+ return value;
163
+ };
150
164
 
151
- function cloneDpt (d) {
152
- let result = {}
153
- result = JSON.parse(JSON.stringify(d))
154
- result.fromBuffer = d.fromBuffer
155
- result.formatAPDU = d.formatAPDU
156
- return result
165
+ function cloneDpt(d) {
166
+ let result = {};
167
+ result = JSON.parse(JSON.stringify(d));
168
+ result.fromBuffer = d.fromBuffer;
169
+ result.formatAPDU = d.formatAPDU;
170
+ return result;
157
171
  }
158
172
 
159
- module.exports = dpts
173
+ module.exports = dpts;