bt-sensors-plugin-sk 1.2.0-beta.0.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/BTSensor.js CHANGED
@@ -286,6 +286,8 @@ class BTSensor extends EventEmitter {
286
286
 
287
287
  async initSchema(){
288
288
  this._schema = {
289
+ type: "object",
290
+ title: this.getName(),
289
291
  properties:{
290
292
  active: {title: "Active", type: "boolean", default: true },
291
293
  discoveryTimeout: {title: "Device discovery timeout (in seconds)",
@@ -295,13 +297,12 @@ class BTSensor extends EventEmitter {
295
297
 
296
298
  params:{
297
299
  title:`Device parameters`,
298
- description: this.getDescription(),
299
300
  type:"object",
300
301
  properties:{}
301
302
  },
302
303
  paths:{
303
304
  title:"Signalk Paths",
304
- description: `Signalk paths to be updated when ${this.getName()}'s values change`,
305
+ description: `Signalk paths to be updated when the device's values change`,
305
306
  type:"object",
306
307
  properties:{}
307
308
  }
@@ -354,7 +355,7 @@ class BTSensor extends EventEmitter {
354
355
  }
355
356
  if (this.usingGATT()){
356
357
  try {
357
- this.activateGATT()
358
+ this.activateGATT()
358
359
  } catch (e) {
359
360
  this.debug(`GATT services unavailable for ${this.getName()}. Reason: ${e}`)
360
361
  this._state="ERROR"
@@ -368,7 +369,7 @@ class BTSensor extends EventEmitter {
368
369
 
369
370
  activateGATT(){
370
371
  this.initGATTConnection().then(async ()=>{
371
- this.emitGATT()
372
+ await this.emitGATT()
372
373
  if (this.pollFreq)
373
374
  this.initGATTInterval()
374
375
  else
@@ -497,9 +498,7 @@ class BTSensor extends EventEmitter {
497
498
  console.log(e)
498
499
  }
499
500
  /* END HACK*/
500
- this.device.on("disconnect", ()=>{
501
- this.debug(`${this.macAndName()} disconnected`)
502
- })
501
+
503
502
  })
504
503
 
505
504
  }
@@ -516,8 +515,8 @@ class BTSensor extends EventEmitter {
516
515
  this.device.disconnect().then(()=>{
517
516
  this.initPropertiesChanged()
518
517
  this.intervalID = setInterval( () => {
519
- this.initGATTConnection().then(()=>{
520
- this.emitGATT()
518
+ this.initGATTConnection().then(async ()=>{
519
+ await this.emitGATT()
521
520
  this.device.disconnect()
522
521
  .then(()=>
523
522
  this.initPropertiesChanged()
@@ -585,9 +584,21 @@ class BTSensor extends EventEmitter {
585
584
  return this.currentProperties.Address
586
585
  }
587
586
 
588
- getDescription(){
587
+ getImage(){
588
+ return "bluetooth-logo.png"
589
+ }
590
+ getImageHTML(){
591
+ return `<img src="../bt-sensors-plugin-sk/images/${this.getImage()}" height="150" object-fit="cover" ></img>`
592
+ }
593
+
594
+ getTextDescription(){
589
595
  return `${this.getName()} from ${this.getManufacturer()}`
590
596
  }
597
+
598
+ getDescription(){
599
+ return `<div>${this.getImageHTML()} ${this.getTextDescription()} </div>`
600
+
601
+ }
591
602
  getName(){
592
603
  const name = this?.name??this.currentProperties.Name
593
604
  return name?name:"Unknown"
@@ -733,7 +744,7 @@ class BTSensor extends EventEmitter {
733
744
  *
734
745
  */
735
746
 
736
- emitGATT(){
747
+ async emitGATT(){
737
748
  throw new Error("Subclass must implement ::emitGATT function")
738
749
  }
739
750
 
@@ -752,6 +763,27 @@ class BTSensor extends EventEmitter {
752
763
  )
753
764
  }
754
765
 
766
+ /**
767
+ * ::reactivate()
768
+ */
769
+
770
+ async reactivate(){
771
+ await this.stopListening()
772
+ this.listen()
773
+ this.currentProperties = await this.constructor.getDeviceProps(this.device)
774
+ if (this.usingGATT()){
775
+ try {
776
+ this.activateGATT()
777
+ } catch (e) {
778
+ this.debug(`GATT services unavailable for ${this.getName()}. Reason: ${e}`)
779
+ this._state="ERROR"
780
+ return
781
+ }
782
+ }
783
+ this._state="ACTIVE"
784
+ this._propertiesChanged(this.currentProperties)
785
+ }
786
+
755
787
  /**
756
788
  * Listen to sensor.
757
789
  * ::listen() sets up listeners for property changes thru ::propertiesChanged(props)
@@ -839,6 +871,8 @@ class BTSensor extends EventEmitter {
839
871
  elapsedTimeSinceLastContact(){
840
872
  return (Date.now()-this?._lastContact??Date.now())/1000
841
873
  }
874
+
875
+
842
876
  }
843
877
 
844
878
  module.exports = BTSensor
package/README.md CHANGED
@@ -1,106 +1,107 @@
1
- # Bluetooth Sensors for [Signal K](http://www.signalk.org)
1
+ # Bluetooth Sensors for [Signal K](http://www.signalk.org)
2
+ # Version 1.2.0
2
3
 
3
- ## What's New
4
-
5
- ## 1.2.0-beta-0.1.5/6
6
-
7
- Renogy and JBDBMS fixes
8
-
9
- ## 1.2.0-beta-0.1.4
10
-
11
- Added support for paired Shelly devices.
12
-
13
- ## 1.2.0-beta-0.1.3
14
-
15
- Added support for [Shelly Blu Motion sensor ](https://us.shelly.com/products/shelly-blu-motion)
16
-
17
- ## 1.2.0-beta-0.1.2.b
18
-
19
- Module load unfix
20
-
21
- ## 1.2.0-beta-0.1.2.a
22
-
23
- Module load fix
24
-
25
- ## 1.2.0-beta-0.1.2
26
-
27
- SSE deregister fix (long story)
28
-
29
- ## 1.2.0-beta-0.1.1
30
-
31
- Gobius fix for inclination path, id parameter.
32
-
33
- ## 1.2.0-beta-0.1.0
34
-
35
- Sensors on config page appear under tabs indicating their domain (Environmental, Electrical etc.)
36
-
37
- Gobius C tank meter support added.
4
+ ## WHAT IT IS
38
5
 
39
- ## 1.2.0-beta-0.0.10
6
+ BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for listening and connecting to Bluetooth sensors on your boat. After discovery and configuration the plugin sends deltas to Signalk paths with values your sensor reports. <br>
40
7
 
41
- Better handling of plugin config screen when Disabled
8
+ It runs on any 2.0 or greater SignalK installation but on Linux only. It's been tested on Desktop and headless RPis, OpenPlotter, and Cerbo GX/Ekrano.
42
9
 
43
- ## 1.2.0-beta-0.0.9
10
+ A typical use case is a Bluetooth thermometer like the Xiaomi LYWSD03MMC, an inexpensive Bluetooth thermometer that runs on a 3V watch battery that can report the current temperature and humidity in your refrigerator or cabin or wherever you want to stick it (no judgement.) <br>
44
11
 
45
- Fixes to config page RSSI update.
12
+ The reported temperature can then be displayed on a Signalk app like Kip, WilhelmSK or, with appropriate mapping to NMEA-2000, a NMEA 2000 Multi-function display.
46
13
 
47
- ## 1.2.0-beta-0.0.8
14
+ It's pretty easy to write and deploy your own sensor class for any currently unsupported sensor. More on that in [the development README](./sensor_classes/DEVELOPMENT.md).
48
15
 
49
- Improved handling of changed state on config page.
16
+ ## WHAT'S NEW SINCE VERSION 1.1.x
50
17
 
51
- Several small fixes to various sensor classes.
18
+ - Dynamic configuration screen. The Device list automatically updates when new devices are found by the BT scanner. (No more annoying screen refresh necessary!).
52
19
 
53
- ## 1.2.0-beta-0.0.7
20
+ - Sensors appear under tabs indicating their domain (Environmental, Electrical etc.)
54
21
 
55
- Added zone config param for environmental sensors. Removed location parameter.
22
+ - Support added for [Shelly Blu Motion sensor ](https://us.shelly.com/products/shelly-blu-motion), Gobius C tank meter and LiTime/Rebodo Smart Batteries.
56
23
 
57
- ## 1.2.0-beta-0.0.6
58
- Default values for paths for most sensor classes.
24
+ - Default values for paths for most sensor classes.
59
25
 
60
- ## 1.2.0-beta-0.0.5
26
+ - Support for multiple simultaneous GATT connections.
61
27
 
62
- 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.
28
+ ## SUPPORTED SENSORS
63
29
 
64
- ## 1.2.0-beta-0.0.1
30
+ ### NOTE
65
31
 
66
- Dynamic configuration added. List updates when new devices are found by scanner. (No more screen refreshing necessary).
32
+ - Not all listed devices and variants have been thoroughly tested. If you encounter any issues, please report on github.
33
+ - Some supported devices cannot be automatically identified -- at least not reliably. You'll need to select the sensor class configuration variable manually.
34
+ - Not all supported devices are enumerated under their manufacturer. Some device models that are not known to this developer may share a protocol with another brand/model and in fact be supported. Please raise an issue on github if you discover a supported device that's not listed or implied below.
67
35
 
68
- ## WHAT IT IS
36
+ ### Electrical
37
+ | Manufacturer | Devices |
38
+ |--------------|---------|
39
+ |[Victron](https://www.victronenergy.com/)|All documented (as of May 2025) devices that support instant readout (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus) See: https://www.victronenergy.com/panel-systems-remote-monitoring/victronconnect|
40
+ |[Renogy](https://www.renogy.com/)| Renogy Smart Batteries, Inverters and Rover Client |
41
+ |[JBD/Jibada](https://www.jbdbms.com/)| Smart BMS devices see: https://www.jbdbms.com/collections/smart-bms |
42
+ |Xiaoxiang| Rebranded JBD/Jibada |
43
+ |[LiTime](https://www.litime.com/)| LifePo4 Smart Batteries |
44
+ |Redodo| Rebranded LiTime |
45
+ |Kilovault| [Kilovault HLX+ smart batteries ](https://sunwatts.com/content/manual/KiloVault_HLX_PLUS_Datasheet_06252021%20%281%29.pdf?srsltid=AfmBOooY-cGnC_Qm6V1T9Vg5oZzBCJurS0AOGoWqWeyy-dwz2vA-l1Jb) (Note: Kilovault appears to be out of business as of March 2024) |
46
+ |[Lancol](www.Lancol.com)| [Micro 10C 12V Car Battery Monitor](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/)|
69
47
 
70
- BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for listening and connecting to Bluetooth sensors on your boat and sending deltas to Signalk paths with the values the sensors reports. <br>
71
48
 
72
- The Plugin currently supports every documented Victron device (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus), [Lancol Battery Meters ](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/), [Kilovault HLX+ smart batteries ](https://sunwatts.com/content/manual/KiloVault_HLX_PLUS_Datasheet_06252021%20%281%29.pdf?srsltid=AfmBOooY-cGnC_Qm6V1T9Vg5oZzBCJurS0AOGoWqWeyy-dwz2vA-l1Jb), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags, Renogy BMS, Ultrasonic Wind Meters, SwitchBotTH and Meterplus, Aranet 2/4 environmental sensors, Govee 50/51xx sensors, and Inkbird thermometers.
49
+ ### Environmental
50
+ | Manufacturer | Devices |
51
+ |--------------|----------|
52
+ |[Ruuvi](https://ruuvi.com/)| Ruuvi Tag and Ruuvi Tag Pro|
53
+ |[Switch Bot](https://www.switch-bot.com/)| Meter Plus and TH devices |
54
+ |[Xiaomi](https://www.mi.com/global/)| LYWSD03MMC (and variants) Temp and Humidity Sensor |
55
+ |[ATC](https://github.com/atc1441/ATC_MiThermometer)| Custom firmware for LYWSD03MMC |
56
+ |[Shelly](https://www.shelly.com/)| Shelly SBHT003C Temperature and Humidity Sensor and Shelly SBMO003Z Motion Sensor |
57
+ |[Calypso Instruments](https://calypsoinstruments.com)| [Wireless Wind Meters](https://calypsoinstruments.com/sailing) |
58
+ |[Aranet](https://www.aranet.com)| Aranet 2 Temp and Humidity Sensor. Aranet 4 Temp/Humidity/Co2 Sensor. |
59
+ |[Govee](http://www.govee.com)| Govee H50xx and H510x Temperature and humidity sensors |
60
+ |[BTHome](https://bthome.io/)| NOTE: Framework for IOT sensor devices. |
61
+ |[Inkbird](https://inkbird.com/)| TH-2 Temp and Humidity Sensor |
73
62
 
74
- A typical use case is a Bluetooth thermometer like the Xiaomi LYWSD03MMC, an inexpensive Bluetooth thermometer that runs on a 3V watch battery that can report the current temperature and humidity in your refrigerator or cabin or wherever you want to stick it (no judgement.) <br>
75
63
 
76
- The reported temperature can then be displayed on a Signalk app like Kip, WilhelmSK or, with appropiate mapping to NMEA-2000, a NMEA 2000 Multi-function display.
64
+ ### Tanks
65
+ | Manufacturer | Devices |
66
+ |--------------|----------|
67
+ | [Gobius](https://gobiusc.com/) | Gobius-C Tank level sensor|
68
+ | [Mopeka](https://www.mopeka.com) | [Mopeka Pro Chek](https://mopeka.com/product-category/recreational-sensors-rv-bbq-etc/) ultrasonic tank level sensor |
77
69
 
78
- It's pretty easy to write and deploy your own sensor class for any currently unsupported sensor. More on that in [the development README](./sensor_classes/DEVELOPMENT.md).
79
70
 
80
- ## WHO IS IT FOR
71
+ ## WHO IT'S FOR
81
72
 
82
73
  Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently supported) and Bluetooth sensors they'd like to integrate into their Signalk datastream.
83
74
 
84
75
  ## REQUIREMENTS
85
76
 
86
- * A Linux Signalk boat-puter with bluetooth-DBUS support
87
- * A Bluetooth adapter
77
+ * A Linux Signalk boat-puter with bluetooth-DBUS support
78
+ * A Bluetooth adapter, either built-in or a USB external adapter
88
79
  * [Bluez](https://www.bluez.org) installed
89
80
  (Go here for [Snap installation instructions](https://snapcraft.io/bluez))
90
81
  * [Node-ble](https://www.npmjs.com/package/node-ble) (installs with the plugin)
91
82
 
92
83
  ## INSTALLATION
93
84
 
94
- NOTE: If you're running the 1.0.3 release, you will have to reconfigure your devices.<br>
95
-
96
85
  ### Signalk Appstore
97
- The beta plugin is not currently available in the Signalk Appstore. <br>
86
+ The plugin is available in the Signalk Appstore. Yay. <br>
87
+
88
+ ### Signal K Server in Docker
89
+
90
+ If you are running SK Server in a Docker container you'll need to mount the dbus system from the host and run as privileged. <br><br>
91
+
92
+ Add the following lines `docker-compose.yml`:
93
+
94
+ ```
95
+ volumes:
96
+ - /var/run/dbus:/var/run/dbus
97
+ privileged: true
98
+ ```
98
99
 
99
- ### NPM
100
+ ### NPM Installation
100
101
 
101
102
  Go to you signalk home (usually ~/.signalk) and run:
102
103
 
103
- npm i bt-sensors-plugin-sk@1.2.0-beta.0.0.7
104
+ npm i bt-sensors-plugin-sk@latest
104
105
 
105
106
  ### Linux
106
107
 
@@ -109,7 +110,6 @@ If you want to install directly from source (this is mostly of interest to custo
109
110
  <pre> cd ~/[some_dir]
110
111
  git clone https://github.com/naugehyde/bt-sensors-plugin-sk
111
112
  cd bt-sensors-plugin-sk
112
- git switch '1.2.0-beta'
113
113
  git pull
114
114
  npm i
115
115
  [sudo] npm link
@@ -125,33 +125,49 @@ Finally, restart SK. Plugin should appear in your server plugins list.<br>
125
125
 
126
126
  After installing and restarting Signalk you should see a "BT Sensors Plugin" option in the Signalk->Server->Plugin Config page.<br><br>
127
127
 
128
- <img width="1130" alt="Screenshot 2024-10-13 at 6 50 09 PM" src="https://github.com/user-attachments/assets/4180504e-1ca8-48af-babf-899486fb0a24"><br><br>
128
+ 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. (NOTE: You only need to scan for new devices during configuration. Once your config is set, set the new devices interval to 0.) <br><br>
129
129
 
130
- 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>
130
+ <img width="1182" alt="Screenshot 2025-06-12 at 9 33 57 AM" src="https://github.com/user-attachments/assets/f0e1d644-3090-4f13-820e-748e9c83bc82" />
131
+ <br><br>
132
+
133
+ Select the sensor you want Signalk to listen to from the categorized list.<br>
134
+
135
+ <img width="1115" alt="Screenshot 2025-06-12 at 9 34 16 AM" src="https://github.com/user-attachments/assets/70baf082-c181-4482-bde4-1e65c07702de" />
136
+
137
+ NOTE: Devices that are not configured appear in *italics*. Configured devices with unsaved changes are asterisked.
138
+
139
+ The selected device's configuration will appear below the list of found devices.
131
140
 
141
+ <img width="1095" alt="Screenshot 2025-06-12 at 9 34 48 AM" src="https://github.com/user-attachments/assets/48670df2-7f85-4ca1-9ea2-c1cf49087858" />
142
+
143
+ If you see your device in the UNKNOWN tab it may not be currently supported. But fear not, you can add custom sensor classes yourself. (Check out the [development README](./sensor_classes/DEVELOPMENT.md).)
144
+
145
+ If your device appears as UNKNOWN and is a supported sensor it's likely that it can't be automatically identified by the plugin. Select the senor and then select the appropriate Sensor Class from the dropdown list. After saving, you should see your sensor in the tab under its type (Electrical, etc). You'll be able to edit the paths for the sensor now.
132
146
  <br><br>
133
147
 
134
- Select the sensor you want Signalk to listen to from the list.<br>
135
148
 
136
- 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>
137
149
 
138
150
  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>
139
151
 
140
- <img width="1125" alt="Screenshot 2024-10-13 at 9 30 53 PM" src="https://github.com/user-attachments/assets/5284539e-d5ee-488f-bbab-901258eb1c0b">
152
+ <img width="1087" alt="Screenshot 2025-06-12 at 9 35 01 AM" src="https://github.com/user-attachments/assets/6deee310-57ec-4df6-979a-6ca16699392d" />
153
+
154
+ Most devices will have default paths that should align with the SignalK spec. If the sensor is missing defaults or the defaults are not consistent with the SK spec, create an issue or open a pull request and add your own to the sensor's `initSchema()` method.
155
+
156
+ Be sure to Save your device configuration by selecting **Save**.
141
157
 
142
- The plugin doesn't need for Signalk to restart after submitting your config but restart if that makes you more comfortable. <br><br>
158
+ <img width="325" alt="Screenshot 2025-06-12 at 9 35 16 AM" src="https://github.com/user-attachments/assets/951f41ce-2363-4527-9d9b-d5aa9aca7e14" />
143
159
 
144
- ### ADVERTISED DATA
160
+ To delete the configuration, select **Delete**. To undo any changes, select **Undo**.
145
161
 
146
- Most supported Bluetooth sensors broadcast (or advertise) their device's data without an explicit connection. Some devices broadcast their data encrypted. Supported devices with encrypted advertised data will allow you to input the encryption key as a config parameter. NOTE: the encryption key is currently stored on the server in plain text.
162
+ Your device will restart its connection automatically after saving.
147
163
 
148
- ### GATT CONNECTIONS
164
+ You may see embedded in a default path values like `{zone}`. This is a parameter variable you can set in the config whose value can cascade through the configuration of your paths.
149
165
 
150
- If you see something like this on your device config:
166
+ <img width="309" alt="Screenshot 2025-06-12 at 9 37 15 AM" src="https://github.com/user-attachments/assets/3b02e819-4408-4b37-a257-cda8f4a035c5" /><img width="192" alt="Screenshot 2025-06-12 at 9 37 28 AM" src="https://github.com/user-attachments/assets/1cf13934-4c09-47b3-8eef-d215fcd93374" />
151
167
 
152
- <img width="727" alt="Screenshot 2024-10-13 at 9 42 26 PM" src="https://github.com/user-attachments/assets/65fe1aa7-99a5-41ac-85dc-cfde00e95932">
168
+ Any member variable of the sensor class can be used as a parameter variable. Equally, any unary function that returns a string can be used as a parameter variable (`{macAndName}`, for example). Use with caution.
153
169
 
154
- You have the option of making a GATT Connection to your device. A GATT Connection is energy-inefficient but serves data from your device in more or less real-time. To learn more about GATT check out: https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt
170
+ <br><br>
155
171
 
156
172
  ## NOW WHAT?
157
173
 
@@ -161,7 +177,97 @@ You should see data appear in your data browser. Here's a screenshot of Signalk
161
177
 
162
178
  You can now take the data and display it using Kip, WilhelmSK or route it to NMEA-2K and display it on a N2K MFD, or use it to create and respond to alerts in Node-Red. Isn't life grand?
163
179
 
180
+ ## NOTES ON WORKING WITH BLUETOOTH DEVICES
181
+
182
+ This project got started when I wanted to monitor the temperature in my boat's refrigerator. The thought of spending > $100 US and straining my back muscles so I could run NMEA wires under my cabin sole or behind my water heater got me thinking "Wireless, that sounds like a good idea."
183
+
184
+ A year or so later, I have wireless Bluetooth sensors reporting not only on my refrigerator temperature but on the temperature and humidity in the vberth, on the deck and at the nav table. I'm getting wireless electrical data from my house and starter battery banks as well as on the levels of my propane tank. When there's motion detected on my boat, I get an email and a text telling me so. All wirelessly. All on inexpensive devices powered by inexpensice batteries that last sometimes for years and can be purchased at my local CVS (a US mega-pharmacy).
185
+
186
+ Bluetooth LE (Low Energy) sensors are very inexpensive (especially compared to marine vendor options), widely available, and depending on the brand, very durable (even in marine environments).
187
+
188
+ Some things I've learned that may be of use.
189
+
190
+ ### The Interference is Real
191
+
192
+ Bluetooth LE runs at 2.4 GHZ. This is an, uh, oversubscribed part of the EM spectrum. If you're not careful with the placement and shielding of your 2.4Ghz devices, you will encounter interference and signal/connection dropoff.
193
+
194
+ HDMI and USB 3.0, both of which operate at approximately twice the frequency of BT-LE can cause as much signal interference as a 2.4Ghz device. One plugin user noticed that when he turned on the HDMI monitor to his RPI, the plugin would lose a connection to some of his devices.
195
+
196
+ ### It's Not as Simple as You Think
197
+
198
+ There are two ways that Bluetooth devices emit their data. One, the old-fashioned way is through a full-fledged connection. Generally, that's a GATT (for Generic ATTribute Profile) connection. The other, which makes sense for IOT devices like most sensors, is by squeezing data into the device's Advertisement protocol.
199
+
200
+ GATT connections require a lot of power and drain batteries pretty quickly, hence the Advertisement protocol hack that IOT devices use.
201
+
202
+ It's also a pain in the butt to maintain a GATT connection. AND you can only have so many per client device (seven, usually).
203
+
204
+ Victron, for example, started off its instant readout program with GATT connections (the battery power didn't matter as their devices are all powered by wire) but eventually gave up and went to an encrypted Advertising protocol which allowed them in the Connect App and their Cerbo devices to monitor multiple devices without a connection.
205
+
206
+ Not all BMS manufacturers are as wise. Many of the inexpensive BMSes developed in China (several of which the plugin supports) use GATT connections. GATT connections are perfectly adequate if all you're doing is connecting through an app, checking your starter battery voltage, for instance, then closing the app. It's another thing when you've got a client like Signal K which is listening for changes to the starter battery voltage at all times.
207
+
208
+ All of which is to say, be aware that if your device uses a GATT connection, it may suffer from connection loss.
209
+
210
+ If your device is connection-y and frequently drops its connection and there's not a lot of obvious interference, please fill out an issue on github.
211
+
212
+ ### RPI 3/4/4b/5 Bluetooth Sucks
213
+
214
+ The Raspberry Pi's native BT radio strength is weak. Unless all your sensors are within direct line of sight and less than 20 or so feet from your RPI you'll get inconsistent results.
215
+
216
+ Get yourself a BT 5.3 USB adapter. They're inexpensive (around $20 US). I have a [Tp-link UB500-Plus](https://www.tp-link.com/us/home-networking/usb-adapter/ub500-plus/) on my boat. *chef's kiss*
217
+
218
+
219
+ ### Bluetooth on Linux ain't Great
220
+
221
+ Linux, IMHO, is a server OS first, a desktop OS second. Unfortunately, a lot of Linux developers have dreams of killing OSx and Windows and saving the world with open source (a noble but foolosh conceit). As a result, things like the Bluetooth stack on Linux skew toward desktop-friendly devices like headphones, keyboards, mice and the like.
222
+
223
+ The use pattern for a connected bluetooth peripheral is: start the scanner for one-time discovery and pairing which is followed by regular connection and usage (with no scanning necessary).
224
+
225
+ For most Advertisement-oriented devices, the use pattern is turn scanner on and listen for changes to Advertised values. As most sensors are Advertisement-oriented, the plug-in keeps the Bluetooth scanner on at all times.
226
+
227
+ If a process on the server turns off the scanner, the plugin will, at least for Advertisement-oriented devices, appear to have stopped. Connected devices will continue to run normally, however.
228
+
229
+ Because Desktop installations are oriented toward connected peripherals, the Bluetooth manager on Rasberry Pi's may turn off the scanner when it's closed by a user. So keep that in mind.
230
+
231
+ Disable/enable the plugin or submit your changes to restart the scanner if it stops.
232
+
233
+ #### D-BUS
234
+ Linux uses D-Bus, a path-oriented data store for getting and setting system info.
235
+
236
+ Bluez, the Debian standard for interacting with BT devices is D-Bus oriented. This plug-in relies on D-Bus for its BT data.
237
+
238
+ D-Bus keeps a reference to scanned BT devices for a default of 30s. If a device is not paired, D-Bus tosses that reference out and waits for another Advertisement from the device before adding the device back into its database.
239
+
240
+ As a result, some devices with Advertisement periods of less than the default Bluez stay alive parameter (30s) appear to be permanently unavailable.
241
+
242
+ The simplest solution is pair any device that fails to show up in the scan. See [pairing guide](./pairing.md) for more info on how to pair a device to your Signal K server.
243
+
244
+ Future versions of the plugin will allow you to pair from the configuration interface (assuming it's possible and I have the time.)
245
+
246
+
247
+ ## THANKS
248
+
249
+ Many thanks to all those who contributed to the project either with code or testing or just letting me sit on a dock near their boat and connect to their devices.
250
+
251
+ - Michael aboard the A Bientot
252
+ - Kevin on the Luna Miel
253
+ - Jason on the Apres Ski
254
+ - Teppo
255
+ - Ilya
256
+ - Guthip
257
+ - Peter
258
+ - Greg
259
+ - Bram
260
+ - Karl
261
+ - Mat in NZ
262
+ - Kees
263
+ - Scarns
264
+ - John W.
265
+ - Sebastian
266
+ - Arjen
267
+ - SDLee
268
+ - Jordan
164
269
 
270
+ It takes a village. Or more appropriately, an armada. Okay, regatta. But you get the idea.
165
271
 
166
272
 
167
273
 
package/index.js CHANGED
@@ -88,7 +88,7 @@ class MissingSensor {
88
88
 
89
89
  }
90
90
  module.exports = function (app) {
91
- var deviceConfigs
91
+ var deviceConfigs=[]
92
92
  var starts=0
93
93
 
94
94
  var plugin = {};
@@ -100,6 +100,9 @@ module.exports = function (app) {
100
100
 
101
101
  plugin.schema = {
102
102
  type: "object",
103
+ htmlDescription:
104
+ `<h2><a href="https://github.com/naugehyde/bt-sensors-plugin-sk/tree/1.2.0-beta#configuration" target="_blank">Plugin Documenation</a><p/><a href="https://github.com/naugehyde/bt-sensors-plugin-sk/issues/new/choose" target="_blank">Report an issue</a><p/><a href="https://discord.com/channels/1170433917761892493/1295425963466952725" target="_blank">Discord thread</a></h2>
105
+ `,
103
106
  required:["adapter","discoveryTimeout", "discoveryInterval"],
104
107
  properties: {
105
108
  adapter: {title: "Bluetooth adapter",
@@ -130,21 +133,6 @@ module.exports = function (app) {
130
133
  const classMap = loadClassMap(app)
131
134
  const sensorMap=new Map()
132
135
 
133
-
134
- /* plugin.registerWithRouter = function(router) {
135
- router.get('/sendPluginState', async (req, res) => {
136
-
137
- res.status(200).json({
138
- "state":(plugin.started?"started":"stopped")
139
- })
140
- });
141
- router.get("/sse", async (req, res) => {
142
- const session = await createSession(req, res);
143
- channel.register(session)
144
- });
145
-
146
- }*/
147
-
148
136
  plugin.start = async function (options, restartPlugin) {
149
137
  plugin.started=true
150
138
  var adapterID=options.adapter
@@ -288,9 +276,11 @@ module.exports = function (app) {
288
276
 
289
277
  function sensorToJSON(sensor){
290
278
  const config = getDeviceConfig(sensor.getMacAddress())
279
+ const schema = sensor.getJSONSchema()
280
+ schema.htmlDescription = sensor.getDescription()
291
281
  return {
292
282
  info: getSensorInfo(sensor),
293
- schema: sensor.getJSONSchema(),
283
+ schema: schema,
294
284
  config: config?config:{}
295
285
  }
296
286
  }
@@ -330,8 +320,7 @@ module.exports = function (app) {
330
320
 
331
321
  function addSensorToList(sensor){
332
322
  app.debug(`adding sensor to list ${sensor.getMacAddress()}`)
333
- if (sensorMap.has(sensor.getMacAddress()) )
334
- debugger
323
+
335
324
  sensorMap.set(sensor.getMacAddress(),sensor)
336
325
  channel.broadcast(sensorToJSON(sensor),"newsensor");
337
326
  }
@@ -350,8 +339,9 @@ module.exports = function (app) {
350
339
  return
351
340
  }
352
341
  s = await instantiateSensor(device,config)
353
- //app.debug(`Instantiated ${config.mac_address}`)
354
-
342
+ if (!s)
343
+ reject("Unable to create sensor")
344
+ else
355
345
  if (s instanceof BLACKLISTED)
356
346
  reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
357
347
  else{
@@ -384,45 +374,47 @@ module.exports = function (app) {
384
374
  }
385
375
  function getDeviceConfig(mac){
386
376
  return deviceConfigs.find((p)=>p.mac_address==mac)
387
- }
388
- async function instantiateSensor(device,config){
389
- try{
377
+ }
378
+ async function getClassFor(device,config){
379
+
380
+ if (config.params?.sensorClass){
381
+ const c = classMap.get(config.params.sensorClass)
382
+ if (c==null)
383
+ throw new Error ("Cannot find class "+config.params.sensorClass)
384
+ return c
385
+ }
390
386
  for (var [clsName, cls] of classMap) {
391
387
  if (clsName.startsWith("_")) continue
392
388
  const c = await cls.identify(device)
393
389
  if (c) {
394
- c.debug=app.debug
395
- const sensor = new c(device,config?.params, config?.gattParams)
396
- sensor.debug=app.debug
397
- sensor.app=app
398
- sensor._adapter=adapter //HACK!
399
-
400
- await sensor.init()
401
- //app.debug(`instantiated ${await BTSensor.getDeviceProp(device,"Address")}`)
402
-
403
- return sensor
390
+ if (Object.hasOwn(config, "params")) {
391
+ config.params.sensorClass=clsName
392
+ }
393
+ return c
404
394
  }
405
- }} catch(error){
395
+ }
396
+ return classMap.get('UNKNOWN')
397
+ }
398
+
399
+ async function instantiateSensor(device,config){
400
+ try{
401
+ const c = await getClassFor(device,config)
402
+ c.debug=app.debug
403
+ const sensor = new c(device, config?.params, config?.gattParams)
404
+ sensor.debug=app.debug
405
+ sensor.app=app
406
+ sensor._adapter=adapter //HACK!
407
+ await sensor.init()
408
+ return sensor
409
+ }
410
+ catch(error){
406
411
  const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
407
412
  app.debug(msg)
408
413
  app.debug(error)
409
414
  app.setPluginError(msg)
415
+ return null
410
416
  }
411
- //if we're here ain't got no class for the device
412
- var sensor
413
- if (config.params?.sensorClass){
414
- var c = classMap.get(config.params.sensorClass)
415
- } else{
416
- c = classMap.get('UNKNOWN')
417
- }
418
- c.debug=app.debug
419
- sensor = new c(device,config?.params, config?.gattParams)
420
- sensor.debug=app.debug
421
- sensor.app=app
422
- sensor._adapter=adapter //HACK!
423
-
424
- await sensor.init()
425
- return sensor
417
+
426
418
  }
427
419
 
428
420
  function initConfiguredDevice(deviceConfig){
@@ -479,7 +471,7 @@ module.exports = function (app) {
479
471
 
480
472
  if (!deviceConfig) {
481
473
  deviceConfig = {mac_address: mac,
482
- discoveryTimeout: discoveryTimeout*1000,
474
+ discoveryTimeout: discoveryTimeout,
483
475
  active: false, unconfigured: true}
484
476
  initConfiguredDevice(deviceConfig)
485
477
  }