bt-sensors-plugin-sk 1.1.0-beta.2 → 1.1.0-beta.2.1
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/README.md +36 -192
- package/bt_co.json +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/sensor_classes/DEVELOPMENT.md +153 -0
- package/sensor_classes/Victron/VictronConstants.js +1 -0
- package/sensor_classes/Victron/VictronSensor.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +4 -1
- package/test.js +0 -32
package/README.md
CHANGED
|
@@ -5,49 +5,45 @@
|
|
|
5
5
|
|
|
6
6
|
BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for connecting to Bluetooth sensors on your boat and sending deltas to Signalk paths with the values the sensors reports. <br>
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
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), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags and Inkbird thermometers.
|
|
9
9
|
|
|
10
|
-
|
|
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>
|
|
11
11
|
|
|
12
|
-
The
|
|
12
|
+
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.
|
|
13
13
|
|
|
14
|
-
|
|
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).
|
|
15
15
|
|
|
16
16
|
## WHO IS IT FOR
|
|
17
17
|
|
|
18
|
-
Signalk users with a Linux boat-puter (Windows and MacOS are NOT supported) and Bluetooth sensors they'd like to integrate into their Signalk datastream.
|
|
19
|
-
|
|
20
|
-
## ALTERNATIVES
|
|
21
|
-
|
|
22
|
-
An [MQTT](https://mqtt.org/) server with an appropriate SK client plugin. There are several MQTT plugin clients in the Signalk appstore.
|
|
23
|
-
|
|
24
|
-
Advantages of this plugin over an MQTT server and client plugin are:
|
|
25
|
-
* simplicity of setup
|
|
26
|
-
* reduced overhead on server
|
|
27
|
-
* one less piece of software to maintain on your boat-puter
|
|
28
|
-
* ease of rolling your own sensor classes
|
|
29
|
-
|
|
30
|
-
The key advantages of an MQTT setup is comprehensive support for BT devices and non-Linux platforms.
|
|
18
|
+
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.
|
|
31
19
|
|
|
32
20
|
## REQUIREMENTS
|
|
33
21
|
|
|
34
|
-
* A
|
|
22
|
+
* A Linux Signalk boat-puter with System-D (NOTE: Most Linux installations support System-D)
|
|
35
23
|
* A Bluetooth adapter
|
|
36
24
|
* [Bluez](https://www.bluez.org) installed
|
|
37
25
|
(Go here for [Snap installation instructions](https://snapcraft.io/bluez))
|
|
38
26
|
* [Node-ble](https://www.npmjs.com/package/node-ble) (installs with the plugin)
|
|
39
|
-
* [utilities-sk](https://github.com/naugehyde/utilities-sk)
|
|
40
27
|
|
|
41
28
|
## INSTALLATION
|
|
29
|
+
|
|
30
|
+
NOTE: If you're running the 1.0.3 release, you will have to reconfigure your devices.<br>
|
|
31
|
+
|
|
42
32
|
### Signalk Appstore
|
|
43
|
-
|
|
33
|
+
The plugin is currently available in the Signalk Appstore. <br>
|
|
34
|
+
|
|
35
|
+
### NPM
|
|
36
|
+
|
|
37
|
+
See https://www.npmjs.com/package/bt-sensors-plugin-sk/v/1.1.0-beta.2 for more information.
|
|
44
38
|
|
|
45
39
|
### Linux
|
|
46
|
-
|
|
40
|
+
|
|
41
|
+
If you want to install directly from source (this is mostly of interest to custom sensor class developers) execute the following from a command prompt:<br>
|
|
47
42
|
|
|
48
43
|
<pre> cd ~/[some_dir]
|
|
49
44
|
git clone https://github.com/naugehyde/bt-sensors-plugin-sk
|
|
50
45
|
cd bt-sensors-plugin-sk
|
|
46
|
+
git switch '1.1.0'
|
|
51
47
|
npm i
|
|
52
48
|
[sudo] npm link
|
|
53
49
|
cd [signalk_home]
|
|
@@ -62,37 +58,38 @@ Finally, restart SK. Plugin should appear in your server plugins list.<br>
|
|
|
62
58
|
|
|
63
59
|
After installing and restarting Signalk you should see a "BT Sensors Plugin" option in the Signalk->Server->Plugin Config page.<br><br>
|
|
64
60
|
|
|
65
|
-
<img width="
|
|
66
|
-
|
|
67
|
-
On initial configuration, wait 45 seconds (by default) for the Bluetooth device to complete its scan of nearby devices. Until the scan is complete, the Sensors section will be disabled. When the scan is complete your screen should look something like this:<br><br>
|
|
61
|
+
<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>
|
|
68
62
|
|
|
69
|
-
|
|
63
|
+
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
64
|
|
|
71
|
-
> TIP:
|
|
65
|
+
> 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
66
|
|
|
73
|
-
Then press the + button to add a sensor. Your screen should look like this:<br><br>
|
|
67
|
+
Then press the + button to add a sensor. Your screen should look something like this:<br><br>
|
|
68
|
+
<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">
|
|
74
69
|
|
|
75
|
-
<img width="1116" alt="Screenshot 2024-09-01 at 8 47 53 PM" src="https://github.com/user-attachments/assets/6fdab5cc-ab01-4441-a88f-2753a49aadbd">
|
|
76
70
|
<br><br>
|
|
77
71
|
|
|
78
|
-
|
|
72
|
+
Select the sensor you want to connect to from the drop down.<br>
|
|
79
73
|
|
|
80
|
-
|
|
74
|
+
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>
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
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>
|
|
77
|
+
|
|
78
|
+
<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">
|
|
79
|
+
|
|
80
|
+
The plugin doesn't need for Signalk to restart after submitting your config but restart if that makes you more comfortable. <br><br>
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
### ADVERTISED DATA
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
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.
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
### GATT CONNECTIONS
|
|
90
87
|
|
|
91
|
-
|
|
88
|
+
If you see something like this on your device config:
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
<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">
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
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
|
|
96
93
|
|
|
97
94
|
## NOW WHAT?
|
|
98
95
|
|
|
@@ -100,161 +97,8 @@ You should see data appear in your data browser. Here's a screenshot of Signalk
|
|
|
100
97
|
|
|
101
98
|
<img width="1142" alt="Screenshot 2024-09-01 at 9 14 27 PM" src="https://github.com/user-attachments/assets/80abbc1c-e01e-4908-aa1a-eec83679cd7c"><br><br>
|
|
102
99
|
|
|
103
|
-
You can now take the data and display it using Kip, 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.
|
|
100
|
+
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?
|
|
104
101
|
|
|
105
|
-
# <a name="development"></a>BLUETOOTH SENSOR CLASS DEVELOPMENT
|
|
106
|
-
|
|
107
|
-
The goal of this project is to support as many mariner-useful sensors as possible. If there's anything we can do to make sensor class development easier, please let us know.<br><br>
|
|
108
|
-
|
|
109
|
-
## REQUIREMENTS
|
|
110
|
-
|
|
111
|
-
* programming knowledge, preferably class programming in Nodejs
|
|
112
|
-
* familiarity with [Node-ble](https://www.npmjs.com/package/node-ble)
|
|
113
|
-
* ideally, the device manufacturer's specification for their bluetooth API
|
|
114
|
-
* failing the above, a willingness to hack or at the very least google aggressively
|
|
115
|
-
|
|
116
|
-
## PROGRAMMING PROCESS
|
|
117
|
-
|
|
118
|
-
### Discovery
|
|
119
|
-
|
|
120
|
-
You'll first need to know what data your sensor produces and the means by which it provides data (GATT Server or advertisement) and the details thereof.<br><br>
|
|
121
|
-
|
|
122
|
-
The first approach is to see if the device manufacturer has documented this. Not all do. Don't worry if you can't find OEM docs, it's likely someone on the internet has figured out your device's whys and wherefores for you already. Google that thang. <br><br>
|
|
123
|
-
|
|
124
|
-
If you're still coming up empty, there are any number of tools you can use to examine the Bluetooth data stream of your device. <br><br>
|
|
125
|
-
|
|
126
|
-
* bluetoothctl (installed with Bluez on Linux)
|
|
127
|
-
* [NRF Connect](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en_US)
|
|
128
|
-
|
|
129
|
-
With these tools you can see what data your device advertises, and what data it provides via a connection to its GATT Server. <br><br>
|
|
130
|
-
|
|
131
|
-
### Coding
|
|
132
|
-
|
|
133
|
-
#### Get the code
|
|
134
|
-
|
|
135
|
-
To get the code you'll first need to clone this repository then create a branch. That's git talk. Google it if you don't know what that means.<br>
|
|
136
|
-
|
|
137
|
-
Once you've done that you're ready for...
|
|
138
|
-
|
|
139
|
-
#### Actual coding
|
|
140
|
-
|
|
141
|
-
Below is a simple Device class for the Xiaomi thermometer with stock firmware. The code demonstrates the core responsibilities of a Bluetooth sensor device class in the BT-Sensor-plugin's framework:
|
|
142
|
-
|
|
143
|
-
* resides in the sensor_classes subdirectory
|
|
144
|
-
* extends the BTSensor class
|
|
145
|
-
* provides metadata for the device's various data points (the static metadata class member)
|
|
146
|
-
* overrides the BTSensor::connect() and disconnect() methods
|
|
147
|
-
* emits values for each data point
|
|
148
|
-
* exports the class
|
|
149
|
-
|
|
150
|
-
<pre>const BTSensor = require("../BTSensor");
|
|
151
|
-
|
|
152
|
-
class LYWSD03MMC extends BTSensor{
|
|
153
|
-
|
|
154
|
-
static needsScannerOn(){
|
|
155
|
-
return false
|
|
156
|
-
}
|
|
157
|
-
static metadata = new Map()
|
|
158
|
-
.set('temp',{unit:'K', description: 'temperature'})
|
|
159
|
-
.set('humidity',{unit:'ratio', description: 'humidity'})
|
|
160
|
-
.set('voltage',{unit:'V', description: 'sensor battery voltage'})
|
|
161
|
-
|
|
162
|
-
constructor(device){
|
|
163
|
-
super(device)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
emitValues(buffer){
|
|
167
|
-
this.emit("temp",((buffer.readInt16LE(0))/100) + 273.1);
|
|
168
|
-
this.emit("humidity",buffer.readUInt8(2)/100);
|
|
169
|
-
this.emit("voltage",buffer.readUInt16LE(3)/1000);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async connect() {
|
|
173
|
-
await this.device.connect()
|
|
174
|
-
var gattServer = await this.device.gatt()
|
|
175
|
-
var gattService = await gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
176
|
-
var gattCharacteristic = await gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
177
|
-
this.emitValues(await gattCharacteristic.readValue())
|
|
178
|
-
await gattCharacteristic.startNotifications();
|
|
179
|
-
gattCharacteristic.on('valuechanged', buffer => {
|
|
180
|
-
this.emitValues(buffer)
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
async disconnect(){
|
|
184
|
-
super.disconnect()
|
|
185
|
-
await this.device.disconnect()
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
module.exports=LYWSD03MMC</pre>
|
|
189
|
-
|
|
190
|
-
Most of the work is done in the connect() method. In this case, the connect() method creates a connection to the device:
|
|
191
|
-
<pre>await this.device.connect()</pre>
|
|
192
|
-
Then it gets the device's gattServer and primary service:
|
|
193
|
-
<pre>
|
|
194
|
-
var gattServer = await this.device.gatt()
|
|
195
|
-
var gattService = await gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
196
|
-
</pre>
|
|
197
|
-
Then, it requests the device's "characteristic" that will send us data.
|
|
198
|
-
<pre>var gattCharacteristic = await gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")</pre>
|
|
199
|
-
Then it asks the characteristic for notifications when its value changes.
|
|
200
|
-
<pre>await gattCharacteristic.startNotifications();</pre>
|
|
201
|
-
Then most importantly it emits the values when the data has changed:
|
|
202
|
-
<pre>gattCharacteristic.on('valuechanged', buffer => {
|
|
203
|
-
this.emitValues(buffer)
|
|
204
|
-
})</pre>
|
|
205
|
-
In this implementation, the emitValues member function does the work of parsing the buffer and emitting the values.
|
|
206
|
-
<pre>
|
|
207
|
-
emitValues(buffer){
|
|
208
|
-
this.emit("temp",((buffer.readInt16LE(0))/100) + 273.1);
|
|
209
|
-
this.emit("humidity",buffer.readUInt8(2)/100);
|
|
210
|
-
this.emit("voltage",buffer.readUInt16LE(3)/1000);
|
|
211
|
-
}
|
|
212
|
-
</pre>
|
|
213
|
-
NOTE: If you guessed that the plugin listens to changes to device objects and then publishes the deltas, you guessed right.
|
|
214
|
-
|
|
215
|
-
### All that said
|
|
216
|
-
The problem with Gatt Server devices is they stay connected and eat up a lot of energy, draining your device's batteries. You can deactivate the device from the config screen when it's not in use or in this case you can flash the device with custom firmware that changes the device to a broadcast device that advertises its data obviating the need for a battery-draining connection. In the case of Xiaomi LYWSD03MMC you can flash it with some very useful software called [ATC](https://github.com/atc1441/ATC_MiThermometer?tab=readme-ov-file) </pre>
|
|
217
|
-
|
|
218
|
-
Below is an example of a BTSensor subclass that uses the advertising protocol to get the data from a flashed Xiaomi thermometer.
|
|
219
|
-
|
|
220
|
-
<pre>const BTSensor = require("../BTSensor");
|
|
221
|
-
const LYWSD03MMC = require('./LYWSD03MMC.js')
|
|
222
|
-
class ATC extends BTSensor{
|
|
223
|
-
|
|
224
|
-
constructor(device){
|
|
225
|
-
super(device)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
static metadata = LYWSD03MMC.metadata
|
|
229
|
-
|
|
230
|
-
connect() {
|
|
231
|
-
const cb = async (propertiesChanged) => {
|
|
232
|
-
try{
|
|
233
|
-
this.device.getServiceData().then((data)=>{
|
|
234
|
-
//TBD Check if the service ID is universal across ATC variants
|
|
235
|
-
const buff=data['0000181a-0000-1000-8000-00805f9b34fb'];
|
|
236
|
-
this.emit("temp",((buff.readInt16LE(6))/100) + 273.1);
|
|
237
|
-
this.emit("humidity",buff.readUInt16LE(8)/10000);
|
|
238
|
-
this.emit("voltage",buff.readUInt16LE(10)/1000);
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
throw new Error(`Unable to read data from ${util.inspect(device)}: ${error}` )
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
cb();
|
|
246
|
-
this.device.helper.on('PropertiesChanged', cb)
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
module.exports=ATC</pre>
|
|
250
|
-
|
|
251
|
-
The big difference here is in the connect() method. All it does is wait on propertiesChanged and when that event occurs, the device object parses the buffer and emits the data. NOTE: Both classes have the same metadata, so the ATC class "borrows" the metadata from the LYWSD03MMC class.<br>
|
|
252
|
-
|
|
253
|
-
## LET US KNOW
|
|
254
|
-
|
|
255
|
-
When you're done working on your class and satisified that it's functioning properly, commit and request a merge (more git talk).<br>
|
|
256
|
-
|
|
257
|
-
We love to see new sensor classes!
|
|
258
102
|
|
|
259
103
|
|
|
260
104
|
|
package/bt_co.json
CHANGED
|
@@ -14517,4 +14517,4 @@
|
|
|
14517
14517
|
"name": "Ericsson AB"
|
|
14518
14518
|
}
|
|
14519
14519
|
]
|
|
14520
|
-
}
|
|
14520
|
+
}
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# BLUETOOTH SENSOR CLASS DEVELOPMENT (OUT OF DATE AS OF 1.1.0 Beta release NEW VERSION COMING SOON)
|
|
2
|
+
|
|
3
|
+
The goal of this project is to support as many mariner-useful sensors as possible. If there's anything we can do to make sensor class development easier, please let us know.<br><br>
|
|
4
|
+
|
|
5
|
+
## REQUIREMENTS
|
|
6
|
+
|
|
7
|
+
* programming knowledge, preferably class programming in Nodejs
|
|
8
|
+
* familiarity with [Node-ble](https://www.npmjs.com/package/node-ble)
|
|
9
|
+
* ideally, the device manufacturer's specification for their bluetooth API
|
|
10
|
+
* failing the above, a willingness to hack or at the very least google aggressively
|
|
11
|
+
|
|
12
|
+
## PROGRAMMING PROCESS
|
|
13
|
+
|
|
14
|
+
### Discovery
|
|
15
|
+
|
|
16
|
+
You'll first need to know what data your sensor produces and the means by which it provides data (GATT Server or advertisement) and the details thereof.<br><br>
|
|
17
|
+
|
|
18
|
+
The first approach is to see if the device manufacturer has documented this. Not all do. Don't worry if you can't find OEM docs, it's likely someone on the internet has figured out your device's whys and wherefores for you already. Google that thang. <br><br>
|
|
19
|
+
|
|
20
|
+
If you're still coming up empty, there are any number of tools you can use to examine the Bluetooth data stream of your device. <br><br>
|
|
21
|
+
|
|
22
|
+
* bluetoothctl (installed with Bluez on Linux)
|
|
23
|
+
* [NRF Connect](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en_US)
|
|
24
|
+
|
|
25
|
+
With these tools you can see what data your device advertises, and what data it provides via a connection to its GATT Server. <br><br>
|
|
26
|
+
|
|
27
|
+
### Coding
|
|
28
|
+
|
|
29
|
+
#### Get the code
|
|
30
|
+
|
|
31
|
+
To get the code you'll first need to clone this repository then create a branch. That's git talk. Google it if you don't know what that means.<br>
|
|
32
|
+
|
|
33
|
+
Once you've done that you're ready for...
|
|
34
|
+
|
|
35
|
+
#### Actual coding
|
|
36
|
+
|
|
37
|
+
Below is a simple Device class for the Xiaomi thermometer with stock firmware. The code demonstrates the core responsibilities of a Bluetooth sensor device class in the BT-Sensor-plugin's framework:
|
|
38
|
+
|
|
39
|
+
* resides in the sensor_classes subdirectory
|
|
40
|
+
* extends the BTSensor class
|
|
41
|
+
* provides metadata for the device's various data points (the static metadata class member)
|
|
42
|
+
* overrides the BTSensor::connect() and disconnect() methods
|
|
43
|
+
* emits values for each data point
|
|
44
|
+
* exports the class
|
|
45
|
+
|
|
46
|
+
<pre>const BTSensor = require("../BTSensor");
|
|
47
|
+
|
|
48
|
+
class LYWSD03MMC extends BTSensor{
|
|
49
|
+
|
|
50
|
+
static needsScannerOn(){
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
static metadata = new Map()
|
|
54
|
+
.set('temp',{unit:'K', description: 'temperature'})
|
|
55
|
+
.set('humidity',{unit:'ratio', description: 'humidity'})
|
|
56
|
+
.set('voltage',{unit:'V', description: 'sensor battery voltage'})
|
|
57
|
+
|
|
58
|
+
constructor(device){
|
|
59
|
+
super(device)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
emitValues(buffer){
|
|
63
|
+
this.emit("temp",((buffer.readInt16LE(0))/100) + 273.1);
|
|
64
|
+
this.emit("humidity",buffer.readUInt8(2)/100);
|
|
65
|
+
this.emit("voltage",buffer.readUInt16LE(3)/1000);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async connect() {
|
|
69
|
+
await this.device.connect()
|
|
70
|
+
var gattServer = await this.device.gatt()
|
|
71
|
+
var gattService = await gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
72
|
+
var gattCharacteristic = await gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
73
|
+
this.emitValues(await gattCharacteristic.readValue())
|
|
74
|
+
await gattCharacteristic.startNotifications();
|
|
75
|
+
gattCharacteristic.on('valuechanged', buffer => {
|
|
76
|
+
this.emitValues(buffer)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
async disconnect(){
|
|
80
|
+
super.disconnect()
|
|
81
|
+
await this.device.disconnect()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
module.exports=LYWSD03MMC</pre>
|
|
85
|
+
|
|
86
|
+
Most of the work is done in the connect() method. In this case, the connect() method creates a connection to the device:
|
|
87
|
+
<pre>await this.device.connect()</pre>
|
|
88
|
+
Then it gets the device's gattServer and primary service:
|
|
89
|
+
<pre>
|
|
90
|
+
var gattServer = await this.device.gatt()
|
|
91
|
+
var gattService = await gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
|
|
92
|
+
</pre>
|
|
93
|
+
Then, it requests the device's "characteristic" that will send us data.
|
|
94
|
+
<pre>var gattCharacteristic = await gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")</pre>
|
|
95
|
+
Then it asks the characteristic for notifications when its value changes.
|
|
96
|
+
<pre>await gattCharacteristic.startNotifications();</pre>
|
|
97
|
+
Then most importantly it emits the values when the data has changed:
|
|
98
|
+
<pre>gattCharacteristic.on('valuechanged', buffer => {
|
|
99
|
+
this.emitValues(buffer)
|
|
100
|
+
})</pre>
|
|
101
|
+
In this implementation, the emitValues member function does the work of parsing the buffer and emitting the values.
|
|
102
|
+
<pre>
|
|
103
|
+
emitValues(buffer){
|
|
104
|
+
this.emit("temp",((buffer.readInt16LE(0))/100) + 273.1);
|
|
105
|
+
this.emit("humidity",buffer.readUInt8(2)/100);
|
|
106
|
+
this.emit("voltage",buffer.readUInt16LE(3)/1000);
|
|
107
|
+
}
|
|
108
|
+
</pre>
|
|
109
|
+
NOTE: If you guessed that the plugin listens to changes to device objects and then publishes the deltas, you guessed right.
|
|
110
|
+
|
|
111
|
+
### All that said
|
|
112
|
+
The problem with Gatt Server devices is they stay connected and eat up a lot of energy, draining your device's batteries. You can deactivate the device from the config screen when it's not in use or in this case you can flash the device with custom firmware that changes the device to a broadcast device that advertises its data obviating the need for a battery-draining connection. In the case of Xiaomi LYWSD03MMC you can flash it with some very useful software called [ATC](https://github.com/atc1441/ATC_MiThermometer?tab=readme-ov-file) </pre>
|
|
113
|
+
|
|
114
|
+
Below is an example of a BTSensor subclass that uses the advertising protocol to get the data from a flashed Xiaomi thermometer.
|
|
115
|
+
|
|
116
|
+
<pre>const BTSensor = require("../BTSensor");
|
|
117
|
+
const LYWSD03MMC = require('./LYWSD03MMC.js')
|
|
118
|
+
class ATC extends BTSensor{
|
|
119
|
+
|
|
120
|
+
constructor(device){
|
|
121
|
+
super(device)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static metadata = LYWSD03MMC.metadata
|
|
125
|
+
|
|
126
|
+
connect() {
|
|
127
|
+
const cb = async (propertiesChanged) => {
|
|
128
|
+
try{
|
|
129
|
+
this.device.getServiceData().then((data)=>{
|
|
130
|
+
//TBD Check if the service ID is universal across ATC variants
|
|
131
|
+
const buff=data['0000181a-0000-1000-8000-00805f9b34fb'];
|
|
132
|
+
this.emit("temp",((buff.readInt16LE(6))/100) + 273.1);
|
|
133
|
+
this.emit("humidity",buff.readUInt16LE(8)/10000);
|
|
134
|
+
this.emit("voltage",buff.readUInt16LE(10)/1000);
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error(`Unable to read data from ${util.inspect(device)}: ${error}` )
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
cb();
|
|
142
|
+
this.device.helper.on('PropertiesChanged', cb)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
module.exports=ATC</pre>
|
|
146
|
+
|
|
147
|
+
The big difference here is in the connect() method. All it does is wait on propertiesChanged and when that event occurs, the device object parses the buffer and emits the data. NOTE: Both classes have the same metadata, so the ATC class "borrows" the metadata from the LYWSD03MMC class.<br>
|
|
148
|
+
|
|
149
|
+
## LET US KNOW
|
|
150
|
+
|
|
151
|
+
When you're done working on your class and satisified that it's functioning properly, commit and request a merge (more git talk).<br>
|
|
152
|
+
|
|
153
|
+
We love to see new sensor classes!
|
|
@@ -209,6 +209,7 @@ module.exports = {
|
|
|
209
209
|
0xA38B: "SmartShunt 2000A/50mV",
|
|
210
210
|
0xA3A4: "Smart Battery Sense",
|
|
211
211
|
0xA3A5: "Smart Battery Sense",
|
|
212
|
+
0xA3B0: "Smart Battery Protect",
|
|
212
213
|
0xA3C0: "Orion Smart 12V|12V-18A Isolated DC-DC Charger",
|
|
213
214
|
0xA3C8: "Orion Smart 12V|12V-30A Isolated DC-DC Charger",
|
|
214
215
|
0xA3D0: "Orion Smart 12V|12V-30A Non-isolated DC-DC Charger",
|
|
@@ -56,7 +56,7 @@ const VC = require('./VictronConstants.js')
|
|
|
56
56
|
return this.constructor.AlarmReason[alarmValue]
|
|
57
57
|
}
|
|
58
58
|
getModelName(){
|
|
59
|
-
return VC
|
|
59
|
+
return VC?.MODEL_ID_MAP[this.model_id]??this.constructor.name+" (Model ID:"+this.model_id+")"
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
decrypt(data){
|
|
@@ -106,7 +106,10 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
106
106
|
return this.GATTwarning
|
|
107
107
|
}
|
|
108
108
|
initGATT(){
|
|
109
|
-
|
|
109
|
+
if (!this?.gattWarningDelivered) {
|
|
110
|
+
this.debug(this.GATTwarning.toUpperCase())
|
|
111
|
+
this.gattWarningDelivered=true
|
|
112
|
+
}
|
|
110
113
|
return new Promise((resolve,reject )=>{
|
|
111
114
|
this.device.connect().then(async ()=>{
|
|
112
115
|
if (!this.gattServer) {
|
package/test.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const {createBluetooth} = require('node-ble')
|
|
2
|
-
const {bluetooth, destroy} = createBluetooth()
|
|
3
|
-
const { argv } = require('node:process');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async function main(){
|
|
7
|
-
|
|
8
|
-
const adapter = await bluetooth.defaultAdapter()
|
|
9
|
-
|
|
10
|
-
try{await adapter.startDiscovery()} catch{}
|
|
11
|
-
const device = await adapter.waitDevice(argv[2])
|
|
12
|
-
var trials=0
|
|
13
|
-
await device.connect()
|
|
14
|
-
const gatt = await device.gatt()
|
|
15
|
-
await device.disconnect()
|
|
16
|
-
|
|
17
|
-
async function search(){
|
|
18
|
-
await device.connect()
|
|
19
|
-
|
|
20
|
-
console.log(`GATT trial #${++trials}...`)
|
|
21
|
-
const s = await gatt.getPrimaryService("65970000-4bda-4c1e-af4b-551c4cf74769")
|
|
22
|
-
const c = await s.getCharacteristic('6597ffff-4bda-4c1e-af4b-551c4cf74769')
|
|
23
|
-
await c.readValue()
|
|
24
|
-
await device.disconnect()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
setInterval(() => {
|
|
28
|
-
search()
|
|
29
|
-
},argv[3]);
|
|
30
|
-
search()
|
|
31
|
-
}
|
|
32
|
-
main()
|