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 +45 -11
- package/README.md +184 -78
- package/Screenshot 2025-06-12 at 9.33.57/342/200/257AM.png +0 -0
- package/index.js +43 -51
- package/package.json +7 -6
- package/plugin_defaults.json +14 -5
- package/public/159.js +2 -1
- package/public/681.js +14 -0
- package/public/{239.js.LICENSE.txt → 681.js.LICENSE.txt} +9 -0
- package/public/847.js +1 -0
- package/public/main.js +1 -1
- package/public/remoteEntry.js +1 -1
- package/sensor_classes/ATC.js +22 -1
- package/sensor_classes/BlackListedDevice.js +10 -7
- package/sensor_classes/GobiusCTankMeter.js +18 -1
- package/sensor_classes/JBDBMS.js +67 -39
- package/sensor_classes/RenogyRoverClient.js +0 -1
- package/sensor_classes/ShellySBMO003Z.js +4 -0
- package/sensor_classes/ShenzhenLiOnBMS.js +177 -0
- package/sensor_classes/Victron/VictronConstants.js +17 -32
- package/sensor_classes/Victron/VictronImages.js +8 -0
- package/sensor_classes/Victron/VictronSensor.js +27 -3
- package/sensor_classes/VictronBatteryMonitor.js +4 -0
- package/sensor_classes/VictronSolarCharger.js +1 -0
- package/sensor_classes/XiaomiMiBeacon.js +5 -0
- package/src/components/PluginConfigurationPanel.js +145 -60
- package/webpack.config.js +1 -1
- package/public/239.js +0 -14
- package/public/778.js +0 -2
- /package/public/{778.js.LICENSE.txt → 159.js.LICENSE.txt} +0 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
+
## WHAT'S NEW SINCE VERSION 1.1.x
|
|
50
17
|
|
|
51
|
-
|
|
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
|
-
|
|
20
|
+
- Sensors appear under tabs indicating their domain (Environmental, Electrical etc.)
|
|
54
21
|
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
Default values for paths for most sensor classes.
|
|
24
|
+
- Default values for paths for most sensor classes.
|
|
59
25
|
|
|
60
|
-
|
|
26
|
+
- Support for multiple simultaneous GATT connections.
|
|
61
27
|
|
|
62
|
-
|
|
28
|
+
## SUPPORTED SENSORS
|
|
63
29
|
|
|
64
|
-
|
|
30
|
+
### NOTE
|
|
65
31
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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@
|
|
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
|
-
|
|
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
|
-
|
|
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="
|
|
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
|
-
|
|
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
|
-
|
|
160
|
+
To delete the configuration, select **Delete**. To undo any changes, select **Undo**.
|
|
145
161
|
|
|
146
|
-
|
|
162
|
+
Your device will restart its connection automatically after saving.
|
|
147
163
|
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
Binary file
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
389
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
474
|
+
discoveryTimeout: discoveryTimeout,
|
|
483
475
|
active: false, unconfigured: true}
|
|
484
476
|
initConfiguredDevice(deviceConfig)
|
|
485
477
|
}
|