bt-sensors-plugin-sk 1.1.0-beta.2.2.0 → 1.1.0-beta.2.2.1.0

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
@@ -227,7 +227,7 @@ class BTSensor extends EventEmitter {
227
227
  const _propsProxy = await this._getPropsProxy(device)
228
228
  try{
229
229
  const rawProps = await _propsProxy.Get(device.helper.iface,prop)
230
- return rawProps?.value
230
+ return rawProps?.value
231
231
  }
232
232
  catch(e){
233
233
  return null //Property $prop (probably) doesn't exist in $device
@@ -258,7 +258,7 @@ class BTSensor extends EventEmitter {
258
258
  if (!md) return null
259
259
  const keys = Object.keys(md)
260
260
  if (keys && keys.length>0){
261
- return parseInt(keys[0])
261
+ return parseInt(keys[keys.length-1])
262
262
  }
263
263
  return null
264
264
  }
@@ -374,16 +374,21 @@ class BTSensor extends EventEmitter {
374
374
  * NOTE: The function mucks about with node-ble internal functions to help make sure the
375
375
  * DBUS connection stays alive, doesn't tax resources and doesn't spit out spurious errors.
376
376
  */
377
- initPropertiesChanged(){
377
+ initPropertiesChanged(){
378
378
 
379
379
  this.propertiesChanged.bind(this)
380
380
  this.device.helper._prepare()
381
381
  this.device.helper.on("PropertiesChanged",
382
382
  ((props)=> {
383
- this.propertiesChanged(props)
383
+ try{
384
+ this.propertiesChanged(props)
385
+ }
386
+ catch(error){
387
+ this.debug(`Error occured on ${this.getNameAndAddress()}: ${error?.message??error}`)
388
+ this.debug(error)
389
+ }
384
390
  }))
385
391
  }
386
-
387
392
  //END instance initialization functions
388
393
 
389
394
  //Metadata functions
@@ -569,8 +574,12 @@ class BTSensor extends EventEmitter {
569
574
 
570
575
 
571
576
  listen(){
572
- this.initPropertiesChanged()
573
- this.propertiesChanged(this.currentProperties)
577
+ try{
578
+ this.initPropertiesChanged()
579
+ this.propertiesChanged(this.currentProperties)
580
+ } catch(e){
581
+ this.debug(e)
582
+ }
574
583
  if (this.usingGATT()){
575
584
  this.initGATTConnection().then(async ()=>{
576
585
  this.emitGATT()
package/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Bluetooth Sensors for [Signal K](http://www.signalk.org)
2
2
 
3
+ ## BETA 2.2.1
4
+
5
+ ### What's New
6
+
7
+ GoveeH50xx sensor support. Selectable bluetooth adapter on config screen (in case you have more than bluetooth adapter on your server).
8
+
9
+
3
10
  ## BETA 2.2.0
4
11
 
5
12
  ### What's New
13
+
6
14
  Support for Aranet4 environment sensor and Renogy Rover/Wanderer Controllers. Untested support for Renogy Battery and Inverter clients. If you have a Renogy Battery or Inverter with bluetooth support, please give it a try and let me know how it goes.
7
15
 
8
16
  ### RENOGY NOTES
@@ -30,7 +38,7 @@ Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently suppo
30
38
 
31
39
  ## REQUIREMENTS
32
40
 
33
- * A Linux Signalk boat-puter with System-D (NOTE: Most Linux installations support System-D)
41
+ * A Linux Signalk boat-puter with bluetooth-DBUS support
34
42
  * A Bluetooth adapter
35
43
  * [Bluez](https://www.bluez.org) installed
36
44
  (Go here for [Snap installation instructions](https://snapcraft.io/bluez))
@@ -57,6 +65,7 @@ If you want to install directly from source (this is mostly of interest to custo
57
65
  git clone https://github.com/naugehyde/bt-sensors-plugin-sk
58
66
  cd bt-sensors-plugin-sk
59
67
  git switch '1.1.0'
68
+ git pull
60
69
  npm i
61
70
  [sudo] npm link
62
71
  cd [signalk_home]
package/index.js CHANGED
@@ -102,6 +102,8 @@ module.exports = function (app) {
102
102
  const sensor = new c(device,config?.params, config?.gattParams)
103
103
  sensor.debug=app.debug
104
104
  await sensor.init()
105
+ app.debug(`instantiated ${await BTSensor.getDeviceProp(device,"Address")}`)
106
+
105
107
  return sensor
106
108
  }
107
109
  }} catch(error){
@@ -174,8 +176,11 @@ module.exports = function (app) {
174
176
  "2) You will have to wait until the scanner has found your device before seeing your device's config fields and saving the configuration. \n"+
175
177
  "3) To refresh the list of available devices and their configurations, just open and close the config screen by clicking on the arrow symbol in the config's top bar. \n"+
176
178
  "4) If you submit and get errors it may be because the configured devices have not yet all been discovered.",
177
- required:["discoveryTimeout", "discoveryInterval"],
179
+ required:["adapter","discoveryTimeout", "discoveryInterval"],
178
180
  properties: {
181
+ adapter: {title: "Bluetooth adapter",
182
+ type: "string", default: "hci0"},
183
+
179
184
  discoveryTimeout: {title: "Default device discovery timeout (in seconds)",
180
185
  type: "integer", default: 30,
181
186
  minimum: 10,
@@ -358,13 +363,16 @@ module.exports = function (app) {
358
363
  createSensor(adapter, deviceConfig).then((sensor)=>{
359
364
  deviceConfig.sensor=sensor
360
365
  if (deviceConfig.active) {
361
- createPaths(deviceConfig)
362
- initPaths(deviceConfig)
366
+ if (deviceConfig.paths){
367
+ createPaths(deviceConfig)
368
+ initPaths(deviceConfig)
369
+ }
363
370
  const result = Promise.resolve(deviceConfig.sensor.listen())
364
371
  result.then(() => {
365
372
  app.debug(`Listening for changes from ${deviceConfig.sensor.getDisplayName()}`);
366
373
  app.setPluginStatus(`Initial scan complete. Listening to ${++found} sensors.`);
367
374
  })
375
+
368
376
  }
369
377
 
370
378
  })
@@ -417,13 +425,32 @@ module.exports = function (app) {
417
425
  sensorMap.clear()
418
426
  deviceConfigs=options?.peripherals??[]
419
427
 
428
+
429
+
420
430
  if (plugin.stopped) {
421
431
  await sleep(5000) //Make sure plugin.stop() completes first
422
432
  //plugin.start is called asynchronously for some reason
423
433
  //and does not wait for plugin.stop to complete
424
434
  plugin.stopped=false
425
435
  }
426
- adapter = await bluetooth.getAdapter(app.settings?.btAdapter??adapterID)
436
+ var adapterID=options.adapter
437
+
438
+ if (!adapterID || adapterID==="")
439
+ adapterID = "hci0"
440
+
441
+ app.debug(`Connecting to bluetooth adapter ${adapterID}`);
442
+
443
+ const activeAdapters = await bluetooth.activeAdapters()
444
+ plugin.schema.properties.adapter.enum=[]
445
+ plugin.schema.properties.adapter.enumNames=[]
446
+ for (a of activeAdapters){
447
+ plugin.schema.properties.adapter.enum.push(a.adapter)
448
+ plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
449
+ }
450
+
451
+ plugin.uiSchema.adapter={'ui:disabled': (activeAdapters.length==1)}
452
+
453
+ adapter = await bluetooth.getAdapter(adapterID)
427
454
  await startScanner()
428
455
  if (starts>0){
429
456
  app.debug(`Plugin ${packageInfo.version} restarting...`);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "bt-sensors-plugin-sk",
3
- "version": "1.1.0-beta.2.2.0",
3
+ "version": "1.1.0-beta.2.2.1.0",
4
4
  "description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird, Ultrasonic wind meters, Mopeka tank readers, Renogy Battery and Solar Controllers (new), Aranet4 environment sensors, and Govee GVH51xx temp sensors",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "dbus-next": "^0.10.2",
8
- "node-ble": "^1.9.0",
8
+ "node-ble": "^1.12.0",
9
9
  "int24":"^0.0.1"
10
10
  },
11
11
  "scripts": {
@@ -2,10 +2,13 @@ const BTSensor = require("../BTSensor");
2
2
  class BLACKLISTED extends BTSensor{
3
3
  static isSystem = true
4
4
  static async identify(device){
5
- if (await this.getManufacturerID(device)===0x004C) //apple devices use
6
- return this //randomised macs and clog up our list
7
- else
8
- return null
5
+ const md = await this.getDeviceProp(device,'ManufacturerData')
6
+ if (md){
7
+ const keys = Object.keys(md)
8
+ if (keys.length==1 && keys[0]==0x004C)
9
+ return this
10
+ }
11
+ return null
9
12
  }
10
13
  async init(){
11
14
  await super.init()
@@ -13,7 +16,7 @@ class BLACKLISTED extends BTSensor{
13
16
  }
14
17
  reasonForBlacklisting() {
15
18
  switch ( this.getManufacturerID()){
16
- case (0x004C): return "Randomized MAC address"
19
+ case (0x004C): return "Randomized MAC address (Apple)"
17
20
  case (0x02e1): return "Device is using VE.Smart" //NOTE: Victron/VictronSensor class
18
21
  //determines if a device is using VE.Smart
19
22
  //in identify(). If so, identify() returns
@@ -0,0 +1,47 @@
1
+ const BTSensor = require("../BTSensor");
2
+
3
+ class GoveeH50xx extends BTSensor {
4
+
5
+ static async identify(device){
6
+ const regex = /^Govee_H50[0-9]{2}_[a-f,A-F,0-9]{4}$/
7
+ //this.getManufacturer()
8
+ const name = await this.getDeviceProp(device,"Name")
9
+ const uuids = await this.getDeviceProp(device,'UUIDs')
10
+
11
+ if (name && name.match(regex) &&
12
+ uuids && uuids.length > 0 &&
13
+ uuids[0] == '0000ec88-0000-1000-8000-00805f9b34fb')
14
+ return this
15
+ else
16
+ return null
17
+ t
18
+ }
19
+
20
+ async init(){
21
+ await super.init()
22
+ this.initMetadata()
23
+ }
24
+ initMetadata(){
25
+ this.addMetadatum('temp','K', 'temperature',
26
+ (buffer)=>{return 273.15+(buffer.readUInt16LE(1)/100)
27
+ })
28
+ this.addMetadatum('humidity','ratio', 'humidity',
29
+ (buffer)=>{return buffer.readUInt16LE(3)/10000
30
+ })
31
+ this.addMetadatum('battery','ratio', 'battery strength', (buffer)=>{return buffer.readUInt8(5)/100})
32
+ }
33
+
34
+ getManufacturer(){
35
+ return "Govee"
36
+ }
37
+ async propertiesChanged(props){
38
+ super.propertiesChanged(props)
39
+ if (props.ManufacturerData) {
40
+ const buffer = this.getManufacturerData(0xec88)
41
+ if (buffer) {
42
+ this.emitValuesFrom(buffer)
43
+ }
44
+ }
45
+ }
46
+ }
47
+ module.exports=GoveeH50xx
@@ -18,10 +18,10 @@ function decodeTempHumid(tempHumidBytes) {
18
18
  return {t: tempAsFloat, h: humid};
19
19
  }
20
20
  }
21
- class GoveeTH extends BTSensor{
21
+ class GoveeH510x extends BTSensor{
22
22
 
23
23
  static async identify(device){
24
- const regex = /^GVH5[0-9]{3}_[a-f,A-F,0-9]{4}$/
24
+ const regex = /^GVH510[0-9]_[a-f,A-F,0-9]{4}$/
25
25
  const name = await this.getDeviceProp(device,"Name")
26
26
  const uuids = await this.getDeviceProp(device,'UUIDs')
27
27
 
@@ -64,4 +64,4 @@ class GoveeTH extends BTSensor{
64
64
  }
65
65
  }
66
66
  }
67
- module.exports=GoveeTH
67
+ module.exports=GoveeH510x
@@ -64,7 +64,7 @@ function sleep(x) {
64
64
 
65
65
  decrypt(data){
66
66
  if (!this.encryptionKey)
67
- throw Error("Unable to decrypt: no encryption key set")
67
+ throw new Error("Unable to decrypt: no encryption key set")
68
68
 
69
69
  const encMethod = 'aes-128-ctr';
70
70
  const iv = data.readUInt16LE(5);
@@ -166,6 +166,9 @@ class XiaomiMiBeacon extends BTSensor{
166
166
  if (this.usingGATT()) return
167
167
  const data = this.getServiceData(this.constructor.SERVICE_MIBEACON)
168
168
  var dec
169
+ if (!this.encryptionKey){
170
+ throw new Error(`${this.getNameAndAddress()} requires an encryption key.`)
171
+ }
169
172
  if (this.encryptionVersion >= 4) {
170
173
  dec = this.decryptV4and5(data)
171
174
  } else {