bt-sensors-plugin-sk 1.2.6-beta-3 → 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 +5 -0
- package/classLoader.js +9 -2
- package/development/FakeBTDevice.js +4 -1
- package/index.js +107 -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(){
|
|
@@ -96,12 +100,46 @@ module.exports = function (app) {
|
|
|
96
100
|
var deviceConfigs=[]
|
|
97
101
|
var starts=0
|
|
98
102
|
|
|
99
|
-
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
|
+
}
|
|
100
138
|
plugin.id = 'bt-sensors-plugin-sk';
|
|
101
139
|
plugin.name = 'BT Sensors plugin';
|
|
102
140
|
plugin.description = 'Plugin to communicate with and update paths to BLE Sensors in Signalk';
|
|
103
|
-
|
|
104
|
-
|
|
141
|
+
plugin.log = []
|
|
142
|
+
plugin.errorLog = []
|
|
105
143
|
|
|
106
144
|
plugin.schema = {
|
|
107
145
|
type: "object",
|
|
@@ -134,6 +172,8 @@ module.exports = function (app) {
|
|
|
134
172
|
var discoveryIntervalID, progressID, progressTimeoutID, deviceHealthID
|
|
135
173
|
var adapter
|
|
136
174
|
const channel = createChannel()
|
|
175
|
+
|
|
176
|
+
plugin.debug(`Loading plugin ${packageInfo.version}`)
|
|
137
177
|
|
|
138
178
|
const sensorMap=new Map()
|
|
139
179
|
|
|
@@ -169,7 +209,7 @@ module.exports = function (app) {
|
|
|
169
209
|
|
|
170
210
|
_tempSensor = new _class ( _sensor.device )
|
|
171
211
|
_tempSensor.currentProperties=_sensor.currentProperties
|
|
172
|
-
_tempSensor.
|
|
212
|
+
_tempSensor._app = app
|
|
173
213
|
_tempSensor._adapter=adapter
|
|
174
214
|
await _tempSensor.init()
|
|
175
215
|
const _json = sensorToJSON(_tempSensor)
|
|
@@ -188,7 +228,6 @@ module.exports = function (app) {
|
|
|
188
228
|
})
|
|
189
229
|
|
|
190
230
|
router.post('/updateSensorData', async (req, res) => {
|
|
191
|
-
app.debug(req.body)
|
|
192
231
|
const sensor = sensorMap.get(req.body.mac_address)
|
|
193
232
|
sensor.prepareConfig(req.body)
|
|
194
233
|
const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
|
|
@@ -206,7 +245,6 @@ module.exports = function (app) {
|
|
|
206
245
|
deviceConfigs=options.peripherals
|
|
207
246
|
app.savePluginOptions(
|
|
208
247
|
options, async () => {
|
|
209
|
-
app.debug('Plugin options saved')
|
|
210
248
|
res.status(200).json({message: "Sensor updated"})
|
|
211
249
|
if (sensor) {
|
|
212
250
|
removeSensorFromList(sensor)
|
|
@@ -219,7 +257,6 @@ module.exports = function (app) {
|
|
|
219
257
|
|
|
220
258
|
});
|
|
221
259
|
router.post('/removeSensorData', async (req, res) => {
|
|
222
|
-
app.debug(req.body)
|
|
223
260
|
const sensor = sensorMap.get(req.body.mac_address)
|
|
224
261
|
if (!sensor) {
|
|
225
262
|
res.status(404).json({message: "Sensor not found"})
|
|
@@ -237,7 +274,6 @@ module.exports = function (app) {
|
|
|
237
274
|
sensorMap.delete(req.body.mac_address)
|
|
238
275
|
app.savePluginOptions(
|
|
239
276
|
options, () => {
|
|
240
|
-
app.debug('Plugin options saved')
|
|
241
277
|
res.status(200).json({message: "Sensor updated"})
|
|
242
278
|
channel.broadcast({},"resetSensors")
|
|
243
279
|
}
|
|
@@ -247,11 +283,9 @@ module.exports = function (app) {
|
|
|
247
283
|
|
|
248
284
|
router.post('/updateBaseData', async (req, res) => {
|
|
249
285
|
|
|
250
|
-
app.debug(req.body)
|
|
251
286
|
Object.assign(options,req.body)
|
|
252
287
|
app.savePluginOptions(
|
|
253
288
|
options, () => {
|
|
254
|
-
app.debug('Plugin options saved')
|
|
255
289
|
res.status(200).json({message: "Plugin updated"})
|
|
256
290
|
channel.broadcast({},"pluginRestarted")
|
|
257
291
|
restartPlugin(options)
|
|
@@ -277,13 +311,11 @@ module.exports = function (app) {
|
|
|
277
311
|
|
|
278
312
|
|
|
279
313
|
router.get('/getSensors', (req, res) => {
|
|
280
|
-
app.debug("Sending sensors")
|
|
281
314
|
const t = sensorsToJSON()
|
|
282
315
|
res.status(200).json(t)
|
|
283
316
|
});
|
|
284
317
|
|
|
285
318
|
router.get('/getProgress', (req, res) => {
|
|
286
|
-
app.debug("Sending progress")
|
|
287
319
|
let deviceCount = deviceConfigs.filter((dc)=>dc.active).length
|
|
288
320
|
const json = {"progress":foundConfiguredDevices/deviceCount, "maxTimeout": 1,
|
|
289
321
|
"deviceCount":foundConfiguredDevices,
|
|
@@ -302,7 +334,6 @@ module.exports = function (app) {
|
|
|
302
334
|
const session = await createSession(req,res)
|
|
303
335
|
channel.register(session)
|
|
304
336
|
req.on("close", ()=>{
|
|
305
|
-
app.debug("deregistering session")
|
|
306
337
|
channel.deregister(session)
|
|
307
338
|
})
|
|
308
339
|
});
|
|
@@ -318,14 +349,18 @@ module.exports = function (app) {
|
|
|
318
349
|
|
|
319
350
|
function getSensorInfo(sensor){
|
|
320
351
|
|
|
321
|
-
|
|
352
|
+
const s = sensor.getState()
|
|
322
353
|
return { mac: sensor.getMacAddress(),
|
|
323
354
|
name: sensor.getName(),
|
|
324
355
|
class: sensor.constructor.name,
|
|
325
356
|
domain: sensor.getDomain().name,
|
|
357
|
+
state: sensor.getState(),
|
|
358
|
+
errorLog: sensor.getErrorLog(),
|
|
359
|
+
debugLog: sensor.getDebugLog(),
|
|
326
360
|
RSSI: sensor.getRSSI(),
|
|
327
361
|
signalStrength: sensor.getSignalStrength(),
|
|
328
|
-
|
|
362
|
+
connected: sensor.isConnected(),
|
|
363
|
+
lastContactDelta: sensor.elapsedTimeSinceLastContact()
|
|
329
364
|
}
|
|
330
365
|
}
|
|
331
366
|
|
|
@@ -346,14 +381,14 @@ module.exports = function (app) {
|
|
|
346
381
|
|
|
347
382
|
const transport = options?.transport??"le"
|
|
348
383
|
const duplicateData = options?.duplicateData??false
|
|
349
|
-
|
|
384
|
+
plugin.debug("Starting scan...");
|
|
350
385
|
//Use adapter.helper directly to get around Adapter::startDiscovery()
|
|
351
386
|
//filter options which can cause issues with Device::Connect()
|
|
352
387
|
//turning off Discovery
|
|
353
388
|
//try {await adapter.startDiscovery()}
|
|
354
389
|
try{
|
|
355
390
|
if (transport) {
|
|
356
|
-
|
|
391
|
+
plugin.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
|
|
357
392
|
await adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
358
393
|
Transport: new Variant('s', transport),
|
|
359
394
|
DuplicateData: new Variant('b', duplicateData)
|
|
@@ -362,7 +397,7 @@ module.exports = function (app) {
|
|
|
362
397
|
await adapter.helper.callMethod('StartDiscovery')
|
|
363
398
|
}
|
|
364
399
|
catch (error){
|
|
365
|
-
|
|
400
|
+
plugin.debug(error)
|
|
366
401
|
}
|
|
367
402
|
|
|
368
403
|
}
|
|
@@ -376,8 +411,6 @@ module.exports = function (app) {
|
|
|
376
411
|
}
|
|
377
412
|
|
|
378
413
|
function addSensorToList(sensor){
|
|
379
|
-
app.debug(`adding sensor to list ${sensor.getMacAddress()}`)
|
|
380
|
-
|
|
381
414
|
sensorMap.set(sensor.getMacAddress(),sensor)
|
|
382
415
|
channel.broadcast(sensorToJSON(sensor),"newsensor");
|
|
383
416
|
}
|
|
@@ -389,7 +422,6 @@ module.exports = function (app) {
|
|
|
389
422
|
return new Promise( ( resolve, reject )=>{
|
|
390
423
|
var s
|
|
391
424
|
const startNumber=starts
|
|
392
|
-
//app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
|
|
393
425
|
adapter.waitDevice(config.mac_address,(config?.discoveryTimeout??30)*1000)
|
|
394
426
|
.then(async (device)=> {
|
|
395
427
|
if (startNumber != starts ) {
|
|
@@ -402,13 +434,11 @@ module.exports = function (app) {
|
|
|
402
434
|
if (s instanceof BLACKLISTED)
|
|
403
435
|
reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
|
|
404
436
|
else{
|
|
405
|
-
//app.debug(`Adding sensor to list ${config.mac_address}`)
|
|
406
437
|
|
|
407
438
|
addSensorToList(s)
|
|
408
439
|
s._lastRSSI=-1*Infinity
|
|
409
440
|
s.on("RSSI",(()=>{
|
|
410
441
|
if (Date.now()-s._lastRSSI > 30000) { //only update RSSI on client every 30 seconds
|
|
411
|
-
//app.debug(`Updating ${s.getMacAddress()} RSSI after ${Date.now()-s._lastRSSI} ms`)
|
|
412
442
|
|
|
413
443
|
s._lastRSSI=Date.now()
|
|
414
444
|
|
|
@@ -434,13 +464,29 @@ module.exports = function (app) {
|
|
|
434
464
|
removeSensorFromList(s)
|
|
435
465
|
addSensorToList(s)
|
|
436
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
|
+
})
|
|
437
476
|
addSensorToList(s)
|
|
438
477
|
resolve(s)
|
|
439
478
|
}
|
|
440
479
|
}
|
|
441
480
|
if (startNumber == starts ) {
|
|
442
|
-
|
|
443
|
-
|
|
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
|
+
|
|
444
490
|
reject( e?.message??e )
|
|
445
491
|
}
|
|
446
492
|
})})
|
|
@@ -476,20 +522,17 @@ module.exports = function (app) {
|
|
|
476
522
|
|
|
477
523
|
const sensor = new c(device, config?.params, config?.gattParams)
|
|
478
524
|
sensor._paths=config.paths //this might be a good candidate for refactoring
|
|
479
|
-
sensor.
|
|
480
|
-
sensor.setPluginError=app.setPluginError
|
|
481
|
-
sensor.app=app
|
|
482
|
-
if (!adapter) debugger
|
|
525
|
+
sensor._app=app
|
|
483
526
|
sensor._adapter=adapter //HACK!
|
|
484
527
|
await sensor.init()
|
|
485
528
|
return sensor
|
|
486
529
|
}
|
|
487
530
|
catch(error){
|
|
488
531
|
const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
|
|
489
|
-
|
|
490
|
-
|
|
532
|
+
plugin.debug(msg)
|
|
533
|
+
plugin.debug(error)
|
|
491
534
|
if (config.active)
|
|
492
|
-
|
|
535
|
+
plugin.setError(msg)
|
|
493
536
|
return null
|
|
494
537
|
}
|
|
495
538
|
|
|
@@ -497,7 +540,7 @@ module.exports = function (app) {
|
|
|
497
540
|
|
|
498
541
|
function initConfiguredDevice(deviceConfig){
|
|
499
542
|
const startNumber=starts
|
|
500
|
-
|
|
543
|
+
plugin.setStatusText(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
|
|
501
544
|
if (!deviceConfig.discoveryTimeout)
|
|
502
545
|
deviceConfig.discoveryTimeout = options.discoveryTimeout
|
|
503
546
|
createSensor(adapter, deviceConfig).then((sensor)=>{
|
|
@@ -505,7 +548,7 @@ module.exports = function (app) {
|
|
|
505
548
|
return
|
|
506
549
|
}
|
|
507
550
|
if (deviceConfig.active && !(sensor.device instanceof OutOfRangeDevice) ) {
|
|
508
|
-
|
|
551
|
+
plugin.setStatusText(`Listening to ${++foundConfiguredDevices} sensors.`);
|
|
509
552
|
sensor.activate(deviceConfig, plugin)
|
|
510
553
|
}
|
|
511
554
|
|
|
@@ -517,10 +560,10 @@ module.exports = function (app) {
|
|
|
517
560
|
return
|
|
518
561
|
}
|
|
519
562
|
const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
|
|
520
|
-
|
|
521
|
-
|
|
563
|
+
plugin.debug(msg)
|
|
564
|
+
|
|
522
565
|
if (deviceConfig.active)
|
|
523
|
-
|
|
566
|
+
plugin.setError(msg)
|
|
524
567
|
const sensor=new MissingSensor(deviceConfig)
|
|
525
568
|
++foundConfiguredDevices
|
|
526
569
|
|
|
@@ -530,7 +573,7 @@ module.exports = function (app) {
|
|
|
530
573
|
}
|
|
531
574
|
function findDevices (discoveryTimeout) {
|
|
532
575
|
const startNumber = starts
|
|
533
|
-
|
|
576
|
+
plugin.setStatusText("Scanning for new Bluetooth devices...");
|
|
534
577
|
|
|
535
578
|
adapter.devices().then( (macs)=>{
|
|
536
579
|
if (startNumber != starts ) {
|
|
@@ -580,7 +623,7 @@ module.exports = function (app) {
|
|
|
580
623
|
//Connect to adapter
|
|
581
624
|
|
|
582
625
|
if (!adapter){
|
|
583
|
-
|
|
626
|
+
plugin.debug(`Connecting to bluetooth adapter ${adapterID}`);
|
|
584
627
|
|
|
585
628
|
adapter = await bluetooth.getAdapter(adapterID)
|
|
586
629
|
|
|
@@ -588,11 +631,10 @@ module.exports = function (app) {
|
|
|
588
631
|
|
|
589
632
|
await adapter.helper._prepare()
|
|
590
633
|
adapter.helper._propsProxy.on('PropertiesChanged', async (iface,changedProps,invalidated) => {
|
|
591
|
-
app.debug(changedProps)
|
|
592
634
|
if (Object.hasOwn(changedProps,"Powered")){
|
|
593
635
|
if (changedProps.Powered.value==false) {
|
|
594
636
|
if (plugin.started){ //only call stop() if plugin is started
|
|
595
|
-
|
|
637
|
+
plugin.setStatusText(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
|
|
596
638
|
await plugin.stop()
|
|
597
639
|
}
|
|
598
640
|
} else {
|
|
@@ -601,8 +643,8 @@ module.exports = function (app) {
|
|
|
601
643
|
}
|
|
602
644
|
})
|
|
603
645
|
if (!await adapter.isPowered()) {
|
|
604
|
-
|
|
605
|
-
|
|
646
|
+
plugin.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
647
|
+
plugin.setError(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
606
648
|
await plugin.stop()
|
|
607
649
|
return
|
|
608
650
|
}
|
|
@@ -618,19 +660,26 @@ module.exports = function (app) {
|
|
|
618
660
|
plugin.stopped=false
|
|
619
661
|
}
|
|
620
662
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
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}`)
|
|
627
676
|
}
|
|
628
677
|
|
|
629
678
|
await startScanner(options)
|
|
630
679
|
if (starts>0){
|
|
631
|
-
|
|
680
|
+
plugin.debug(`Plugin ${packageInfo.version} restarting...`);
|
|
632
681
|
} else {
|
|
633
|
-
|
|
682
|
+
plugin.debug(`Plugin ${packageInfo.version} started` )
|
|
634
683
|
|
|
635
684
|
}
|
|
636
685
|
starts++
|
|
@@ -638,7 +687,7 @@ module.exports = function (app) {
|
|
|
638
687
|
try{
|
|
639
688
|
await startScanner(options)
|
|
640
689
|
} catch (e){
|
|
641
|
-
|
|
690
|
+
plugin.setError(`Error starting scan: ${e.message}`)
|
|
642
691
|
}
|
|
643
692
|
if (!(deviceConfigs===undefined)){
|
|
644
693
|
const maxTimeout=Math.max(...deviceConfigs.map((dc)=>dc?.discoveryTimeout??options.discoveryTimeout))
|
|
@@ -677,7 +726,6 @@ module.exports = function (app) {
|
|
|
677
726
|
const dt = config?.discoveryTimeout??options.discoveryTimeout
|
|
678
727
|
const lc=sensor.elapsedTimeSinceLastContact()
|
|
679
728
|
if (lc > dt) {
|
|
680
|
-
//app.debug(`${sensor.getMacAddress()} not heard from in ${lc} seconds`)
|
|
681
729
|
channel.broadcast(getSensorInfo(sensor), "sensorchanged")
|
|
682
730
|
}
|
|
683
731
|
})
|
|
@@ -691,7 +739,7 @@ module.exports = function (app) {
|
|
|
691
739
|
options.discoveryInterval)
|
|
692
740
|
}
|
|
693
741
|
plugin.stop = async function () {
|
|
694
|
-
|
|
742
|
+
plugin.debug("Stopping plugin")
|
|
695
743
|
plugin.stopped=true
|
|
696
744
|
plugin.started=false
|
|
697
745
|
channel.broadcast({state:"stopped"},"pluginstate")
|
|
@@ -712,10 +760,10 @@ module.exports = function (app) {
|
|
|
712
760
|
for await (const sensorEntry of sensorMap.entries()) {
|
|
713
761
|
try{
|
|
714
762
|
await sensorEntry[1].stopListening()
|
|
715
|
-
|
|
763
|
+
plugin.debug(`No longer listening to ${sensorEntry[0]}`)
|
|
716
764
|
}
|
|
717
765
|
catch (e){
|
|
718
|
-
|
|
766
|
+
plugin.setError(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
|
|
719
767
|
}
|
|
720
768
|
}
|
|
721
769
|
}
|
|
@@ -726,12 +774,12 @@ module.exports = function (app) {
|
|
|
726
774
|
if( await adapter.isDiscovering())
|
|
727
775
|
try{
|
|
728
776
|
await adapter.stopDiscovery()
|
|
729
|
-
|
|
777
|
+
plugin.debug('Scan stopped')
|
|
730
778
|
} catch (e){
|
|
731
|
-
|
|
779
|
+
plugin.setError(`Error stopping scan: ${e.message}`)
|
|
732
780
|
}
|
|
733
781
|
}
|
|
734
|
-
|
|
782
|
+
plugin.debug('BT Sensors plugin stopped')
|
|
735
783
|
|
|
736
784
|
}
|
|
737
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