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.
- package/BTSensor.js +285 -107
- package/README.md +8 -10
- package/definitions.json +941 -0
- package/index.js +419 -317
- package/package.json +52 -6
- package/plugin_defaults.json +57 -0
- package/public/159.js +2 -0
- package/public/159.js.LICENSE.txt +14 -0
- package/public/30.js +8 -0
- package/public/30.js.LICENSE.txt +65 -0
- package/public/540.js +2 -0
- package/public/540.js.LICENSE.txt +8 -0
- package/public/893.js +1 -0
- package/public/main.js +1 -0
- package/public/remoteEntry.js +1 -0
- package/sensor_classes/ATC.js +31 -20
- package/sensor_classes/BlackListedDevice.js +4 -0
- package/sensor_classes/DEVELOPMENT.md +1 -6
- package/sensor_classes/GoveeH510x.js +3 -3
- package/sensor_classes/IBeacon.js +2 -3
- package/sensor_classes/Inkbird.js +4 -4
- package/sensor_classes/JBDBMS.js +3 -3
- package/sensor_classes/KilovaultHLXPlus.js +1 -1
- package/sensor_classes/MopekaTankSensor.js +13 -6
- package/sensor_classes/Renogy/RenogySensor.js +15 -6
- package/sensor_classes/UNKNOWN.js +8 -4
- package/sensor_classes/UltrasonicWindMeter.js +1 -1
- package/sensor_classes/Victron/VictronSensor.js +6 -2
- package/sensor_classes/VictronBatteryMonitor.js +9 -7
- package/sensor_classes/VictronDCEnergyMeter.js +1 -1
- package/sensor_classes/VictronInverter.js +1 -1
- package/sensor_classes/VictronInverterRS.js +1 -1
- package/sensor_classes/VictronOrionXS.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +18 -9
- package/spec/electrical.json +688 -0
- package/spec/environment.json +401 -0
- package/spec/sensors.json +39 -0
- package/spec/tanks.json +115 -0
- package/src/components/PluginConfigurationPanel.js +368 -0
- package/src/index.js +0 -0
- 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
|
-
|
|
81
|
-
|
|
82
|
-
this
|
|
83
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
280
|
-
|
|
323
|
+
this.addDefaultParam("name")
|
|
324
|
+
|
|
325
|
+
//create the 'location' parameter
|
|
326
|
+
|
|
327
|
+
this.addDefaultParam("location")
|
|
328
|
+
|
|
281
329
|
//create the 'RSSI' parameter
|
|
282
|
-
this.
|
|
283
|
-
this.
|
|
284
|
-
this.
|
|
285
|
-
this.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
402
|
-
return this.
|
|
536
|
+
getPath(tag){
|
|
537
|
+
return this._schema.properties.paths.properties[tag]
|
|
403
538
|
}
|
|
404
539
|
|
|
405
|
-
|
|
406
|
-
return
|
|
407
|
-
[...this.getMetadata().entries()].filter(([key,value]) => !(value?.isParam??false))
|
|
408
|
-
)
|
|
540
|
+
getPaths(){
|
|
541
|
+
return this._schema.properties.paths.properties
|
|
409
542
|
}
|
|
410
|
-
|
|
411
|
-
return
|
|
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
|
-
|
|
416
|
-
return
|
|
417
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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.
|
|
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
|
-
##
|
|
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,
|
|
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
|
-
|
|
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]
|