bt-sensors-plugin-sk 1.1.0 → 1.2.0-beta.0.0.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.
Files changed (41) hide show
  1. package/BTSensor.js +285 -107
  2. package/README.md +8 -10
  3. package/definitions.json +941 -0
  4. package/index.js +419 -317
  5. package/package.json +52 -6
  6. package/plugin_defaults.json +57 -0
  7. package/public/159.js +2 -0
  8. package/public/159.js.LICENSE.txt +14 -0
  9. package/public/30.js +8 -0
  10. package/public/30.js.LICENSE.txt +65 -0
  11. package/public/540.js +2 -0
  12. package/public/540.js.LICENSE.txt +8 -0
  13. package/public/893.js +1 -0
  14. package/public/main.js +1 -0
  15. package/public/remoteEntry.js +1 -0
  16. package/sensor_classes/ATC.js +31 -20
  17. package/sensor_classes/BlackListedDevice.js +4 -0
  18. package/sensor_classes/DEVELOPMENT.md +1 -6
  19. package/sensor_classes/GoveeH510x.js +3 -3
  20. package/sensor_classes/IBeacon.js +2 -3
  21. package/sensor_classes/Inkbird.js +4 -4
  22. package/sensor_classes/JBDBMS.js +3 -3
  23. package/sensor_classes/KilovaultHLXPlus.js +1 -1
  24. package/sensor_classes/MopekaTankSensor.js +13 -6
  25. package/sensor_classes/Renogy/RenogySensor.js +15 -6
  26. package/sensor_classes/UNKNOWN.js +8 -4
  27. package/sensor_classes/UltrasonicWindMeter.js +1 -1
  28. package/sensor_classes/Victron/VictronSensor.js +6 -2
  29. package/sensor_classes/VictronBatteryMonitor.js +9 -7
  30. package/sensor_classes/VictronDCEnergyMeter.js +1 -1
  31. package/sensor_classes/VictronInverter.js +1 -1
  32. package/sensor_classes/VictronInverterRS.js +1 -1
  33. package/sensor_classes/VictronOrionXS.js +1 -1
  34. package/sensor_classes/XiaomiMiBeacon.js +18 -9
  35. package/spec/electrical.json +688 -0
  36. package/spec/environment.json +401 -0
  37. package/spec/sensors.json +39 -0
  38. package/spec/tanks.json +115 -0
  39. package/src/components/PluginConfigurationPanel.js +368 -0
  40. package/src/index.js +0 -0
  41. package/webpack.config.js +71 -0
package/BTSensor.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { Variant } = require('dbus-next');
2
2
  const { log } = require('node:console');
3
3
  const EventEmitter = require('node:events');
4
+ var {exec} = require('child_process');
4
5
 
5
6
  /**
6
7
  * @author Andrew Gerngross <oh.that.andy@gmail.com>
@@ -10,7 +11,8 @@ const EventEmitter = require('node:events');
10
11
  * {@link module:node-ble}
11
12
  */
12
13
 
13
- const BTCompanies = require('./bt_co.json')
14
+ const BTCompanies = require('./bt_co.json');
15
+
14
16
  /**
15
17
  * @global A map of company names keyed by their Bluetooth ID
16
18
  * {@link ./sensor_classes/bt_co.json} file derived from bluetooth-sig source:
@@ -51,6 +53,38 @@ function signalQualityPercentQuad(rssi, perfect_rssi=-20, worst_rssi=-85) {
51
53
  }
52
54
  return Math.ceil(signal_quality);
53
55
  }
56
+ function preparePath(obj, str) {
57
+ const regex = /\{([^}]+)\}/g;
58
+ let match;
59
+ let resultString = "";
60
+ let lastIndex = 0;
61
+
62
+ while ((match = regex.exec(str)) !== null) {
63
+ const fullMatch = match[0];
64
+ const keyToAccess = match[1].trim();
65
+
66
+ // Append the text before the current curly braces
67
+ resultString += str.substring(lastIndex, match.index);
68
+ lastIndex = regex.lastIndex;
69
+
70
+ try {
71
+ let evalResult = obj[keyToAccess];
72
+ if (typeof evalResult === 'function'){
73
+ evalResult= evalResult.call(obj)
74
+ }
75
+
76
+ resultString += evalResult !== undefined ? evalResult : `${keyToAccess}_value_undefined`;
77
+ } catch (error) {
78
+ console.error(`Error accessing key '${keyToAccess}':`, error);
79
+ resultString += fullMatch; // Keep the original curly braces on error
80
+ }
81
+ }
82
+
83
+ // Append any remaining text after the last curly braces
84
+ resultString += str.substring(lastIndex);
85
+
86
+ return resultString || str; // Return original string if no replacements were made
87
+ }
54
88
 
55
89
  /**
56
90
  * @classdesc Class that all sensor classes should inherit from. Sensor subclasses
@@ -65,8 +99,9 @@ function signalQualityPercentQuad(rssi, perfect_rssi=-20, worst_rssi=-85) {
65
99
  */
66
100
 
67
101
  class BTSensor extends EventEmitter {
68
- static metadata=new Map()
69
-
102
+ //static metadata=new Map()
103
+ static DEFAULTS = require('./plugin_defaults.json');
104
+
70
105
  /**
71
106
  *
72
107
  * @param {module:node-ble/Device} device
@@ -77,13 +112,11 @@ class BTSensor extends EventEmitter {
77
112
  super()
78
113
 
79
114
  this.device=device
80
- this.name = config?.name
81
-
82
- this.useGATT = gattConfig?.useGATT
83
- this.pollFreq = gattConfig?.pollFreq
84
-
85
- this.Metadatum = this.constructor.Metadatum
86
- this.metadata = new Map()
115
+
116
+ Object.assign(this,config)
117
+ Object.assign(this,gattConfig)
118
+
119
+ this._state = null
87
120
  }
88
121
  /**
89
122
  * @function _test Test sensor parsing
@@ -133,7 +166,7 @@ class BTSensor extends EventEmitter {
133
166
  var b = Buffer.from(data.replaceAll(" ",""),"hex")
134
167
  const d = new this(null,config)
135
168
  d.initMetadata()
136
- d.getPathMetadata().forEach((datum,tag)=>{
169
+ Object.keys(d.getPaths()).forEach((tag)=>{
137
170
  d.on(tag,(v)=>console.log(`${tag}=${v}`))
138
171
  })
139
172
  if (key) {
@@ -145,38 +178,7 @@ class BTSensor extends EventEmitter {
145
178
  d.removeAllListeners()
146
179
 
147
180
  }
148
- static Metadatum =
149
- /**
150
- * @class encapsulates a sensor's metadata
151
- * @todo refactor and/or just plain rethink constructor parameters
152
- */
153
- class Metadatum{
154
-
155
- constructor(tag, unit, description, read, gatt=null, type){
156
- this.tag = tag
157
- this.unit = unit
158
- this.description = description
159
- this.read = read
160
- this.gatt = gatt
161
- this.type = type //schema type e.g. 'number'
162
- }
163
- /**
164
- *
165
- * @returns A JSON object passed by plugin to the plugin's schema
166
- * dynamically updated at runtime upon discovery and interrogation
167
- * of the device
168
- */
169
- asJSONSchema(){
170
- return {
171
- type:this?.type??'string',
172
- title: this?.description,
173
- unit: this?.unit,
174
- enum: this?.enum,
175
- default: this?.default
176
- }
177
- }
178
- }
179
-
181
+
180
182
  //static utility Functions
181
183
  /**
182
184
  *
@@ -274,28 +276,93 @@ class BTSensor extends EventEmitter {
274
276
  *
275
277
  */
276
278
 
279
+ initSchema(){
280
+ this._schema = {
281
+ properties:{
282
+ active: {title: "Active", type: "boolean", default: true },
283
+ discoveryTimeout: {title: "Device discovery timeout (in seconds)",
284
+ type: "integer", default:30,
285
+ minimum: 10,
286
+ maximum: 600 },
287
+
288
+ params:{
289
+ title:`Device parameters`,
290
+ description: this.getDescription(),
291
+ type:"object",
292
+ properties:{}
293
+ },
294
+ paths:{
295
+ title:"Signalk Paths",
296
+ description: `Signalk paths to be updated when ${this.getName()}'s values change`,
297
+ type:"object",
298
+ properties:{}
299
+ }
300
+ }
301
+ }
302
+
303
+ if (this.hasGATT()){
304
+
305
+ this._schema.properties.gattParams={
306
+ title:`GATT Specific device parameters`,
307
+ description: this.getGATTDescription(),
308
+ type:"object",
309
+ properties:{
310
+ useGATT: {title: "Use GATT connection", type: "boolean", default: false },
311
+ pollFreq: { type: "number", title: "Polling frequency in seconds"}
312
+ }
313
+ }
314
+ }
315
+
316
+ }
277
317
  async init(){
318
+ this.currentProperties = await this.constructor.getDeviceProps(this.device)
319
+ this.initSchema()
320
+
321
+
278
322
  //create the 'name' parameter
279
- var md = this.addMetadatum("name", "string","Name of sensor" )
280
- md.isParam=true
323
+ this.addDefaultParam("name")
324
+
325
+ //create the 'location' parameter
326
+
327
+ this.addDefaultParam("location")
328
+
281
329
  //create the 'RSSI' parameter
282
- this.currentProperties = await this.constructor.getDeviceProps(this.device)
283
- this.addMetadatum("RSSI","db","Signal strength in db")
284
- this.getMetadatum("RSSI").default=`sensors.${this.getMacAddress().replaceAll(':', '')}.rssi`
285
- this.getMetadatum("RSSI").read=()=>{return this.getRSSI()}
286
- this.getMetadatum("RSSI").read.bind(this)
287
- //create GATT params (iff sensor is GATT-ish)
288
- if (this.hasGATT()) {
289
- md = this.addMetadatum("useGATT", "boolean", "Use GATT connection")
290
- md.type="boolean"
291
- md.isParam=true
292
- md.isGATT=true
293
-
294
- md = this.addMetadatum("pollFreq", "s", "Polling frequency in seconds")
295
- md.type="number"
296
- md.isParam=true
297
- md.isGATT=true
330
+ this.addDefaultPath("RSSI","sensors.RSSI")
331
+ this.getPath("RSSI").read=()=>{return this.getRSSI()}
332
+ this.getPath("RSSI").read.bind(this)
333
+ this.initListen()
334
+ }
335
+
336
+ initListen(){
337
+ Promise.resolve(this.listen())
338
+ }
339
+ activate(config, plugin){
340
+ if (config.paths){
341
+ this.createPaths(config,plugin.id)
342
+ this.initPaths(config,plugin.id)
343
+ this.debug(`Paths activated for ${this.getDisplayName()}`);
344
+ }
345
+ if (this.usingGATT()){
346
+ try {
347
+ this.activateGATT()
348
+ } catch (e) {
349
+ this.debug(`GATT services unavailable for ${this.getName()}. Reason: ${e}`)
350
+ this._state="ERROR"
351
+ return
352
+ }
298
353
  }
354
+ this._state="ACTIVE"
355
+ }
356
+
357
+ activateGATT(){
358
+ this.initGATTConnection().then(async ()=>{
359
+ this.emitGATT()
360
+ if (this.pollFreq){
361
+ this.initGATTInterval()
362
+ }
363
+ else
364
+ await this.initGATTNotifications()
365
+ })
299
366
  }
300
367
 
301
368
  /**
@@ -307,9 +374,55 @@ class BTSensor extends EventEmitter {
307
374
  */
308
375
 
309
376
  addMetadatum(tag, ...args){
310
- var metadatum = new this.Metadatum(tag, ...args)
311
- this.getMetadata().set(tag, metadatum)
312
- return metadatum
377
+
378
+ const md = {}
379
+ if (args[0]) md.unit = args[0]
380
+ if (args[1]) md.title = args[1]
381
+ if (args[2]) md.read = args[2]
382
+ if (args[3]) md.gatt = args[3]
383
+ if (args[4]) md.type = args[4]
384
+
385
+ return this.addPath(tag,md)
386
+ }
387
+
388
+ addParameter(tag, param){
389
+
390
+ if (!param.type)
391
+ param.type="string"
392
+
393
+ this._schema.properties.params.properties[tag]=param
394
+ return this._schema.properties.params.properties[tag]
395
+ }
396
+
397
+ addPath(tag, path){
398
+ if (!path.type)
399
+ path.type="string"
400
+
401
+ if (!path.pattern)
402
+ path.pattern="^(?:[^{}\\s]*\\{[a-zA-Z0-9]+\\}[^{}\\s]*|[^{}\\s]*)$"
403
+ this._schema.properties.paths.properties[tag]=path
404
+ return this._schema.properties.paths.properties[tag]
405
+ }
406
+
407
+ addGATTParameter(tag, param){
408
+
409
+ if (!param.type)
410
+ param.type="string"
411
+
412
+ return this._schema.properties.gattParams.properties[tag]=param
413
+ }
414
+
415
+ addDefaultPath(tag,defaultPath){
416
+ const path = eval(`BTSensor.DEFAULTS.${defaultPath}`)
417
+ return this.addPath(tag,Object.assign({}, path))
418
+ }
419
+
420
+ addDefaultParam(tag){
421
+ return this.addParameter(tag,Object.assign({}, BTSensor.DEFAULTS.params[tag]))
422
+ }
423
+
424
+ getJSONSchema(){
425
+ return this._schema
313
426
  }
314
427
 
315
428
  //GATT Initialization functions
@@ -337,6 +450,33 @@ class BTSensor extends EventEmitter {
337
450
  throw new Error("::initGATTNotifications() should be implemented by the BTSensor subclass")
338
451
  }
339
452
 
453
+ async deviceConnect() {
454
+ await this.device.connect()
455
+
456
+ /* CAUTION: HACK AHEAD
457
+
458
+ Bluez for some cockamamie reason (It's 2025 for chrissake.
459
+ BLUETOOTH ISN'T JUST FOR DESKTOPS ANYMORE, BLUEZ DEVS!)
460
+ SUSPENDS scanning while connected to a device.
461
+
462
+ The next line of code gives the scanner a kick in the arse,
463
+ starting it up again so, I dunno, another device might be able
464
+ to connect and sensor classes could maybe get beacon updates.
465
+
466
+ You know, the little things.
467
+ */
468
+ try {
469
+ await this._adapter.helper.callMethod('SetDiscoveryFilter', {
470
+ Transport: new Variant('s', this._adapter?._transport??"le")
471
+ })
472
+
473
+ } catch (e){
474
+ //probably ignorable error. probably.
475
+ console.log(e)
476
+ }
477
+ /* END HACK*/
478
+ }
479
+
340
480
  /**
341
481
  *
342
482
  * Subclasses do NOT need to override this function
@@ -376,12 +516,12 @@ class BTSensor extends EventEmitter {
376
516
  */
377
517
  initPropertiesChanged(){
378
518
 
379
- this.propertiesChanged.bind(this)
519
+ this._propertiesChanged.bind(this)
380
520
  this.device.helper._prepare()
381
521
  this.device.helper.on("PropertiesChanged",
382
522
  ((props)=> {
383
523
  try{
384
- this.propertiesChanged(props)
524
+ this._propertiesChanged(props)
385
525
  }
386
526
  catch(error){
387
527
  this.debug(`Error occured on ${this.getNameAndAddress()}: ${error?.message??error}`)
@@ -392,30 +532,20 @@ class BTSensor extends EventEmitter {
392
532
  //END instance initialization functions
393
533
 
394
534
  //Metadata functions
395
- getMetadata(){
396
- if (this.metadata==undefined)
397
- this.metadata= new Map(this.constructor.getMetadata())
398
- return this.metadata
399
- }
400
535
 
401
- getMetadatum(tag){
402
- return this.getMetadata().get(tag)
536
+ getPath(tag){
537
+ return this._schema.properties.paths.properties[tag]
403
538
  }
404
539
 
405
- getPathMetadata(){
406
- return new Map(
407
- [...this.getMetadata().entries()].filter(([key,value]) => !(value?.isParam??false))
408
- )
540
+ getPaths(){
541
+ return this._schema.properties.paths.properties
409
542
  }
410
- getParamMetadata(){
411
- return new Map(
412
- [...this.getMetadata().entries()].filter(([key,value]) => (value?.isParam??false) && !(value?.isGATT??false))
413
- )
543
+ getParams(){
544
+ return this._schema.properties.params.properties
414
545
  }
415
- getGATTParamMetadata(){
416
- return new Map(
417
- [...this.getMetadata().entries()].filter(([key,value]) => (value?.isParam??false) && (value?.isGATT??false))
418
- )
546
+ getGATTParams(){
547
+ return this._schema.properties.gattParams.properties
548
+
419
549
  }
420
550
  //End metadata functions
421
551
 
@@ -429,9 +559,13 @@ class BTSensor extends EventEmitter {
429
559
  return `${this.getName()} from ${this.getManufacturer()}`
430
560
  }
431
561
  getName(){
432
- return this?.name??this.currentProperties.Name
433
- }
562
+ const name = this?.name??this.currentProperties.Name
563
+ return name?name:"Unknown"
434
564
 
565
+ }
566
+ macAndName(){
567
+ return `${this.getName().replaceAll(':', '-').replaceAll(" ","_")}-${this.getMacAddress().replaceAll(':', '-')}`
568
+ }
435
569
  getNameAndAddress(){
436
570
  return `${this.getName()} at ${this.getMacAddress()}`
437
571
  }
@@ -475,6 +609,8 @@ class BTSensor extends EventEmitter {
475
609
  else
476
610
  return null
477
611
  }
612
+
613
+
478
614
  //END Device property functions
479
615
 
480
616
  //Sensor RSSI state functions
@@ -490,6 +626,13 @@ class BTSensor extends EventEmitter {
490
626
  return signalQualityPercentQuad(rssi)
491
627
  }
492
628
 
629
+ getState(){
630
+ return this._state
631
+ }
632
+
633
+ isActive(){
634
+ return this._state=="ACTIVE"
635
+ }
493
636
  getBars(){
494
637
  const ss = this.getSignalStrength()
495
638
  var bars = ""
@@ -532,7 +675,8 @@ class BTSensor extends EventEmitter {
532
675
  * @param {*} props which contains ManufacturerData and ServiceData (where the sensor's data resides)
533
676
  * set up by BTSensor::initPropertiesChanged()
534
677
  */
535
- propertiesChanged(props){
678
+ _propertiesChanged(props){
679
+ this._lastContact=Date.now()
536
680
 
537
681
  if (props.RSSI) {
538
682
  this.currentProperties.RSSI=this.valueIfVariant(props.RSSI)
@@ -543,8 +687,13 @@ class BTSensor extends EventEmitter {
543
687
 
544
688
  if (props.ManufacturerData)
545
689
  this.currentProperties.ManufacturerData=this.valueIfVariant(props.ManufacturerData)
690
+ if (this.isActive())
691
+ this.propertiesChanged(props)
546
692
 
547
693
  }
694
+ propertiesChanged(props){
695
+ //implemented by subclass
696
+ }
548
697
 
549
698
  /**
550
699
  *
@@ -555,14 +704,18 @@ class BTSensor extends EventEmitter {
555
704
  }
556
705
 
557
706
  emitData(tag, buffer, ...args){
558
- this.emit(tag, this.getMetadatum(tag).read(buffer, ...args))
707
+ const md = this.getPath(tag)
708
+ if (md && md.read)
709
+ this.emit(tag, md.read(buffer, ...args))
710
+
711
+
559
712
  }
560
713
 
561
714
  emitValuesFrom(buffer){
562
- this.getMetadata().forEach((datum, tag)=>{
563
- if (!(datum.isParam||datum.notify) && datum.read)
564
- this.emit(tag, datum.read(buffer))
565
- })
715
+ Object.keys(this.getPaths())
716
+ .forEach(
717
+ (tag)=>this.emitData(tag,buffer)
718
+ )
566
719
  }
567
720
 
568
721
  /**
@@ -576,21 +729,10 @@ class BTSensor extends EventEmitter {
576
729
  listen(){
577
730
  try{
578
731
  this.initPropertiesChanged()
579
- this.propertiesChanged(this.currentProperties)
732
+ this._propertiesChanged(this.currentProperties)
580
733
  } catch(e){
581
734
  this.debug(e)
582
735
  }
583
- if (this.usingGATT()){
584
- this.initGATTConnection().then(async ()=>{
585
- this.emitGATT()
586
- if (this.pollFreq){
587
- this.initGATTInterval()
588
- }
589
- else
590
- await this.initGATTNotifications()
591
- })
592
- .catch((e)=>this.debug(`GATT services unavailable for ${this.getName()}. Reason: ${e}`))
593
- }
594
736
  return this
595
737
  }
596
738
 
@@ -601,12 +743,13 @@ class BTSensor extends EventEmitter {
601
743
  * Called automatically by Plugin::plugin.stop()
602
744
  */
603
745
 
604
- stopListening(){
746
+ async stopListening(){
605
747
  this.removeAllListeners()
606
748
  this.device.helper.removeListeners()
607
749
  if (this.intervalID){
608
750
  clearInterval(this.intervalID)
609
- }
751
+ }
752
+ this._state="ASLEEP"
610
753
  }
611
754
  //END Sensor listen-to-changes functions
612
755
 
@@ -622,6 +765,41 @@ class BTSensor extends EventEmitter {
622
765
 
623
766
  }
624
767
  //End instance utility functions
768
+
769
+ createPaths(config, id){
770
+ Object.keys(this.getPaths()).forEach((tag)=>{
771
+ const pathMeta=this.getPath(tag)
772
+ const path = config.paths[tag]
773
+ if (!(path===undefined))
774
+ this.app.handleMessage(id,
775
+ {
776
+ updates:
777
+ [{ meta: [{path: preparePath(this, path), value: { units: pathMeta?.unit }}]}]
778
+ })
779
+ })
780
+ }
781
+
782
+ initPaths(deviceConfig, id){
783
+ Object.keys(this.getPaths()).forEach((tag)=>{
784
+ const pathMeta=this.getPath(tag)
785
+ const path = deviceConfig.paths[tag];
786
+ if (!(path === undefined)) {
787
+ this.on(tag, (val)=>{
788
+ if (pathMeta.notify){
789
+ this.app.notify(tag, val, id )
790
+ } else {
791
+ this.updatePath(preparePath(this,path),val,id)
792
+ }
793
+ })
794
+ }
795
+ })
796
+ }
797
+ updatePath(path, val,id){
798
+ this.app.handleMessage(id, {updates: [ { values: [ {path: path, value: val }] } ] })
799
+ }
800
+ elapsedTimeSinceLastContact(){
801
+ return (Date.now()-this?._lastContact??Date.now())/1000
802
+ }
625
803
  }
626
804
 
627
805
  module.exports = BTSensor
package/README.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # Bluetooth Sensors for [Signal K](http://www.signalk.org)
2
2
 
3
- ## RELEASE 1.1.0
3
+ ## Beta 1.0.0
4
+
5
+ Dynamic configuration added (no more screen refreshing necessary).
4
6
 
5
7
  ## WHAT IT IS
6
8
 
7
9
  BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for listening and connecting to Bluetooth sensors on your boat and sending deltas to Signalk paths with the values the sensors reports. <br>
8
10
 
9
- The Plugin currently supports every documented Victron device (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus), [Lancol Battery Meters ](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/), [Kilovault HLX+ smart batteries ](https://sunwatts.com/content/manual/KiloVault_HLX_PLUS_Datasheet_06252021%20%281%29.pdf?srsltid=AfmBOooY-cGnC_Qm6V1T9Vg5oZzBCJurS0AOGoWqWeyy-dwz2vA-l1Jb), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags, Renogy BMS, Ultrasonic Wind Meters, SwitchBotTH and Meterplus, Aranet 2/4 environmental sensors, Govee 50/51xx sensors, ShellySBHT003C enviromental sensor,
10
- [JBD/Jiabaida/Xiaoxiang Battery management systems](https://jiabaida-bms.com/), IBeacon and clone devices, and Inkbird thermometers.
11
+ The Plugin currently supports every documented Victron device (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus), [Lancol Battery Meters ](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/), [Kilovault HLX+ smart batteries ](https://sunwatts.com/content/manual/KiloVault_HLX_PLUS_Datasheet_06252021%20%281%29.pdf?srsltid=AfmBOooY-cGnC_Qm6V1T9Vg5oZzBCJurS0AOGoWqWeyy-dwz2vA-l1Jb), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags, Renogy BMS, Ultrasonic Wind Meters, SwitchBotTH and Meterplus, Aranet 2/4 environmental sensors, Govee 50/51xx sensors, and Inkbird thermometers.
11
12
 
12
13
  A typical use case is a Bluetooth thermometer like the Xiaomi LYWSD03MMC, an inexpensive Bluetooth thermometer that runs on a 3V watch battery that can report the current temperature and humidity in your refrigerator or cabin or wherever you want to stick it (no judgement.) <br>
13
14
 
@@ -15,12 +16,6 @@ The reported temperature can then be displayed on a Signalk app like Kip, Wilhel
15
16
 
16
17
  It's pretty easy to write and deploy your own sensor class for any currently unsupported sensor. More on that in [the development README](./sensor_classes/DEVELOPMENT.md).
17
18
 
18
- ### RENOGY NOTES
19
-
20
- The class of Renogy Devices cannot be reliably identified from their Bluetooth advertisements. <br>
21
-
22
- On the plugin config page, You will need to select the device from the Device dropdown, then select the appropriate class from the Class dropdown. After that, you will need to hit the Submit button. On restart of the plugin, the plugin should recognize the device. If you've selected the appropriate class (RenogyRoverClient for example), you should see configs for paths. <br>
23
-
24
19
  ## WHO IS IT FOR
25
20
 
26
21
  Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently supported) and Bluetooth sensors they'd like to integrate into their Signalk datastream.
@@ -35,8 +30,9 @@ Signalk users with a Linux boat-puter (Windows and MacOS are NOT currently suppo
35
30
 
36
31
  ## INSTALLATION
37
32
 
38
- ### Signalk Appstore
33
+ NOTE: If you're running the 1.0.3 release, you will have to reconfigure your devices.<br>
39
34
 
35
+ ### Signalk Appstore
40
36
  The plugin is currently available in the Signalk Appstore. <br>
41
37
 
42
38
  ### NPM
@@ -52,6 +48,8 @@ If you want to install directly from source (this is mostly of interest to custo
52
48
  <pre> cd ~/[some_dir]
53
49
  git clone https://github.com/naugehyde/bt-sensors-plugin-sk
54
50
  cd bt-sensors-plugin-sk
51
+ git switch '1.1.0'
52
+ git pull
55
53
  npm i
56
54
  [sudo] npm link
57
55
  cd [signalk_home]