@mschaeffler/node-red-bthome 0.5.1 → 1.1.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/btevent.js CHANGED
@@ -4,15 +4,19 @@ class BtEvent {
4
4
  this._events = {};
5
5
  this._prefix = prefix;
6
6
  }
7
- pushEvent(type,event)
7
+ pushEvent(type,event,data=null)
8
8
  {
9
+ if( event && data !== null )
10
+ {
11
+ event = `${event}|${data}`;
12
+ }
9
13
  switch( typeof this._events[type] )
10
14
  {
11
15
  case "undefined":
12
16
  this._events[type] = event;
13
17
  break;
14
18
  case "string":
15
- this._events[type] = [this._events[type]];
19
+ this._events[type] = [ this._events[type] ];
16
20
  // fall through
17
21
  case "object":
18
22
  this._events[type].push( event );
@@ -21,26 +25,47 @@ class BtEvent {
21
25
  }
22
26
  eventMessages(name)
23
27
  {
24
- let result = [];
28
+ function pushResult(type,event,index=null)
29
+ {
30
+ if( event )
31
+ {
32
+ const help = event.split( '|' );
33
+ let payload = { type: type, event: help[0] };
34
+ let topic;
35
+ if( index === null )
36
+ {
37
+ topic = `${prefix}${name}/${help[0]}`;
38
+ }
39
+ else
40
+ {
41
+ topic = `${prefix}${name}/${index}/${help[0]}`;
42
+ payload.id = index;
43
+ }
44
+ if( help[1] !== undefined )
45
+ {
46
+ payload.data = help[1];
47
+ }
48
+ result.push( {
49
+ topic: topic,
50
+ payload: payload
51
+ } );
52
+ }
53
+ }
54
+
55
+ let result = [];
56
+ const prefix = this._prefix;
25
57
  for( const t in this._events )
26
58
  {
27
59
  const event = this._events[t];
28
60
  if( typeof event == "string" )
29
61
  {
30
- if( event )
31
- {
32
- result.push( { topic: `${this._prefix}${name}/${event}`, payload: { type: t, event: event } } );
33
- }
62
+ pushResult( t, event );
34
63
  }
35
64
  else
36
65
  {
37
66
  for( const i in event )
38
67
  {
39
- if( event[i] )
40
- {
41
- const index = Number( i ) + 1;
42
- result.push( { topic: `${this._prefix}${name}/${index}/${event[i]}`, payload: { type: t, id: index, event: event[i] } } );
43
- }
68
+ pushResult( t, event[i], Number( i ) + 1 );
44
69
  }
45
70
  }
46
71
  }
package/bthome.html CHANGED
@@ -5,11 +5,12 @@
5
5
  defaults: {
6
6
  name: {value:""},
7
7
  devices:{value:"[]",required:true},
8
- counterTime:{value:false},
8
+ counterMode:{value:"none",required:true},
9
9
  statusPrefix:{value:""},
10
10
  eventPrefix:{value:""},
11
11
  contextVar:{value:"bthome",required:true},
12
- contextStore:{value:"none",required:true}
12
+ contextStore:{value:"none",required:true},
13
+ batteryState:{value:true,required:true}
13
14
  },
14
15
  inputs:1,
15
16
  outputs:2,
@@ -67,8 +68,12 @@
67
68
  <input type="text" id="node-input-devices">
68
69
  </div>
69
70
  <div class="form-row">
70
- <label for="node-input-counterTime"><i class="fa fa-clock-o"></i> counter is time</label>
71
- <input type="checkbox" id="node-input-counterTime" style="display:inline-block; width:20px; vertical-align:baseline;">
71
+ <label for="node-input-counterMode"><i class="fa fa-clock-o"></i> Countermode</label>
72
+ <select type="text" id="node-input-counterMode">
73
+ <option value="none">no check</option>
74
+ <option value="rising">always rising</option>
75
+ <option value="time">time stamp</option>
76
+ </select>
72
77
  </div>
73
78
  <div class="form-row">
74
79
  <label for="node-input-statusPrefix"><i class="fa fa-star"></i> Status-Prefix</label>
@@ -86,6 +91,10 @@
86
91
  <label for="node-input-contextVar"><i class="fa fa-database"></i> Context-Variable</label>
87
92
  <input type="text" id="node-input-contextVar"></input>
88
93
  </div>
94
+ <div class="form-row">
95
+ <label for="node-input-batteryState"><i class="fa fa-"></i> battery is state</label>
96
+ <input type="checkbox" id="node-input-batteryState" style="display:inline-block; width:20px; vertical-align:baseline;">
97
+ </div>
89
98
  </script>
90
99
 
91
100
  <script type="text/x-red" data-help-name="bthome">
@@ -177,6 +186,8 @@
177
186
  <dd> name of the variable in flow context storage.</dd>
178
187
  <dt>Contextstore <span class="property-type">string</span></dt>
179
188
  <dd> context store to be used.</dd>
189
+ <dt>battery is state <span class="property-type">Boolean</span></dt>
190
+ <dd> battery level is included in state mesage.</dt>
180
191
  </dl>
181
192
 
182
193
  <h4>Device-Configuration</h4>
package/bthome.js CHANGED
@@ -10,11 +10,12 @@ module.exports = function(RED) {
10
10
  var node = this;
11
11
  this.flowcontext = this.context().flow;
12
12
  this.devices = JSON.parse( config.devices ?? "{}" );
13
- this.counterTime = Boolean( config.counterTime );
13
+ this.counterMode = config.counterMode ?? "none";
14
14
  this.statusPrefix = config.statusPrefix ? config.statusPrefix+'/' : "";
15
15
  this.eventPrefix = config.eventPrefix ? config.eventPrefix +'/' : "";
16
16
  this.contextVar = config.contextVar ?? "bthome";
17
17
  this.contextStore = config.contextStore ?? "none";
18
+ this.batteryState = Boolean( config.batteryState );
18
19
  this.data = {};
19
20
  this.statistics = { ok:0, dup:0, old:0, err:0 };
20
21
  node.status( "" );
@@ -84,14 +85,26 @@ module.exports = function(RED) {
84
85
  const mac = Tools.mac2bytes( msg.payload.addr );
85
86
  const ciphertext = Buffer.from( rawdata.slice( 0, -8 ) );
86
87
  const counter = rawdata.slice( -8, -4 );
87
- if( node.counterTime )
88
+ const counterInt = counter[0] | (counter[1]<<8) | (counter[2]<<16) | (counter[3]<<24);
89
+ switch( node.counterMode )
88
90
  {
89
- const counterInt = counter[0] | (counter[1]<<8) | (counter[2]<<16) | (counter[3]<<24);
90
- const delta = msgTime - counterInt*1000;
91
- if( delta > 30000 || delta < -15000 )
92
- {
93
- throw new Error( "bthome "+msg.payload.gateway+" "+name+" "+(new Date(counterInt*1000))+" "+delta );
94
- }
91
+ case "rising":
92
+ if( counterInt > ( item.lastCounter ?? -1 ) )
93
+ {
94
+ item.lastCounter = counterInt;
95
+ }
96
+ else
97
+ {
98
+ throw new Error( "bthome "+msg.payload.gateway+" "+name+" "+counterInt+" <= "+item.lastCounter );
99
+ }
100
+ break;
101
+ case "time":
102
+ const deltaTime = msgTime - counterInt*1000;
103
+ if( deltaTime > 30000 || deltaTime < -15000 )
104
+ {
105
+ throw new Error( "bthome "+msg.payload.gateway+" "+name+" "+(new Date(counterInt*1000))+" "+deltaTime );
106
+ }
107
+ break;
95
108
  }
96
109
  const mic = Buffer.from( rawdata.slice( -4 ) );
97
110
  const nonce = Buffer.from( mac.concat( Tools.uuid16, dib, counter ) );
@@ -101,17 +114,34 @@ module.exports = function(RED) {
101
114
  decipher.final();
102
115
  }
103
116
 
104
- function setData(name,value)
117
+ function decodeMsg()
105
118
  {
106
- if( item.data === undefined )
119
+ let counter = {};
120
+
121
+ function setData(name,value)
107
122
  {
108
- item.data = {};
123
+ if( item.data === undefined )
124
+ {
125
+ item.data = {};
126
+ }
127
+ switch( typeof counter[name] )
128
+ {
129
+ case "undefined":
130
+ counter[name] = 1;
131
+ item.data[name] = value;
132
+ break;
133
+ case "boolean":
134
+ case "number":
135
+ case "string":
136
+ item.data[name] = [ item.data[name] ];
137
+ // fall through
138
+ case "object":
139
+ counter[name]++;
140
+ item.data[name].push( value );
141
+ break;
142
+ }
109
143
  }
110
- item.data[name] = value;
111
- }
112
144
 
113
- function decodeMsg()
114
- {
115
145
  rawdata = new Rawdata( rawdata );
116
146
  while( rawdata.length() > 0 )
117
147
  {
@@ -122,11 +152,32 @@ module.exports = function(RED) {
122
152
  pid = rawdata.getUInt8();
123
153
  break;
124
154
  case 0x01:
125
- item.battery = rawdata.getUInt8();
155
+ if( node.batteryState )
156
+ {
157
+ setData( "battery", rawdata.getUInt8() );
158
+ delete item.battery;
159
+ }
160
+ else
161
+ {
162
+ item.battery = rawdata.getUInt8();
163
+ delete item.data?.battery;
164
+ }
165
+ break;
166
+ case 0x04:
167
+ setData( "pressure", rawdata.getUInt24() * 0.01 );
126
168
  break;
127
169
  case 0x05:
128
170
  setData( "lux", rawdata.getUInt24() * 0.01 );
129
171
  break;
172
+ case 0x08:
173
+ setData( "dewpoint", rawdata.getInt16() * 0.01 );
174
+ break;
175
+ case 0x0C:
176
+ setData( "voltage", rawdata.getUInt16() * 0.001 );
177
+ break;
178
+ case 0x20:
179
+ setData( "rain", Boolean( rawdata.getUInt8() ) );
180
+ break;
130
181
  case 0x21:
131
182
  events.pushEvent( "motion", rawdata.getEnum( ["","motion"] ) );
132
183
  break;
@@ -147,15 +198,33 @@ module.exports = function(RED) {
147
198
  case 0x3A:
148
199
  events.pushEvent( "button", rawdata.getEnum( ["","S","SS","SSS","L"] ) );
149
200
  break;
201
+ case 0x3C:
202
+ events.pushEvent( "dimmer", rawdata.getEnum( ["","Left","Right"] ), rawdata.getUInt8() );
203
+ break;
150
204
  case 0x3F:
151
205
  setData( "tilt", rawdata.getInt16() * 0.1 );
152
206
  break;
153
207
  case 0x40:
154
208
  setData( "distance", rawdata.getUInt16() );
155
209
  break;
210
+ case 0x44:
211
+ setData( "wind", rawdata.getUInt16() * 0.01 );
212
+ break;
156
213
  case 0x45:
157
214
  setData( "temperature", rawdata.getInt16() * 0.1 );
158
215
  break;
216
+ case 0x46:
217
+ setData( "uv", rawdata.getUInt8() * 0.1 );
218
+ break;
219
+ case 0x5E:
220
+ setData( "direction", rawdata.getUInt16() * 0.01 );
221
+ break;
222
+ case 0x5F:
223
+ setData( "precipitation", rawdata.getUInt16() * 0.1 );
224
+ break;
225
+ case 0x60:
226
+ setData( "channel", rawdata.getUInt8() );
227
+ break;
159
228
  case 0xF0:
160
229
  item.typeId = rawdata.getUInt16();
161
230
  break;
@@ -179,6 +248,7 @@ module.exports = function(RED) {
179
248
  rawdata.reset();
180
249
  }
181
250
  }
251
+ //console.log(counter)
182
252
  }
183
253
 
184
254
  function checkPid()
@@ -238,15 +308,15 @@ module.exports = function(RED) {
238
308
  try
239
309
  {
240
310
  checkMsg();
241
- if( encrypted )
242
- {
243
- decryptMsg();
244
- }
245
311
  if( item == undefined )
246
312
  {
247
313
  item = { pid: null, typeId: null, gw: {} };
248
314
  node.data[name] = item;
249
315
  }
316
+ if( encrypted )
317
+ {
318
+ decryptMsg();
319
+ }
250
320
  decodeMsg();
251
321
  if( checkPid() )
252
322
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-bthome",
3
- "version": "0.5.1",
3
+ "version": "1.1.0",
4
4
  "description": "A Node Red node to decrypt and decode BT-Home frames",
5
5
  "author": {
6
6
  "name": "Mathias Schäffler",