bt-sensors-plugin-sk 1.2.6-beta-2 → 1.2.6-beta-4
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/BTSensor.js +165 -62
- package/README.md +6 -1
- package/classLoader.js +9 -2
- package/development/FakeBTDevice.js +4 -1
- package/index.js +110 -59
- package/package.json +1 -1
- package/plugin_defaults.json +9 -0
- package/public/847.js +1 -1
- package/public/images/EcoWorthyBW2.webp +0 -0
- package/public/images/JikongBMS.jpg +0 -0
- package/sensor_classes/{EcoWorthy.js → EcoWorthyBW02.js} +20 -8
- package/sensor_classes/Inkbird.js +1 -1
- package/sensor_classes/JBDBMS.js +3 -0
- package/sensor_classes/JikongBMS.js +481 -0
- package/sensor_classes/RemoranWave3.js +3 -4
- package/sensor_classes/RuuviTag.js +1 -0
- package/sensor_classes/ShellySBMO003Z.js +5 -0
- package/sensor_classes/TestData/Jikong.json +36 -0
- package/sensor_classes/XiaomiMiBeacon.js +12 -14
- package/src/components/PluginConfigurationPanel.js +6 -1
package/BTSensor.js
CHANGED
|
@@ -75,7 +75,7 @@ function signalQualityPercentQuad(rssi, perfect_rssi=-20, worst_rssi=-85) {
|
|
|
75
75
|
class BTSensor extends EventEmitter {
|
|
76
76
|
|
|
77
77
|
static DEFAULTS = require('./plugin_defaults.json');
|
|
78
|
-
static DistanceManagerSingleton= new DistanceManager({DISTANCE_FIND_LAST_FEW_SAMPLE_TIME_FRAME_MILLIS:60000})
|
|
78
|
+
static DistanceManagerSingleton= new DistanceManager({DISTANCE_FIND_LAST_FEW_SAMPLE_TIME_FRAME_MILLIS:60000})
|
|
79
79
|
|
|
80
80
|
static IsRoaming = false;
|
|
81
81
|
|
|
@@ -255,6 +255,36 @@ class BTSensor extends EventEmitter {
|
|
|
255
255
|
|
|
256
256
|
//END static identity functions
|
|
257
257
|
|
|
258
|
+
_debugLog=[]
|
|
259
|
+
_errorLog=[]
|
|
260
|
+
|
|
261
|
+
getErrorLog(){
|
|
262
|
+
return this._errorLog
|
|
263
|
+
}
|
|
264
|
+
getDebugLog(){
|
|
265
|
+
return this._debugLog
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
debug(message){
|
|
269
|
+
if (this._app)
|
|
270
|
+
this._app.debug(message)
|
|
271
|
+
else
|
|
272
|
+
console.log(message)
|
|
273
|
+
this._debugLog.push({timestamp:Date.now(), message:message})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
setError(message){
|
|
277
|
+
if (this._app){
|
|
278
|
+
this._app.debug(message)
|
|
279
|
+
this._app.setPluginError(message)
|
|
280
|
+
}
|
|
281
|
+
else
|
|
282
|
+
console.log(message)
|
|
283
|
+
|
|
284
|
+
this._errorLog.push({timestamp:Date.now(), message:message})
|
|
285
|
+
this.setState("ERROR")
|
|
286
|
+
}
|
|
287
|
+
|
|
258
288
|
//Instance Initialization functions
|
|
259
289
|
/**
|
|
260
290
|
*
|
|
@@ -264,6 +294,7 @@ class BTSensor extends EventEmitter {
|
|
|
264
294
|
*
|
|
265
295
|
*/
|
|
266
296
|
|
|
297
|
+
|
|
267
298
|
async initSchema(){
|
|
268
299
|
this._schema = {
|
|
269
300
|
type: "object",
|
|
@@ -337,26 +368,50 @@ class BTSensor extends EventEmitter {
|
|
|
337
368
|
try {
|
|
338
369
|
this.activateGATT()
|
|
339
370
|
} catch (e) {
|
|
340
|
-
this.
|
|
341
|
-
this._state="ERROR"
|
|
371
|
+
this.setError(`GATT services unavailable for ${this.getName()}. Reason: ${e}`)
|
|
342
372
|
return
|
|
343
373
|
}
|
|
344
374
|
}
|
|
345
|
-
this.
|
|
375
|
+
this.setState("ACTIVE")
|
|
346
376
|
this._propertiesChanged(this.currentProperties)
|
|
347
377
|
|
|
348
378
|
}
|
|
349
379
|
|
|
350
|
-
|
|
351
|
-
this.
|
|
380
|
+
async getGATTServer(){
|
|
381
|
+
if (!this._gattServer)
|
|
382
|
+
this._gattServer = await this.device.gatt()
|
|
383
|
+
return this._gattServer
|
|
384
|
+
}
|
|
385
|
+
async activateGATT(isReconnecting=false){
|
|
386
|
+
try{
|
|
387
|
+
await this.initGATTConnection(isReconnecting)
|
|
352
388
|
await this.emitGATT()
|
|
353
389
|
if (this.pollFreq)
|
|
354
390
|
this.initGATTInterval()
|
|
355
391
|
else
|
|
356
392
|
await this.initGATTNotifications()
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
393
|
+
}
|
|
394
|
+
catch(e) {
|
|
395
|
+
this.debug(`Unable to activate GATT connection for ${this.getName()} (${this.getMacAddress()}): ${e}`)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async deactivateGATT(){
|
|
400
|
+
if (this.device) {
|
|
401
|
+
this.debug(`(${this.getName()}) disconnecting from GATT server`)
|
|
402
|
+
if (this._gattServer) { //getGATTServer() was called which calls node-ble's Device::gatt() which needs cleaning up after
|
|
403
|
+
(await this._gattServer.services()).forEach(async (uuid) => {
|
|
404
|
+
await this._gattServer.getPrimaryService(uuid).then(async (service) => {
|
|
405
|
+
(await service.characteristics()).forEach(async (uuid) => {
|
|
406
|
+
(await service.getCharacteristic(uuid)).helper.removeListeners();
|
|
407
|
+
});
|
|
408
|
+
service.helper.removeListeners();
|
|
409
|
+
});
|
|
410
|
+
this._gattServer.helper.removeListeners();
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
await this.device.disconnect()
|
|
414
|
+
}
|
|
360
415
|
}
|
|
361
416
|
|
|
362
417
|
/**
|
|
@@ -451,8 +506,8 @@ class BTSensor extends EventEmitter {
|
|
|
451
506
|
* see: VictronBatteryMonitor for an example
|
|
452
507
|
*/
|
|
453
508
|
|
|
454
|
-
initGATTConnection(){
|
|
455
|
-
|
|
509
|
+
async initGATTConnection(isReconnecting=false, retries=5, retryInterval=3){
|
|
510
|
+
await this.connectDevice(isReconnecting,retries, retryInterval);
|
|
456
511
|
}
|
|
457
512
|
/**
|
|
458
513
|
* Subclasses providing GATT support should override this method
|
|
@@ -462,11 +517,36 @@ class BTSensor extends EventEmitter {
|
|
|
462
517
|
*
|
|
463
518
|
* see: VictronBatteryMonitor for an example
|
|
464
519
|
*/
|
|
465
|
-
initGATTNotifications(){
|
|
520
|
+
async initGATTNotifications(){
|
|
466
521
|
throw new Error("::initGATTNotifications() should be implemented by the BTSensor subclass")
|
|
467
522
|
}
|
|
468
523
|
|
|
469
|
-
|
|
524
|
+
isConnected(){
|
|
525
|
+
return this?._connected??false
|
|
526
|
+
}
|
|
527
|
+
async connectDevice( isReconnecting, retries=5, retryInterval=3){
|
|
528
|
+
for (let attempts = 0; attempts<retries; attempts++) {
|
|
529
|
+
try
|
|
530
|
+
{
|
|
531
|
+
this.debug( `(${this.getName()}) Trying to connect (attempt # ${attempts +1}) to Bluetooth device.` );
|
|
532
|
+
await this.deviceConnect(isReconnecting);
|
|
533
|
+
return this.device
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
catch (e){
|
|
537
|
+
if (attempts==retries)
|
|
538
|
+
throw new Error (`(${this.getName}) Unable to connect to Bluetooth device after ${attempts} attempts`)
|
|
539
|
+
this.debug(`(${this.getName()}) Error connecting to device. Retrying... `);
|
|
540
|
+
await new Promise((r) => setTimeout(r, retryInterval*1000));
|
|
541
|
+
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
setConnected(state){
|
|
546
|
+
this._connected=state
|
|
547
|
+
this.emit("connected", this._connected)
|
|
548
|
+
}
|
|
549
|
+
deviceConnect(isReconnecting=false, autoReconnect=false) {
|
|
470
550
|
|
|
471
551
|
|
|
472
552
|
return connectQueue.enqueue( async ()=>{
|
|
@@ -474,33 +554,50 @@ class BTSensor extends EventEmitter {
|
|
|
474
554
|
await this.device.helper.callMethod('Connect')
|
|
475
555
|
|
|
476
556
|
this.debug(`Connected to ${this.getName()}`)
|
|
477
|
-
if (!
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
557
|
+
if (!isReconnecting) {
|
|
558
|
+
this.device.helper.on(
|
|
559
|
+
"PropertiesChanged",
|
|
560
|
+
(propertiesChanged) => {
|
|
561
|
+
if ("Connected" in propertiesChanged) {
|
|
562
|
+
const { value } = propertiesChanged.Connected;
|
|
563
|
+
if (value) {
|
|
564
|
+
this._connected = true;
|
|
565
|
+
this.device.emit("connect", { connected: true });
|
|
566
|
+
} else {
|
|
567
|
+
this._connected = false;
|
|
568
|
+
this.device.emit("disconnect", { connected: false });
|
|
486
569
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
if (autoReconnect){
|
|
574
|
+
this.device.on("disconnect", async () => {
|
|
575
|
+
this._connected = false;
|
|
576
|
+
if (this.isActive()) {
|
|
577
|
+
this.debug(
|
|
578
|
+
`Device disconnected. Attempting to reconnect to ${this.getName()}`
|
|
579
|
+
);
|
|
580
|
+
try {
|
|
581
|
+
await this.deactivateGATT();
|
|
582
|
+
await this.activateGATT(true);
|
|
583
|
+
this.debug(`(${this.getName()}) Device reconnected.`);
|
|
584
|
+
} catch (e) {
|
|
585
|
+
this.setError(
|
|
586
|
+
`Error while reconnecting to ${this.getName()}: ${
|
|
587
|
+
e.message
|
|
588
|
+
}`
|
|
589
|
+
);
|
|
590
|
+
this.debug(
|
|
591
|
+
`Error while reconnecting to ${this.getName()}: ${
|
|
592
|
+
e.message
|
|
593
|
+
}`
|
|
594
|
+
);
|
|
595
|
+
this.debug(e);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
503
599
|
}
|
|
600
|
+
}
|
|
504
601
|
|
|
505
602
|
try {
|
|
506
603
|
|
|
@@ -517,9 +614,10 @@ class BTSensor extends EventEmitter {
|
|
|
517
614
|
You know, the little things.
|
|
518
615
|
*/
|
|
519
616
|
await this._adapter.helper.callMethod('StopDiscovery')
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
617
|
+
|
|
618
|
+
//await this._adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
619
|
+
// Transport: new Variant('s', this._adapter?._transport??"le")
|
|
620
|
+
//})
|
|
523
621
|
await this._adapter.helper.callMethod('StartDiscovery')
|
|
524
622
|
|
|
525
623
|
} catch (e){
|
|
@@ -542,22 +640,21 @@ class BTSensor extends EventEmitter {
|
|
|
542
640
|
|
|
543
641
|
initGATTInterval(){
|
|
544
642
|
this.device.disconnect().then(()=>{
|
|
545
|
-
this.
|
|
546
|
-
|
|
547
|
-
|
|
643
|
+
this.intervalID = setInterval( async () => {
|
|
644
|
+
try {
|
|
645
|
+
await this.initGATTConnection()
|
|
548
646
|
await this.emitGATT()
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
this.initPropertiesChanged()
|
|
552
|
-
)
|
|
553
|
-
.catch((e)=>{
|
|
554
|
-
this.debug(`Error disconnecting from ${this.getName()}: ${e.message}`)
|
|
555
|
-
})
|
|
556
|
-
})
|
|
557
|
-
.catch((error)=>{
|
|
647
|
+
}
|
|
648
|
+
catch(error) {
|
|
558
649
|
this.debug(error)
|
|
559
650
|
throw new Error(`unable to emit values for device ${this.getName()}:${error}`)
|
|
560
|
-
}
|
|
651
|
+
}
|
|
652
|
+
finally{
|
|
653
|
+
this.deactivateGATT().catch(
|
|
654
|
+
(e)=>{
|
|
655
|
+
this.debug(`(${this.getName()}) Error deactivating GATT Connection: ${e.message}`)
|
|
656
|
+
})
|
|
657
|
+
}
|
|
561
658
|
}
|
|
562
659
|
, this.pollFreq*1000)
|
|
563
660
|
})
|
|
@@ -721,6 +818,10 @@ class BTSensor extends EventEmitter {
|
|
|
721
818
|
getState(){
|
|
722
819
|
return this._state
|
|
723
820
|
}
|
|
821
|
+
setState(state){
|
|
822
|
+
this._state = state
|
|
823
|
+
this.emit("state",this._state)
|
|
824
|
+
}
|
|
724
825
|
|
|
725
826
|
getDomain(){
|
|
726
827
|
return this.constructor.Domain
|
|
@@ -824,14 +925,13 @@ class BTSensor extends EventEmitter {
|
|
|
824
925
|
this.currentProperties = await this.constructor.getDeviceProps(this.device)
|
|
825
926
|
if (this.usingGATT()){
|
|
826
927
|
try {
|
|
827
|
-
this.activateGATT()
|
|
928
|
+
await this.activateGATT(true)
|
|
828
929
|
} catch (e) {
|
|
829
|
-
this.
|
|
830
|
-
this._state="ERROR"
|
|
930
|
+
this.setError(`GATT services unavailable for ${this.getName()}. Reason: ${e}`)
|
|
831
931
|
return
|
|
832
932
|
}
|
|
833
933
|
}
|
|
834
|
-
this.
|
|
934
|
+
this.setState("ACTIVE")
|
|
835
935
|
this._propertiesChanged(this.currentProperties)
|
|
836
936
|
}
|
|
837
937
|
|
|
@@ -863,9 +963,12 @@ class BTSensor extends EventEmitter {
|
|
|
863
963
|
this.device.helper.removeListeners()
|
|
864
964
|
|
|
865
965
|
if (this.intervalID){
|
|
966
|
+
this.debug(`${this.getName()}::stopListening called clearInterval()`)
|
|
866
967
|
clearInterval(this.intervalID)
|
|
867
968
|
}
|
|
868
|
-
this.
|
|
969
|
+
if( this.usingGATT() )
|
|
970
|
+
await this.deactivateGATT()
|
|
971
|
+
this.setState("ASLEEP")
|
|
869
972
|
}
|
|
870
973
|
//END Sensor listen-to-changes functions
|
|
871
974
|
|
|
@@ -890,7 +993,7 @@ class BTSensor extends EventEmitter {
|
|
|
890
993
|
const path = config.paths[tag]
|
|
891
994
|
if (!(path===undefined)) {
|
|
892
995
|
const preparedPath =
|
|
893
|
-
this.
|
|
996
|
+
this._app.handleMessage(id,
|
|
894
997
|
{
|
|
895
998
|
updates:
|
|
896
999
|
[{ meta: [{path: this.preparePath(path), value: { units: pathMeta?.unit }}]}]
|
|
@@ -908,7 +1011,7 @@ class BTSensor extends EventEmitter {
|
|
|
908
1011
|
let preparedPath = this.preparePath(path)
|
|
909
1012
|
this.on(tag, (val)=>{
|
|
910
1013
|
if (pathMeta.notify){
|
|
911
|
-
this.
|
|
1014
|
+
this._app.notify(tag, val, id )
|
|
912
1015
|
} else {
|
|
913
1016
|
this.updatePath(preparedPath,val, id, source)
|
|
914
1017
|
}
|
|
@@ -917,7 +1020,7 @@ class BTSensor extends EventEmitter {
|
|
|
917
1020
|
})
|
|
918
1021
|
}
|
|
919
1022
|
updatePath(path, val, id, source){
|
|
920
|
-
this.
|
|
1023
|
+
this._app.handleMessage(id, {updates: [ { $source: source, values: [ { path: path, value: val }] } ] })
|
|
921
1024
|
}
|
|
922
1025
|
elapsedTimeSinceLastContact(){
|
|
923
1026
|
return (Date.now()-this?._lastContact??Date.now())/1000
|
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
2
|
|
|
3
3
|
## WHAT'S NEW
|
|
4
|
+
# Version 1.2.6-beta-4
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
- Improved reconnect logic for connected devices
|
|
7
|
+
- Error display in config (preliminary effort)
|
|
8
|
+
- JikongBMS support (aka JKBMS)
|
|
9
|
+
|
|
10
|
+
# Version 1.2.6-beta-3
|
|
6
11
|
|
|
7
12
|
- UI improvements for selecting Sensor Class (if available, paths appear immediately upon selection)
|
|
8
13
|
- EcoWorthy Batteries/BMS (need testers)
|
package/classLoader.js
CHANGED
|
@@ -10,8 +10,15 @@ const semver = require('semver')
|
|
|
10
10
|
|
|
11
11
|
classFiles.forEach( (file) => {
|
|
12
12
|
const filePath = path.join(dir, file);
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
try{
|
|
14
|
+
const cls = require (filePath)
|
|
15
|
+
classMap.set(cls.name, cls);
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
console.log(`Unable to load class (${cls?.name}): ${e.message}`)
|
|
19
|
+
console.log(e)
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
})
|
|
16
23
|
return classMap
|
|
17
24
|
}
|
|
@@ -26,10 +26,13 @@ export class FakeGATTCharacteristic extends EventEmitter{
|
|
|
26
26
|
this.values=values
|
|
27
27
|
this.interval = interval
|
|
28
28
|
}
|
|
29
|
+
writeValueWithoutResponse(buffer){
|
|
30
|
+
//do nothing for now
|
|
31
|
+
}
|
|
29
32
|
startNotifications(){
|
|
30
33
|
this.intervalID=setInterval(()=>{
|
|
31
34
|
if (this.values.length>0){
|
|
32
|
-
this.emit("valuechanged",Buffer.from(this.values[this.valueIndex++],"hex") )
|
|
35
|
+
this.emit("valuechanged",Buffer.from((this.values[this.valueIndex++]).replaceAll(" ",""),"hex") )
|
|
33
36
|
if (this.valueIndex>=this.values.length)
|
|
34
37
|
this.valueIndex=0
|
|
35
38
|
}
|