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 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.3
2
+ # Version 1.2.4-2
3
3
 
4
- ## WHAT'S NEW SINCE VERSION 1.2.2
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
- Bug fixes Remoran Wave.3, JunctekBMS, and ShenzhenLiOn
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
- # Version 1.2.2
21
+ Bug fixes Remoran Wave.3, JunctekBMS, and ShenzhenLiOn
9
22
 
10
- ## WHAT'S NEW SINCE VERSION 1.2.1
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
- ## WHAT'S NEW SINCE VERSION 1.1.x
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
- - Renogy Rover Client, Victron GX, Victron Smart Battery Protect, and Victron VE Bus sensor classes have no default paths currently. Users will need to manually input.
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
- app.setPluginError(msg)
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",
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
@@ -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
- this.device.on("disconnect", ()=>{
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 BTSensor = require("../BTSensor");
2
- function decodeTempHumid(tempHumidBytes) {
3
- // Convert the bytes to a 24-bit integer
4
- const baseNum = (tempHumidBytes[0] << 16) + (tempHumidBytes[1] << 8) + tempHumidBytes[2];
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
- if (name && name.match(regex) &&
29
- uuids && uuids.length > 0 &&
30
- uuids[0] == '0000ec88-0000-1000-8000-00805f9b34fb')
31
- return this
32
- else
33
- return null
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
- const th = decodeTempHumid(buffer.subarray(2,5))
48
- this.emit("temp", parseFloat((273.15+th.t).toFixed(2))) ;
49
- this.emit("humidity", th.h/100 )
50
- this.emit('battery', buffer[5]/100)
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
- const buffer = this.getManufacturerData(0x0001)
61
- if (buffer) {
62
- this.emitValuesFrom(buffer)
63
- }
64
- }
39
+
65
40
  }
66
41
  module.exports=GoveeH510x
@@ -195,19 +195,7 @@ async getAndEmitCellVoltages(){
195
195
  }
196
196
 
197
197
  initGATTInterval(){
198
- this.device.on("disconnect", ()=>{
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.`)
@@ -42,13 +42,7 @@ class RenogySensor extends BTSensor{
42
42
 
43
43
  async initSchema(){
44
44
  await super.initSchema()
45
- this.addParameter(
46
- "refreshInterval",
47
- {
48
- title: 'refresh interval',
49
- type: 'number'
50
- }
51
- )
45
+
52
46
  this.addParameter(
53
47
  "deviceID",
54
48
  {
@@ -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 "SwitchBotMeterPlus"
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
- "SwitchBotTH"
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.initMetadata()
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