@mschaeffler/node-red-bthome 1.1.0 → 1.3.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/README.md CHANGED
@@ -34,6 +34,7 @@ $ npm install @mschaeffler/node-red-bthome
34
34
  |msg. | type | description |
35
35
  |:-------|:-------|:----------------------------------|
36
36
  |payload | object | data from Shelly script|
37
+ |resync | boolean| if set to yes, allow all packed ids; to resync after some issue|
37
38
 
38
39
  ### msg.payload
39
40
 
package/btevent.js CHANGED
@@ -1,52 +1,49 @@
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
13
  pushEvent(type,event,data=null)
8
14
  {
9
- if( event && data !== null )
10
- {
11
- event = `${event}|${data}`;
12
- }
13
- switch( typeof this._events[type] )
15
+ if( this._events[type] === undefined )
14
16
  {
15
- case "undefined":
16
- this._events[type] = event;
17
- break;
18
- case "string":
19
- this._events[type] = [ this._events[type] ];
20
- // fall through
21
- case "object":
22
- this._events[type].push( event );
23
- break;
17
+ this._events[type] = [];
24
18
  }
19
+ this._events[type].push( { event:event, data:data } );
25
20
  }
26
- eventMessages(name)
21
+ eventMessages(name,channel)
27
22
  {
28
23
  function pushResult(type,event,index=null)
29
24
  {
30
- if( event )
25
+ if( event.event && event.data !== 0 )
31
26
  {
32
- const help = event.split( '|' );
33
- let payload = { type: type, event: help[0] };
34
- let topic;
35
- if( index === null )
27
+ let payload = { type: type, event: event.event };
28
+ let indexStr = "";;
29
+ if( channel !== null )
36
30
  {
37
- topic = `${prefix}${name}/${help[0]}`;
31
+ indexStr += "/"
32
+ indexStr += channel;
33
+ payload.channel = channel;
38
34
  }
39
- else
35
+ if( index !== null )
40
36
  {
41
- topic = `${prefix}${name}/${index}/${help[0]}`;
37
+ indexStr += "/";
38
+ indexStr += index;
42
39
  payload.id = index;
43
40
  }
44
- if( help[1] !== undefined )
41
+ if( event.data !== null )
45
42
  {
46
- payload.data = help[1];
43
+ payload.data = event.data;
47
44
  }
48
45
  result.push( {
49
- topic: topic,
46
+ topic: `${prefix}${name}${indexStr}/${event.event}`,
50
47
  payload: payload
51
48
  } );
52
49
  }
@@ -57,15 +54,19 @@ class BtEvent {
57
54
  for( const t in this._events )
58
55
  {
59
56
  const event = this._events[t];
60
- if( typeof event == "string" )
57
+ if( event.length == 1 )
61
58
  {
62
- pushResult( t, event );
59
+ pushResult( t, event[0] );
63
60
  }
64
61
  else
65
62
  {
66
63
  for( const i in event )
67
64
  {
68
- pushResult( t, event[i], Number( i ) + 1 );
65
+ pushResult(
66
+ t,
67
+ event[i],
68
+ BtEvent.eventLut[this._item.typeId]?.[t]?.[i] ?? Number( i ) + 1
69
+ );
69
70
  }
70
71
  }
71
72
  }
package/bthome.html CHANGED
@@ -123,6 +123,8 @@
123
123
  <dl class="message-properties">
124
124
  <dt>payload <span class="property-type">object</span></dt>
125
125
  <dd> data from Shelly script</dd>
126
+ <dt>resync <span class="property-type">boolean</span></dt>
127
+ <dd> if set to yes, allow all packed ids; to resync after some issue</dd>
126
128
  </dl>
127
129
 
128
130
  <h4>msg.payload</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,7 +16,7 @@ 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";
@@ -53,7 +59,20 @@ module.exports = function(RED) {
53
59
  }
54
60
 
55
61
  node.on('input', function(msg,send,done) {
56
- if( ! Array.isArray( msg.payload.data ) )
62
+ if( msg.resync )
63
+ {
64
+ for( const t in node.data )
65
+ {
66
+ node.data[t].pid = null;
67
+ }
68
+ if( node.contextStore !== "none" )
69
+ {
70
+ node.flowcontext.set( node.contextVar, node.data, node.contextStore );
71
+ }
72
+ done();
73
+ return;
74
+ }
75
+ if( ! Array.isArray( msg.payload?.data ) )
57
76
  {
58
77
  node.statistics.err++;
59
78
  done( "msg.payload.data must be an Array!" );
@@ -124,18 +143,18 @@ module.exports = function(RED) {
124
143
  {
125
144
  item.data = {};
126
145
  }
127
- switch( typeof counter[name] )
146
+ switch( counter[name] )
128
147
  {
129
- case "undefined":
148
+ case undefined:
130
149
  counter[name] = 1;
131
150
  item.data[name] = value;
132
151
  break;
133
- case "boolean":
134
- case "number":
135
- case "string":
152
+ case 1:
136
153
  item.data[name] = [ item.data[name] ];
137
154
  // fall through
138
- case "object":
155
+ case 2:
156
+ case 3:
157
+ case 4:
139
158
  counter[name]++;
140
159
  item.data[name].push( value );
141
160
  break;
@@ -173,10 +192,19 @@ module.exports = function(RED) {
173
192
  setData( "dewpoint", rawdata.getInt16() * 0.01 );
174
193
  break;
175
194
  case 0x0C:
176
- setData( "voltage", rawdata.getUInt16() * 0.001 );
195
+ if( node.batteryState )
196
+ {
197
+ setData( "voltage", rawdata.getUInt16() * 0.001 );
198
+ delete item.voltage;
199
+ }
200
+ else
201
+ {
202
+ item.voltage = rawdata.getUInt16() * 0.001;
203
+ delete item.data?.voltage;
204
+ }
177
205
  break;
178
206
  case 0x20:
179
- setData( "rain", Boolean( rawdata.getUInt8() ) );
207
+ setData( "moisture", Boolean( rawdata.getUInt8() ) );
180
208
  break;
181
209
  case 0x21:
182
210
  events.pushEvent( "motion", rawdata.getEnum( ["","motion"] ) );
@@ -185,13 +213,15 @@ module.exports = function(RED) {
185
213
  setData( "vibration", Boolean( rawdata.getUInt8() ) );
186
214
  break;
187
215
  case 0x2D:
216
+ {
188
217
  let state = rawdata.getUInt8();
189
- if( item.typeId === 0x0202 )
218
+ if( item.typeId === TypeIds.bluDW )
190
219
  {
191
220
  state = Boolean( state );
192
221
  }
193
222
  setData( "state", state );
194
223
  break;
224
+ }
195
225
  case 0x2E:
196
226
  setData( "humidity", rawdata.getUInt8() );
197
227
  break;
@@ -199,14 +229,21 @@ module.exports = function(RED) {
199
229
  events.pushEvent( "button", rawdata.getEnum( ["","S","SS","SSS","L"] ) );
200
230
  break;
201
231
  case 0x3C:
202
- events.pushEvent( "dimmer", rawdata.getEnum( ["","Left","Right"] ), rawdata.getUInt8() );
232
+ {
233
+ const dimmer = rawdata.getUInt8();
234
+ const data = rawdata.getUInt8();
235
+ events.pushEvent( "dimmer", "dimmer", dimmer==1 ? data : -data );
203
236
  break;
237
+ }
204
238
  case 0x3F:
205
239
  setData( "tilt", rawdata.getInt16() * 0.1 );
206
240
  break;
207
241
  case 0x40:
208
- setData( "distance", rawdata.getUInt16() );
242
+ {
243
+ const distance = rawdata.getUInt16();
244
+ setData( "distance", distance != 0 ? distance : null );
209
245
  break;
246
+ }
210
247
  case 0x44:
211
248
  setData( "wind", rawdata.getUInt16() * 0.01 );
212
249
  break;
@@ -216,6 +253,9 @@ module.exports = function(RED) {
216
253
  case 0x46:
217
254
  setData( "uv", rawdata.getUInt8() * 0.1 );
218
255
  break;
256
+ case 0x59:
257
+ setData( "count", rawdata.getInt8() );
258
+ break;
219
259
  case 0x5E:
220
260
  setData( "direction", rawdata.getUInt16() * 0.01 );
221
261
  break;
@@ -223,7 +263,7 @@ module.exports = function(RED) {
223
263
  setData( "precipitation", rawdata.getUInt16() * 0.1 );
224
264
  break;
225
265
  case 0x60:
226
- setData( "channel", rawdata.getUInt8() );
266
+ setData( "channel", rawdata.getUInt8() + 1 );
227
267
  break;
228
268
  case 0xF0:
229
269
  item.typeId = rawdata.getUInt16();
@@ -248,7 +288,11 @@ module.exports = function(RED) {
248
288
  rawdata.reset();
249
289
  }
250
290
  }
251
- //console.log(counter)
291
+ if( item.typeId === TypeIds.bluRemote && item.data?.tilt )
292
+ {
293
+ events.pushEvent( "rotation", "rotation", item.data.tilt );
294
+ delete item.data.tilt;
295
+ }
252
296
  }
253
297
 
254
298
  function checkPid()
@@ -257,7 +301,7 @@ module.exports = function(RED) {
257
301
  {
258
302
  // veraltete Nachricht und nicht reboot
259
303
  node.statistics.old++;
260
- node.warn( `old ble message ${name} from ${msg.payload.gateway} dropped, ${pid} < ${item.pid}` );
304
+ node.warn( `old ble message ${name} dropped, ${pid} < ${item.pid}` );
261
305
  return false;
262
306
  }
263
307
  if( msg.payload.gateway )
@@ -291,7 +335,7 @@ module.exports = function(RED) {
291
335
  node.status( name );
292
336
  send( [
293
337
  item.data ? { topic:node.statusPrefix+name, payload:item.data } : null,
294
- events.eventMessages( name )
338
+ events.eventMessages( name, item.data?.channel ?? null )
295
339
  ] );
296
340
  }
297
341
 
@@ -302,8 +346,8 @@ module.exports = function(RED) {
302
346
  const encrypted = Boolean( dib & 0x1 );
303
347
  const version = dib >> 5;
304
348
  let pid = null;
305
- const events = new BtEvent( node.eventPrefix );
306
349
  let item = node.data[name];
350
+ let events;
307
351
 
308
352
  try
309
353
  {
@@ -317,6 +361,7 @@ module.exports = function(RED) {
317
361
  {
318
362
  decryptMsg();
319
363
  }
364
+ events = new BtEvent( node.eventPrefix, item );
320
365
  decodeMsg();
321
366
  if( checkPid() )
322
367
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mschaeffler/node-red-bthome",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A Node Red node to decrypt and decode BT-Home frames",
5
5
  "author": {
6
6
  "name": "Mathias Schäffler",