bt-sensors-plugin-sk 1.2.0-beta.0.0.1.test → 1.2.0-beta.0.0.3
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 +42 -3
- package/Queue.js +48 -0
- package/index.js +33 -15
- package/package.json +2 -1
- package/sensor_classes/ATC.js +2 -1
- package/sensor_classes/JBDBMS.js +4 -4
- package/sensor_classes/KilovaultHLXPlus.js +1 -1
- package/sensor_classes/Renogy/RenogySensor.js +1 -1
- package/sensor_classes/UltrasonicWindMeter.js +1 -1
- package/sensor_classes/VictronBatteryMonitor.js +1 -1
- package/sensor_classes/VictronOrionXS.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +1 -1
package/BTSensor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { Variant } = require('dbus-next');
|
|
2
2
|
const { log } = require('node:console');
|
|
3
3
|
const EventEmitter = require('node:events');
|
|
4
|
+
const AutoQueue = require("./Queue")
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @author Andrew Gerngross <oh.that.andy@gmail.com>
|
|
@@ -11,6 +12,7 @@ const EventEmitter = require('node:events');
|
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
const BTCompanies = require('./bt_co.json');
|
|
15
|
+
const connectQueue = new AutoQueue()
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* @global A map of company names keyed by their Bluetooth ID
|
|
@@ -449,6 +451,39 @@ class BTSensor extends EventEmitter {
|
|
|
449
451
|
throw new Error("::initGATTNotifications() should be implemented by the BTSensor subclass")
|
|
450
452
|
}
|
|
451
453
|
|
|
454
|
+
deviceConnect() {
|
|
455
|
+
|
|
456
|
+
/* CAUTION: HACK AHEAD
|
|
457
|
+
|
|
458
|
+
Bluez for some cockamamie reason (It's 2025 for chrissake.
|
|
459
|
+
BLUETOOTH ISN'T JUST FOR DESKTOPS ANYMORE, BLUEZ DEVS!)
|
|
460
|
+
SUSPENDS scanning while connected to a device.
|
|
461
|
+
|
|
462
|
+
The next line of code gives the scanner a kick in the arse,
|
|
463
|
+
starting it up again so, I dunno, another device might be able
|
|
464
|
+
to connect and sensor classes could maybe get beacon updates.
|
|
465
|
+
|
|
466
|
+
You know, the little things.
|
|
467
|
+
*/
|
|
468
|
+
return connectQueue.enqueue( async ()=>{
|
|
469
|
+
this.debug(`Connecting to ${this.getName()}`)
|
|
470
|
+
await this.device.connect()
|
|
471
|
+
try {
|
|
472
|
+
|
|
473
|
+
await this._adapter.helper.callMethod('StopDiscovery')
|
|
474
|
+
await this._adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
475
|
+
Transport: new Variant('s', this._adapter?._transport??"le")
|
|
476
|
+
})
|
|
477
|
+
await this._adapter.helper.callMethod('StartDiscovery')
|
|
478
|
+
|
|
479
|
+
} catch (e){
|
|
480
|
+
//probably ignorable error. probably.
|
|
481
|
+
console.log(e)
|
|
482
|
+
}
|
|
483
|
+
})
|
|
484
|
+
/* END HACK*/
|
|
485
|
+
}
|
|
486
|
+
|
|
452
487
|
/**
|
|
453
488
|
*
|
|
454
489
|
* Subclasses do NOT need to override this function
|
|
@@ -739,12 +774,15 @@ class BTSensor extends EventEmitter {
|
|
|
739
774
|
//End instance utility functions
|
|
740
775
|
|
|
741
776
|
createPaths(config, id){
|
|
777
|
+
// const source = `${this.getName()} (${id})`
|
|
778
|
+
|
|
742
779
|
Object.keys(this.getPaths()).forEach((tag)=>{
|
|
743
780
|
const pathMeta=this.getPath(tag)
|
|
744
781
|
const path = config.paths[tag]
|
|
745
782
|
if (!(path===undefined))
|
|
746
783
|
this.app.handleMessage(id,
|
|
747
784
|
{
|
|
785
|
+
// $source: source,
|
|
748
786
|
updates:
|
|
749
787
|
[{ meta: [{path: preparePath(this, path), value: { units: pathMeta?.unit }}]}]
|
|
750
788
|
})
|
|
@@ -752,6 +790,7 @@ class BTSensor extends EventEmitter {
|
|
|
752
790
|
}
|
|
753
791
|
|
|
754
792
|
initPaths(deviceConfig, id){
|
|
793
|
+
const source = this.getName()
|
|
755
794
|
Object.keys(this.getPaths()).forEach((tag)=>{
|
|
756
795
|
const pathMeta=this.getPath(tag)
|
|
757
796
|
const path = deviceConfig.paths[tag];
|
|
@@ -760,14 +799,14 @@ class BTSensor extends EventEmitter {
|
|
|
760
799
|
if (pathMeta.notify){
|
|
761
800
|
this.app.notify(tag, val, id )
|
|
762
801
|
} else {
|
|
763
|
-
this.updatePath(preparePath(this,path),val,id)
|
|
802
|
+
this.updatePath(preparePath(this,path),val, id, source)
|
|
764
803
|
}
|
|
765
804
|
})
|
|
766
805
|
}
|
|
767
806
|
})
|
|
768
807
|
}
|
|
769
|
-
updatePath(path, val,id){
|
|
770
|
-
this.app.handleMessage(id, {updates: [ { values: [ {path: path, value: val }] } ] })
|
|
808
|
+
updatePath(path, val, id, source){
|
|
809
|
+
this.app.handleMessage(id, {updates: [ { $source: source, values: [ { path: path, value: val }] } ] })
|
|
771
810
|
}
|
|
772
811
|
elapsedTimeSinceLastContact(){
|
|
773
812
|
return (Date.now()-this?._lastContact??Date.now())/1000
|
package/Queue.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
see: https://stackoverflow.com/questions/53540348/js-async-await-tasks-queue
|
|
3
|
+
*/
|
|
4
|
+
class Queue {
|
|
5
|
+
constructor() { this._items = []; }
|
|
6
|
+
enqueue(item) { this._items.push(item); }
|
|
7
|
+
dequeue() { return this._items.shift(); }
|
|
8
|
+
get size() { return this._items.length; }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class AutoQueue extends Queue {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this._pendingPromise = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
enqueue(action) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
super.enqueue({ action, resolve, reject });
|
|
20
|
+
this.dequeue();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async dequeue() {
|
|
25
|
+
if (this._pendingPromise) return false;
|
|
26
|
+
|
|
27
|
+
let item = super.dequeue();
|
|
28
|
+
|
|
29
|
+
if (!item) return false;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
this._pendingPromise = true;
|
|
33
|
+
|
|
34
|
+
let payload = await item.action(this);
|
|
35
|
+
|
|
36
|
+
this._pendingPromise = false;
|
|
37
|
+
item.resolve(payload);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
this._pendingPromise = false;
|
|
40
|
+
item.reject(e);
|
|
41
|
+
} finally {
|
|
42
|
+
this.dequeue();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
module.exports = AutoQueue
|
package/index.js
CHANGED
|
@@ -47,6 +47,12 @@ class MissingSensor {
|
|
|
47
47
|
hasGATT(){
|
|
48
48
|
return this.config.gattParams
|
|
49
49
|
}
|
|
50
|
+
initGATTConnection(){
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
getGATTDescription(){
|
|
54
|
+
return ""
|
|
55
|
+
}
|
|
50
56
|
getMetadata(){
|
|
51
57
|
return this.metadata
|
|
52
58
|
}
|
|
@@ -196,6 +202,12 @@ module.exports = function (app) {
|
|
|
196
202
|
app.debug(req.body)
|
|
197
203
|
const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
|
|
198
204
|
if (i<0){
|
|
205
|
+
if (!options.peripherals){
|
|
206
|
+
if (!options.hasOwnProperty("peripherals"))
|
|
207
|
+
options.peripherals=[]
|
|
208
|
+
|
|
209
|
+
options.peripherals=[]
|
|
210
|
+
}
|
|
199
211
|
options.peripherals.push(req.body)
|
|
200
212
|
} else {
|
|
201
213
|
options.peripherals[i] = req.body
|
|
@@ -330,13 +342,15 @@ module.exports = function (app) {
|
|
|
330
342
|
//filter options which can cause issues with Device::Connect()
|
|
331
343
|
//turning off Discovery
|
|
332
344
|
//try {await adapter.startDiscovery()}
|
|
345
|
+
const _transport = transport?plugin.schema.properties.transport.default:transport
|
|
333
346
|
try{
|
|
334
|
-
if (
|
|
335
|
-
app.debug(`Setting Bluetooth transport option to ${
|
|
347
|
+
if (_transport) {
|
|
348
|
+
app.debug(`Setting Bluetooth transport option to ${_transport}`)
|
|
336
349
|
await adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
337
|
-
Transport: new Variant('s',
|
|
350
|
+
Transport: new Variant('s', _transport)
|
|
338
351
|
})
|
|
339
352
|
}
|
|
353
|
+
adapter._transport=_transport
|
|
340
354
|
await adapter.helper.callMethod('StartDiscovery')
|
|
341
355
|
}
|
|
342
356
|
catch (error){
|
|
@@ -370,7 +384,7 @@ module.exports = function (app) {
|
|
|
370
384
|
var s
|
|
371
385
|
const startNumber=starts
|
|
372
386
|
//app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
|
|
373
|
-
adapter.waitDevice(config.mac_address,config
|
|
387
|
+
adapter.waitDevice(config.mac_address,(config?.discoveryTimeout??30)*1000)
|
|
374
388
|
.then(async (device)=> {
|
|
375
389
|
if (startNumber != starts ) {
|
|
376
390
|
debugger
|
|
@@ -382,7 +396,6 @@ module.exports = function (app) {
|
|
|
382
396
|
if (s instanceof BLACKLISTED)
|
|
383
397
|
reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
|
|
384
398
|
else{
|
|
385
|
-
|
|
386
399
|
addSensorToList(s)
|
|
387
400
|
s.on("RSSI",(()=>{
|
|
388
401
|
updateSensor(s)
|
|
@@ -413,6 +426,8 @@ module.exports = function (app) {
|
|
|
413
426
|
const sensor = new c(device,config?.params, config?.gattParams)
|
|
414
427
|
sensor.debug=app.debug
|
|
415
428
|
sensor.app=app
|
|
429
|
+
sensor._adapter=adapter //HACK!
|
|
430
|
+
|
|
416
431
|
await sensor.init()
|
|
417
432
|
//app.debug(`instantiated ${await BTSensor.getDeviceProp(device,"Address")}`)
|
|
418
433
|
|
|
@@ -427,15 +442,15 @@ module.exports = function (app) {
|
|
|
427
442
|
//if we're here ain't got no class for the device
|
|
428
443
|
var sensor
|
|
429
444
|
if (config.params?.sensorClass){
|
|
430
|
-
|
|
431
|
-
c.debug=app.debug
|
|
432
|
-
sensor = new c(device,config?.params, config?.gattParams)
|
|
433
|
-
sensor.debug=app.debug
|
|
434
|
-
sensor.app=app
|
|
445
|
+
var c = classMap.get(config.params.sensorClass)
|
|
435
446
|
} else{
|
|
436
|
-
|
|
437
|
-
sensor.app=app
|
|
447
|
+
c = classMap.get('UNKNOWN')
|
|
438
448
|
}
|
|
449
|
+
c.debug=app.debug
|
|
450
|
+
sensor = new c(device,config?.params, config?.gattParams)
|
|
451
|
+
sensor.debug=app.debug
|
|
452
|
+
sensor.app=app
|
|
453
|
+
|
|
439
454
|
await sensor.init()
|
|
440
455
|
return sensor
|
|
441
456
|
}
|
|
@@ -626,11 +641,14 @@ module.exports = function (app) {
|
|
|
626
641
|
if (sensor.elapsedTimeSinceLastContact()> dt)
|
|
627
642
|
channel.broadcast(getSensorInfo(sensor), "sensorchanged")
|
|
628
643
|
})
|
|
629
|
-
}, minTimeout
|
|
644
|
+
}, (minTimeout==Infinity)?(options?.discoveryTimeout??plugin.schema.properties.discoveryTimeout.default):minTimeout)
|
|
630
645
|
|
|
646
|
+
if (!options.hasOwnProperty("discoveryInterval" )) //no config -- first run
|
|
647
|
+
options.discoveryInterval = plugin.schema.properties.discoveryInterval.default
|
|
648
|
+
|
|
631
649
|
if (options.discoveryInterval && !discoveryIntervalID)
|
|
632
|
-
findDeviceLoop(options.discoveryTimeout,
|
|
633
|
-
|
|
650
|
+
findDeviceLoop(options?.discoveryTimeout??plugin.schema.properties.discoveryTimeout.default,
|
|
651
|
+
options.discoveryInterval)
|
|
634
652
|
}
|
|
635
653
|
plugin.stop = async function () {
|
|
636
654
|
app.debug("Stopping plugin")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.2.0-beta.0.0.
|
|
3
|
+
"version": "1.2.0-beta.0.0.3",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird, Ultrasonic wind meters, Mopeka tank readers, Renogy Battery and Solar Controllers, Aranet4 environment sensors, SwitchBot temp and humidity sensors, KilovaultHLXPlus smart batteries, and Govee GVH51xx temp sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"signalk-category-hardware",
|
|
66
66
|
"signalk-plugin-configurator"
|
|
67
67
|
],
|
|
68
|
+
"signalk-plugin-enabled-by-default": true,
|
|
68
69
|
"author": "Andrew Gerngross",
|
|
69
70
|
"license": "ISC",
|
|
70
71
|
"bugs": {
|
package/sensor_classes/ATC.js
CHANGED
package/sensor_classes/JBDBMS.js
CHANGED
|
@@ -49,7 +49,7 @@ class JBDBMS extends BTSensor {
|
|
|
49
49
|
this.addMetadatum('FET', '', 'FET Control',
|
|
50
50
|
(buffer)=>{return buffer.readUInt8(24)} )
|
|
51
51
|
|
|
52
|
-
await this.
|
|
52
|
+
await this.deviceConnect()
|
|
53
53
|
await this.initCharacteristics()
|
|
54
54
|
const cellsAndTemps = await this.getNumberOfCellsAndTemps()
|
|
55
55
|
this.numberOfCells=cellsAndTemps.cells
|
|
@@ -74,14 +74,14 @@ class JBDBMS extends BTSensor {
|
|
|
74
74
|
return true
|
|
75
75
|
}
|
|
76
76
|
initCharacteristics(){
|
|
77
|
-
return new Promise((resolve,reject )=>{
|
|
77
|
+
return new Promise( async (resolve,reject )=>{
|
|
78
78
|
const gattServer = await this.device.gatt()
|
|
79
79
|
const txRxService= await gattServer.getPrimaryService(this.constructor.TX_RX_SERVICE)
|
|
80
80
|
this.rxChar = await txRxService.getCharacteristic(this.constructor.NOTIFY_CHAR_UUID)
|
|
81
81
|
this.txChar = await txRxService.getCharacteristic(this.constructor.WRITE_CHAR_UUID)
|
|
82
82
|
await this.rxChar.startNotifications()
|
|
83
83
|
resolve(this)
|
|
84
|
-
|
|
84
|
+
.catch((e)=>{ reject(e.message) }) })
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
async initGATTNotifications(){
|
|
@@ -92,7 +92,7 @@ class JBDBMS extends BTSensor {
|
|
|
92
92
|
|
|
93
93
|
emitGATT(){
|
|
94
94
|
this.getAndEmitBatteryInfo()
|
|
95
|
-
setTimeout(()=>{this.getAndEmitCellVoltages()},
|
|
95
|
+
setTimeout(()=>{this.getAndEmitCellVoltages()}, 10000)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
async getNumberOfCellsAndTemps(){
|
|
@@ -175,7 +175,7 @@ class KilovaultHLXPlus extends BTSensor{
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
initGATTConnection(){
|
|
178
|
-
return new Promise((resolve,reject )=>{ this.
|
|
178
|
+
return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
|
|
179
179
|
if (!this.gattServer) {
|
|
180
180
|
this.gattServer = await this.device.gatt()
|
|
181
181
|
this.battService = await this.gattServer.getPrimaryService("0000ffe0-0000-1000-8000-00805f9b34fb")
|
|
@@ -40,7 +40,7 @@ class UltrasonicWindMeter extends BTSensor{
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
initGATTConnection(){
|
|
43
|
-
return new Promise((resolve,reject )=>{ this.
|
|
43
|
+
return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
|
|
44
44
|
if (!this.gattServer) {
|
|
45
45
|
this.gattServer = await this.device.gatt()
|
|
46
46
|
this.battService = await this.gattServer.getPrimaryService("0000180f-0000-1000-8000-00805f9b34fb")
|
|
@@ -125,7 +125,7 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
125
125
|
return new Promise((resolve,reject )=>{
|
|
126
126
|
if (!this.valueIfVariant(this.currentProperties.Paired))
|
|
127
127
|
reject(`${this.getName()} must be paired with the Signalk server to use GATT protocol`)
|
|
128
|
-
this.
|
|
128
|
+
this.deviceConnect().then(async ()=>{
|
|
129
129
|
this.debug(`${this.getName()} connected.`)
|
|
130
130
|
if (!this.gattServer) {
|
|
131
131
|
this.gattServer = await this.device.gatt()
|
|
@@ -23,7 +23,7 @@ class VictronOrionXS extends VictronSensor{
|
|
|
23
23
|
this.addMetadatum('inputCurrent','A','input current',
|
|
24
24
|
(buff)=>{return this.NaNif(buff.readUInt16LE(8),0xFFFF)/10})
|
|
25
25
|
this.addMetadatum('deviceOffReason','', 'device off reason',
|
|
26
|
-
(buff)=>{return VC.OffReasons(buff.
|
|
26
|
+
(buff)=>{return VC.OffReasons(buff.readUInt32BE(10))})
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
}
|
|
@@ -110,7 +110,7 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
110
110
|
this.gattWarningDelivered=true
|
|
111
111
|
}
|
|
112
112
|
return new Promise((resolve,reject )=>{
|
|
113
|
-
this.
|
|
113
|
+
this.deviceConnect().then(async ()=>{
|
|
114
114
|
if (!this.gattServer) {
|
|
115
115
|
this.gattServer = await this.device.gatt()
|
|
116
116
|
this.gattService = await this.gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
|