bt-sensors-plugin-sk 1.2.6-beta-3 → 1.2.6-beta-5

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/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,11 +77,17 @@ 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(){
82
86
  return false
83
87
  }
88
+ isConnected(){return false}
89
+
90
+
84
91
  elapsedTimeSinceLastContact(){
85
92
  return NaN
86
93
  }
@@ -90,18 +97,58 @@ class MissingSensor {
90
97
  prepareConfig(){
91
98
 
92
99
  }
100
+ getErrorLog(){
101
+ return []
102
+ }
103
+ getDebugLog(){
104
+ return []
105
+ }
93
106
 
94
107
  }
95
108
  module.exports = function (app) {
96
109
  var deviceConfigs=[]
97
110
  var starts=0
98
111
 
99
- var plugin = {};
112
+ var plugin = {
113
+ debug (message) {
114
+ app.debug(message)
115
+ const logEntry = {
116
+ timestamp: Date.now(),
117
+ message: message
118
+ }
119
+ this.log.push(logEntry)
120
+ if (channel)
121
+ channel.broadcast(logEntry,"pluginDebug")
122
+ },
123
+ setError( error ){
124
+ app.setPluginError(error)
125
+ app.debug(error)
126
+ const logEntry = {
127
+ timestamp: Date.now(),
128
+ message: error
129
+ }
130
+ this.errorLog.push(logEntry)
131
+ if (channel)
132
+ channel.broadcast( logEntry, "pluginError" )
133
+ },
134
+ setStatusText( status ){
135
+ app.setPluginStatus(status)
136
+ const logEntry = {timestamp: Date.now(), message: status}
137
+ this.log.push(logEntry)
138
+ if (channel)
139
+ channel.broadcast( logEntry, "pluginStatusText" )
140
+ },
141
+ setFatalError( error ){
142
+ this.setError(`FATAL ERROR: ${error}`)
143
+ this.stop()
144
+
145
+ }
146
+ }
100
147
  plugin.id = 'bt-sensors-plugin-sk';
101
148
  plugin.name = 'BT Sensors plugin';
102
149
  plugin.description = 'Plugin to communicate with and update paths to BLE Sensors in Signalk';
103
-
104
- app.debug(`Loading plugin ${packageInfo.version}`)
150
+ plugin.log = []
151
+ plugin.errorLog = []
105
152
 
106
153
  plugin.schema = {
107
154
  type: "object",
@@ -134,6 +181,8 @@ module.exports = function (app) {
134
181
  var discoveryIntervalID, progressID, progressTimeoutID, deviceHealthID
135
182
  var adapter
136
183
  const channel = createChannel()
184
+
185
+ plugin.debug(`Loading plugin ${packageInfo.version}`)
137
186
 
138
187
  const sensorMap=new Map()
139
188
 
@@ -169,7 +218,7 @@ module.exports = function (app) {
169
218
 
170
219
  _tempSensor = new _class ( _sensor.device )
171
220
  _tempSensor.currentProperties=_sensor.currentProperties
172
- _tempSensor.debug = app.debug
221
+ _tempSensor._app = app
173
222
  _tempSensor._adapter=adapter
174
223
  await _tempSensor.init()
175
224
  const _json = sensorToJSON(_tempSensor)
@@ -188,7 +237,6 @@ module.exports = function (app) {
188
237
  })
189
238
 
190
239
  router.post('/updateSensorData', async (req, res) => {
191
- app.debug(req.body)
192
240
  const sensor = sensorMap.get(req.body.mac_address)
193
241
  sensor.prepareConfig(req.body)
194
242
  const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
@@ -206,7 +254,6 @@ module.exports = function (app) {
206
254
  deviceConfigs=options.peripherals
207
255
  app.savePluginOptions(
208
256
  options, async () => {
209
- app.debug('Plugin options saved')
210
257
  res.status(200).json({message: "Sensor updated"})
211
258
  if (sensor) {
212
259
  removeSensorFromList(sensor)
@@ -219,7 +266,6 @@ module.exports = function (app) {
219
266
 
220
267
  });
221
268
  router.post('/removeSensorData', async (req, res) => {
222
- app.debug(req.body)
223
269
  const sensor = sensorMap.get(req.body.mac_address)
224
270
  if (!sensor) {
225
271
  res.status(404).json({message: "Sensor not found"})
@@ -237,7 +283,6 @@ module.exports = function (app) {
237
283
  sensorMap.delete(req.body.mac_address)
238
284
  app.savePluginOptions(
239
285
  options, () => {
240
- app.debug('Plugin options saved')
241
286
  res.status(200).json({message: "Sensor updated"})
242
287
  channel.broadcast({},"resetSensors")
243
288
  }
@@ -247,11 +292,9 @@ module.exports = function (app) {
247
292
 
248
293
  router.post('/updateBaseData', async (req, res) => {
249
294
 
250
- app.debug(req.body)
251
295
  Object.assign(options,req.body)
252
296
  app.savePluginOptions(
253
297
  options, () => {
254
- app.debug('Plugin options saved')
255
298
  res.status(200).json({message: "Plugin updated"})
256
299
  channel.broadcast({},"pluginRestarted")
257
300
  restartPlugin(options)
@@ -277,13 +320,11 @@ module.exports = function (app) {
277
320
 
278
321
 
279
322
  router.get('/getSensors', (req, res) => {
280
- app.debug("Sending sensors")
281
323
  const t = sensorsToJSON()
282
324
  res.status(200).json(t)
283
325
  });
284
326
 
285
327
  router.get('/getProgress', (req, res) => {
286
- app.debug("Sending progress")
287
328
  let deviceCount = deviceConfigs.filter((dc)=>dc.active).length
288
329
  const json = {"progress":foundConfiguredDevices/deviceCount, "maxTimeout": 1,
289
330
  "deviceCount":foundConfiguredDevices,
@@ -302,7 +343,6 @@ module.exports = function (app) {
302
343
  const session = await createSession(req,res)
303
344
  channel.register(session)
304
345
  req.on("close", ()=>{
305
- app.debug("deregistering session")
306
346
  channel.deregister(session)
307
347
  })
308
348
  });
@@ -318,14 +358,18 @@ module.exports = function (app) {
318
358
 
319
359
  function getSensorInfo(sensor){
320
360
 
321
-
361
+ const s = sensor.getState()
322
362
  return { mac: sensor.getMacAddress(),
323
363
  name: sensor.getName(),
324
364
  class: sensor.constructor.name,
325
365
  domain: sensor.getDomain().name,
366
+ state: sensor.getState(),
367
+ errorLog: sensor.getErrorLog(),
368
+ debugLog: sensor.getDebugLog(),
326
369
  RSSI: sensor.getRSSI(),
327
370
  signalStrength: sensor.getSignalStrength(),
328
- lastContactDelta: sensor.elapsedTimeSinceLastContact()
371
+ connected: sensor.isConnected(),
372
+ lastContactDelta: sensor.elapsedTimeSinceLastContact()
329
373
  }
330
374
  }
331
375
 
@@ -346,14 +390,14 @@ module.exports = function (app) {
346
390
 
347
391
  const transport = options?.transport??"le"
348
392
  const duplicateData = options?.duplicateData??false
349
- app.debug("Starting scan...");
393
+ plugin.debug("Starting scan...");
350
394
  //Use adapter.helper directly to get around Adapter::startDiscovery()
351
395
  //filter options which can cause issues with Device::Connect()
352
396
  //turning off Discovery
353
397
  //try {await adapter.startDiscovery()}
354
398
  try{
355
399
  if (transport) {
356
- app.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
400
+ plugin.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
357
401
  await adapter.helper.callMethod('SetDiscoveryFilter', {
358
402
  Transport: new Variant('s', transport),
359
403
  DuplicateData: new Variant('b', duplicateData)
@@ -362,7 +406,7 @@ module.exports = function (app) {
362
406
  await adapter.helper.callMethod('StartDiscovery')
363
407
  }
364
408
  catch (error){
365
- app.debug(error)
409
+ plugin.debug(error)
366
410
  }
367
411
 
368
412
  }
@@ -376,8 +420,6 @@ module.exports = function (app) {
376
420
  }
377
421
 
378
422
  function addSensorToList(sensor){
379
- app.debug(`adding sensor to list ${sensor.getMacAddress()}`)
380
-
381
423
  sensorMap.set(sensor.getMacAddress(),sensor)
382
424
  channel.broadcast(sensorToJSON(sensor),"newsensor");
383
425
  }
@@ -389,7 +431,6 @@ module.exports = function (app) {
389
431
  return new Promise( ( resolve, reject )=>{
390
432
  var s
391
433
  const startNumber=starts
392
- //app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
393
434
  adapter.waitDevice(config.mac_address,(config?.discoveryTimeout??30)*1000)
394
435
  .then(async (device)=> {
395
436
  if (startNumber != starts ) {
@@ -402,13 +443,11 @@ module.exports = function (app) {
402
443
  if (s instanceof BLACKLISTED)
403
444
  reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
404
445
  else{
405
- //app.debug(`Adding sensor to list ${config.mac_address}`)
406
446
 
407
447
  addSensorToList(s)
408
448
  s._lastRSSI=-1*Infinity
409
449
  s.on("RSSI",(()=>{
410
450
  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
451
 
413
452
  s._lastRSSI=Date.now()
414
453
 
@@ -434,13 +473,29 @@ module.exports = function (app) {
434
473
  removeSensorFromList(s)
435
474
  addSensorToList(s)
436
475
  })
476
+ s.on("state", (state)=>{
477
+ channel.broadcast(getSensorInfo(s), "sensorchanged")
478
+ })
479
+ s.on("error",(error)=>{
480
+ channel.broadcast(getSensorInfo(s), "sensorchanged")
481
+ })
482
+ s.on("debug", ()=>{
483
+ channel.broadcast(getSensorInfo(s), "sensorchanged")
484
+ })
437
485
  addSensorToList(s)
438
486
  resolve(s)
439
487
  }
440
488
  }
441
489
  if (startNumber == starts ) {
442
- app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
443
- app.debug(e)
490
+ const errorTxt = `Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`
491
+ if(s)
492
+ {
493
+ s.setError(errorTxt)
494
+ } else {
495
+ plugin.setError(errorTxt)
496
+ }
497
+ plugin.debug(e)
498
+
444
499
  reject( e?.message??e )
445
500
  }
446
501
  })})
@@ -476,20 +531,17 @@ module.exports = function (app) {
476
531
 
477
532
  const sensor = new c(device, config?.params, config?.gattParams)
478
533
  sensor._paths=config.paths //this might be a good candidate for refactoring
479
- sensor.debug=app.debug
480
- sensor.setPluginError=app.setPluginError
481
- sensor.app=app
482
- if (!adapter) debugger
534
+ sensor._app=app
483
535
  sensor._adapter=adapter //HACK!
484
536
  await sensor.init()
485
537
  return sensor
486
538
  }
487
539
  catch(error){
488
540
  const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
489
- app.debug(msg)
490
- app.debug(error)
541
+ plugin.debug(msg)
542
+ plugin.debug(error)
491
543
  if (config.active)
492
- app.setPluginError(msg)
544
+ plugin.setError(msg)
493
545
  return null
494
546
  }
495
547
 
@@ -497,7 +549,7 @@ module.exports = function (app) {
497
549
 
498
550
  function initConfiguredDevice(deviceConfig){
499
551
  const startNumber=starts
500
- app.setPluginStatus(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
552
+ plugin.setStatusText(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
501
553
  if (!deviceConfig.discoveryTimeout)
502
554
  deviceConfig.discoveryTimeout = options.discoveryTimeout
503
555
  createSensor(adapter, deviceConfig).then((sensor)=>{
@@ -505,7 +557,7 @@ module.exports = function (app) {
505
557
  return
506
558
  }
507
559
  if (deviceConfig.active && !(sensor.device instanceof OutOfRangeDevice) ) {
508
- app.setPluginStatus(`Listening to ${++foundConfiguredDevices} sensors.`);
560
+ plugin.setStatusText(`Listening to ${++foundConfiguredDevices} sensors.`);
509
561
  sensor.activate(deviceConfig, plugin)
510
562
  }
511
563
 
@@ -517,10 +569,10 @@ module.exports = function (app) {
517
569
  return
518
570
  }
519
571
  const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
520
- app.debug(msg)
521
- app.debug(error)
572
+ plugin.debug(msg)
573
+
522
574
  if (deviceConfig.active)
523
- app.setPluginError(msg)
575
+ plugin.setError(msg)
524
576
  const sensor=new MissingSensor(deviceConfig)
525
577
  ++foundConfiguredDevices
526
578
 
@@ -530,7 +582,7 @@ module.exports = function (app) {
530
582
  }
531
583
  function findDevices (discoveryTimeout) {
532
584
  const startNumber = starts
533
- app.setPluginStatus("Scanning for new Bluetooth devices...");
585
+ plugin.setStatusText("Scanning for new Bluetooth devices...");
534
586
 
535
587
  adapter.devices().then( (macs)=>{
536
588
  if (startNumber != starts ) {
@@ -580,7 +632,7 @@ module.exports = function (app) {
580
632
  //Connect to adapter
581
633
 
582
634
  if (!adapter){
583
- app.debug(`Connecting to bluetooth adapter ${adapterID}`);
635
+ plugin.debug(`Connecting to bluetooth adapter ${adapterID}`);
584
636
 
585
637
  adapter = await bluetooth.getAdapter(adapterID)
586
638
 
@@ -588,11 +640,10 @@ module.exports = function (app) {
588
640
 
589
641
  await adapter.helper._prepare()
590
642
  adapter.helper._propsProxy.on('PropertiesChanged', async (iface,changedProps,invalidated) => {
591
- app.debug(changedProps)
592
643
  if (Object.hasOwn(changedProps,"Powered")){
593
644
  if (changedProps.Powered.value==false) {
594
645
  if (plugin.started){ //only call stop() if plugin is started
595
- app.setPluginStatus(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
646
+ plugin.setStatusText(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
596
647
  await plugin.stop()
597
648
  }
598
649
  } else {
@@ -601,8 +652,8 @@ module.exports = function (app) {
601
652
  }
602
653
  })
603
654
  if (!await adapter.isPowered()) {
604
- app.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
605
- app.setPluginError(`Bluetooth Adapter ${adapterID} not powered on.`)
655
+ plugin.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
656
+ plugin.setError(`Bluetooth Adapter ${adapterID} not powered on.`)
606
657
  await plugin.stop()
607
658
  return
608
659
  }
@@ -618,19 +669,26 @@ module.exports = function (app) {
618
669
  plugin.stopped=false
619
670
  }
620
671
 
621
- const activeAdapters = await bluetooth.activeAdapters()
622
- plugin.schema.properties.adapter.enum=[]
623
- plugin.schema.properties.adapter.enumNames=[]
624
- for (a of activeAdapters){
625
- plugin.schema.properties.adapter.enum.push(a.adapter)
626
- plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
672
+ try{
673
+ const activeAdapters = await bluetooth.activeAdapters()
674
+ if (activeAdapters.length==0){
675
+ plugin.setError("No active Bluetooth adapters found.")
676
+ }
677
+ plugin.schema.properties.adapter.enum=[]
678
+ plugin.schema.properties.adapter.enumNames=[]
679
+ for (a of activeAdapters){
680
+ plugin.schema.properties.adapter.enum.push(a.adapter)
681
+ plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
682
+ }}
683
+ catch(e){
684
+ plugin.setError(`Unable to get adapters: ${e.message}`)
627
685
  }
628
686
 
629
687
  await startScanner(options)
630
688
  if (starts>0){
631
- app.debug(`Plugin ${packageInfo.version} restarting...`);
689
+ plugin.debug(`Plugin ${packageInfo.version} restarting...`);
632
690
  } else {
633
- app.debug(`Plugin ${packageInfo.version} started` )
691
+ plugin.debug(`Plugin ${packageInfo.version} started` )
634
692
 
635
693
  }
636
694
  starts++
@@ -638,7 +696,7 @@ module.exports = function (app) {
638
696
  try{
639
697
  await startScanner(options)
640
698
  } catch (e){
641
- app.debug(`Error starting scan: ${e.message}`)
699
+ plugin.setError(`Error starting scan: ${e.message}`)
642
700
  }
643
701
  if (!(deviceConfigs===undefined)){
644
702
  const maxTimeout=Math.max(...deviceConfigs.map((dc)=>dc?.discoveryTimeout??options.discoveryTimeout))
@@ -677,7 +735,6 @@ module.exports = function (app) {
677
735
  const dt = config?.discoveryTimeout??options.discoveryTimeout
678
736
  const lc=sensor.elapsedTimeSinceLastContact()
679
737
  if (lc > dt) {
680
- //app.debug(`${sensor.getMacAddress()} not heard from in ${lc} seconds`)
681
738
  channel.broadcast(getSensorInfo(sensor), "sensorchanged")
682
739
  }
683
740
  })
@@ -691,7 +748,7 @@ module.exports = function (app) {
691
748
  options.discoveryInterval)
692
749
  }
693
750
  plugin.stop = async function () {
694
- app.debug("Stopping plugin")
751
+ plugin.debug("Stopping plugin")
695
752
  plugin.stopped=true
696
753
  plugin.started=false
697
754
  channel.broadcast({state:"stopped"},"pluginstate")
@@ -712,10 +769,10 @@ module.exports = function (app) {
712
769
  for await (const sensorEntry of sensorMap.entries()) {
713
770
  try{
714
771
  await sensorEntry[1].stopListening()
715
- app.debug(`No longer listening to ${sensorEntry[0]}`)
772
+ plugin.debug(`No longer listening to ${sensorEntry[0]}`)
716
773
  }
717
774
  catch (e){
718
- app.debug(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
775
+ plugin.setError(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
719
776
  }
720
777
  }
721
778
  }
@@ -726,12 +783,12 @@ module.exports = function (app) {
726
783
  if( await adapter.isDiscovering())
727
784
  try{
728
785
  await adapter.stopDiscovery()
729
- app.debug('Scan stopped')
786
+ plugin.debug('Scan stopped')
730
787
  } catch (e){
731
- app.debug(`Error stopping scan: ${e.message}`)
788
+ plugin.setError(`Error stopping scan: ${e.message}`)
732
789
  }
733
790
  }
734
- app.debug('BT Sensors plugin stopped')
791
+ plugin.debug('BT Sensors plugin stopped')
735
792
 
736
793
  }
737
794
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bt-sensors-plugin-sk",
3
- "version": "1.2.6-beta-3",
3
+ "version": "1.2.6-beta-5",
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": {
@@ -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,x]=(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 "},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);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:N,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}}]);
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
- ({FakeDevice,FakeGATTService,FakeGATTCharacteristic }=require( "../development/FakeBTDevice.js"))
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 EcoWorthy extends BTSensor {
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 EcoWorthy(device)
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 = "EcoWorthy.webp"
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 = EcoWorthy;
172
+ module.exports = EcoWorthyBW02;
@@ -12,7 +12,7 @@ class Inkbird extends BTSensor{
12
12
  return null
13
13
 
14
14
  }
15
- static ImageFile = "InkbirdTH3.jpg"
15
+ static ImageFile = "InkbirdTH3.webp"
16
16
 
17
17
  initSchema() {
18
18
  super.initSchema()
@@ -104,6 +104,9 @@ class JBDBMS extends BTSensor {
104
104
  hasGATT(){
105
105
  return true
106
106
  }
107
+ usingGATT(){
108
+ return true
109
+ }
107
110
  initGATTNotifications(){
108
111
  this.intervalID = setInterval( async ()=>{
109
112
  await this.emitGATT()