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/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
- app.debug(`Loading plugin ${packageInfo.version}`)
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.debug = app.debug
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
- lastContactDelta: sensor.elapsedTimeSinceLastContact()
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
- app.debug("Starting scan...");
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
- app.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
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
- app.debug(error)
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
- app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
440
- app.debug(e)
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.debug=app.debug
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
- app.debug(msg)
487
- app.debug(error)
532
+ plugin.debug(msg)
533
+ plugin.debug(error)
488
534
  if (config.active)
489
- app.setPluginError(msg)
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
- app.setPluginStatus(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
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
- app.setPluginStatus(`Listening to ${++foundConfiguredDevices} sensors.`);
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
- app.debug(msg)
518
- app.debug(error)
563
+ plugin.debug(msg)
564
+
519
565
  if (deviceConfig.active)
520
- app.setPluginError(msg)
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
- app.setPluginStatus("Scanning for new Bluetooth devices...");
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
- app.debug(`Connecting to bluetooth adapter ${adapterID}`);
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
- app.setPluginStatus(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
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
- app.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
602
- app.setPluginError(`Bluetooth Adapter ${adapterID} not powered on.`)
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
- const activeAdapters = await bluetooth.activeAdapters()
619
- plugin.schema.properties.adapter.enum=[]
620
- plugin.schema.properties.adapter.enumNames=[]
621
- for (a of activeAdapters){
622
- plugin.schema.properties.adapter.enum.push(a.adapter)
623
- plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
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
- app.debug(`Plugin ${packageInfo.version} restarting...`);
680
+ plugin.debug(`Plugin ${packageInfo.version} restarting...`);
629
681
  } else {
630
- app.debug(`Plugin ${packageInfo.version} started` )
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
- app.debug(`Error starting scan: ${e.message}`)
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
- app.debug("Stopping plugin")
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
- app.debug(`No longer listening to ${sensorEntry[0]}`)
763
+ plugin.debug(`No longer listening to ${sensorEntry[0]}`)
713
764
  }
714
765
  catch (e){
715
- app.debug(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
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
- app.debug('Scan stopped')
777
+ plugin.debug('Scan stopped')
727
778
  } catch (e){
728
- app.debug(`Error stopping scan: ${e.message}`)
779
+ plugin.setError(`Error stopping scan: ${e.message}`)
729
780
  }
730
781
  }
731
- app.debug('BT Sensors plugin stopped')
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-2",
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": {
@@ -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()