@mschaeffler/node-red-bthome 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,6 +83,7 @@ There are two output ports:
83
83
  |config | type | description |
84
84
  |:------------|:-------|:----------------------------------|
85
85
  |Devices | JSON | configuration of the BT-Home devices |
86
+ |counter is time|Boolean| the counter in encrypted messages is checked again the actual time |
86
87
  |Status-Prefix| string | prefix for the topic for state output |
87
88
  |Event-Prefix | string | prefix for the topic for event output |
88
89
  |Context-Variable| string | name of the variable in flow context storage |
@@ -124,7 +125,7 @@ Example:
124
125
  "encrypted": false,
125
126
  "battery": 100,
126
127
  "gw": { "Shelly Gateway": { "time": 1745395033113, "rssi":-85 } },
127
- data": { "humidity":56, "temperature":-21.3 }
128
+ "data": { "humidity":56, "temperature":-21.3 }
128
129
  }
129
130
  }
130
131
  ```
package/bthome.html CHANGED
@@ -167,6 +167,8 @@
167
167
  <dl class="message-properties">
168
168
  <dt>Devices <span class="property-type">JSON</span></dt>
169
169
  <dd> configuration of the BT-Home devices.</dd>
170
+ <dt>counter is times <span class="property-type">Boolean</span></dt>
171
+ <dd> the counter in encrypted messages is checked again the actual time.</dd>
170
172
  <dt>Status-Prefix <span class="property-type">string</span></dt>
171
173
  <dd> prefix for the topic for state output.</dd>
172
174
  <dt>Event-Prefix <span class="property-type">string</span></dt>
@@ -208,7 +210,7 @@
208
210
  "encrypted": false,<br>
209
211
  "battery": 100,<br>
210
212
  "gw": { "Shelly Gateway": { "time": 1745395033113, "rssi":-85 } },<br>
211
- data": { "humidity":56, "temperature":-21.3 }<br>
213
+ "data": { "humidity":56, "temperature":-21.3 }<br>
212
214
  }
213
215
  }<code>
214
216
 
package/bthome.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const Crypto = require( 'node:crypto' );
2
+ const Tools = require( './tools.js' );
2
3
  const Rawdata = require( "./rawdata.js" );
3
4
  const BtEvent = require( "./btevent.js" );
4
5
 
@@ -37,28 +38,14 @@ module.exports = function(RED) {
37
38
  for( const mac in node.devices )
38
39
  {
39
40
  const device = node.devices[mac];
40
- switch( typeof device.key )
41
+ try
41
42
  {
42
- case "undefined":
43
- break;
44
- case "string":
45
- if( device.key.length == 32 )
46
- {
47
- const buf = Buffer.alloc( 16 );
48
- for( let i=0; i<16 ; i++ )
49
- {
50
- buf[i] = Number.parseInt( device.key.slice( i*2, i*2 + 2 ), 16 );
51
- }
52
- device.key = buf;
53
- }
54
- else
55
- {
56
- device.key = Buffer.alloc( 16 );
57
- node.log( mac + " keylength must be 16 bytes" );
58
- }
59
- break;
60
- default:
61
- device.key = Buffer.from( device.key );
43
+ device.key = Tools.key2bytes( device.key );
44
+ }
45
+ catch( e )
46
+ {
47
+ node.log( mac + e.message );
48
+ device.key = null;
62
49
  }
63
50
  }
64
51
 
@@ -91,25 +78,24 @@ module.exports = function(RED) {
91
78
 
92
79
  function decryptMsg()
93
80
  {
94
- let mac = [];
95
- for( const help of msg.payload.addr.split( ":" ) )
96
- {
97
- mac.push( Number.parseInt( help, 16 ) );
98
- }
99
- const uuid16 = [0xD2,0xFC];
81
+ const mac = Tools.mac2bytes( msg.payload.addr );
100
82
  const ciphertext = Buffer.from( rawdata.slice( 0, -8 ) );
101
83
  const counter = rawdata.slice( -8, -4 );
102
- const counterInt = counter[0] | (counter[1]<<8) | (counter[2]<<16) | (counter[3]<<24);
84
+ if( node.counterTime )
85
+ {
86
+ const counterInt = counter[0] | (counter[1]<<8) | (counter[2]<<16) | (counter[3]<<24);
87
+ const delta = Math.abs( msgTime - counterInt*1000 );
88
+ if( delta > 15000 )
89
+ {
90
+ throw new Error( "bthome "+msg.payload.gateway+" "+name+" "+(new Date(counterInt*1000))+" "+delta );
91
+ }
92
+ }
103
93
  const mic = Buffer.from( rawdata.slice( -4 ) );
104
- const nonce = Buffer.from( mac.concat( uuid16, dib, counter ) );
94
+ const nonce = Buffer.from( mac.concat( Tools.uuid16, dib, counter ) );
105
95
  const decipher = Crypto.createDecipheriv( "aes-128-ccm", node.devices[msg.payload.addr].key, nonce, { authTagLength: 4 } );
106
96
  decipher.setAuthTag( mic );
107
97
  rawdata = Array.from( decipher.update( ciphertext ) );
108
98
  decipher.final();
109
- if( node.counterTime && Date.now() - counterInt*1000 > 7500 )
110
- {
111
- node.error("bthome "+name+" "+(new Date(counterInt*1000))+" "+(Date.now()-counterInt*1000));
112
- }
113
99
  }
114
100
 
115
101
  function setData(name,value)
@@ -186,13 +172,13 @@ module.exports = function(RED) {
186
172
  if( pid < item.pid && pid > 10 /*&& pid > item.pid - 10*/ )
187
173
  {
188
174
  // veraltete Nachricht und nicht reboot
189
- node.warn( `old ble message (${name}) dropped, ${msg.payload.pid} < ${item.data?.pid}` );
175
+ node.warn( `old ble message ${name} from ${msg.payload.gateway} dropped, ${pid} < ${item.pid}` );
190
176
  return false;
191
177
  }
192
178
  if( msg.payload.gateway )
193
179
  {
194
180
  item.gw[msg.payload.gateway] = {
195
- time: msg.payload.time ?? Date.now(),
181
+ time: msgTime,
196
182
  rssi: msg.payload.rssi ?? null
197
183
  };
198
184
  }
@@ -201,7 +187,7 @@ module.exports = function(RED) {
201
187
 
202
188
  function newMessage()
203
189
  {
204
- item.time = msg.payload.time ?? Date.now();
190
+ item.time = msgTime;
205
191
  item.pid = pid;
206
192
  item.encrypted = encrypted;
207
193
  if( node.contextStore !== "none" )
@@ -216,6 +202,7 @@ module.exports = function(RED) {
216
202
  }
217
203
 
218
204
  const name = node.devices[msg.payload.addr]?.topic;
205
+ const msgTime = msg.payload.time ?? Date.now()
219
206
  const dib = msg.payload.data[0];
220
207
  let rawdata = msg.payload.data.slice( 1 );
221
208
  const encrypted = Boolean( dib & 0x1 );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-bthome",
3
- "version": "0.2.3",
3
+ "version": "0.3.1",
4
4
  "description": "A Node Red node to decrypt and decode BT-Home frames",
5
5
  "author": {
6
6
  "name": "Mathias Schäffler",
package/tools.js ADDED
@@ -0,0 +1,38 @@
1
+ // Tooling for bthome
2
+
3
+ exports.uuid16 = [0xd2,0xfc];
4
+
5
+ exports.key2bytes = function(str)
6
+ {
7
+ switch( typeof str )
8
+ {
9
+ case "undefined":
10
+ return null;
11
+ case "string":
12
+ if( str.length == 32 )
13
+ {
14
+ const buf = Buffer.alloc( 16 );
15
+ for( let i=0; i<16 ; i++ )
16
+ {
17
+ buf[i] = Number.parseInt( str.slice( i*2, i*2 + 2 ), 16 );
18
+ }
19
+ return buf;
20
+ }
21
+ else
22
+ {
23
+ throw new Error( " keylength must be 16 bytes" );
24
+ }
25
+ default:
26
+ return Buffer.from( str );
27
+ }
28
+ }
29
+
30
+ exports.mac2bytes = function(str)
31
+ {
32
+ let mac = [];
33
+ for( const help of str.split( ":" ) )
34
+ {
35
+ mac.push( Number.parseInt( help, 16 ) );
36
+ }
37
+ return mac;
38
+ }