@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 +1 -0
- package/btevent.js +31 -30
- package/bthome.html +2 -0
- package/bthome.js +63 -18
- package/package.json +1 -1
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
|
-
|
|
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(
|
|
10
|
-
{
|
|
11
|
-
event = `${event}|${data}`;
|
|
12
|
-
}
|
|
13
|
-
switch( typeof this._events[type] )
|
|
15
|
+
if( this._events[type] === undefined )
|
|
14
16
|
{
|
|
15
|
-
|
|
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
|
-
|
|
33
|
-
let
|
|
34
|
-
|
|
35
|
-
if( index === null )
|
|
27
|
+
let payload = { type: type, event: event.event };
|
|
28
|
+
let indexStr = "";;
|
|
29
|
+
if( channel !== null )
|
|
36
30
|
{
|
|
37
|
-
|
|
31
|
+
indexStr += "/"
|
|
32
|
+
indexStr += channel;
|
|
33
|
+
payload.channel = channel;
|
|
38
34
|
}
|
|
39
|
-
|
|
35
|
+
if( index !== null )
|
|
40
36
|
{
|
|
41
|
-
|
|
37
|
+
indexStr += "/";
|
|
38
|
+
indexStr += index;
|
|
42
39
|
payload.id = index;
|
|
43
40
|
}
|
|
44
|
-
if(
|
|
41
|
+
if( event.data !== null )
|
|
45
42
|
{
|
|
46
|
-
payload.data =
|
|
43
|
+
payload.data = event.data;
|
|
47
44
|
}
|
|
48
45
|
result.push( {
|
|
49
|
-
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
146
|
+
switch( counter[name] )
|
|
128
147
|
{
|
|
129
|
-
case
|
|
148
|
+
case undefined:
|
|
130
149
|
counter[name] = 1;
|
|
131
150
|
item.data[name] = value;
|
|
132
151
|
break;
|
|
133
|
-
case
|
|
134
|
-
case "number":
|
|
135
|
-
case "string":
|
|
152
|
+
case 1:
|
|
136
153
|
item.data[name] = [ item.data[name] ];
|
|
137
154
|
// fall through
|
|
138
|
-
case
|
|
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
|
-
|
|
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( "
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
{
|