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/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const OutOfRangeDevice = require("./OutOfRangeDevice.js")
|
|
|
11
11
|
const { createChannel, createSession } = require("better-sse");
|
|
12
12
|
const { clearTimeout } = require('timers')
|
|
13
13
|
const loadClassMap = require('./classLoader.js')
|
|
14
|
+
const { debug } = require("node:console")
|
|
14
15
|
class MissingSensor {
|
|
15
16
|
|
|
16
17
|
constructor(config){
|
|
@@ -76,6 +77,9 @@ class MissingSensor {
|
|
|
76
77
|
getRSSI(){
|
|
77
78
|
return NaN
|
|
78
79
|
}
|
|
80
|
+
getState(){
|
|
81
|
+
return "ERROR"
|
|
82
|
+
}
|
|
79
83
|
stopListening(){}
|
|
80
84
|
listen(){}
|
|
81
85
|
isActive(){
|
|
@@ -87,18 +91,55 @@ class MissingSensor {
|
|
|
87
91
|
getSignalStrength(){
|
|
88
92
|
return NaN
|
|
89
93
|
}
|
|
94
|
+
prepareConfig(){
|
|
95
|
+
|
|
96
|
+
}
|
|
90
97
|
|
|
91
98
|
}
|
|
92
99
|
module.exports = function (app) {
|
|
93
100
|
var deviceConfigs=[]
|
|
94
101
|
var starts=0
|
|
95
102
|
|
|
96
|
-
var plugin = {
|
|
103
|
+
var plugin = {
|
|
104
|
+
debug (message) {
|
|
105
|
+
app.debug(message)
|
|
106
|
+
const logEntry = {
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
message: message
|
|
109
|
+
}
|
|
110
|
+
this.log.push(logEntry)
|
|
111
|
+
if (channel)
|
|
112
|
+
channel.broadcast(logEntry,"pluginDebug")
|
|
113
|
+
},
|
|
114
|
+
setError( error ){
|
|
115
|
+
app.setPluginError(error)
|
|
116
|
+
app.debug(error)
|
|
117
|
+
const logEntry = {
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
message: error
|
|
120
|
+
}
|
|
121
|
+
this.errorLog.push(logEntry)
|
|
122
|
+
if (channel)
|
|
123
|
+
channel.broadcast( logEntry, "pluginError" )
|
|
124
|
+
},
|
|
125
|
+
setStatusText( status ){
|
|
126
|
+
app.setPluginStatus(status)
|
|
127
|
+
const logEntry = {timestamp: Date.now(), message: status}
|
|
128
|
+
this.log.push(logEntry)
|
|
129
|
+
if (channel)
|
|
130
|
+
channel.broadcast( logEntry, "pluginStatusText" )
|
|
131
|
+
},
|
|
132
|
+
setFatalError( error ){
|
|
133
|
+
this.setError(`FATAL ERROR: ${error}`)
|
|
134
|
+
this.stop()
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
}
|
|
97
138
|
plugin.id = 'bt-sensors-plugin-sk';
|
|
98
139
|
plugin.name = 'BT Sensors plugin';
|
|
99
140
|
plugin.description = 'Plugin to communicate with and update paths to BLE Sensors in Signalk';
|
|
100
|
-
|
|
101
|
-
|
|
141
|
+
plugin.log = []
|
|
142
|
+
plugin.errorLog = []
|
|
102
143
|
|
|
103
144
|
plugin.schema = {
|
|
104
145
|
type: "object",
|
|
@@ -131,6 +172,8 @@ module.exports = function (app) {
|
|
|
131
172
|
var discoveryIntervalID, progressID, progressTimeoutID, deviceHealthID
|
|
132
173
|
var adapter
|
|
133
174
|
const channel = createChannel()
|
|
175
|
+
|
|
176
|
+
plugin.debug(`Loading plugin ${packageInfo.version}`)
|
|
134
177
|
|
|
135
178
|
const sensorMap=new Map()
|
|
136
179
|
|
|
@@ -166,7 +209,7 @@ module.exports = function (app) {
|
|
|
166
209
|
|
|
167
210
|
_tempSensor = new _class ( _sensor.device )
|
|
168
211
|
_tempSensor.currentProperties=_sensor.currentProperties
|
|
169
|
-
_tempSensor.
|
|
212
|
+
_tempSensor._app = app
|
|
170
213
|
_tempSensor._adapter=adapter
|
|
171
214
|
await _tempSensor.init()
|
|
172
215
|
const _json = sensorToJSON(_tempSensor)
|
|
@@ -185,7 +228,6 @@ module.exports = function (app) {
|
|
|
185
228
|
})
|
|
186
229
|
|
|
187
230
|
router.post('/updateSensorData', async (req, res) => {
|
|
188
|
-
app.debug(req.body)
|
|
189
231
|
const sensor = sensorMap.get(req.body.mac_address)
|
|
190
232
|
sensor.prepareConfig(req.body)
|
|
191
233
|
const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
|
|
@@ -203,7 +245,6 @@ module.exports = function (app) {
|
|
|
203
245
|
deviceConfigs=options.peripherals
|
|
204
246
|
app.savePluginOptions(
|
|
205
247
|
options, async () => {
|
|
206
|
-
app.debug('Plugin options saved')
|
|
207
248
|
res.status(200).json({message: "Sensor updated"})
|
|
208
249
|
if (sensor) {
|
|
209
250
|
removeSensorFromList(sensor)
|
|
@@ -216,7 +257,6 @@ module.exports = function (app) {
|
|
|
216
257
|
|
|
217
258
|
});
|
|
218
259
|
router.post('/removeSensorData', async (req, res) => {
|
|
219
|
-
app.debug(req.body)
|
|
220
260
|
const sensor = sensorMap.get(req.body.mac_address)
|
|
221
261
|
if (!sensor) {
|
|
222
262
|
res.status(404).json({message: "Sensor not found"})
|
|
@@ -234,7 +274,6 @@ module.exports = function (app) {
|
|
|
234
274
|
sensorMap.delete(req.body.mac_address)
|
|
235
275
|
app.savePluginOptions(
|
|
236
276
|
options, () => {
|
|
237
|
-
app.debug('Plugin options saved')
|
|
238
277
|
res.status(200).json({message: "Sensor updated"})
|
|
239
278
|
channel.broadcast({},"resetSensors")
|
|
240
279
|
}
|
|
@@ -244,11 +283,9 @@ module.exports = function (app) {
|
|
|
244
283
|
|
|
245
284
|
router.post('/updateBaseData', async (req, res) => {
|
|
246
285
|
|
|
247
|
-
app.debug(req.body)
|
|
248
286
|
Object.assign(options,req.body)
|
|
249
287
|
app.savePluginOptions(
|
|
250
288
|
options, () => {
|
|
251
|
-
app.debug('Plugin options saved')
|
|
252
289
|
res.status(200).json({message: "Plugin updated"})
|
|
253
290
|
channel.broadcast({},"pluginRestarted")
|
|
254
291
|
restartPlugin(options)
|
|
@@ -274,13 +311,11 @@ module.exports = function (app) {
|
|
|
274
311
|
|
|
275
312
|
|
|
276
313
|
router.get('/getSensors', (req, res) => {
|
|
277
|
-
app.debug("Sending sensors")
|
|
278
314
|
const t = sensorsToJSON()
|
|
279
315
|
res.status(200).json(t)
|
|
280
316
|
});
|
|
281
317
|
|
|
282
318
|
router.get('/getProgress', (req, res) => {
|
|
283
|
-
app.debug("Sending progress")
|
|
284
319
|
let deviceCount = deviceConfigs.filter((dc)=>dc.active).length
|
|
285
320
|
const json = {"progress":foundConfiguredDevices/deviceCount, "maxTimeout": 1,
|
|
286
321
|
"deviceCount":foundConfiguredDevices,
|
|
@@ -299,7 +334,6 @@ module.exports = function (app) {
|
|
|
299
334
|
const session = await createSession(req,res)
|
|
300
335
|
channel.register(session)
|
|
301
336
|
req.on("close", ()=>{
|
|
302
|
-
app.debug("deregistering session")
|
|
303
337
|
channel.deregister(session)
|
|
304
338
|
})
|
|
305
339
|
});
|
|
@@ -315,14 +349,18 @@ module.exports = function (app) {
|
|
|
315
349
|
|
|
316
350
|
function getSensorInfo(sensor){
|
|
317
351
|
|
|
318
|
-
|
|
352
|
+
const s = sensor.getState()
|
|
319
353
|
return { mac: sensor.getMacAddress(),
|
|
320
354
|
name: sensor.getName(),
|
|
321
355
|
class: sensor.constructor.name,
|
|
322
356
|
domain: sensor.getDomain().name,
|
|
357
|
+
state: sensor.getState(),
|
|
358
|
+
errorLog: sensor.getErrorLog(),
|
|
359
|
+
debugLog: sensor.getDebugLog(),
|
|
323
360
|
RSSI: sensor.getRSSI(),
|
|
324
361
|
signalStrength: sensor.getSignalStrength(),
|
|
325
|
-
|
|
362
|
+
connected: sensor.isConnected(),
|
|
363
|
+
lastContactDelta: sensor.elapsedTimeSinceLastContact()
|
|
326
364
|
}
|
|
327
365
|
}
|
|
328
366
|
|
|
@@ -343,14 +381,14 @@ module.exports = function (app) {
|
|
|
343
381
|
|
|
344
382
|
const transport = options?.transport??"le"
|
|
345
383
|
const duplicateData = options?.duplicateData??false
|
|
346
|
-
|
|
384
|
+
plugin.debug("Starting scan...");
|
|
347
385
|
//Use adapter.helper directly to get around Adapter::startDiscovery()
|
|
348
386
|
//filter options which can cause issues with Device::Connect()
|
|
349
387
|
//turning off Discovery
|
|
350
388
|
//try {await adapter.startDiscovery()}
|
|
351
389
|
try{
|
|
352
390
|
if (transport) {
|
|
353
|
-
|
|
391
|
+
plugin.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
|
|
354
392
|
await adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
355
393
|
Transport: new Variant('s', transport),
|
|
356
394
|
DuplicateData: new Variant('b', duplicateData)
|
|
@@ -359,7 +397,7 @@ module.exports = function (app) {
|
|
|
359
397
|
await adapter.helper.callMethod('StartDiscovery')
|
|
360
398
|
}
|
|
361
399
|
catch (error){
|
|
362
|
-
|
|
400
|
+
plugin.debug(error)
|
|
363
401
|
}
|
|
364
402
|
|
|
365
403
|
}
|
|
@@ -373,8 +411,6 @@ module.exports = function (app) {
|
|
|
373
411
|
}
|
|
374
412
|
|
|
375
413
|
function addSensorToList(sensor){
|
|
376
|
-
app.debug(`adding sensor to list ${sensor.getMacAddress()}`)
|
|
377
|
-
|
|
378
414
|
sensorMap.set(sensor.getMacAddress(),sensor)
|
|
379
415
|
channel.broadcast(sensorToJSON(sensor),"newsensor");
|
|
380
416
|
}
|
|
@@ -386,7 +422,6 @@ module.exports = function (app) {
|
|
|
386
422
|
return new Promise( ( resolve, reject )=>{
|
|
387
423
|
var s
|
|
388
424
|
const startNumber=starts
|
|
389
|
-
//app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
|
|
390
425
|
adapter.waitDevice(config.mac_address,(config?.discoveryTimeout??30)*1000)
|
|
391
426
|
.then(async (device)=> {
|
|
392
427
|
if (startNumber != starts ) {
|
|
@@ -399,13 +434,11 @@ module.exports = function (app) {
|
|
|
399
434
|
if (s instanceof BLACKLISTED)
|
|
400
435
|
reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
|
|
401
436
|
else{
|
|
402
|
-
//app.debug(`Adding sensor to list ${config.mac_address}`)
|
|
403
437
|
|
|
404
438
|
addSensorToList(s)
|
|
405
439
|
s._lastRSSI=-1*Infinity
|
|
406
440
|
s.on("RSSI",(()=>{
|
|
407
441
|
if (Date.now()-s._lastRSSI > 30000) { //only update RSSI on client every 30 seconds
|
|
408
|
-
//app.debug(`Updating ${s.getMacAddress()} RSSI after ${Date.now()-s._lastRSSI} ms`)
|
|
409
442
|
|
|
410
443
|
s._lastRSSI=Date.now()
|
|
411
444
|
|
|
@@ -431,13 +464,29 @@ module.exports = function (app) {
|
|
|
431
464
|
removeSensorFromList(s)
|
|
432
465
|
addSensorToList(s)
|
|
433
466
|
})
|
|
467
|
+
s.on("state", (state)=>{
|
|
468
|
+
channel.broadcast(getSensorInfo(s), "sensorchanged")
|
|
469
|
+
})
|
|
470
|
+
s.on("error",(error)=>{
|
|
471
|
+
channel.broadcast(getSensorInfo(s), "sensorchanged")
|
|
472
|
+
})
|
|
473
|
+
s.on("debug", ()=>{
|
|
474
|
+
channel.broadcast(getSensorInfo(s), "sensorchanged")
|
|
475
|
+
})
|
|
434
476
|
addSensorToList(s)
|
|
435
477
|
resolve(s)
|
|
436
478
|
}
|
|
437
479
|
}
|
|
438
480
|
if (startNumber == starts ) {
|
|
439
|
-
|
|
440
|
-
|
|
481
|
+
const errorTxt = `Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`
|
|
482
|
+
if(s)
|
|
483
|
+
{
|
|
484
|
+
s.setError(errorTxt)
|
|
485
|
+
} else {
|
|
486
|
+
plugin.setError(errorTxt)
|
|
487
|
+
}
|
|
488
|
+
plugin.debug(e)
|
|
489
|
+
|
|
441
490
|
reject( e?.message??e )
|
|
442
491
|
}
|
|
443
492
|
})})
|
|
@@ -473,20 +522,17 @@ module.exports = function (app) {
|
|
|
473
522
|
|
|
474
523
|
const sensor = new c(device, config?.params, config?.gattParams)
|
|
475
524
|
sensor._paths=config.paths //this might be a good candidate for refactoring
|
|
476
|
-
sensor.
|
|
477
|
-
sensor.setPluginError=app.setPluginError
|
|
478
|
-
sensor.app=app
|
|
479
|
-
if (!adapter) debugger
|
|
525
|
+
sensor._app=app
|
|
480
526
|
sensor._adapter=adapter //HACK!
|
|
481
527
|
await sensor.init()
|
|
482
528
|
return sensor
|
|
483
529
|
}
|
|
484
530
|
catch(error){
|
|
485
531
|
const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
|
|
486
|
-
|
|
487
|
-
|
|
532
|
+
plugin.debug(msg)
|
|
533
|
+
plugin.debug(error)
|
|
488
534
|
if (config.active)
|
|
489
|
-
|
|
535
|
+
plugin.setError(msg)
|
|
490
536
|
return null
|
|
491
537
|
}
|
|
492
538
|
|
|
@@ -494,7 +540,7 @@ module.exports = function (app) {
|
|
|
494
540
|
|
|
495
541
|
function initConfiguredDevice(deviceConfig){
|
|
496
542
|
const startNumber=starts
|
|
497
|
-
|
|
543
|
+
plugin.setStatusText(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
|
|
498
544
|
if (!deviceConfig.discoveryTimeout)
|
|
499
545
|
deviceConfig.discoveryTimeout = options.discoveryTimeout
|
|
500
546
|
createSensor(adapter, deviceConfig).then((sensor)=>{
|
|
@@ -502,7 +548,7 @@ module.exports = function (app) {
|
|
|
502
548
|
return
|
|
503
549
|
}
|
|
504
550
|
if (deviceConfig.active && !(sensor.device instanceof OutOfRangeDevice) ) {
|
|
505
|
-
|
|
551
|
+
plugin.setStatusText(`Listening to ${++foundConfiguredDevices} sensors.`);
|
|
506
552
|
sensor.activate(deviceConfig, plugin)
|
|
507
553
|
}
|
|
508
554
|
|
|
@@ -514,10 +560,10 @@ module.exports = function (app) {
|
|
|
514
560
|
return
|
|
515
561
|
}
|
|
516
562
|
const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
|
|
517
|
-
|
|
518
|
-
|
|
563
|
+
plugin.debug(msg)
|
|
564
|
+
|
|
519
565
|
if (deviceConfig.active)
|
|
520
|
-
|
|
566
|
+
plugin.setError(msg)
|
|
521
567
|
const sensor=new MissingSensor(deviceConfig)
|
|
522
568
|
++foundConfiguredDevices
|
|
523
569
|
|
|
@@ -527,7 +573,7 @@ module.exports = function (app) {
|
|
|
527
573
|
}
|
|
528
574
|
function findDevices (discoveryTimeout) {
|
|
529
575
|
const startNumber = starts
|
|
530
|
-
|
|
576
|
+
plugin.setStatusText("Scanning for new Bluetooth devices...");
|
|
531
577
|
|
|
532
578
|
adapter.devices().then( (macs)=>{
|
|
533
579
|
if (startNumber != starts ) {
|
|
@@ -577,7 +623,7 @@ module.exports = function (app) {
|
|
|
577
623
|
//Connect to adapter
|
|
578
624
|
|
|
579
625
|
if (!adapter){
|
|
580
|
-
|
|
626
|
+
plugin.debug(`Connecting to bluetooth adapter ${adapterID}`);
|
|
581
627
|
|
|
582
628
|
adapter = await bluetooth.getAdapter(adapterID)
|
|
583
629
|
|
|
@@ -585,11 +631,10 @@ module.exports = function (app) {
|
|
|
585
631
|
|
|
586
632
|
await adapter.helper._prepare()
|
|
587
633
|
adapter.helper._propsProxy.on('PropertiesChanged', async (iface,changedProps,invalidated) => {
|
|
588
|
-
app.debug(changedProps)
|
|
589
634
|
if (Object.hasOwn(changedProps,"Powered")){
|
|
590
635
|
if (changedProps.Powered.value==false) {
|
|
591
636
|
if (plugin.started){ //only call stop() if plugin is started
|
|
592
|
-
|
|
637
|
+
plugin.setStatusText(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
|
|
593
638
|
await plugin.stop()
|
|
594
639
|
}
|
|
595
640
|
} else {
|
|
@@ -598,8 +643,8 @@ module.exports = function (app) {
|
|
|
598
643
|
}
|
|
599
644
|
})
|
|
600
645
|
if (!await adapter.isPowered()) {
|
|
601
|
-
|
|
602
|
-
|
|
646
|
+
plugin.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
647
|
+
plugin.setError(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
603
648
|
await plugin.stop()
|
|
604
649
|
return
|
|
605
650
|
}
|
|
@@ -615,19 +660,26 @@ module.exports = function (app) {
|
|
|
615
660
|
plugin.stopped=false
|
|
616
661
|
}
|
|
617
662
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
plugin.schema.properties.adapter.
|
|
663
|
+
try{
|
|
664
|
+
const activeAdapters = await bluetooth.activeAdapters()
|
|
665
|
+
if (activeAdapters.length==0){
|
|
666
|
+
plugin.setError("No active Bluetooth adapters found.")
|
|
667
|
+
}
|
|
668
|
+
plugin.schema.properties.adapter.enum=[]
|
|
669
|
+
plugin.schema.properties.adapter.enumNames=[]
|
|
670
|
+
for (a of activeAdapters){
|
|
671
|
+
plugin.schema.properties.adapter.enum.push(a.adapter)
|
|
672
|
+
plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
|
|
673
|
+
}}
|
|
674
|
+
catch(e){
|
|
675
|
+
plugin.setError(`Unable to get adapters: ${e.message}`)
|
|
624
676
|
}
|
|
625
677
|
|
|
626
678
|
await startScanner(options)
|
|
627
679
|
if (starts>0){
|
|
628
|
-
|
|
680
|
+
plugin.debug(`Plugin ${packageInfo.version} restarting...`);
|
|
629
681
|
} else {
|
|
630
|
-
|
|
682
|
+
plugin.debug(`Plugin ${packageInfo.version} started` )
|
|
631
683
|
|
|
632
684
|
}
|
|
633
685
|
starts++
|
|
@@ -635,7 +687,7 @@ module.exports = function (app) {
|
|
|
635
687
|
try{
|
|
636
688
|
await startScanner(options)
|
|
637
689
|
} catch (e){
|
|
638
|
-
|
|
690
|
+
plugin.setError(`Error starting scan: ${e.message}`)
|
|
639
691
|
}
|
|
640
692
|
if (!(deviceConfigs===undefined)){
|
|
641
693
|
const maxTimeout=Math.max(...deviceConfigs.map((dc)=>dc?.discoveryTimeout??options.discoveryTimeout))
|
|
@@ -674,7 +726,6 @@ module.exports = function (app) {
|
|
|
674
726
|
const dt = config?.discoveryTimeout??options.discoveryTimeout
|
|
675
727
|
const lc=sensor.elapsedTimeSinceLastContact()
|
|
676
728
|
if (lc > dt) {
|
|
677
|
-
//app.debug(`${sensor.getMacAddress()} not heard from in ${lc} seconds`)
|
|
678
729
|
channel.broadcast(getSensorInfo(sensor), "sensorchanged")
|
|
679
730
|
}
|
|
680
731
|
})
|
|
@@ -688,7 +739,7 @@ module.exports = function (app) {
|
|
|
688
739
|
options.discoveryInterval)
|
|
689
740
|
}
|
|
690
741
|
plugin.stop = async function () {
|
|
691
|
-
|
|
742
|
+
plugin.debug("Stopping plugin")
|
|
692
743
|
plugin.stopped=true
|
|
693
744
|
plugin.started=false
|
|
694
745
|
channel.broadcast({state:"stopped"},"pluginstate")
|
|
@@ -709,10 +760,10 @@ module.exports = function (app) {
|
|
|
709
760
|
for await (const sensorEntry of sensorMap.entries()) {
|
|
710
761
|
try{
|
|
711
762
|
await sensorEntry[1].stopListening()
|
|
712
|
-
|
|
763
|
+
plugin.debug(`No longer listening to ${sensorEntry[0]}`)
|
|
713
764
|
}
|
|
714
765
|
catch (e){
|
|
715
|
-
|
|
766
|
+
plugin.setError(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
|
|
716
767
|
}
|
|
717
768
|
}
|
|
718
769
|
}
|
|
@@ -723,12 +774,12 @@ module.exports = function (app) {
|
|
|
723
774
|
if( await adapter.isDiscovering())
|
|
724
775
|
try{
|
|
725
776
|
await adapter.stopDiscovery()
|
|
726
|
-
|
|
777
|
+
plugin.debug('Scan stopped')
|
|
727
778
|
} catch (e){
|
|
728
|
-
|
|
779
|
+
plugin.setError(`Error stopping scan: ${e.message}`)
|
|
729
780
|
}
|
|
730
781
|
}
|
|
731
|
-
|
|
782
|
+
plugin.debug('BT Sensors plugin stopped')
|
|
732
783
|
|
|
733
784
|
}
|
|
734
785
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.2.6-beta-
|
|
3
|
+
"version": "1.2.6-beta-4",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk - see https://www.npmjs.com/package/bt-sensors-plugin-sk#supported-sensors for a list of supported sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
package/plugin_defaults.json
CHANGED
|
@@ -134,10 +134,19 @@
|
|
|
134
134
|
"default": "electrical.batteries.{batteryID}.capacity.totalCharge"
|
|
135
135
|
},
|
|
136
136
|
"stateOfCharge":{
|
|
137
|
+
"description": "Battery's state of charge as ratio",
|
|
137
138
|
"unit":"ratio",
|
|
138
139
|
"default": "electrical.batteries.{batteryID}.capacity.stateOfCharge"
|
|
139
140
|
},
|
|
141
|
+
"stateOfHealth":{
|
|
142
|
+
"description": "Battery's state of health as ratio",
|
|
143
|
+
"unit":"ratio",
|
|
144
|
+
"default": "electrical.batteries.{batteryID}.capacity.stateOfHealth"
|
|
145
|
+
},
|
|
146
|
+
|
|
140
147
|
"timeRemaining":{
|
|
148
|
+
"description": "Time in seconds until battery reaches its discharge floor at current usage",
|
|
149
|
+
|
|
141
150
|
"unit":"s",
|
|
142
151
|
"default": "electrical.batteries.{batteryID}.capacity.timeRemaining"
|
|
143
152
|
|
package/public/847.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(self.webpackChunkbt_sensors_plugin_sk=self.webpackChunkbt_sensors_plugin_sk||[]).push([[847],{9337:()=>{},62995:(e,t,n)=>{"use strict";n.r(t),n.d(t,{BTConfig:()=>k,default:()=>C});var a=n(73490),s=n(74810),r=n(40334),o=n(86528),c=n.n(o),i=n(11363),l=n(97493),u=n(27606),m=n(82096),d=n(3768),g=n(71431),f=n(39676),h=n(47041),p=n(86038),E=n(95027),w=n(43540),y=n(38250),S=n(31008),b=n(20455),v=n(23399);const A=e=>console.log.bind(console,e);function k(e){const t=(0,m.A)((e=>({root:{"& > *":{margin:e.spacing(1)}}}))),[n,k]=(0,o.useState)({}),[C,D]=(0,o.useState)({}),[_,$]=(0,o.useState)({}),[N
|
|
1
|
+
(self.webpackChunkbt_sensors_plugin_sk=self.webpackChunkbt_sensors_plugin_sk||[]).push([[847],{9337:()=>{},62995:(e,t,n)=>{"use strict";n.r(t),n.d(t,{BTConfig:()=>k,default:()=>C});var a=n(73490),s=n(74810),r=n(40334),o=n(86528),c=n.n(o),i=n(11363),l=n(97493),u=n(27606),m=n(82096),d=n(3768),g=n(71431),f=n(39676),h=n(47041),p=n(86038),E=n(95027),w=n(43540),y=n(38250),S=n(31008),b=n(20455),v=n(23399);const A=e=>console.log.bind(console,e);function k(e){const t=(0,m.A)((e=>({root:{"& > *":{margin:e.spacing(1)}}}))),[n,k]=(0,o.useState)({}),[C,D]=(0,o.useState)({}),[_,$]=(0,o.useState)({}),[x,N]=(0,o.useState)({"ui:options":{label:!1},paths:{enableMarkdownInDescription:!0},title:{"ui:widget":"hidden"}}),[O,T]=(0,o.useState)(),[j,M]=(0,o.useState)(!1),[U,J]=(0,o.useState)(!0),[L,I]=(0,o.useState)(new Map),[P,K]=(0,o.useState)({progress:0,maxTimeout:100,deviceCount:0,totalDevices:0}),[R,B]=(0,o.useState)("unknown"),[G,W]=(0,o.useState)(),[F,H]=(0,o.useState)(!1),[z,Y]=(0,o.useState)(""),q=t();function Q(e,t){const n=new Headers;return n.append("Content-Type","application/json"),fetch(`/plugins/bt-sensors-plugin-sk/${e}`,{credentials:"include",method:"POST",body:JSON.stringify(t),headers:n})}async function V(e,t={}){let n;try{const a=Object.keys(t).length?"?"+new URLSearchParams(t).toString():"";n=await fetch(`/plugins/bt-sensors-plugin-sk/${e}${a}`,{credentials:"include",method:"GET"})}catch(e){n={status:500,statusText:e.toString()}}return n}function X(e){return Object.keys(e.configCopy).length>0}function Z(e){const t=X(e);return c().createElement(b.A,{action:!0,onClick:()=>{e.config.mac_address=e.info.mac,$(e.schema),T(e.config)}},c().createElement("div",{class:"d-flex justify-content-between align-items-center",style:t?{fontWeight:"normal"}:{fontStyle:"italic"}},`${e._changesMade?"*":""}${e.info.name} MAC: ${e.info.mac} RSSI: ${n=e.info.RSSI,null==n?NaN:n}`,c().createElement("div",{class:"d-flex justify-content-between "},e.info.state,c().createElement("div",{class:"d-flex justify-content-between "},function(e){return null==e.info.lastContactDelta||e.info.lastContactDelta>e.config.discoveryTimeout?c().createElement(d.A,null):e.info.signalStrength>80?c().createElement(g.A,null):e.info.signalStrength>60?c().createElement(f.A,null):e.info.signalStrength>40?c().createElement(h.A,null):e.info.signalStrength>20?c().createElement(p.A,null):c().createElement(E.A,null)}(e)))));var n}function ee(e){window.open(e,"_blank","noreferrer")}return(0,o.useEffect)((()=>{let e=null;return V("getPluginState").then((async t=>{if(404==t.status)throw B("unknown"),new Error("unable to get plugin state");const n=await t.json();e=new EventSource("/plugins/bt-sensors-plugin-sk/sse",{withCredentials:!0}),e.addEventListener("newsensor",(e=>{!function(e){let t=JSON.parse(e.data);console.log(`New sensor: ${t.info.mac}`),I((e=>(e.set(t.info.mac,t),new Map(e))))}(e)})),e.addEventListener("sensorchanged",(e=>{!function(e){console.log("sensorchanged");const t=JSON.parse(e.data);console.log(t),I((e=>{const n=e.get(t.mac);return n&&Object.assign(n.info,t),new Map(e)}))}(e)})),e.addEventListener("progress",(e=>{const t=JSON.parse(e.data);K(t)})),e.addEventListener("pluginstate",(e=>{const t=JSON.parse(e.data);B(t.state)})),B(n.state),(async()=>{const e=await async function(){const e=await V("getSensors");if(200!=e.status)throw new Error(`Unable get sensor data: ${e.statusText} (${e.status}) `);return await e.json()}();I(new Map(e.map((e=>[e.info.mac,e]))))})()})).catch((e=>{W(e.message)})),()=>{console.log("Closing connection to SSE"),e.close()}}),[]),(0,o.useEffect)((()=>{if(!j)return;if(!O||!L)return;const e=L.get(O.mac_address);e&&_&&O&&Object.hasOwn(O,"params")&&"UNKNOWN"==e.info.class&&O.params.sensorClass&&"UNKNOWN"!=O.params.sensorClass&&(J(!1),Y(`Please wait. Fetching schema for ${O.params.sensorClass}...`),async function(e,t){const n=await V("getSensorInfo",{mac_address:e,class:t});if(200!=n.status)throw new Error(`Unable get sensor info: ${n.statusText} (${n.status}) `);return await n.json()}(O.mac_address,O.params.sensorClass).then((e=>{$(e.schema)})).catch((e=>{alert(e.message)})).finally((()=>{Y(""),J(!0),M(!1)})))}),[j]),(0,o.useEffect)((()=>{H(""!=z)}),[z]),(0,o.useEffect)((()=>{"started"==R?(async function(){const e=await V("getBaseData");if(200!=e.status)throw new Error(`Unable to get base data: ${e.statusText} (${e.status}) `);const t=await e.json();return t.schema.htmlDescription=c().createElement("div",null,(0,r.Ay)(t.schema.htmlDescription),c().createElement("p",null)),t}().then((e=>{k(e.schema),D(e.data)})).catch((e=>{W(e.message)})),async function(){const e=await V("getProgress");if(200!=e.status)throw new Error(`Unable to get progress: ${e.statusText} (${e.status}) `);return await e.json()}().then((e=>{K(e)})).catch((e=>{W(e.message)}))):(k({}),D({}))}),[R]),"stopped"==R||"unknown"==R?c().createElement("h3",null,"Enable plugin to see configuration"):c().createElement("div",null,c().createElement(i.A,{anchorOrigin:{horizontal:"center",vertical:"bottom"},onClose:()=>H(!1),open:F,message:z,key:"snackbar"}),c().createElement("div",{className:q.root},c().createElement(l.A,{variant:"contained",onClick:()=>{ee("https://github.com/naugehyde/bt-sensors-plugin-sk/tree/1.2.0-beta#configuration")}},"Documentation"),c().createElement(l.A,{variant:"contained",onClick:()=>{ee("https://github.com/naugehyde/bt-sensors-plugin-sk/issues/new/choose")}},"Report Issue"),c().createElement(l.A,{variant:"contained",onClick:()=>{ee("https://discord.com/channels/1170433917761892493/1295425963466952725")}},"Discord Thread"),c().createElement("p",null),c().createElement("p",null)),c().createElement("hr",{style:{width:"100%",height:"1px",color:"gray","background-color":"gray","text-align":"left","margin-left":0}}),G?c().createElement("h2",{style:{color:"red"}},G):"",c().createElement(a.Ay,{schema:n,validator:s.Ay,uiSchema:{"ui:field":"LayoutGridField","ui:layoutGrid":{"ui:row":[{"ui:row":{className:"row",children:[{"ui:columns":{className:"col-xs-4",children:["adapter","transport","duplicateData","discoveryTimeout","discoveryInterval"]}}]}}]}},onChange:e=>D(e.formData),onSubmit:({formData:e},t)=>{var n;n=e,I(new Map),Q("updateBaseData",n).then((e=>{200!=e.status&&W(`Unable to update base data: ${e.statusText} (${e.status})`)})),$({})},onError:A("errors"),formData:C}),c().createElement("hr",{style:{width:"100%",height:"1px",color:"gray","background-color":"gray","text-align":"left","margin-left":0}}),c().createElement("p",null),c().createElement("p",null),P.deviceCount<P.totalDevices?c().createElement(v.A,{max:P.maxTimeout,now:P.progress}):"",c().createElement("p",null),c().createElement(y.A,{defaultActiveKey:"_configured",id:"domain-tabs",className:"mb-3"},function(){const e=[...new Set(L.entries().map((e=>e[1].info.domain)))].sort(),t=Array.from(L.entries()).filter((e=>X(e[1])));let n={};return n._configured=0==t.length?"Select a device from its domain tab (Electrical etc.) and configure it.":t.map((e=>Z(L.get(e[0])))),e.forEach((e=>{var t;n[e]=(t=e,Array.from(L.entries()).filter((e=>e[1].info.domain===t))).map((e=>Z(L.get(e[0]))))})),Object.keys(n).map((e=>function(e,t){let n=e.slice("_"===e.charAt(0)?1:0);return c().createElement(S.A,{eventKey:e,title:`${n.charAt(0).toUpperCase()}${n.slice(1)}${"string"==typeof t?"":" ("+t.length+")"}`},c().createElement(w.A,{style:{maxHeight:"300px",overflowY:"auto"}},t))}(e,n[e])))}()),c().createElement("div",{style:{paddingLeft:10,paddingTop:10,display:0==Object.keys(_).length?"none":""}},c().createElement(u.A,{container:!0,direction:"column",style:{spacing:5}},c().createElement(u.A,{item:!0},c().createElement("h2",null,_?.title),c().createElement("p",null)),c().createElement(u.A,{item:!0},(0,r.Ay)(_?.htmlDescription))),c().createElement("fieldset",{disabled:!U},c().createElement(a.Ay,{schema:_,validator:s.Ay,uiSchema:x,onChange:(e,t)=>{const n=L.get(e.formData.mac_address);n&&(n._changesMade=!0,n.config=e.formData,T(e.formData)),"root_params_sensorClass"==t&&M(!0)},onSubmit:({formData:e},t)=>{var n;Q("updateSensorData",n=e).then((e=>{if(200!=e.status)throw new Error(e.statusText);I((e=>(e.delete(n.mac_address),new Map(e)))),$({})})),alert("Changes saved")},onError:A("errors"),formData:O},c().createElement("div",{className:q.root},c().createElement(l.A,{type:"submit",color:"primary",variant:"contained"},"Save"),c().createElement(l.A,{variant:"contained",onClick:()=>{var e;e=O.mac_address,L.get(e)._changesMade=!1,L.get(e).config=JSON.parse(JSON.stringify(L.get(e).configCopy)),T(L.get(e).config)}},"Undo"),c().createElement(l.A,{variant:"contained",color:"secondary",onClick:e=>function(e){const t=L.get(e);(!X(t)||window.confirm(`Delete configuration for ${t.info.name}?`))&&function(e){try{Q("removeSensorData",{mac_address:e}).then((e=>{if(200!=e.status)throw new Error(e.statusText)})),I((t=>(t.delete(e),new Map(t)))),$({})}catch{}}(e)}(O.mac_address)},"Delete"))))))}const C=k}}]);
|
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
const BTSensor = require("../BTSensor");
|
|
2
|
-
|
|
1
|
+
const BTSensor = require("../BTSensor.js");
|
|
2
|
+
let FakeDevice,FakeGATTService,FakeGATTCharacteristic;
|
|
3
|
+
|
|
4
|
+
// Dynamically import FakeBTDevice.js for node<= 20
|
|
5
|
+
import('../development/FakeBTDevice.js')
|
|
6
|
+
.then(module => {
|
|
7
|
+
FakeDevice = module.FakeDevice;
|
|
8
|
+
FakeGATTService= module.FakeGATTService
|
|
9
|
+
FakeGATTCharacteristic=module.FakeGATTCharacteristic
|
|
10
|
+
|
|
11
|
+
})
|
|
12
|
+
.catch(error => {
|
|
13
|
+
console.error('Error loading FakeBTDevice:', error);
|
|
14
|
+
});
|
|
15
|
+
//({FakeDevice,FakeGATTService,FakeGATTCharacteristic }=require( "../development/FakeBTDevice.js"))
|
|
3
16
|
//a1000000650000000000180103440018004800640531ff8000002710000100010000000000000000000100020000ffff00000000000000000000000000000000000000000000000000000000000000000000418b
|
|
4
17
|
//a20000006500000000001801035600040cfb0cfd0cfb0cfaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000300cd00c000befc18fc18fc18fc18fc18fc18976a
|
|
5
18
|
|
|
@@ -29,7 +42,7 @@ function waitForVariable(obj, variableName, interval = 100) {
|
|
|
29
42
|
});
|
|
30
43
|
}
|
|
31
44
|
|
|
32
|
-
class
|
|
45
|
+
class EcoWorthyBW02 extends BTSensor {
|
|
33
46
|
static Domain = BTSensor.SensorDomains.electrical
|
|
34
47
|
|
|
35
48
|
static TX_RX_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"
|
|
@@ -45,7 +58,7 @@ class EcoWorthy extends BTSensor {
|
|
|
45
58
|
)]
|
|
46
59
|
)]
|
|
47
60
|
)
|
|
48
|
-
const obj = new
|
|
61
|
+
const obj = new EcoWorthyBW02(device)
|
|
49
62
|
obj.currentProperties={Name:"Fake EcoWorthy", Address:"<mac>"}
|
|
50
63
|
obj.debug=(m)=>{console.log(m)}
|
|
51
64
|
obj.deviceConnect=()=>{}
|
|
@@ -59,9 +72,8 @@ class EcoWorthy extends BTSensor {
|
|
|
59
72
|
static identify(device){
|
|
60
73
|
return null
|
|
61
74
|
}
|
|
62
|
-
static ImageFile = "
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
static ImageFile = "EcoWorthyBW02.webp"
|
|
76
|
+
|
|
65
77
|
async initSchema(){
|
|
66
78
|
super.initSchema()
|
|
67
79
|
this.addDefaultParam("batteryID")
|
|
@@ -157,4 +169,4 @@ async stopListening(){
|
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
}
|
|
160
|
-
module.exports =
|
|
172
|
+
module.exports = EcoWorthyBW02;
|
package/sensor_classes/JBDBMS.js
CHANGED