bt-sensors-plugin-sk 1.2.2 → 1.2.4-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 +31 -2
- package/README.md +35 -5
- package/index.js +4 -1
- package/package.json +2 -2
- 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 +7 -6
- package/sensor_classes/Junctek.js +10 -10
- package/sensor_classes/MercurySmartcraft.js +126 -0
- package/sensor_classes/RemoranWave3.js +36 -28
- package/sensor_classes/Renogy/RenogyConstants.js +2 -1
- package/sensor_classes/Renogy/RenogySensor.js +1 -11
- package/sensor_classes/RenogyRoverClient.js +62 -2
- package/sensor_classes/ShenzhenLiOnBMS.js +3 -1
- 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
|
|
@@ -465,13 +466,41 @@ class BTSensor extends EventEmitter {
|
|
|
465
466
|
throw new Error("::initGATTNotifications() should be implemented by the BTSensor subclass")
|
|
466
467
|
}
|
|
467
468
|
|
|
468
|
-
deviceConnect() {
|
|
469
|
+
deviceConnect(reconnect=false) {
|
|
469
470
|
|
|
470
471
|
|
|
471
472
|
return connectQueue.enqueue( async ()=>{
|
|
472
473
|
this.debug(`Connecting... ${this.getName()}`)
|
|
473
|
-
await this.device.
|
|
474
|
+
await this.device.helper.callMethod('Connect')
|
|
475
|
+
|
|
474
476
|
this.debug(`Connected to ${this.getName()}`)
|
|
477
|
+
if (!reconnect) {
|
|
478
|
+
this.device.helper.on('PropertiesChanged', (propertiesChanged) => {
|
|
479
|
+
if ('Connected' in propertiesChanged) {
|
|
480
|
+
const { value } = propertiesChanged.Connected
|
|
481
|
+
if (value) {
|
|
482
|
+
this.device.emit('connect', { connected: true })
|
|
483
|
+
} else {
|
|
484
|
+
this.device.emit('disconnect', { connected: false })
|
|
485
|
+
}
|
|
486
|
+
}
|
|
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
|
+
}
|
|
475
504
|
|
|
476
505
|
try {
|
|
477
506
|
|
package/README.md
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
|
-
# Version 1.2.
|
|
2
|
+
# Version 1.2.4-1
|
|
3
3
|
|
|
4
|
-
## WHAT'S NEW
|
|
4
|
+
## WHAT'S NEW
|
|
5
|
+
|
|
6
|
+
- **NEW SENSOR** [Bank Manager](https://marinedcac.com/pages/bankmanager) (tested)
|
|
7
|
+
- **NEW SENSOR** [Mercury Smartcraft](https://www.mercurymarine.com/us/en/gauges-and-controls/displays/smartcraft-connect) (untested)
|
|
8
|
+
- Fixed Govee 5075 parsing
|
|
9
|
+
- Added defaults for Renogy Rover Client
|
|
10
|
+
- Automatic reconnect for GATT connected devices
|
|
11
|
+
|
|
12
|
+
### Version 1.2.3
|
|
13
|
+
|
|
14
|
+
Bug fixes Remoran Wave.3, JunctekBMS, and ShenzhenLiOn
|
|
15
|
+
|
|
16
|
+
### Version 1.2.2
|
|
5
17
|
|
|
6
18
|
- Junctek BMS and Remoran Wave 3 support (Note: both are not currently field tested)
|
|
7
19
|
|
|
8
20
|
- Fixes to ShenzhenLiOn BMS, Victron Orion XS, Victron DC DC Converter, Victron Smart Lithium Classes
|
|
9
21
|
|
|
10
|
-
|
|
22
|
+
### Version 1.2.1
|
|
11
23
|
|
|
12
24
|
- Dynamic configuration screen. The Device list automatically updates when new devices are found by the BT scanner. (No more annoying screen refresh necessary!).
|
|
13
25
|
|
|
@@ -53,7 +65,7 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
53
65
|
|[Lancol](www.Lancol.com)| [Micro 10C 12V Car Battery Monitor](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/)|
|
|
54
66
|
|[Junctek](https://www.junteks.com)|[Junctek BMS](https://www.junteks.com/pages/product/index) |
|
|
55
67
|
|[Remoran](https://remoran.eu)| [Remoran Wave.3](https://remoran.eu/wave.html)|
|
|
56
|
-
|
|
68
|
+
|[AC DC Systems](https://marinedcac.com) | [Bank Manager] hybrid (Pb and Li) charger(https://marinedcac.com/pages/bankmanager)|
|
|
57
69
|
|
|
58
70
|
### Environmental
|
|
59
71
|
| Manufacturer | Devices |
|
|
@@ -76,6 +88,13 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
76
88
|
| [Gobius](https://gobiusc.com/) | Gobius-C Tank level sensor|
|
|
77
89
|
| [Mopeka](https://www.mopeka.com) | [Mopeka Pro Chek](https://mopeka.com/product-category/recreational-sensors-rv-bbq-etc/) ultrasonic tank level sensor |
|
|
78
90
|
|
|
91
|
+
### Propulsion
|
|
92
|
+
| Manufacturer | Devices |
|
|
93
|
+
|--------------|----------|
|
|
94
|
+
| [Mercury](https://www.mercurymarine.com)| [Mercury Smartcraft](https://www.mercurymarine.com/us/en/gauges-and-controls/displays/smartcraft-connect) connect engine sensor|
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
79
98
|
|
|
80
99
|
## WHO IT'S FOR
|
|
81
100
|
|
|
@@ -132,16 +151,27 @@ Finally, restart SK. Plugin should appear in your server plugins list.<br>
|
|
|
132
151
|
|
|
133
152
|
## KNOWN ISSUES
|
|
134
153
|
|
|
154
|
+
### Connected Devices on Raspberry Pi platform (4/4b/5/CM400/CM500)
|
|
155
|
+
|
|
156
|
+
Onboard Raspberry Pi WiFi can cause interference with the onboard Bluetooth resulting in lost connections to GATT connected devices (Renogy, JBD, etc. )
|
|
157
|
+
|
|
158
|
+
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/)
|
|
159
|
+
|
|
160
|
+
### USB 3.0 and HDMI interference
|
|
161
|
+
|
|
162
|
+
Poorly shielded USB 3.0 and HDMI (5ghz) can interfere with BT transmission (2.4ghz).
|
|
163
|
+
|
|
135
164
|
### Configuration Panel
|
|
136
165
|
|
|
137
166
|
- 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.
|
|
138
167
|
- Unsaved sensor configuration changes are lost after selecting a different sensor. Be sure to Save changes for now.
|
|
139
|
-
-
|
|
168
|
+
- Victron GX, Victron Smart Battery Protect, and Victron VE Bus sensor classes have no default paths currently. Users will need to manually input.
|
|
140
169
|
|
|
141
170
|
### Runtime
|
|
142
171
|
|
|
143
172
|
- 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.
|
|
144
173
|
- 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.
|
|
174
|
+
- 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.
|
|
145
175
|
|
|
146
176
|
## CONFIGURATION
|
|
147
177
|
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Bluetooth Sensors for Signalk - see https://www.npmjs.com/package/bt-sensors-plugin-sk for a list of supported
|
|
3
|
+
"version": "1.2.4-1",
|
|
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": {
|
|
7
7
|
"@rjsf/bootstrap-4": "^5.24.11",
|
|
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
|
@@ -52,10 +52,10 @@ class JBDBMS extends BTSensor {
|
|
|
52
52
|
(buffer)=>{return buffer.readInt16BE(6) / 100}
|
|
53
53
|
|
|
54
54
|
this.addDefaultPath('remainingCapacity','electrical.batteries.capacity.remaining')
|
|
55
|
-
.read=(buffer)=>{return buffer.readUInt16BE(8) / 100}
|
|
55
|
+
.read=(buffer)=>{return (buffer.readUInt16BE(8) / 100)*3600}
|
|
56
56
|
|
|
57
57
|
this.addDefaultPath('capacity','electrical.batteries.capacity.actual')
|
|
58
|
-
.read=(buffer)=>{return buffer.readUInt16BE(10) / 100}
|
|
58
|
+
.read=(buffer)=>{return (buffer.readUInt16BE(10) / 100)*3600}
|
|
59
59
|
|
|
60
60
|
this.addDefaultPath('cycles','electrical.batteries.cycles' )
|
|
61
61
|
.read=(buffer)=>{return buffer.readUInt16BE(12)}
|
|
@@ -139,7 +139,7 @@ class JBDBMS extends BTSensor {
|
|
|
139
139
|
let datasize = -1
|
|
140
140
|
const timer = setTimeout(() => {
|
|
141
141
|
clearTimeout(timer)
|
|
142
|
-
reject(new Error(`Response timed out from JBDBMS device ${this.getName()}. `));
|
|
142
|
+
reject(new Error(`Response timed out (+30s) from JBDBMS device ${this.getName()}. `));
|
|
143
143
|
}, 30000);
|
|
144
144
|
|
|
145
145
|
const valChanged = async (buffer) => {
|
|
@@ -147,13 +147,12 @@ class JBDBMS extends BTSensor {
|
|
|
147
147
|
if (buffer[0]!==0xDD || buffer.length < 5 || buffer[1] !== command)
|
|
148
148
|
reject(`Invalid buffer from ${this.getName()}, not processing.`)
|
|
149
149
|
else
|
|
150
|
-
datasize=buffer[
|
|
150
|
+
datasize=buffer[3]
|
|
151
151
|
}
|
|
152
152
|
buffer.copy(result,offset)
|
|
153
|
-
if (buffer[buffer.length-1]==0x77 && offset+buffer.length-
|
|
153
|
+
if (buffer[buffer.length-1]==0x77 && offset+buffer.length-7==datasize){
|
|
154
154
|
|
|
155
155
|
result = Uint8Array.prototype.slice.call(result, 0, offset+buffer.length)
|
|
156
|
-
this.debug(result)
|
|
157
156
|
this.rxChar.removeAllListeners()
|
|
158
157
|
clearTimeout(timer)
|
|
159
158
|
if (!checkSum(result))
|
|
@@ -196,6 +195,8 @@ async getAndEmitCellVoltages(){
|
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
initGATTInterval(){
|
|
198
|
+
|
|
199
|
+
this.emitGATT()
|
|
199
200
|
this.initGATTNotifications()
|
|
200
201
|
}
|
|
201
202
|
|
|
@@ -69,10 +69,10 @@ class JunctekBMS extends BTSensor{
|
|
|
69
69
|
const v = parseInt(bytesToBase10String(value))
|
|
70
70
|
switch (byte){
|
|
71
71
|
case 0xC0:{
|
|
72
|
-
|
|
72
|
+
emit("voltage",v/100)
|
|
73
73
|
}
|
|
74
74
|
case 0xC1:{
|
|
75
|
-
|
|
75
|
+
emit("current",(v/100)*chargeDirection)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
case 0xD1:{
|
|
@@ -81,29 +81,29 @@ class JunctekBMS extends BTSensor{
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
case 0xD2:{
|
|
84
|
-
|
|
84
|
+
emit("remainingAh",v/1000)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
case 0xD3:{
|
|
88
|
-
|
|
88
|
+
emit("discharge",v/100000)
|
|
89
89
|
}
|
|
90
90
|
case 0xD4:{
|
|
91
|
-
|
|
91
|
+
emit("charge",v/100000)
|
|
92
92
|
}
|
|
93
93
|
case 0xD6:{
|
|
94
|
-
|
|
94
|
+
emit("timeRemaining",v*60)
|
|
95
95
|
}
|
|
96
96
|
case 0xD7:{
|
|
97
|
-
|
|
97
|
+
emit("impedance",v/100)
|
|
98
98
|
}
|
|
99
99
|
case 0xD8:{
|
|
100
|
-
|
|
100
|
+
emit("power",(v/100)*chargeDirection)
|
|
101
101
|
}
|
|
102
102
|
case 0xD9:{
|
|
103
|
-
|
|
103
|
+
emit("temperature",v + 173.15) //assume C not F
|
|
104
104
|
}
|
|
105
105
|
case 0xB1:{
|
|
106
|
-
|
|
106
|
+
emit("capacityActual",v /10 )
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -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 == `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
|
|
@@ -33,10 +33,10 @@ const states= ["Charging Needed", "Charging", "Floating", "Idle"]
|
|
|
33
33
|
const BTSensor = require("../BTSensor");
|
|
34
34
|
class RemoranWave3 extends BTSensor{
|
|
35
35
|
static Domain = BTSensor.SensorDomains.electrical
|
|
36
|
-
serviceUUID = "
|
|
37
|
-
info1CharUUID = "
|
|
36
|
+
serviceUUID = "81d08df0-c0f8-422a-9d9d-e4379bb1ea3b"
|
|
37
|
+
info1CharUUID = "62c91222-fafe-4f6e-95f0-afc02bd19f2e"
|
|
38
38
|
info2CharUUID = "f5d12d34-4390-486c-b906-24ea8906af71"
|
|
39
|
-
|
|
39
|
+
eventUUID = "f12a8e25-59f7-42f2-b7ae-ba96fb25c13c"
|
|
40
40
|
|
|
41
41
|
static async identify(device){
|
|
42
42
|
|
|
@@ -57,53 +57,61 @@ const BTSensor = require("../BTSensor");
|
|
|
57
57
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 20 bytes or more.`)
|
|
58
58
|
return
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
this.emit("versionNumber", buffer.readUInt8(0))
|
|
61
61
|
const errors = buffer.readUInt8(2)
|
|
62
62
|
const errorState = []
|
|
63
63
|
for (var i = 0; i < 8; ++i) {
|
|
64
64
|
var c = 1 << i;
|
|
65
|
-
|
|
65
|
+
errors & c && errorState.push(errors[i])
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
this.emit("errors", errorState)
|
|
68
|
+
this.emit("state", states[buffer.readUInt8(3)])
|
|
69
|
+
this.emit("rpm", buffer.readUInt32LE(4))
|
|
70
|
+
this.emit( "voltage" , buffer.readFloatLE(8))
|
|
71
|
+
this.emit("current", buffer.readFloatLE(12))
|
|
72
|
+
this.emit( "power", buffer.readFloatLE(16))
|
|
73
73
|
|
|
74
74
|
if (buffer.length > 23) {
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
this.emit( "temp", ((buffer.readFloatLE(20))+273.15))
|
|
76
|
+
this.emit( "uptime", buffer.readUInt32LE(24))
|
|
77
77
|
if (versionNumber>1 && buffer.size > 31) {
|
|
78
|
-
|
|
78
|
+
this.emit("energy", buffer.readFloatLE(32))
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
}
|
|
83
83
|
emitInfo2Data(buffer){
|
|
84
|
+
|
|
84
85
|
if (buffer.size < 12) {
|
|
85
86
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 12 bytes or more.`)
|
|
86
87
|
return
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
this.emit("versionNumber", buffer.readUInt8(0))
|
|
90
|
+
this.emit("temp", ((buffer.readFloatLE(4))+273.15))
|
|
91
|
+
this.emit("uptime", buffer.readUInt32LE(8))
|
|
92
|
+
this.emit("lastBootTime", arduinoDateDecode(buffer.readUInt32LE(12)))
|
|
93
|
+
this.emit("energy", buffer.readFloatLE(16))
|
|
93
94
|
}
|
|
94
95
|
emitEventData(buffer){
|
|
95
|
-
if (buffer.
|
|
96
|
+
if (buffer.length < 14) {
|
|
97
|
+
this.debug(buffer)
|
|
96
98
|
app.debug(`Bad buffer size ${buffer.length}. Buffer size must be 14 bytes or more.`)
|
|
97
99
|
return
|
|
98
100
|
}
|
|
99
|
-
|
|
101
|
+
const eventType = buffer.readUInt16LE(8)
|
|
102
|
+
var eventDesc = eventType.toString()
|
|
103
|
+
if (Object.hasOwn(eventTypes,eventType))
|
|
104
|
+
eventDesc = eventTypes[eventType]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
this.emit("event",
|
|
100
108
|
{
|
|
101
109
|
firstDate: arduinoDateDecode(buffer.readUInt32LE(0)),
|
|
102
110
|
lastDate: arduinoDateDecode(buffer.readUInt32LE(4)),
|
|
103
|
-
eventType:
|
|
111
|
+
eventType: eventType,
|
|
104
112
|
count: buffer.readUInt16LE(10),
|
|
105
113
|
index: buffer.readUInt16LE(12),
|
|
106
|
-
eventDesc:
|
|
114
|
+
eventDesc: eventDesc
|
|
107
115
|
}
|
|
108
116
|
)
|
|
109
117
|
}
|
|
@@ -174,10 +182,10 @@ const BTSensor = require("../BTSensor");
|
|
|
174
182
|
this.gattServer = await this.device.gatt()
|
|
175
183
|
this.service = await this.gattServer.getPrimaryService(this.serviceUUID)
|
|
176
184
|
this.info1Characteristic = await this.service.getCharacteristic(this.info1CharUUID)
|
|
177
|
-
this.info2Characteristic = await this.service.
|
|
185
|
+
this.info2Characteristic = await this.service.getCharacteristic(this.info2CharUUID)
|
|
178
186
|
this.eventCharacteristic = await this.service.getCharacteristic(this.eventUUID)
|
|
179
187
|
resolve(this)
|
|
180
|
-
}}) .catch((e)=>{ reject(e.message) }) })
|
|
188
|
+
}}) .catch((e)=>{ this.debug(e); reject(e.message) }) })
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
initGATTNotifications() {
|
|
@@ -205,9 +213,9 @@ const BTSensor = require("../BTSensor");
|
|
|
205
213
|
}
|
|
206
214
|
async stopListening(){
|
|
207
215
|
super.stopListening()
|
|
208
|
-
await
|
|
209
|
-
await
|
|
210
|
-
await
|
|
216
|
+
await this.stopNotifications(this?.info1Characteristic)
|
|
217
|
+
await this.stopNotifications(this?.info2Characteristic)
|
|
218
|
+
await this.stopNotifications(this?.eventCharacteristic)
|
|
211
219
|
if (await this.device.isConnected()){
|
|
212
220
|
await this.device.disconnect()
|
|
213
221
|
}
|
|
@@ -42,13 +42,7 @@ class RenogySensor extends BTSensor{
|
|
|
42
42
|
|
|
43
43
|
async initSchema(){
|
|
44
44
|
await super.initSchema()
|
|
45
|
-
|
|
46
|
-
"refreshInterval",
|
|
47
|
-
{
|
|
48
|
-
title: 'refresh interval',
|
|
49
|
-
type: 'number'
|
|
50
|
-
}
|
|
51
|
-
)
|
|
45
|
+
|
|
52
46
|
this.addParameter(
|
|
53
47
|
"deviceID",
|
|
54
48
|
{
|
|
@@ -57,9 +51,6 @@ class RenogySensor extends BTSensor{
|
|
|
57
51
|
)
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
emitGATT(){
|
|
61
|
-
}
|
|
62
|
-
|
|
63
54
|
getModelName(){
|
|
64
55
|
return this?.modelID??`${this.constructor.name} Unknown model`
|
|
65
56
|
}
|
|
@@ -86,7 +77,6 @@ class RenogySensor extends BTSensor{
|
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
initGATTInterval(){
|
|
89
|
-
this.emitGATT()
|
|
90
80
|
this.intervalID = setInterval(()=>{
|
|
91
81
|
this.emitGATT()
|
|
92
82
|
}, 1000*(this?.pollFreq??60) )
|
|
@@ -6,53 +6,113 @@ const RenogySensor = require("./Renogy/RenogySensor.js");
|
|
|
6
6
|
const RC=require("./Renogy/RenogyConstants.js")
|
|
7
7
|
|
|
8
8
|
class RenogyRoverClient extends RenogySensor {
|
|
9
|
-
|
|
9
|
+
/*
|
|
10
|
+
"batteryType": "electrical.charger.battery.type",
|
|
11
|
+
"batteryPercentage": "electrical.charger.battery.charge",
|
|
12
|
+
"batteryVoltage": "electrical.charger.battery.voltage",
|
|
13
|
+
"batteryCurrent": "electrical.charger.battery.current",
|
|
14
|
+
"controllerTemperature": "electrical.charger.temperature",
|
|
15
|
+
"batteryTemperature": "electrical.charger.battery.temperature",
|
|
16
|
+
"loadVoltage": "electrical.charger.load.voltage",
|
|
17
|
+
"loadCurrent": "electrical.charger.load.current",
|
|
18
|
+
"loadPower": "electrical.charger.load.power",
|
|
19
|
+
"pvVoltage": "electrical.charger.solar.voltage",
|
|
20
|
+
"pvCurrent": "electrical.charger.solar.current",
|
|
21
|
+
"pvPower": "electrical.charger.solar.power",
|
|
22
|
+
"maxChargingPowerToday": "electrical.charger.today.max",
|
|
23
|
+
"maxDischargingPowerToday": "electrical.charger.discharging.maximum",
|
|
24
|
+
"chargingAmpHoursToday": "electrical.charger.charged.today",
|
|
25
|
+
"powerGenerationToday": "electrical.charger.power.today",
|
|
26
|
+
"powerGenerationTotal": "electrical.charger.power.total",
|
|
27
|
+
"loadStatus": "electrical.charger.load.status",
|
|
28
|
+
"chargingStatus": "electrical.charger.status"
|
|
29
|
+
*/
|
|
10
30
|
|
|
11
31
|
initSchema(){
|
|
12
32
|
//Buffer(73) [1, 3, 68, 32, 32, 82, 78, 71, 45, 67, 84, 82, 76, 45, 87, 78, 68, 51, 48, 7, 140, 0, 132, 0, 126, 0, 120, 0, 111, 0, 106, 100, 50, 0, 5, 0, 120, 0, 120, 0, 28, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 5, 0, 5, 2, 148, 0, 5, 206, 143, 34, 228, buffer: ArrayBuffer(8192), byteLength: 73, byteOffset: 6144, length: 73, Symbol(Symbol.toStringTag): 'Uint8Array']
|
|
13
33
|
super.initSchema()
|
|
14
34
|
this.addMetadatum('batteryType', '', "battery type")
|
|
35
|
+
.default="electrical.chargers.{id}.battery.type"
|
|
15
36
|
this.addMetadatum('batteryPercentage', 'ratio', "battery percentage",
|
|
16
37
|
(buffer)=>{return buffer.readUInt16BE(3) })
|
|
38
|
+
.default="electrical.chargers.{id}.battery.soc"
|
|
39
|
+
|
|
17
40
|
this.addMetadatum('batteryVoltage', 'V', "battery voltage",
|
|
18
41
|
(buffer)=>{return buffer.readUInt16BE((5))/10})
|
|
42
|
+
.default="electrical.chargers.{id}.battery.voltage"
|
|
43
|
+
|
|
19
44
|
this.addMetadatum('batteryCurrent', 'A', 'battery current',
|
|
20
45
|
(buffer)=>{return buffer.readUInt16BE((7))/100})
|
|
46
|
+
.default="electrical.chargers.{id}.battery.current"
|
|
47
|
+
|
|
21
48
|
this.addMetadatum('controllerTemperature', 'K', 'controller temperature',
|
|
22
49
|
(buffer)=>{return buffer.readInt8((9))+273.15})
|
|
50
|
+
.default="electrical.chargers.{id}.controller.temperature"
|
|
51
|
+
|
|
23
52
|
this.addMetadatum('batteryTemperature', 'K', 'battery temperature',
|
|
24
53
|
(buffer)=>{return buffer.readInt8((10))+273.15})
|
|
54
|
+
.default="electrical.chargers.{id}.battery.temperature"
|
|
55
|
+
|
|
25
56
|
this.addMetadatum('loadVoltage', 'V', 'load voltage',
|
|
26
57
|
(buffer)=>{return buffer.readUInt16BE((11))/10})
|
|
58
|
+
.default="electrical.chargers.{id}.load.voltage"
|
|
59
|
+
|
|
27
60
|
this.addMetadatum('loadCurrent', 'A', 'load current',
|
|
28
61
|
(buffer)=>{return buffer.readUInt16BE((13))/100})
|
|
62
|
+
.default="electrical.chargers.{id}.load.current"
|
|
29
63
|
this.addMetadatum('loadPower', 'W', 'load power',
|
|
30
64
|
(buffer)=>{return buffer.readUInt16BE((15))})
|
|
65
|
+
.default="electrical.chargers.{id}.load.power"
|
|
31
66
|
this.addMetadatum('pvVoltage', 'V', 'pv voltage',
|
|
32
67
|
(buffer)=>{return buffer.readUInt16BE((17))/10})
|
|
68
|
+
.default="electrical.chargers.{id}.solar.voltage"
|
|
33
69
|
this.addMetadatum('pvCurrent', 'A', 'pv current',
|
|
34
70
|
(buffer)=>{return buffer.readUInt16BE((19))/100})
|
|
71
|
+
.default="electrical.chargers.{id}.solar.current"
|
|
35
72
|
this.addMetadatum('pvPower', 'W', 'pv power',
|
|
36
73
|
(buffer)=>{return buffer.readUInt16BE(21)})
|
|
74
|
+
.default="electrical.chargers.{id}.solar.power"
|
|
37
75
|
this.addMetadatum('maxChargingPowerToday', 'W', 'max charging power today',
|
|
38
76
|
(buffer)=>{return buffer.readUInt16BE(33)})
|
|
77
|
+
.default="electrical.chargers.{id}.charge.max.today"
|
|
39
78
|
this.addMetadatum('maxDischargingPowerToday', 'W', 'max discharging power today',
|
|
40
79
|
(buffer)=>{return buffer.readUInt16BE(35)})
|
|
80
|
+
.default="electrical.chargers.{id}.discharge.max.today"
|
|
41
81
|
this.addMetadatum('chargingAmpHoursToday', 'Ah', 'charging amp hours today',
|
|
42
82
|
(buffer)=>{return buffer.readUInt16BE(37)})
|
|
83
|
+
.default="electrical.chargers.{id}.charge.ampHours.today"
|
|
84
|
+
|
|
43
85
|
this.addMetadatum('dischargingAmpHoursToday', 'Ah', 'discharging amp hours today',
|
|
44
86
|
(buffer)=>{return buffer.readUInt16BE(39)})
|
|
87
|
+
.default="electrical.chargers.{id}.discharge.ampHours.today"
|
|
88
|
+
|
|
45
89
|
this.addMetadatum('powerGenerationToday', 'W', 'power generation today',
|
|
46
90
|
(buffer)=>{return buffer.readUInt16BE(41)})
|
|
91
|
+
.default="electrical.chargers.{id}.power.generated.today"
|
|
92
|
+
|
|
47
93
|
this.addMetadatum('powerConsumptionToday', 'W', 'power consumption today',
|
|
48
94
|
(buffer)=>{return buffer.readUInt16BE(43)})
|
|
95
|
+
.default="electrical.chargers.{id}.power.consumed.today"
|
|
96
|
+
|
|
49
97
|
this.addMetadatum('powerGenerationTotal', 'W', 'power generation total',
|
|
50
98
|
(buffer)=>{return buffer.readUInt32BE(59)})
|
|
99
|
+
.default="electrical.chargers.{id}.power.generated.total"
|
|
100
|
+
|
|
51
101
|
this.addMetadatum('loadStatus', '', 'load status',
|
|
52
102
|
(buffer)=>{return RC.LOAD_STATE[buffer.readUInt8(67)>>7]})
|
|
103
|
+
.default="electrical.chargers.{id}.load.status"
|
|
53
104
|
|
|
54
105
|
this.addMetadatum('chargingStatus', '', 'charging status',
|
|
55
|
-
(buffer)=>{
|
|
106
|
+
(buffer)=>{
|
|
107
|
+
const cs = buffer.readUInt8(68)
|
|
108
|
+
if (Object.hasOwn(RC.CHARGING_STATE,cs))
|
|
109
|
+
return RC.CHARGING_STATE[cs]
|
|
110
|
+
else
|
|
111
|
+
return null
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
.default="electrical.chargers.{id}.charge.status"
|
|
115
|
+
|
|
56
116
|
}
|
|
57
117
|
|
|
58
118
|
retrieveDeviceID(){
|
|
@@ -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 => {
|
|
@@ -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
|