bt-sensors-plugin-sk 1.2.3 → 1.2.4-2
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 +17 -1
- package/README.md +39 -8
- package/index.js +4 -1
- package/package.json +1 -1
- package/public/images/Bank Manager All-in-onewc.webp +0 -0
- package/public/images/smartcraft.webp +0 -0
- package/sensor_classes/BankManager.js +96 -0
- package/sensor_classes/GobiusCTankMeter.js +1 -13
- package/sensor_classes/Govee/GoveeSensor.js +47 -0
- package/sensor_classes/GoveeH5074.js +23 -0
- package/sensor_classes/GoveeH5075.js +37 -0
- package/sensor_classes/GoveeH510x.js +22 -47
- package/sensor_classes/JBDBMS.js +1 -13
- package/sensor_classes/MercurySmartcraft.js +126 -0
- package/sensor_classes/RemoranWave3.js +0 -3
- package/sensor_classes/Renogy/RenogySensor.js +1 -7
- package/sensor_classes/RenogyRoverClient.js +1 -1
- package/sensor_classes/ShenzhenLiOnBMS.js +3 -1
- package/sensor_classes/SwitchBotMeterPlus.js +2 -2
- package/sensor_classes/SwitchBotTH.js +2 -2
- package/sensor_classes/VictronBatteryMonitor.js +4 -2
- package/sensor_classes/GoveeH50xx.js +0 -47
package/BTSensor.js
CHANGED
|
@@ -107,6 +107,7 @@ class BTSensor extends EventEmitter {
|
|
|
107
107
|
unknown: { name: "unknown", description: "Unknown sensor domain "},
|
|
108
108
|
environmental: { name: "environmental", description: "Sensors that measure environmental conditions - air temperature, humidity etc."},
|
|
109
109
|
electrical: { name: "electrical", description: "Electrical sensor - chargers, batteries, inverters etc."},
|
|
110
|
+
propulsion: { name: "propulsion", description: "Sensors that measure engine state"},
|
|
110
111
|
tanks: { name: "tanks", description: "Sensors that measure level in tanks (gas, propane, water etc.) "}
|
|
111
112
|
}
|
|
112
113
|
static Domain = this.SensorDomains.unknown
|
|
@@ -484,7 +485,22 @@ class BTSensor extends EventEmitter {
|
|
|
484
485
|
}
|
|
485
486
|
}
|
|
486
487
|
})
|
|
487
|
-
|
|
488
|
+
this.device.on("disconnect", ()=>{
|
|
489
|
+
if (this.isActive()) {
|
|
490
|
+
this.debug(`Device disconnected. Attempting to reconnect to ${this.getName()}`)
|
|
491
|
+
try {
|
|
492
|
+
this.deviceConnect(true).then(()=>{
|
|
493
|
+
this.debug(`Device reconnected -- ${this.getName()}`)
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
catch (e) {
|
|
497
|
+
this.setPluginError( `Error while reconnecting to ${this.getName()}: ${e.message}`)
|
|
498
|
+
this.debug( `Error while reconnecting to ${this.getName()}: ${e.message}`)
|
|
499
|
+
this.debug(e)
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
})
|
|
503
|
+
}
|
|
488
504
|
|
|
489
505
|
try {
|
|
490
506
|
|
package/README.md
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
|
-
# Version 1.2.
|
|
2
|
+
# Version 1.2.4-2
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
- RenogyRoverClient fix to Battery SOC calculation
|
|
5
|
+
- VictronBatteryMonitor set default aux mode to secondary voltage
|
|
6
|
+
- SwitchBotTH and Meter Plus ::identify errors fixed
|
|
7
|
+
- MercurySmartcraft::identify method fix
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
## WHAT'S NEW
|
|
10
|
+
|
|
11
|
+
# 1.2.4-1
|
|
12
|
+
|
|
13
|
+
- **NEW SENSOR** [Bank Manager](https://marinedcac.com/pages/bankmanager) (tested)
|
|
14
|
+
- **NEW SENSOR** [Mercury Smartcraft](https://www.mercurymarine.com/us/en/gauges-and-controls/displays/smartcraft-connect) (untested)
|
|
15
|
+
- Fixed Govee 5075 parsing
|
|
16
|
+
- Added defaults for Renogy Rover Client
|
|
17
|
+
- Automatic reconnect for GATT connected devices
|
|
18
|
+
|
|
19
|
+
### Version 1.2.3
|
|
7
20
|
|
|
8
|
-
|
|
21
|
+
Bug fixes Remoran Wave.3, JunctekBMS, and ShenzhenLiOn
|
|
9
22
|
|
|
10
|
-
|
|
23
|
+
### Version 1.2.2
|
|
11
24
|
|
|
12
25
|
- Junctek BMS and Remoran Wave 3 support (Note: both are not currently field tested)
|
|
13
26
|
|
|
14
27
|
- Fixes to ShenzhenLiOn BMS, Victron Orion XS, Victron DC DC Converter, Victron Smart Lithium Classes
|
|
15
28
|
|
|
16
|
-
|
|
29
|
+
### Version 1.2.1
|
|
17
30
|
|
|
18
31
|
- Dynamic configuration screen. The Device list automatically updates when new devices are found by the BT scanner. (No more annoying screen refresh necessary!).
|
|
19
32
|
|
|
@@ -59,7 +72,7 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
59
72
|
|[Lancol](www.Lancol.com)| [Micro 10C 12V Car Battery Monitor](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/)|
|
|
60
73
|
|[Junctek](https://www.junteks.com)|[Junctek BMS](https://www.junteks.com/pages/product/index) |
|
|
61
74
|
|[Remoran](https://remoran.eu)| [Remoran Wave.3](https://remoran.eu/wave.html)|
|
|
62
|
-
|
|
75
|
+
|[AC DC Systems](https://marinedcac.com) | [Bank Manager] hybrid (Pb and Li) charger(https://marinedcac.com/pages/bankmanager)|
|
|
63
76
|
|
|
64
77
|
### Environmental
|
|
65
78
|
| Manufacturer | Devices |
|
|
@@ -82,6 +95,13 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
82
95
|
| [Gobius](https://gobiusc.com/) | Gobius-C Tank level sensor|
|
|
83
96
|
| [Mopeka](https://www.mopeka.com) | [Mopeka Pro Chek](https://mopeka.com/product-category/recreational-sensors-rv-bbq-etc/) ultrasonic tank level sensor |
|
|
84
97
|
|
|
98
|
+
### Propulsion
|
|
99
|
+
| Manufacturer | Devices |
|
|
100
|
+
|--------------|----------|
|
|
101
|
+
| [Mercury](https://www.mercurymarine.com)| [Mercury Smartcraft](https://www.mercurymarine.com/us/en/gauges-and-controls/displays/smartcraft-connect) connect engine sensor|
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
85
105
|
|
|
86
106
|
## WHO IT'S FOR
|
|
87
107
|
|
|
@@ -138,16 +158,27 @@ Finally, restart SK. Plugin should appear in your server plugins list.<br>
|
|
|
138
158
|
|
|
139
159
|
## KNOWN ISSUES
|
|
140
160
|
|
|
161
|
+
### Connected Devices on Raspberry Pi platform (4/4b/5/CM400/CM500)
|
|
162
|
+
|
|
163
|
+
Onboard Raspberry Pi WiFi can cause interference with the onboard Bluetooth resulting in lost connections to GATT connected devices (Renogy, JBD, etc. )
|
|
164
|
+
|
|
165
|
+
One simple solution is to install a USB BT adapter like the [Tp-link UB500-Plus](https://www.tp-link.com/us/home-networking/usb-adapter/ub500-plus/)
|
|
166
|
+
|
|
167
|
+
### USB 3.0 and HDMI interference
|
|
168
|
+
|
|
169
|
+
Poorly shielded USB 3.0 and HDMI (5ghz) can interfere with BT transmission (2.4ghz).
|
|
170
|
+
|
|
141
171
|
### Configuration Panel
|
|
142
172
|
|
|
143
173
|
- Safari 18.1 on OsX produces errors on load that kill the configuration screen. No known cause. Upgrade to most recent Safari or use Chrome.
|
|
144
174
|
- Unsaved sensor configuration changes are lost after selecting a different sensor. Be sure to Save changes for now.
|
|
145
|
-
-
|
|
175
|
+
- Victron GX, Victron Smart Battery Protect, and Victron VE Bus sensor classes have no default paths currently. Users will need to manually input.
|
|
146
176
|
|
|
147
177
|
### Runtime
|
|
148
178
|
|
|
149
179
|
- IMPORTANT Set `Scan for new devices interval` to `0` after configuration is complete. The plugin will run but in Bluetooth-rich environments, or if you have a long range BT 5.3 device, the system Bluetooth stack may fail after 4 hours or so.
|
|
150
180
|
- There's no way that I know of to remove a SK Path without restarting the server. So if any active paths are changed by the plugin, you'll still see them hanging around in the data browser growing stale until you restart the server.
|
|
181
|
+
- RPi 3/4/5/CM400s when running an Access Point on the on board Wifi can cause a problem connecting to devices through the onboard BT. The only known fix is to disable the onboard Bluetooth and use a USB BT adapter. Alternatively, you can use a USB WiFi adapter. NOTE: This only applies to _connected_ devices like Renogy devices, LiTime batteries etc.
|
|
151
182
|
|
|
152
183
|
## CONFIGURATION
|
|
153
184
|
|
package/index.js
CHANGED
|
@@ -409,8 +409,10 @@ module.exports = function (app) {
|
|
|
409
409
|
try{
|
|
410
410
|
const c = await getClassFor(device,config)
|
|
411
411
|
c.debug=app.debug
|
|
412
|
+
|
|
412
413
|
const sensor = new c(device, config?.params, config?.gattParams)
|
|
413
414
|
sensor.debug=app.debug
|
|
415
|
+
sensor.setPluginError=app.setPluginError
|
|
414
416
|
sensor.app=app
|
|
415
417
|
sensor._adapter=adapter //HACK!
|
|
416
418
|
await sensor.init()
|
|
@@ -420,7 +422,8 @@ module.exports = function (app) {
|
|
|
420
422
|
const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
|
|
421
423
|
app.debug(msg)
|
|
422
424
|
app.debug(error)
|
|
423
|
-
|
|
425
|
+
if (config.active)
|
|
426
|
+
app.setPluginError(msg)
|
|
424
427
|
return null
|
|
425
428
|
}
|
|
426
429
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4-2",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk - see https://www.npmjs.com/package/bt-sensors-plugin-sk#supported-sensors for a list of supported sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const BTSensor = require("../BTSensor");
|
|
2
|
+
|
|
3
|
+
class BankManager extends BTSensor{
|
|
4
|
+
|
|
5
|
+
static Domain = BTSensor.SensorDomains.electrical
|
|
6
|
+
static async identify(device){
|
|
7
|
+
|
|
8
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
9
|
+
const regex = /^BankManager*[0-9]{2}/
|
|
10
|
+
|
|
11
|
+
if (name && name.match(regex))
|
|
12
|
+
return this
|
|
13
|
+
else
|
|
14
|
+
return null
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
initSchema(){
|
|
18
|
+
super.initSchema()
|
|
19
|
+
|
|
20
|
+
this.addDefaultParam("id")
|
|
21
|
+
.default="bankManager"
|
|
22
|
+
|
|
23
|
+
this.addMetadatum('liVoltage','V','Lithium Voltage')
|
|
24
|
+
.default="electrical.batteries.{id}.voltage.lithium"
|
|
25
|
+
this.addMetadatum('pbVoltage','V','Lead Voltage')
|
|
26
|
+
.default="electrical.batteries.{id}.voltage.lead"
|
|
27
|
+
this.addMetadatum('current','A','total current')
|
|
28
|
+
.default="electrical.batteries.{id}.current"
|
|
29
|
+
this.addMetadatum('soc','ratio','state of charge')
|
|
30
|
+
.default="electrical.batteries.{id}.soc"
|
|
31
|
+
this.addMetadatum('connectionStatus','','Connection Status')
|
|
32
|
+
.default="electrical.batteries.{id}.connectionStatus"
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getManufacturer(){
|
|
37
|
+
return "Bank Manager"
|
|
38
|
+
}
|
|
39
|
+
hasGATT(){
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
usingGATT(){
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
emitDataFrom(buffer){
|
|
46
|
+
let data=buffer.toString().split(",")
|
|
47
|
+
this.emit("liVoltage", parseFloat(data[1]))
|
|
48
|
+
this.emit("pbVoltage", parseFloat(data[2]))
|
|
49
|
+
this.emit("current", parseFloat(data[3]))
|
|
50
|
+
this.emit("soc", parseFloat(data[4])/100)
|
|
51
|
+
this.emit("connectionStatus", parseInt(data[5]))
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
emitGATT(){
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
initGATTConnection(){
|
|
60
|
+
return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
|
|
61
|
+
if (!this.gattServer) {
|
|
62
|
+
this.gattServer = await this.device.gatt()
|
|
63
|
+
this.service = await this.gattServer.getPrimaryService("0000ffe0-0000-1000-8000-00805f9b34fb")
|
|
64
|
+
this.characteristic = await this.service.getCharacteristic("0000ffe1-0000-1000-8000-00805f9b34fb")
|
|
65
|
+
}
|
|
66
|
+
resolve(this)
|
|
67
|
+
}) .catch((e)=>{ reject(e.message) }) })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
initGATTNotifications() {
|
|
71
|
+
Promise.resolve(this.characteristic.startNotifications().then(()=>{
|
|
72
|
+
let data = null
|
|
73
|
+
this.characteristic.on('valuechanged', buffer => {
|
|
74
|
+
if (buffer.length>0 && buffer[0]==0x23) {
|
|
75
|
+
data = Buffer.from(buffer)
|
|
76
|
+
} else if (data && buffer.indexOf(0x0d0a)!==-1) {
|
|
77
|
+
data=Buffer.concat([data,buffer.subarray(0,buffer.indexOf(0x0d0a)-1)], data.length+buffer.indexOf(0x0d0a)-1)
|
|
78
|
+
this.emitDataFrom(data)
|
|
79
|
+
data=null
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}))
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async stopListening(){
|
|
87
|
+
super.stopListening()
|
|
88
|
+
if (this.characteristic && await this.characteristic.isNotifying()) {
|
|
89
|
+
await this.characteristic.stopNotifications()
|
|
90
|
+
}
|
|
91
|
+
if (await this.device.isConnected()){
|
|
92
|
+
await this.device.disconnect()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
module.exports=BankManager
|
|
@@ -338,19 +338,7 @@ class GobiusCTankMeter extends BTSensor{
|
|
|
338
338
|
this.service = await this.gattServer.getPrimaryService("0000ffe0-0000-1000-8000-00805f9b34fb")
|
|
339
339
|
this.characteristic = await this.service.getCharacteristic("0000ffe9-0000-1000-8000-00805f9b34fb")
|
|
340
340
|
}
|
|
341
|
-
|
|
342
|
-
if (this.isActive()) {
|
|
343
|
-
this.debug(`Device disconnected. Attempting to reconnect to ${this.getName()}`)
|
|
344
|
-
try {
|
|
345
|
-
this.deviceConnect().then(()=>{
|
|
346
|
-
this.debug(`Device reconnected -- ${this.getName()}`)
|
|
347
|
-
})
|
|
348
|
-
}
|
|
349
|
-
catch (e) {
|
|
350
|
-
this.debug(`Error while reconnecting to ${this.getName()}`)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
})
|
|
341
|
+
|
|
354
342
|
|
|
355
343
|
resolve(this)
|
|
356
344
|
}).catch((e)=>{ reject(e.message) }) })
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const BTSensor = require("../../BTSensor");
|
|
2
|
+
|
|
3
|
+
class GoveeSensor extends BTSensor {
|
|
4
|
+
static Domain = this.SensorDomains.environmental
|
|
5
|
+
static ManufacturerUUID = '0000ec88-0000-1000-8000-00805f9b34fb'
|
|
6
|
+
static async identify(device){
|
|
7
|
+
|
|
8
|
+
const regex = /^Govee_H5075_[a-f,A-F,0-9]{4}$/
|
|
9
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
10
|
+
const uuids = await this.getDeviceProp(device,'UUIDs')
|
|
11
|
+
|
|
12
|
+
if (name && name.match(this.getIDRegex()) &&
|
|
13
|
+
uuids && uuids.length > 0 &&
|
|
14
|
+
uuids[0] == this.ManufacturerUUID)
|
|
15
|
+
return this
|
|
16
|
+
else
|
|
17
|
+
return null
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getManufacturer(){
|
|
22
|
+
return "Govee"
|
|
23
|
+
}
|
|
24
|
+
getPackedTempAndHumidity(buffer, beg )
|
|
25
|
+
{
|
|
26
|
+
const negative=buffer[beg]&0x80
|
|
27
|
+
return {
|
|
28
|
+
packedValue: ((buffer.readIntBE(beg,3))&0xFFFFFF) ^ (negative?0x800000:0x000000),
|
|
29
|
+
tempIsNegative: negative
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
emitTemperatureAndHumidity(packedValue, tempIsNegative ){
|
|
33
|
+
this.emit("temp", 273.15+((((Math.trunc(packedValue/1000))/10))*(tempIsNegative?-1:1)))
|
|
34
|
+
this.emit("humidity", (packedValue % 1000) / 1000)
|
|
35
|
+
|
|
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=GoveeSensor
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const GoveeSensor = require("./Govee/GoveeSensor");
|
|
2
|
+
|
|
3
|
+
class GoveeH5074 extends GoveeSensor {
|
|
4
|
+
static getIDRegex() {
|
|
5
|
+
return /^Govee_H5074_[a-f,A-F,0-9]{4}$/
|
|
6
|
+
}
|
|
7
|
+
initSchema(){
|
|
8
|
+
super.initSchema()
|
|
9
|
+
this.addDefaultParam("zone")
|
|
10
|
+
|
|
11
|
+
this.addDefaultPath("temp","environment.temperature")
|
|
12
|
+
.read= (buffer)=>{return 273.15+(buffer.readUInt16LE(1)/100) }
|
|
13
|
+
|
|
14
|
+
this.addDefaultPath("humidity", "environment.humidity")
|
|
15
|
+
.read = (buffer)=>{return buffer.readUInt16LE(3)/10000}
|
|
16
|
+
|
|
17
|
+
this.addDefaultPath("battery","sensors.batteryStrength")
|
|
18
|
+
.read = (buffer)=>{return buffer.readUInt8(5)/100}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
module.exports=GoveeH5074
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const GoveeSensor = require("./Govee/GoveeSensor");
|
|
2
|
+
|
|
3
|
+
class GoveeH5075 extends GoveeSensor {
|
|
4
|
+
static getIDRegex(){
|
|
5
|
+
return /^Govee_H5075_[a-f,A-F,0-9]{4}$/
|
|
6
|
+
}
|
|
7
|
+
static test(){
|
|
8
|
+
const sensor = new GoveeH5075()
|
|
9
|
+
sensor.getName=()=>{return "Govee H5075 fake"}
|
|
10
|
+
sensor.initSchema()
|
|
11
|
+
sensor.on("temp", (t)=>{console.log(`temp => ${t}`)})
|
|
12
|
+
sensor.on("humidity", (t)=>{console.log(`humidity => ${t}`)})
|
|
13
|
+
sensor.on("battery", (t)=>{console.log(`battery => ${t}`)})
|
|
14
|
+
sensor.emitValuesFrom(Buffer.from([0x00, 0x81, 0xc2 ,0x89 ,0x64 ,0x00]))
|
|
15
|
+
sensor.emitValuesFrom(Buffer.from([0x00,0x03,0xbb,0x94,0x64,0x00]))
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
initSchema(){
|
|
19
|
+
super.initSchema()
|
|
20
|
+
this.addDefaultParam("zone")
|
|
21
|
+
|
|
22
|
+
this.addDefaultPath("temp","environment.temperature")
|
|
23
|
+
this.addDefaultPath("humidity", "environment.humidity")
|
|
24
|
+
this.addDefaultPath("battery","sensors.batteryStrength")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
emitValuesFrom(buffer){
|
|
28
|
+
if (buffer.length<6) {
|
|
29
|
+
this.debug(`Invalid buffer received. Cannot parse buffer ${buffer} for ${this.getMacAndName()}`)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
const val = this.getPackedTempAndHumidity(buffer,1)
|
|
33
|
+
this.emitTemperatureAndHumidity(val.packedValue, val.tempIsNegative)
|
|
34
|
+
this.emit("battery", buffer[4]/100)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
module.exports=GoveeH5075
|
|
@@ -1,37 +1,19 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Check if the temperature is negative
|
|
7
|
-
const isNegative = (baseNum & 0x800000) !== 0;
|
|
8
|
-
|
|
9
|
-
// Extract the temperature and humidity values
|
|
10
|
-
const tempAsInt = baseNum & 0x7FFFFF;
|
|
11
|
-
const tempAsFloat = (tempAsInt / 1000) / 10.0;
|
|
12
|
-
const humid = (tempAsInt % 1000) / 10.0;
|
|
13
|
-
|
|
14
|
-
// Apply the negative sign if necessary
|
|
15
|
-
if (isNegative) {
|
|
16
|
-
return {t:-tempAsFloat, h: humid};
|
|
17
|
-
} else {
|
|
18
|
-
return {t: tempAsFloat, h: humid};
|
|
1
|
+
const GoveeSensor = require("./Govee/GoveeSensor");
|
|
2
|
+
|
|
3
|
+
class GoveeH510x extends GoveeSensor{
|
|
4
|
+
static getIDRegex(){
|
|
5
|
+
return /^GVH510[0-9]_[a-f,A-F,0-9]{4}$/
|
|
19
6
|
}
|
|
20
|
-
}
|
|
21
|
-
class GoveeH510x extends BTSensor{
|
|
22
|
-
static Domain = this.SensorDomains.environmental
|
|
23
|
-
static async identify(device){
|
|
24
|
-
const regex = /^GVH510[0-9]_[a-f,A-F,0-9]{4}$/
|
|
25
|
-
const name = await this.getDeviceProp(device,"Name")
|
|
26
|
-
const uuids = await this.getDeviceProp(device,'UUIDs')
|
|
27
7
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
8
|
+
static test(){
|
|
9
|
+
const sensor = new GoveeH510x()
|
|
10
|
+
sensor.getName=()=>{return "Govee H510x fake"}
|
|
11
|
+
sensor.initSchema()
|
|
12
|
+
sensor.on("temp", (t)=>{console.log(`temp => ${t}`)})
|
|
13
|
+
sensor.on("humidity", (t)=>{console.log(`humidity => ${t}`)})
|
|
14
|
+
sensor.on("battery", (t)=>{console.log(`battery => ${t}`)})
|
|
15
|
+
sensor.emitValuesFrom(Buffer.from([0x01,0x01,0x03,0x6d,0xcc,0x5c]))
|
|
16
|
+
|
|
35
17
|
}
|
|
36
18
|
|
|
37
19
|
initSchema(){
|
|
@@ -44,23 +26,16 @@ class GoveeH510x extends BTSensor{
|
|
|
44
26
|
}
|
|
45
27
|
|
|
46
28
|
emitValuesFrom(buffer){
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
29
|
+
if (buffer.length<6) {
|
|
30
|
+
this.debug(`Invalid buffer received. Cannot parse buffer ${buffer} for ${this.getMacAndName()}`)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
const val = this.getPackedTempAndHumidity(buffer,2)
|
|
34
|
+
this.emitTemperatureAndHumidity(val.packedValue, val.tempIsNegative)
|
|
35
|
+
this.emit("battery", buffer[5]/100)
|
|
52
36
|
|
|
53
|
-
getManufacturer(){
|
|
54
|
-
return "Govee"
|
|
55
37
|
}
|
|
56
|
-
async propertiesChanged(props){
|
|
57
|
-
super.propertiesChanged(props)
|
|
58
|
-
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
59
38
|
|
|
60
|
-
|
|
61
|
-
if (buffer) {
|
|
62
|
-
this.emitValuesFrom(buffer)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
39
|
+
|
|
65
40
|
}
|
|
66
41
|
module.exports=GoveeH510x
|
package/sensor_classes/JBDBMS.js
CHANGED
|
@@ -195,19 +195,7 @@ async getAndEmitCellVoltages(){
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
initGATTInterval(){
|
|
198
|
-
|
|
199
|
-
if (this.isActive()) {
|
|
200
|
-
this.debug(`Device disconnected. Attempting to reconnect to ${this.getName()}`)
|
|
201
|
-
try {
|
|
202
|
-
this.deviceConnect(true).then(()=>{
|
|
203
|
-
this.debug(`Device reconnected -- ${this.getName()}`)
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
catch (e) {
|
|
207
|
-
this.debug(`Error while reconnecting to ${this.getName()}`)
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
})
|
|
198
|
+
|
|
211
199
|
this.emitGATT()
|
|
212
200
|
this.initGATTNotifications()
|
|
213
201
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Service UUID Characteristic UUID Type Param Convertion Unit SignalK Path Comment
|
|
3
|
+
00000000-0000-1000-8000-ec55f9f5b963 (Unknown) 00000001-0000-1000-8000-ec55f9f5b963 write / indicate SDP - - - Enable or disable data stream. To enable write 0x0D 0x01; To disable write 0x0D 0x00.
|
|
4
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000101-0000-1000-8000-ec55f9f5b963 read ? - - ? Returns: Value: 1.1.0 (raw: 312e312e30)
|
|
5
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000102-0000-1000-8000-ec55f9f5b963 write / notify ENGINE_RPM_UUID value / 60 Hz propulsion.p0.revolutions
|
|
6
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000103-0000-1000-8000-ec55f9f5b963 write / notify COOLANT_TEMPERATURE_UUID value + 273.15 Kelvin propulsion.p0.temperature
|
|
7
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000104-0000-1000-8000-ec55f9f5b963 write / notify BATTERY_VOLTAGE_UUID value / 1000 Volts propulsion.p0.alternatorVoltage
|
|
8
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000105-0000-1000-8000-ec55f9f5b963 write / notify UNK_105_UUID ? ? ? Need longer data to figure out what is it. Value around 17384.
|
|
9
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000106-0000-1000-8000-ec55f9f5b963 write / notify ENGINE_RUNTIME_UUID value * 60 Seconds propulsion.p0.runTime
|
|
10
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000107-0000-1000-8000-ec55f9f5b963 write / notify CURRENT_FUEL_FLOW_UUID value / 100000 m3 / hour propulsion.p0.fuel.rate
|
|
11
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000108-0000-1000-8000-ec55f9f5b963 write / notify FUEL_TANK_PCT_UUID value / 100 % propulsion.p0.fuel.tank Maybe there is a more appropriate name for the Signalk path
|
|
12
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 00000109-0000-1000-8000-ec55f9f5b963 write / notify UNK_109_UUID ? ? ? Need longer data to figure out what is it. Raw data varied from 102701 to 102700.
|
|
13
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 0000010a-0000-1000-8000-ec55f9f5b963 write / notify OIL_PRESSURE_UUID value / 100 kPascal propulsion.p0.oilPressure
|
|
14
|
+
00000100-0000-1000-8000-ec55f9f5b963 (L2CAP) 0000010b-0000-1000-8000-ec55f9f5b963 write / notify UNK_10B_UUID ? ? ? Always zero. To investigate with more data.
|
|
15
|
+
*/
|
|
16
|
+
const BTSensor = require("../BTSensor");
|
|
17
|
+
class MercurySmartcraft extends BTSensor{
|
|
18
|
+
static Domain = BTSensor.SensorDomains.propulsion
|
|
19
|
+
|
|
20
|
+
static async identify(device){
|
|
21
|
+
|
|
22
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
23
|
+
const address = await this.getDeviceProp(device,"Address")
|
|
24
|
+
if (name && address && name == `VVM_${address.replace(":","")}`)
|
|
25
|
+
return this
|
|
26
|
+
else
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
hasGATT(){
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
usingGATT(){
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
emitGATT(){
|
|
39
|
+
}
|
|
40
|
+
initSchema(){
|
|
41
|
+
const bo = 0
|
|
42
|
+
|
|
43
|
+
super.initSchema()
|
|
44
|
+
this.addParameter(
|
|
45
|
+
"id",
|
|
46
|
+
{
|
|
47
|
+
"title": "Engine ID",
|
|
48
|
+
"examples": ["port","starboard","p0","p1"]
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
_schema.properties.params.required=["id"]
|
|
52
|
+
|
|
53
|
+
this.addMetadatum("rpm","Hz","engine revolutions per sec",
|
|
54
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/60}
|
|
55
|
+
).default='propulsion.{id}.revolutions'
|
|
56
|
+
|
|
57
|
+
this.addMetadatum("coolant","K","temperature of engine coolant in K",
|
|
58
|
+
(buffer)=>{return buffer.readUInt16LE(bo)+273.15}
|
|
59
|
+
).default='propulsion.{id}.coolantTemperature'
|
|
60
|
+
|
|
61
|
+
this.addMetadatum("alternatorVoltage","V","voltage of alternator",
|
|
62
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/1000}
|
|
63
|
+
).default='propulsion.{id}.alternatorVoltage'
|
|
64
|
+
|
|
65
|
+
this.addMetadatum("runtime","s","Total running time for engine (Engine Hours in seconds)",
|
|
66
|
+
(buffer)=>{return buffer.readUInt16LE(bo)*60}
|
|
67
|
+
).default='propulsion.{id}.runTime'
|
|
68
|
+
|
|
69
|
+
this.addMetadatum("rate","m3/s","Fuel rate of consumption (cubic meters per second)",
|
|
70
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/10000}
|
|
71
|
+
).default='propulsion.{id}.fuel.rate'
|
|
72
|
+
|
|
73
|
+
this.addMetadatum("pressure","Pa","Fuel pressure",
|
|
74
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/100}
|
|
75
|
+
).default='propulsion.{id}.pressure'
|
|
76
|
+
|
|
77
|
+
this.addMetadatum("level","ratio","Level of fluid in tank 0-100%",
|
|
78
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/100}
|
|
79
|
+
).default='tanks.petrol.currentLevel'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
initGATTConnection(){
|
|
83
|
+
return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
|
|
84
|
+
if (!this.gattServer) {
|
|
85
|
+
this.gattServer = await this.device.gatt()
|
|
86
|
+
this.sdpService = await this.gattServer.getPrimaryService("00000000-0000-1000-8000-ec55f9f5b963")
|
|
87
|
+
this.sdpCharacteristic = await this.sdpService.getCharacteristic("00000001-0000-1000-8000-ec55f9f5b963")
|
|
88
|
+
this.dataService = await this.gattServer.getPrimaryService("00000100-0000-1000-8000-ec55f9f5b963")
|
|
89
|
+
this.dataCharacteristics = {
|
|
90
|
+
rpm: await this.dataService.getCharacteristic("00000102-0000-1000-8000-ec55f9f5b963"),
|
|
91
|
+
coolant: await this.dataService.getCharacteristic("00000103-0000-1000-8000-ec55f9f5b963"),
|
|
92
|
+
alternatorVoltage: await this.dataService.getCharacteristic("00000104-0000-1000-8000-ec55f9f5b963"),
|
|
93
|
+
runtime: await this.dataService.getCharacteristic("00000106-0000-1000-8000-ec55f9f5b963"),
|
|
94
|
+
rate: await this.dataService.getCharacteristic("00000107-0000-1000-8000-ec55f9f5b963"),
|
|
95
|
+
level: await this.dataService.getCharacteristic("00000108-0000-1000-8000-ec55f9f5b963"),
|
|
96
|
+
pressure: await this.dataService.getCharacteristic("0000010a-0000-1000-8000-ec55f9f5b963")
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
resolve(this)
|
|
100
|
+
}) .catch((e)=>{ reject(e.message) }) })
|
|
101
|
+
}
|
|
102
|
+
async initGATTNotifications() {
|
|
103
|
+
await this.sdpCharacteristic.writeDataWithoutResponse(Buffer.from([0x0D,0x01]))
|
|
104
|
+
for (const c in this.dataCharacteristics){
|
|
105
|
+
Promise.resolve(this.dataCharacteristics[c].startNotifications().then(()=>{
|
|
106
|
+
this.dataCharacteristics[c].on('valuechanged', buffer => {
|
|
107
|
+
this.emitData(c,buffer)
|
|
108
|
+
})
|
|
109
|
+
}))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async stopListening(){
|
|
114
|
+
super.stopListening()
|
|
115
|
+
for (const c in this.dataCharacteristics){
|
|
116
|
+
if (this.dataCharacteristics[c] && await this.dataCharacteristics[char].isNotifying()) {
|
|
117
|
+
await this.dataCharacteristics[c].stopNotifications()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (await this.device.isConnected()){
|
|
122
|
+
await this.device.disconnect()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
module.exports=MercurySmartcraft
|
|
@@ -53,7 +53,6 @@ const BTSensor = require("../BTSensor");
|
|
|
53
53
|
return true
|
|
54
54
|
}
|
|
55
55
|
emitInfo1Data(buffer){
|
|
56
|
-
this.debug(`emitting info 1 data`)
|
|
57
56
|
if (buffer.length < 20) {
|
|
58
57
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 20 bytes or more.`)
|
|
59
58
|
return
|
|
@@ -82,7 +81,6 @@ const BTSensor = require("../BTSensor");
|
|
|
82
81
|
|
|
83
82
|
}
|
|
84
83
|
emitInfo2Data(buffer){
|
|
85
|
-
this.debug(`emitting info 2 data`)
|
|
86
84
|
|
|
87
85
|
if (buffer.size < 12) {
|
|
88
86
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 12 bytes or more.`)
|
|
@@ -95,7 +93,6 @@ const BTSensor = require("../BTSensor");
|
|
|
95
93
|
this.emit("energy", buffer.readFloatLE(16))
|
|
96
94
|
}
|
|
97
95
|
emitEventData(buffer){
|
|
98
|
-
this.debug(`emitting event data`)
|
|
99
96
|
if (buffer.length < 14) {
|
|
100
97
|
this.debug(buffer)
|
|
101
98
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 14 bytes or more.`)
|
|
@@ -34,7 +34,7 @@ class RenogyRoverClient extends RenogySensor {
|
|
|
34
34
|
this.addMetadatum('batteryType', '', "battery type")
|
|
35
35
|
.default="electrical.chargers.{id}.battery.type"
|
|
36
36
|
this.addMetadatum('batteryPercentage', 'ratio', "battery percentage",
|
|
37
|
-
(buffer)=>{return buffer.readUInt16BE(3) })
|
|
37
|
+
(buffer)=>{return buffer.readUInt16BE(3)/100 })
|
|
38
38
|
.default="electrical.chargers.{id}.battery.soc"
|
|
39
39
|
|
|
40
40
|
this.addMetadatum('batteryVoltage', 'V', "battery voltage",
|
|
@@ -160,7 +160,9 @@ class ShenzhenLiONBMS extends BTSensor{
|
|
|
160
160
|
async initGATTInterval(){
|
|
161
161
|
await this.initGATTNotifications()
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
emitGATT(){
|
|
164
|
+
|
|
165
|
+
}
|
|
164
166
|
async initGATTNotifications() {
|
|
165
167
|
await this.rxCharacteristic.startNotifications()
|
|
166
168
|
this.rxCharacteristic.on('valuechanged', buffer => {
|
|
@@ -24,7 +24,7 @@ class SwitchBotMeterPlus extends BTSensor{
|
|
|
24
24
|
const keys = Object.keys(md)
|
|
25
25
|
if ( (keys && keys.length>0) && (sdKeys && sdKeys.length >0) ){
|
|
26
26
|
const id = keys[keys.length-1]
|
|
27
|
-
if (parseInt(id)==this.ID && md[id][0]==modelID && sdKeys[0] == this.serviceDataKey)
|
|
27
|
+
if (parseInt(id)==this.ID && md[id][0]==this.modelID && sdKeys[0] == this.serviceDataKey)
|
|
28
28
|
return this
|
|
29
29
|
}
|
|
30
30
|
return null
|
|
@@ -63,7 +63,7 @@ class SwitchBotMeterPlus extends BTSensor{
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
getName() {
|
|
66
|
-
return "
|
|
66
|
+
return `SwitchBot Meter Plus (${this?.name??"Unnamed"})`
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
}
|
|
@@ -32,7 +32,7 @@ class SwitchBotTH extends BTSensor {
|
|
|
32
32
|
const keys = Object.keys(md)
|
|
33
33
|
if (keys && keys.length>0){
|
|
34
34
|
const id = keys[keys.length-1]
|
|
35
|
-
if (parseInt(id)==this.ID && md[id].length==12 && md[id][0]==modelID)
|
|
35
|
+
if (parseInt(id)==this.ID && md[id].length==12 && md[id][0]==this.modelID)
|
|
36
36
|
return this
|
|
37
37
|
}
|
|
38
38
|
return null
|
|
@@ -59,7 +59,7 @@ class SwitchBotTH extends BTSensor {
|
|
|
59
59
|
return "Wonder Labs"
|
|
60
60
|
}
|
|
61
61
|
getName() {
|
|
62
|
-
|
|
62
|
+
return `SwitchBotTH (${this?.name??"Unnamed"})`
|
|
63
63
|
}
|
|
64
64
|
async propertiesChanged(props){
|
|
65
65
|
super.propertiesChanged(props)
|
|
@@ -22,7 +22,9 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
22
22
|
d.currentProperties = {}
|
|
23
23
|
d.currentProperties.ManufacturerData={}
|
|
24
24
|
d.currentProperties.ManufacturerData[0x02e1]=b
|
|
25
|
-
d.
|
|
25
|
+
d.debug = (m)=>{console.log(m)}
|
|
26
|
+
d.debug.bind(d)
|
|
27
|
+
d.initSchema()
|
|
26
28
|
Object.keys(d.getPaths()).forEach((tag)=>{
|
|
27
29
|
d.on(tag,(v)=>console.log(`${tag}=${v}`))
|
|
28
30
|
})
|
|
@@ -72,7 +74,7 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
72
74
|
this.addDefaultPath( 'ttg',"electrical.batteries.capacity.timeRemaining")
|
|
73
75
|
.read=(buff,offset=0)=>{return this.NaNif(buff.readUInt16LE(offset),0xFFFF)*60}
|
|
74
76
|
this.getPath("ttg").gatt='65970ffe-4bda-4c1e-af4b-551c4cf74769'
|
|
75
|
-
|
|
77
|
+
this.auxMode=VC.AuxMode.STARTER_VOLTAGE
|
|
76
78
|
try {
|
|
77
79
|
if (this.encryptionKey){
|
|
78
80
|
const decData = this.decrypt(this.getManufacturerData(0x02e1))
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const BTSensor = require("../BTSensor");
|
|
2
|
-
|
|
3
|
-
class GoveeH50xx extends BTSensor {
|
|
4
|
-
static Domain = this.SensorDomains.environmental
|
|
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
|
-
initSchema(){
|
|
21
|
-
super.initSchema()
|
|
22
|
-
this.addDefaultParam("zone")
|
|
23
|
-
|
|
24
|
-
this.addDefaultPath("temp","environment.temperature")
|
|
25
|
-
.read= (buffer)=>{return 273.15+(buffer.readUInt16LE(1)/100) }
|
|
26
|
-
|
|
27
|
-
this.addDefaultPath("humidity", "environment.humidity")
|
|
28
|
-
.read = (buffer)=>{return buffer.readUInt16LE(3)/10000}
|
|
29
|
-
|
|
30
|
-
this.addDefaultPath("battery","sensors.batteryStrength")
|
|
31
|
-
.read = (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
|