bt-sensors-plugin-sk 1.1.0-beta.2.2.0.1 → 1.1.0-beta.2.2.1.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/BTSensor.js +2 -2
- package/README.md +10 -1
- package/index.js +24 -2
- package/package.json +2 -2
- package/sensor_classes/ATC.js +2 -0
- package/sensor_classes/Aranet2.js +3 -1
- package/sensor_classes/Aranet4.js +2 -0
- package/sensor_classes/BlackListedDevice.js +8 -5
- package/sensor_classes/GoveeH50xx.js +47 -0
- package/sensor_classes/{GoveeTH.js → GoveeH510x.js} +9 -9
- package/sensor_classes/Inkbird.js +1 -0
- package/sensor_classes/SwitchBotTH.js +81 -0
- package/sensor_classes/UltrasonicWindMeter.js +2 -2
- package/sensor_classes/Victron/VictronSensor.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +19 -6
package/BTSensor.js
CHANGED
|
@@ -227,7 +227,7 @@ class BTSensor extends EventEmitter {
|
|
|
227
227
|
const _propsProxy = await this._getPropsProxy(device)
|
|
228
228
|
try{
|
|
229
229
|
const rawProps = await _propsProxy.Get(device.helper.iface,prop)
|
|
230
|
-
return rawProps?.value
|
|
230
|
+
return rawProps?.value
|
|
231
231
|
}
|
|
232
232
|
catch(e){
|
|
233
233
|
return null //Property $prop (probably) doesn't exist in $device
|
|
@@ -258,7 +258,7 @@ class BTSensor extends EventEmitter {
|
|
|
258
258
|
if (!md) return null
|
|
259
259
|
const keys = Object.keys(md)
|
|
260
260
|
if (keys && keys.length>0){
|
|
261
|
-
return parseInt(keys[
|
|
261
|
+
return parseInt(keys[keys.length-1])
|
|
262
262
|
}
|
|
263
263
|
return null
|
|
264
264
|
}
|
package/README.md
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
2
|
|
|
3
|
+
## BETA 2.2.1
|
|
4
|
+
|
|
5
|
+
### What's New
|
|
6
|
+
|
|
7
|
+
GoveeH50xx sensor support. Selectable bluetooth adapter on config screen (in case you have more than bluetooth adapter on your server).
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
## BETA 2.2.0
|
|
4
11
|
|
|
5
12
|
### What's New
|
|
13
|
+
|
|
6
14
|
Support for Aranet4 environment sensor and Renogy Rover/Wanderer Controllers. Untested support for Renogy Battery and Inverter clients. If you have a Renogy Battery or Inverter with bluetooth support, please give it a try and let me know how it goes.
|
|
7
15
|
|
|
8
16
|
### RENOGY NOTES
|
|
@@ -30,7 +38,7 @@ Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently suppo
|
|
|
30
38
|
|
|
31
39
|
## REQUIREMENTS
|
|
32
40
|
|
|
33
|
-
* A Linux Signalk boat-puter with
|
|
41
|
+
* A Linux Signalk boat-puter with bluetooth-DBUS support
|
|
34
42
|
* A Bluetooth adapter
|
|
35
43
|
* [Bluez](https://www.bluez.org) installed
|
|
36
44
|
(Go here for [Snap installation instructions](https://snapcraft.io/bluez))
|
|
@@ -57,6 +65,7 @@ If you want to install directly from source (this is mostly of interest to custo
|
|
|
57
65
|
git clone https://github.com/naugehyde/bt-sensors-plugin-sk
|
|
58
66
|
cd bt-sensors-plugin-sk
|
|
59
67
|
git switch '1.1.0'
|
|
68
|
+
git pull
|
|
60
69
|
npm i
|
|
61
70
|
[sudo] npm link
|
|
62
71
|
cd [signalk_home]
|
package/index.js
CHANGED
|
@@ -176,8 +176,11 @@ module.exports = function (app) {
|
|
|
176
176
|
"2) You will have to wait until the scanner has found your device before seeing your device's config fields and saving the configuration. \n"+
|
|
177
177
|
"3) To refresh the list of available devices and their configurations, just open and close the config screen by clicking on the arrow symbol in the config's top bar. \n"+
|
|
178
178
|
"4) If you submit and get errors it may be because the configured devices have not yet all been discovered.",
|
|
179
|
-
required:["discoveryTimeout", "discoveryInterval"],
|
|
179
|
+
required:["adapter","discoveryTimeout", "discoveryInterval"],
|
|
180
180
|
properties: {
|
|
181
|
+
adapter: {title: "Bluetooth adapter",
|
|
182
|
+
type: "string", default: "hci0"},
|
|
183
|
+
|
|
181
184
|
discoveryTimeout: {title: "Default device discovery timeout (in seconds)",
|
|
182
185
|
type: "integer", default: 30,
|
|
183
186
|
minimum: 10,
|
|
@@ -422,13 +425,32 @@ module.exports = function (app) {
|
|
|
422
425
|
sensorMap.clear()
|
|
423
426
|
deviceConfigs=options?.peripherals??[]
|
|
424
427
|
|
|
428
|
+
|
|
429
|
+
|
|
425
430
|
if (plugin.stopped) {
|
|
426
431
|
await sleep(5000) //Make sure plugin.stop() completes first
|
|
427
432
|
//plugin.start is called asynchronously for some reason
|
|
428
433
|
//and does not wait for plugin.stop to complete
|
|
429
434
|
plugin.stopped=false
|
|
430
435
|
}
|
|
431
|
-
|
|
436
|
+
var adapterID=options.adapter
|
|
437
|
+
|
|
438
|
+
if (!adapterID || adapterID==="")
|
|
439
|
+
adapterID = "hci0"
|
|
440
|
+
|
|
441
|
+
app.debug(`Connecting to bluetooth adapter ${adapterID}`);
|
|
442
|
+
|
|
443
|
+
const activeAdapters = await bluetooth.activeAdapters()
|
|
444
|
+
plugin.schema.properties.adapter.enum=[]
|
|
445
|
+
plugin.schema.properties.adapter.enumNames=[]
|
|
446
|
+
for (a of activeAdapters){
|
|
447
|
+
plugin.schema.properties.adapter.enum.push(a.adapter)
|
|
448
|
+
plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
plugin.uiSchema.adapter={'ui:disabled': (activeAdapters.length==1)}
|
|
452
|
+
|
|
453
|
+
adapter = await bluetooth.getAdapter(adapterID)
|
|
432
454
|
await startScanner()
|
|
433
455
|
if (starts>0){
|
|
434
456
|
app.debug(`Plugin ${packageInfo.version} restarting...`);
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.1.0-beta.2.2.
|
|
3
|
+
"version": "1.1.0-beta.2.2.1.1",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird, Ultrasonic wind meters, Mopeka tank readers, Renogy Battery and Solar Controllers (new), Aranet4 environment sensors, and Govee GVH51xx temp sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"dbus-next": "^0.10.2",
|
|
8
|
-
"node-ble": "^1.
|
|
8
|
+
"node-ble": "^1.12.0",
|
|
9
9
|
"int24":"^0.0.1"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
package/sensor_classes/ATC.js
CHANGED
|
@@ -51,6 +51,8 @@ class ATC extends BTSensor{
|
|
|
51
51
|
}
|
|
52
52
|
propertiesChanged(props){
|
|
53
53
|
super.propertiesChanged(props)
|
|
54
|
+
if (!props.hasOwnProperty("ServiceData")) return
|
|
55
|
+
|
|
54
56
|
const buff = this.getServiceData("0000181a-0000-1000-8000-00805f9b34fb")
|
|
55
57
|
if (!buff)
|
|
56
58
|
throw new Error("Unable to get service data for "+this.getDisplayName())
|
|
@@ -34,7 +34,9 @@ class Aranet2 extends AranetSensor{
|
|
|
34
34
|
|
|
35
35
|
}
|
|
36
36
|
propertiesChanged(props){
|
|
37
|
-
super.propertiesChanged(props)
|
|
37
|
+
super.propertiesChanged(props)
|
|
38
|
+
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
39
|
+
|
|
38
40
|
const buff = this.getManufacturerData(0x0702)
|
|
39
41
|
this.emitData("temp", buff)
|
|
40
42
|
this.emitData("humidity", buff)
|
|
@@ -39,6 +39,8 @@ class Aranet4 extends AranetSensor{
|
|
|
39
39
|
}
|
|
40
40
|
propertiesChanged(props){
|
|
41
41
|
super.propertiesChanged(props)
|
|
42
|
+
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
43
|
+
|
|
42
44
|
const buff = this.getManufacturerData(0x0702)
|
|
43
45
|
this.emitData("co2", buff)
|
|
44
46
|
this.emitData("temp", buff)
|
|
@@ -2,10 +2,13 @@ const BTSensor = require("../BTSensor");
|
|
|
2
2
|
class BLACKLISTED extends BTSensor{
|
|
3
3
|
static isSystem = true
|
|
4
4
|
static async identify(device){
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const md = await this.getDeviceProp(device,'ManufacturerData')
|
|
6
|
+
if (md){
|
|
7
|
+
const keys = Object.keys(md)
|
|
8
|
+
if (keys.length==1 && keys[0]==0x004C)
|
|
9
|
+
return this
|
|
10
|
+
}
|
|
11
|
+
return null
|
|
9
12
|
}
|
|
10
13
|
async init(){
|
|
11
14
|
await super.init()
|
|
@@ -13,7 +16,7 @@ class BLACKLISTED extends BTSensor{
|
|
|
13
16
|
}
|
|
14
17
|
reasonForBlacklisting() {
|
|
15
18
|
switch ( this.getManufacturerID()){
|
|
16
|
-
case (0x004C): return "Randomized MAC address"
|
|
19
|
+
case (0x004C): return "Randomized MAC address (Apple)"
|
|
17
20
|
case (0x02e1): return "Device is using VE.Smart" //NOTE: Victron/VictronSensor class
|
|
18
21
|
//determines if a device is using VE.Smart
|
|
19
22
|
//in identify(). If so, identify() returns
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const BTSensor = require("../BTSensor");
|
|
2
|
+
|
|
3
|
+
class GoveeH50xx extends BTSensor {
|
|
4
|
+
|
|
5
|
+
static async identify(device){
|
|
6
|
+
const regex = /^Govee_H50[0-9]{2}_[a-f,A-F,0-9]{4}$/
|
|
7
|
+
//this.getManufacturer()
|
|
8
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
9
|
+
const uuids = await this.getDeviceProp(device,'UUIDs')
|
|
10
|
+
|
|
11
|
+
if (name && name.match(regex) &&
|
|
12
|
+
uuids && uuids.length > 0 &&
|
|
13
|
+
uuids[0] == '0000ec88-0000-1000-8000-00805f9b34fb')
|
|
14
|
+
return this
|
|
15
|
+
else
|
|
16
|
+
return null
|
|
17
|
+
t
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async init(){
|
|
21
|
+
await super.init()
|
|
22
|
+
this.initMetadata()
|
|
23
|
+
}
|
|
24
|
+
initMetadata(){
|
|
25
|
+
this.addMetadatum('temp','K', 'temperature',
|
|
26
|
+
(buffer)=>{return 273.15+(buffer.readUInt16LE(1)/100)
|
|
27
|
+
})
|
|
28
|
+
this.addMetadatum('humidity','ratio', 'humidity',
|
|
29
|
+
(buffer)=>{return buffer.readUInt16LE(3)/10000
|
|
30
|
+
})
|
|
31
|
+
this.addMetadatum('battery','ratio', 'battery strength', (buffer)=>{return buffer.readUInt8(5)/100})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getManufacturer(){
|
|
35
|
+
return "Govee"
|
|
36
|
+
}
|
|
37
|
+
async propertiesChanged(props){
|
|
38
|
+
super.propertiesChanged(props)
|
|
39
|
+
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
40
|
+
|
|
41
|
+
const buffer = this.getManufacturerData(0xec88)
|
|
42
|
+
if (buffer) {
|
|
43
|
+
this.emitValuesFrom(buffer)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
module.exports=GoveeH50xx
|
|
@@ -18,10 +18,10 @@ function decodeTempHumid(tempHumidBytes) {
|
|
|
18
18
|
return {t: tempAsFloat, h: humid};
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
class
|
|
21
|
+
class GoveeH510x extends BTSensor{
|
|
22
22
|
|
|
23
23
|
static async identify(device){
|
|
24
|
-
const regex = /^
|
|
24
|
+
const regex = /^GVH510[0-9]_[a-f,A-F,0-9]{4}$/
|
|
25
25
|
const name = await this.getDeviceProp(device,"Name")
|
|
26
26
|
const uuids = await this.getDeviceProp(device,'UUIDs')
|
|
27
27
|
|
|
@@ -56,12 +56,12 @@ class GoveeTH extends BTSensor{
|
|
|
56
56
|
}
|
|
57
57
|
async propertiesChanged(props){
|
|
58
58
|
super.propertiesChanged(props)
|
|
59
|
-
if (props.ManufacturerData)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
59
|
+
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
60
|
+
|
|
61
|
+
const buffer = this.getManufacturerData(0x0001)
|
|
62
|
+
if (buffer) {
|
|
63
|
+
this.emitValuesFrom(buffer)
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
module.exports=
|
|
67
|
+
module.exports=GoveeH510x
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const BTSensor = require("../BTSensor");
|
|
2
|
+
|
|
3
|
+
class SwitchBotTH extends BTSensor {
|
|
4
|
+
static async test(){
|
|
5
|
+
|
|
6
|
+
const p = {[this.ID.toString()]: [0xd8, 0x35, 0x34, 0x38, 0x4f, 0x70, 0x07, 0x02, 0x04, 0x96, 0x2c, 0x00]}
|
|
7
|
+
this.getDeviceProp=async ()=>{
|
|
8
|
+
return p
|
|
9
|
+
}
|
|
10
|
+
const c = await this.identify()
|
|
11
|
+
const sb = new c()
|
|
12
|
+
sb.initMetadata()
|
|
13
|
+
sb.currentProperties={}
|
|
14
|
+
sb.on("temp", (t)=>console.log(t))
|
|
15
|
+
sb.on("humidity", (h)=>console.log(h))
|
|
16
|
+
sb.on("battery", (b)=>console.log(b))
|
|
17
|
+
|
|
18
|
+
sb.propertiesChanged( {ManufacturerData: p})
|
|
19
|
+
|
|
20
|
+
sb.propertiesChanged( {ServiceData: {[this.batteryService]:[0x77,0x00,0x64]}})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
static ID = 0x0969
|
|
25
|
+
static batteryService = "0000fd3d-0000-1000-8000-00805f9b34fb"
|
|
26
|
+
static async identify(device){
|
|
27
|
+
const md = await this.getDeviceProp(device,'ManufacturerData')
|
|
28
|
+
if (!md) return null
|
|
29
|
+
const keys = Object.keys(md)
|
|
30
|
+
if (keys && keys.length>0){
|
|
31
|
+
const id = keys[keys.length-1]
|
|
32
|
+
if (parseInt(id)==this.ID && md[id].length==12)
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async init(){
|
|
40
|
+
await super.init()
|
|
41
|
+
this.initMetadata()
|
|
42
|
+
}
|
|
43
|
+
initMetadata(){
|
|
44
|
+
this.addMetadatum('temp','K', 'temperature',
|
|
45
|
+
(buffer)=>{
|
|
46
|
+
return (27315+(((buffer[8] & 0x0F)/10 + (buffer[9] & 0x7F)) * (((buffer[9] & 0x80)>0)?100:-100)))/100
|
|
47
|
+
})
|
|
48
|
+
this.addMetadatum('humidity','ratio', 'humidity',
|
|
49
|
+
|
|
50
|
+
(buffer)=>{return (buffer[10] & 0x7F)/100
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
this.addMetadatum("battery", "ratio", "battery strength",
|
|
54
|
+
(buffer)=>{return buffer[2]/100}
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getManufacturer(){
|
|
59
|
+
return "Wonder Labs"
|
|
60
|
+
}
|
|
61
|
+
getName() {
|
|
62
|
+
"SwitchBotTH"
|
|
63
|
+
}
|
|
64
|
+
async propertiesChanged(props){
|
|
65
|
+
super.propertiesChanged(props)
|
|
66
|
+
if (props.ManufacturerData) {
|
|
67
|
+
const buffer = this.getManufacturerData(this.constructor.ID)
|
|
68
|
+
if (buffer) {
|
|
69
|
+
this.emitData("temp", buffer)
|
|
70
|
+
this.emitData("humidity", buffer)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (props.ServiceData) {
|
|
74
|
+
const buffer = this.getServiceData(this.constructor.batteryService)
|
|
75
|
+
if (buffer){
|
|
76
|
+
this.emitData("battery", buffer)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
module.exports=SwitchBotTH
|
|
@@ -19,12 +19,12 @@ class UltrasonicWindMeter extends BTSensor{
|
|
|
19
19
|
)
|
|
20
20
|
this.awsCharacteristic.readValue()
|
|
21
21
|
.then((buffer)=>
|
|
22
|
-
this.
|
|
22
|
+
this.emitData("aws", buffer)
|
|
23
23
|
|
|
24
24
|
)
|
|
25
25
|
this.awaCharacteristic.readValue()
|
|
26
26
|
.then((buffer)=>
|
|
27
|
-
this.
|
|
27
|
+
this.emitData("awa", buffer)
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
}
|
|
@@ -88,7 +88,7 @@ function sleep(x) {
|
|
|
88
88
|
propertiesChanged(props){
|
|
89
89
|
super.propertiesChanged(props)
|
|
90
90
|
if (this.usingGATT()) return
|
|
91
|
-
|
|
91
|
+
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
92
92
|
try{
|
|
93
93
|
const md = this.getManufacturerData(0x2e1)
|
|
94
94
|
if (md && md.length && md[0]==0x10){
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { throws } = require("assert");
|
|
2
1
|
const BTSensor = require("../BTSensor");
|
|
3
2
|
|
|
4
3
|
const crypto = require('crypto');
|
|
4
|
+
const util = require('util');
|
|
5
5
|
const { isGeneratorFunction } = require("util/types");
|
|
6
6
|
|
|
7
7
|
const DEVICE_TYPES = new Map([
|
|
@@ -96,7 +96,7 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
96
96
|
|
|
97
97
|
emitValues(buffer){
|
|
98
98
|
this.emitData("temp", buffer, 0)
|
|
99
|
-
this.
|
|
99
|
+
this.emit("humidity", buffer.readUInt8(2)/100)
|
|
100
100
|
this.emitData("voltage",buffer,3);
|
|
101
101
|
}
|
|
102
102
|
getManufacturer(){
|
|
@@ -164,6 +164,8 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
164
164
|
propertiesChanged(props){
|
|
165
165
|
super.propertiesChanged(props)
|
|
166
166
|
if (this.usingGATT()) return
|
|
167
|
+
if (!props.hasOwnProperty("ServiceData")) return
|
|
168
|
+
|
|
167
169
|
const data = this.getServiceData(this.constructor.SERVICE_MIBEACON)
|
|
168
170
|
var dec
|
|
169
171
|
if (!this.encryptionKey){
|
|
@@ -180,14 +182,22 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
180
182
|
throw new Error(`${this.getNameAndAddress()} received empty decrypted packet. Check that the bind/encryption key in config is correct.`)
|
|
181
183
|
|
|
182
184
|
switch(dec[0]){
|
|
185
|
+
case 0x0D:
|
|
186
|
+
this.emitData("temp",dec,3)
|
|
187
|
+
this.emitData("humidity",dec,5)
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
case 0x0A:
|
|
191
|
+
this.emitData("batteryStrength",dec,3)
|
|
192
|
+
break
|
|
183
193
|
case 0x04:
|
|
184
|
-
this.
|
|
194
|
+
this.emitData("temp",dec,3)
|
|
185
195
|
break
|
|
186
196
|
case 0x06:
|
|
187
|
-
this.
|
|
197
|
+
this.emitData("humidity",dec,3)
|
|
188
198
|
break
|
|
189
199
|
default:
|
|
190
|
-
throw new Error(`${this.getNameAndAddress()} unable to parse decrypted service data (${dec})`)
|
|
200
|
+
throw new Error(`${this.getNameAndAddress()} unable to parse decrypted service data (${util.inspect(dec)})`)
|
|
191
201
|
|
|
192
202
|
}
|
|
193
203
|
}
|
|
@@ -197,8 +207,11 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
197
207
|
const md = this.addMetadatum("encryptionKey", "", "encryptionKey (AKA bindKey) for decryption")
|
|
198
208
|
md.isParam=true
|
|
199
209
|
this.addMetadatum('temp','K', 'temperature',
|
|
200
|
-
(buff,offset)=>{return ((buff.readInt16LE(offset))/
|
|
210
|
+
(buff,offset)=>{return ((buff.readInt16LE(offset))/10) + 273.15})
|
|
201
211
|
this.addMetadatum('humidity','ratio', 'humidity',
|
|
212
|
+
(buff,offset)=>{return buff.readInt16LE(offset)/1000})
|
|
213
|
+
|
|
214
|
+
this.addMetadatum('batteryStrength', 'ratio', 'sensor battery strength',
|
|
202
215
|
(buff,offset)=>{return ((buff.readUInt8(offset))/100)})
|
|
203
216
|
this.addMetadatum('voltage', 'V', 'sensor battery voltage',
|
|
204
217
|
(buff,offset)=>{return ((buff.readUInt16LE(offset))/1000)})
|