@mschaeffler/node-red-bthome 1.0.0 → 1.2.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
@@ -1,46 +1,72 @@
1
1
  class BtEvent {
2
- constructor(prefix)
2
+ static eventLut = {
3
+ 9: {
4
+ button: ["left","right"]
5
+ }
6
+ };
7
+ constructor(prefix,item)
3
8
  {
4
9
  this._events = {};
5
10
  this._prefix = prefix;
11
+ this._item = item;
6
12
  }
7
- pushEvent(type,event)
13
+ pushEvent(type,event,data=null)
8
14
  {
9
- switch( typeof this._events[type] )
15
+ if( this._events[type] === undefined )
10
16
  {
11
- case "undefined":
12
- this._events[type] = event;
13
- break;
14
- case "string":
15
- this._events[type] = [this._events[type]];
16
- // fall through
17
- case "object":
18
- this._events[type].push( event );
19
- break;
17
+ this._events[type] = [];
20
18
  }
19
+ this._events[type].push( { event:event, data:data } );
21
20
  }
22
- eventMessages(name)
21
+ eventMessages(name,channel)
23
22
  {
24
- let result = [];
25
- for( const t in this._events )
23
+ function pushResult(type,event,index=null)
26
24
  {
27
- const event = this._events[t];
28
- if( typeof event == "string" )
25
+ if( event.event && event.data !== 0 )
29
26
  {
30
- if( event )
27
+ let payload = { type: type, event: event.event };
28
+ let indexStr = "";;
29
+ if( channel !== null )
30
+ {
31
+ indexStr += "/"
32
+ indexStr += channel;
33
+ payload.channel = channel;
34
+ }
35
+ if( index !== null )
36
+ {
37
+ indexStr += "/";
38
+ indexStr += index;
39
+ payload.id = index;
40
+ }
41
+ if( event.data !== null )
31
42
  {
32
- result.push( { topic: `${this._prefix}${name}/${event}`, payload: { type: t, event: event } } );
43
+ payload.data = event.data;
33
44
  }
45
+ result.push( {
46
+ topic: `${prefix}${name}${indexStr}/${event.event}`,
47
+ payload: payload
48
+ } );
49
+ }
50
+ }
51
+
52
+ let result = [];
53
+ const prefix = this._prefix;
54
+ for( const t in this._events )
55
+ {
56
+ const event = this._events[t];
57
+ if( event.length == 1 )
58
+ {
59
+ pushResult( t, event[0] );
34
60
  }
35
61
  else
36
62
  {
37
63
  for( const i in event )
38
64
  {
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
- }
65
+ pushResult(
66
+ t,
67
+ event[i],
68
+ BtEvent.eventLut[this._item.typeId]?.[t]?.[i] ?? Number( i ) + 1
69
+ );
44
70
  }
45
71
  }
46
72
  }
package/bthome.html CHANGED
@@ -9,7 +9,8 @@
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,
@@ -90,6 +91,10 @@
90
91
  <label for="node-input-contextVar"><i class="fa fa-database"></i> Context-Variable</label>
91
92
  <input type="text" id="node-input-contextVar"></input>
92
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>
93
98
  </script>
94
99
 
95
100
  <script type="text/x-red" data-help-name="bthome">
@@ -181,6 +186,8 @@
181
186
  <dd> name of the variable in flow context storage.</dd>
182
187
  <dt>Contextstore <span class="property-type">string</span></dt>
183
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>
184
191
  </dl>
185
192
 
186
193
  <h4>Device-Configuration</h4>
package/bthome.js CHANGED
@@ -3,6 +3,12 @@ const Tools = require( './tools.js' );
3
3
  const Rawdata = require( "./rawdata.js" );
4
4
  const BtEvent = require( "./btevent.js" );
5
5
 
6
+ class TypeIds {
7
+ static bluDW = 0x0202;
8
+ static bluRemote = 9;
9
+ }
10
+ Object.freeze( TypeIds );
11
+
6
12
  module.exports = function(RED) {
7
13
 
8
14
  function BtHomeNode(config) {
@@ -10,11 +16,12 @@ module.exports = function(RED) {
10
16
  var node = this;
11
17
  this.flowcontext = this.context().flow;
12
18
  this.devices = JSON.parse( config.devices ?? "{}" );
13
- this.counterMode = config.counterMode ?? "none";
19
+ this.counterMode = config.counterMode ?? "none";
14
20
  this.statusPrefix = config.statusPrefix ? config.statusPrefix+'/' : "";
15
21
  this.eventPrefix = config.eventPrefix ? config.eventPrefix +'/' : "";
16
22
  this.contextVar = config.contextVar ?? "bthome";
17
23
  this.contextStore = config.contextStore ?? "none";
24
+ this.batteryState = Boolean( config.batteryState );
18
25
  this.data = {};
19
26
  this.statistics = { ok:0, dup:0, old:0, err:0 };
20
27
  node.status( "" );
@@ -113,17 +120,34 @@ module.exports = function(RED) {
113
120
  decipher.final();
114
121
  }
115
122
 
116
- function setData(name,value)
123
+ function decodeMsg()
117
124
  {
118
- if( item.data === undefined )
125
+ let counter = {};
126
+
127
+ function setData(name,value)
119
128
  {
120
- item.data = {};
129
+ if( item.data === undefined )
130
+ {
131
+ item.data = {};
132
+ }
133
+ switch( counter[name] )
134
+ {
135
+ case undefined:
136
+ counter[name] = 1;
137
+ item.data[name] = value;
138
+ break;
139
+ case 1:
140
+ item.data[name] = [ item.data[name] ];
141
+ // fall through
142
+ case 2:
143
+ case 3:
144
+ case 4:
145
+ counter[name]++;
146
+ item.data[name].push( value );
147
+ break;
148
+ }
121
149
  }
122
- item.data[name] = value;
123
- }
124
150
 
125
- function decodeMsg()
126
- {
127
151
  rawdata = new Rawdata( rawdata );
128
152
  while( rawdata.length() > 0 )
129
153
  {
@@ -134,11 +158,41 @@ module.exports = function(RED) {
134
158
  pid = rawdata.getUInt8();
135
159
  break;
136
160
  case 0x01:
137
- item.battery = rawdata.getUInt8();
161
+ if( node.batteryState )
162
+ {
163
+ setData( "battery", rawdata.getUInt8() );
164
+ delete item.battery;
165
+ }
166
+ else
167
+ {
168
+ item.battery = rawdata.getUInt8();
169
+ delete item.data?.battery;
170
+ }
171
+ break;
172
+ case 0x04:
173
+ setData( "pressure", rawdata.getUInt24() * 0.01 );
138
174
  break;
139
175
  case 0x05:
140
176
  setData( "lux", rawdata.getUInt24() * 0.01 );
141
177
  break;
178
+ case 0x08:
179
+ setData( "dewpoint", rawdata.getInt16() * 0.01 );
180
+ break;
181
+ case 0x0C:
182
+ if( node.batteryState )
183
+ {
184
+ setData( "voltage", rawdata.getUInt16() * 0.001 );
185
+ delete item.voltage;
186
+ }
187
+ else
188
+ {
189
+ item.voltage = rawdata.getUInt16() * 0.001;
190
+ delete item.data?.voltage;
191
+ }
192
+ break;
193
+ case 0x20:
194
+ setData( "moisture", Boolean( rawdata.getUInt8() ) );
195
+ break;
142
196
  case 0x21:
143
197
  events.pushEvent( "motion", rawdata.getEnum( ["","motion"] ) );
144
198
  break;
@@ -146,28 +200,58 @@ module.exports = function(RED) {
146
200
  setData( "vibration", Boolean( rawdata.getUInt8() ) );
147
201
  break;
148
202
  case 0x2D:
203
+ {
149
204
  let state = rawdata.getUInt8();
150
- if( item.typeId === 0x0202 )
205
+ if( item.typeId === TypeIds.bluDW )
151
206
  {
152
207
  state = Boolean( state );
153
208
  }
154
209
  setData( "state", state );
155
210
  break;
211
+ }
156
212
  case 0x2E:
157
213
  setData( "humidity", rawdata.getUInt8() );
158
214
  break;
159
215
  case 0x3A:
160
216
  events.pushEvent( "button", rawdata.getEnum( ["","S","SS","SSS","L"] ) );
161
217
  break;
218
+ case 0x3C:
219
+ {
220
+ const dimmer = rawdata.getUInt8();
221
+ const data = rawdata.getUInt8();
222
+ events.pushEvent( "dimmer", "dimmer", dimmer==1 ? data : -data );
223
+ break;
224
+ }
162
225
  case 0x3F:
163
226
  setData( "tilt", rawdata.getInt16() * 0.1 );
164
227
  break;
165
228
  case 0x40:
166
- setData( "distance", rawdata.getUInt16() );
229
+ {
230
+ const distance = rawdata.getUInt16();
231
+ setData( "distance", distance != 0 ? distance : null );
232
+ break;
233
+ }
234
+ case 0x44:
235
+ setData( "wind", rawdata.getUInt16() * 0.01 );
167
236
  break;
168
237
  case 0x45:
169
238
  setData( "temperature", rawdata.getInt16() * 0.1 );
170
239
  break;
240
+ case 0x46:
241
+ setData( "uv", rawdata.getUInt8() * 0.1 );
242
+ break;
243
+ case 0x59:
244
+ setData( "count", rawdata.getInt8() );
245
+ break;
246
+ case 0x5E:
247
+ setData( "direction", rawdata.getUInt16() * 0.01 );
248
+ break;
249
+ case 0x5F:
250
+ setData( "precipitation", rawdata.getUInt16() * 0.1 );
251
+ break;
252
+ case 0x60:
253
+ setData( "channel", rawdata.getUInt8() + 1 );
254
+ break;
171
255
  case 0xF0:
172
256
  item.typeId = rawdata.getUInt16();
173
257
  break;
@@ -191,6 +275,11 @@ module.exports = function(RED) {
191
275
  rawdata.reset();
192
276
  }
193
277
  }
278
+ if( item.typeId === TypeIds.bluRemote && item.data?.tilt )
279
+ {
280
+ events.pushEvent( "rotation", "rotation", item.data.tilt );
281
+ delete item.data.tilt;
282
+ }
194
283
  }
195
284
 
196
285
  function checkPid()
@@ -233,7 +322,7 @@ module.exports = function(RED) {
233
322
  node.status( name );
234
323
  send( [
235
324
  item.data ? { topic:node.statusPrefix+name, payload:item.data } : null,
236
- events.eventMessages( name )
325
+ events.eventMessages( name, item.data?.channel ?? null )
237
326
  ] );
238
327
  }
239
328
 
@@ -244,8 +333,8 @@ module.exports = function(RED) {
244
333
  const encrypted = Boolean( dib & 0x1 );
245
334
  const version = dib >> 5;
246
335
  let pid = null;
247
- const events = new BtEvent( node.eventPrefix );
248
336
  let item = node.data[name];
337
+ let events;
249
338
 
250
339
  try
251
340
  {
@@ -259,6 +348,7 @@ module.exports = function(RED) {
259
348
  {
260
349
  decryptMsg();
261
350
  }
351
+ events = new BtEvent( node.eventPrefix, item );
262
352
  decodeMsg();
263
353
  if( checkPid() )
264
354
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-bthome",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A Node Red node to decrypt and decode BT-Home frames",
5
5
  "author": {
6
6
  "name": "Mathias Schäffler",