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/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
- app.debug(`Loading plugin ${packageInfo.version}`)
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.debug = app.debug
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
- lastContactDelta: sensor.elapsedTimeSinceLastContact()
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
- app.debug("Starting scan...");
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
- app.debug(`Setting Bluetooth transport option to ${transport}. DuplicateData to ${duplicateData}`)
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
- app.debug(error)
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
- app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
443
- 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
+
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.debug=app.debug
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
- app.debug(msg)
490
- app.debug(error)
532
+ plugin.debug(msg)
533
+ plugin.debug(error)
491
534
  if (config.active)
492
- app.setPluginError(msg)
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
- app.setPluginStatus(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
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
- app.setPluginStatus(`Listening to ${++foundConfiguredDevices} sensors.`);
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
- app.debug(msg)
521
- app.debug(error)
563
+ plugin.debug(msg)
564
+
522
565
  if (deviceConfig.active)
523
- app.setPluginError(msg)
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
- app.setPluginStatus("Scanning for new Bluetooth devices...");
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
- app.debug(`Connecting to bluetooth adapter ${adapterID}`);
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
- app.setPluginStatus(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
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
- app.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
605
- 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.`)
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
- 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()})`)
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
- app.debug(`Plugin ${packageInfo.version} restarting...`);
680
+ plugin.debug(`Plugin ${packageInfo.version} restarting...`);
632
681
  } else {
633
- app.debug(`Plugin ${packageInfo.version} started` )
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
- app.debug(`Error starting scan: ${e.message}`)
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
- app.debug("Stopping plugin")
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
- app.debug(`No longer listening to ${sensorEntry[0]}`)
763
+ plugin.debug(`No longer listening to ${sensorEntry[0]}`)
716
764
  }
717
765
  catch (e){
718
- app.debug(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
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
- app.debug('Scan stopped')
777
+ plugin.debug('Scan stopped')
730
778
  } catch (e){
731
- app.debug(`Error stopping scan: ${e.message}`)
779
+ plugin.setError(`Error stopping scan: ${e.message}`)
732
780
  }
733
781
  }
734
- app.debug('BT Sensors plugin stopped')
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",
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()