bt-sensors-plugin-sk 1.2.0-beta.0.0.1.test → 1.2.0-beta.0.0.10

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.
Files changed (45) hide show
  1. package/.vscode/launch.json +7 -0
  2. package/BTSensor.js +57 -11
  3. package/Queue.js +48 -0
  4. package/README.md +34 -12
  5. package/classLoader.js +38 -0
  6. package/index.js +71 -105
  7. package/package.json +2 -1
  8. package/plugin_defaults.json +99 -10
  9. package/public/893.js +1 -1
  10. package/sensor_classes/ATC.js +5 -3
  11. package/sensor_classes/Aranet/AranetSensor.js +4 -0
  12. package/sensor_classes/Aranet2.js +16 -15
  13. package/sensor_classes/Aranet4.js +23 -17
  14. package/sensor_classes/GoveeH50xx.js +12 -12
  15. package/sensor_classes/GoveeH510x.js +4 -5
  16. package/sensor_classes/IBeacon.js +13 -9
  17. package/sensor_classes/Inkbird.js +4 -2
  18. package/sensor_classes/JBDBMS.js +33 -25
  19. package/sensor_classes/KilovaultHLXPlus.js +33 -16
  20. package/sensor_classes/LancolVoltageMeter.js +4 -3
  21. package/sensor_classes/MopekaTankSensor.js +19 -10
  22. package/sensor_classes/Renogy/RenogySensor.js +1 -1
  23. package/sensor_classes/RenogyBattery.js +15 -13
  24. package/sensor_classes/RenogyRoverClient.js +2 -3
  25. package/sensor_classes/RuuviTag.js +36 -27
  26. package/sensor_classes/ShellySBHT003C.js +19 -22
  27. package/sensor_classes/SwitchBotMeterPlus.js +10 -12
  28. package/sensor_classes/SwitchBotTH.js +13 -16
  29. package/sensor_classes/UltrasonicWindMeter.js +13 -5
  30. package/sensor_classes/VictronACCharger.js +19 -8
  31. package/sensor_classes/VictronBatteryMonitor.js +41 -24
  32. package/sensor_classes/VictronDCDCConverter.js +14 -5
  33. package/sensor_classes/VictronDCEnergyMeter.js +26 -7
  34. package/sensor_classes/VictronGXDevice.js +2 -5
  35. package/sensor_classes/VictronInverter.js +18 -14
  36. package/sensor_classes/VictronInverterRS.js +18 -7
  37. package/sensor_classes/VictronLynxSmartBMS.js +15 -13
  38. package/sensor_classes/VictronOrionXS.js +15 -3
  39. package/sensor_classes/VictronSmartBatteryProtect.js +5 -6
  40. package/sensor_classes/VictronSmartLithium.js +18 -18
  41. package/sensor_classes/VictronSolarCharger.js +31 -18
  42. package/sensor_classes/VictronVEBus.js +2 -5
  43. package/sensor_classes/XiaomiMiBeacon.js +20 -19
  44. package/src/components/PluginConfigurationPanel.js +52 -27
  45. package/testqueue.js +64 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": []
7
+ }
package/BTSensor.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { Variant } = require('dbus-next');
2
2
  const { log } = require('node:console');
3
3
  const EventEmitter = require('node:events');
4
+ const AutoQueue = require("./Queue.js")
4
5
 
5
6
  /**
6
7
  * @author Andrew Gerngross <oh.that.andy@gmail.com>
@@ -11,6 +12,7 @@ const EventEmitter = require('node:events');
11
12
  */
12
13
 
13
14
  const BTCompanies = require('./bt_co.json');
15
+ const connectQueue = new AutoQueue()
14
16
 
15
17
  /**
16
18
  * @global A map of company names keyed by their Bluetooth ID
@@ -98,7 +100,7 @@ function preparePath(obj, str) {
98
100
  */
99
101
 
100
102
  class BTSensor extends EventEmitter {
101
- //static metadata=new Map()
103
+
102
104
  static DEFAULTS = require('./plugin_defaults.json');
103
105
 
104
106
  /**
@@ -312,23 +314,25 @@ class BTSensor extends EventEmitter {
312
314
  }
313
315
  }
314
316
 
315
- }
316
- async init(){
317
- this.currentProperties = await this.constructor.getDeviceProps(this.device)
318
- this.initSchema()
319
-
320
317
 
321
318
  //create the 'name' parameter
322
319
  this.addDefaultParam("name")
320
+ .default=this?.currentProperties?.Name
323
321
 
324
322
  //create the 'location' parameter
325
323
 
326
- this.addDefaultParam("location")
324
+ //this.addDefaultParam("location")
327
325
 
328
326
  //create the 'RSSI' parameter
329
327
  this.addDefaultPath("RSSI","sensors.RSSI")
330
328
  this.getPath("RSSI").read=()=>{return this.getRSSI()}
331
329
  this.getPath("RSSI").read.bind(this)
330
+
331
+ }
332
+ async init(){
333
+ this.currentProperties = await this.constructor.getDeviceProps(this.device)
334
+ this.initSchema()
335
+
332
336
  this.initListen()
333
337
  }
334
338
 
@@ -361,13 +365,15 @@ class BTSensor extends EventEmitter {
361
365
  }
362
366
  else
363
367
  await this.initGATTNotifications()
368
+ }).catch((e)=>{
369
+ this.app.debug(`Unable to activate GATT connection for ${this.getName()} (${this.getMacAddress()}): ${e}`)
364
370
  })
365
371
  }
366
372
 
367
373
  /**
368
374
  * Add a metadatum instance to the sensor instance
369
375
  *
370
- * @param {String} tag
376
+ * @param {String} tag
371
377
  * @param {...any} args
372
378
  * @returns {this.Metadatum} the new metadatum instance
373
379
  */
@@ -449,6 +455,39 @@ class BTSensor extends EventEmitter {
449
455
  throw new Error("::initGATTNotifications() should be implemented by the BTSensor subclass")
450
456
  }
451
457
 
458
+ deviceConnect() {
459
+
460
+ /* CAUTION: HACK AHEAD
461
+
462
+ Bluez for some cockamamie reason (It's 2025 for chrissake.
463
+ BLUETOOTH ISN'T JUST FOR DESKTOPS ANYMORE, BLUEZ DEVS!)
464
+ SUSPENDS scanning while connected to a device.
465
+
466
+ The next line of code gives the scanner a kick in the arse,
467
+ starting it up again so, I dunno, another device might be able
468
+ to connect and sensor classes could maybe get beacon updates.
469
+
470
+ You know, the little things.
471
+ */
472
+ return connectQueue.enqueue( async ()=>{
473
+ this.debug(`Connecting to ${this.getName()}`)
474
+ await this.device.connect()
475
+ try {
476
+
477
+ await this._adapter.helper.callMethod('StopDiscovery')
478
+ await this._adapter.helper.callMethod('SetDiscoveryFilter', {
479
+ Transport: new Variant('s', this._adapter?._transport??"le")
480
+ })
481
+ await this._adapter.helper.callMethod('StartDiscovery')
482
+
483
+ } catch (e){
484
+ //probably ignorable error. probably.
485
+ console.log(e)
486
+ }
487
+ })
488
+ /* END HACK*/
489
+ }
490
+
452
491
  /**
453
492
  *
454
493
  * Subclasses do NOT need to override this function
@@ -515,6 +554,9 @@ class BTSensor extends EventEmitter {
515
554
  getParams(){
516
555
  return this._schema.properties.params.properties
517
556
  }
557
+ getParameter(param){
558
+ return this.getParams()[param]
559
+ }
518
560
  getGATTParams(){
519
561
  return this._schema.properties.gattParams.properties
520
562
 
@@ -739,12 +781,15 @@ class BTSensor extends EventEmitter {
739
781
  //End instance utility functions
740
782
 
741
783
  createPaths(config, id){
784
+ // const source = `${this.getName()} (${id})`
785
+
742
786
  Object.keys(this.getPaths()).forEach((tag)=>{
743
787
  const pathMeta=this.getPath(tag)
744
788
  const path = config.paths[tag]
745
789
  if (!(path===undefined))
746
790
  this.app.handleMessage(id,
747
791
  {
792
+ // $source: source,
748
793
  updates:
749
794
  [{ meta: [{path: preparePath(this, path), value: { units: pathMeta?.unit }}]}]
750
795
  })
@@ -752,6 +797,7 @@ class BTSensor extends EventEmitter {
752
797
  }
753
798
 
754
799
  initPaths(deviceConfig, id){
800
+ const source = this.getName()
755
801
  Object.keys(this.getPaths()).forEach((tag)=>{
756
802
  const pathMeta=this.getPath(tag)
757
803
  const path = deviceConfig.paths[tag];
@@ -760,14 +806,14 @@ class BTSensor extends EventEmitter {
760
806
  if (pathMeta.notify){
761
807
  this.app.notify(tag, val, id )
762
808
  } else {
763
- this.updatePath(preparePath(this,path),val,id)
809
+ this.updatePath(preparePath(this,path),val, id, source)
764
810
  }
765
811
  })
766
812
  }
767
813
  })
768
814
  }
769
- updatePath(path, val,id){
770
- this.app.handleMessage(id, {updates: [ { values: [ {path: path, value: val }] } ] })
815
+ updatePath(path, val, id, source){
816
+ this.app.handleMessage(id, {updates: [ { $source: source, values: [ { path: path, value: val }] } ] })
771
817
  }
772
818
  elapsedTimeSinceLastContact(){
773
819
  return (Date.now()-this?._lastContact??Date.now())/1000
package/Queue.js ADDED
@@ -0,0 +1,48 @@
1
+ /*
2
+ see: https://stackoverflow.com/questions/53540348/js-async-await-tasks-queue
3
+ */
4
+ class Queue {
5
+ constructor() { this._items = []; }
6
+ enqueue(item) { this._items.push(item); }
7
+ dequeue() { return this._items.shift(); }
8
+ get size() { return this._items.length; }
9
+ }
10
+
11
+ class AutoQueue extends Queue {
12
+ constructor() {
13
+ super();
14
+ this._pendingPromise = false;
15
+ }
16
+
17
+ enqueue(action) {
18
+ return new Promise((resolve, reject) => {
19
+ super.enqueue({ action, resolve, reject });
20
+ this.dequeue();
21
+ });
22
+ }
23
+
24
+ async dequeue() {
25
+ if (this._pendingPromise) return false;
26
+
27
+ let item = super.dequeue();
28
+
29
+ if (!item) return false;
30
+
31
+ try {
32
+ this._pendingPromise = true;
33
+
34
+ let payload = await item.action(this);
35
+
36
+ this._pendingPromise = false;
37
+ item.resolve(payload);
38
+ } catch (e) {
39
+ this._pendingPromise = false;
40
+ item.reject(e);
41
+ } finally {
42
+ this.dequeue();
43
+ }
44
+
45
+ return true;
46
+ }
47
+ }
48
+ module.exports = AutoQueue
package/README.md CHANGED
@@ -1,8 +1,35 @@
1
1
  # Bluetooth Sensors for [Signal K](http://www.signalk.org)
2
2
 
3
- ## Beta 1.0.0
3
+ ## What's New
4
4
 
5
- Dynamic configuration added (no more screen refreshing necessary).
5
+ ## 1.2.0-beta-0.0.10
6
+
7
+ Better handling of plugin config screen when Disabled
8
+
9
+ ## 1.2.0-beta-0.0.9
10
+
11
+ Fixes to config page RSSI update.
12
+
13
+ ## 1.2.0-beta-0.0.8
14
+
15
+ Improved handling of changed state on config page.
16
+
17
+ Several small fixes to various sensor classes.
18
+
19
+ ## 1.2.0-beta-0.0.7
20
+
21
+ Added zone config param for environmental sensors. Removed location parameter.
22
+
23
+ ## 1.2.0-beta-0.0.6
24
+ Default values for paths for most sensor classes.
25
+
26
+ ## 1.2.0-beta-0.0.5
27
+
28
+ Added workaround to support multiple simultaneous GATT connections. Owing to problematic behavior in Bluez where making a GATT connection halted the scanner which made additional updates and connections impossible, added a scanner restart after making each GATT connection.
29
+
30
+ ## 1.2.0-beta-0.0.1
31
+
32
+ Dynamic configuration added. List updates when new devices are found by scanner. (No more screen refreshing necessary).
6
33
 
7
34
  ## WHAT IT IS
8
35
 
@@ -33,13 +60,13 @@ Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently suppo
33
60
  NOTE: If you're running the 1.0.3 release, you will have to reconfigure your devices.<br>
34
61
 
35
62
  ### Signalk Appstore
36
- The plugin is currently available in the Signalk Appstore. <br>
63
+ The beta plugin is not currently available in the Signalk Appstore. <br>
37
64
 
38
65
  ### NPM
39
66
 
40
67
  Go to you signalk home (usually ~/.signalk) and run:
41
68
 
42
- npm i bt-sensors-plugin-sk@1.1.0-beta.2.1
69
+ npm i bt-sensors-plugin-sk@1.2.0-beta.0.0.7
43
70
 
44
71
  ### Linux
45
72
 
@@ -48,7 +75,7 @@ If you want to install directly from source (this is mostly of interest to custo
48
75
  <pre> cd ~/[some_dir]
49
76
  git clone https://github.com/naugehyde/bt-sensors-plugin-sk
50
77
  cd bt-sensors-plugin-sk
51
- git switch '1.1.0'
78
+ git switch '1.2.0-beta'
52
79
  git pull
53
80
  npm i
54
81
  [sudo] npm link
@@ -68,16 +95,11 @@ After installing and restarting Signalk you should see a "BT Sensors Plugin" opt
68
95
 
69
96
  On initial configuration, wait for your Bluetooth adapter to scan devices. The plugin will scan for new devices at whatever you set the "scan for new devices interval" value to. <br><br>
70
97
 
71
- > TIP: Close and re-open the config screen to refresh the screen. The config screen isn't as <i>reactive</i> as it oughtta be.<br><br>
72
-
73
- Then press the + button to add a sensor. Your screen should look something like this:<br><br>
74
- <img width="1122" alt="Screenshot 2024-10-13 at 6 52 52 PM" src="https://github.com/user-attachments/assets/0487b8d0-4bc0-4358-85c6-a507bc3c97d2">
75
-
76
98
  <br><br>
77
99
 
78
- Select the sensor you want Signalk to listen to from the drop down.<br>
100
+ Select the sensor you want Signalk to listen to from the list.<br>
79
101
 
80
- If you don't see your device and you know that it's on and nearby to the server, it may not be currently supported. But fear not, you can add custom sensor classes yourself. (Check out [the development section](#development).). <br><br>
102
+ If you don't see your device and you know that it's on and nearby to the server, it may not be currently supported (It could also be out of range.) But fear not, you can add custom sensor classes yourself. (Check out [the development section](#development).). <br><br>
81
103
 
82
104
  Now it's a simple matter of associating the data emitted by the sensor with the Signalk path you want it to update. (Also, you can name your sensor so when it appears in logs its easy to recognize.) <br><br>
83
105
 
package/classLoader.js ADDED
@@ -0,0 +1,38 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const semver = require('semver')
4
+
5
+ function loadClasses (dir, ext='.js')
6
+ {
7
+ const classMap = new Map()
8
+ const classFiles = fs.readdirSync(dir)
9
+ .filter(file => file.endsWith(ext));
10
+
11
+ classFiles.forEach(file => {
12
+ const filePath = path.join(dir, file);
13
+ const cls = require(filePath);
14
+ classMap.set(cls.name, cls);
15
+ })
16
+ return classMap
17
+ }
18
+
19
+ function loadClassMap(app) {
20
+ const _classMap = loadClasses(path.join(__dirname, 'sensor_classes'))
21
+ const classMap = new Map([..._classMap].filter(([k, v]) => !k.startsWith("_") ))
22
+ const libPath = app.config.appPath +(
23
+ semver.gt(app.config.version,"2.13.5")?"dist":"lib"
24
+ )
25
+ import(libPath+"/modules.js").then( (modulesjs)=>{
26
+ const { default:defaultExport} = modulesjs
27
+ const modules = defaultExport.modulesWithKeyword(app.config, "signalk-bt-sensor-class")
28
+ modules.forEach((module)=>{
29
+ module.metadata.classFiles.forEach((classFile)=>{
30
+ const cls = require(module.location+module.module+"/"+classFile);
31
+ classMap.set(cls.name, cls);
32
+ })
33
+ })
34
+ classMap.get('UNKNOWN').classMap=new Map([...classMap].sort().filter(([k, v]) => !v.isSystem )) // share the classMap with Unknown for configuration purposes
35
+ })
36
+ return classMap
37
+ }
38
+ module.exports=loadClassMap