bt-sensors-plugin-sk 1.2.4-2 → 1.2.4-4
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 +32 -8
- package/README.md +23 -3
- package/package.json +2 -2
- package/public/index.html +23 -0
- package/sensor_classes/BTHome/AbstractBTHomeSensor.js +26 -1
- package/sensor_classes/Govee/GoveeSensor.js +2 -1
- package/sensor_classes/GoveeH510x.js +2 -1
- package/sensor_classes/Junctek.js +77 -48
- package/sensor_classes/MercurySmartcraft.js +8 -8
- package/sensor_classes/MopekaTankSensor.js +5 -3
- package/sensor_classes/ShellySBDW002C.js +51 -0
- package/sensor_classes/ShellySBMO003Z.js +1 -2
- package/sensor_classes/ShenzhenLiOnBMS.js +1 -0
- package/sensor_classes/XiaomiMiBeacon.js +2 -1
package/BTSensor.js
CHANGED
|
@@ -74,7 +74,7 @@ function preparePath(obj, str) {
|
|
|
74
74
|
evalResult= evalResult.call(obj)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
resultString += evalResult !== undefined ? evalResult : `${keyToAccess}_value_undefined`;
|
|
77
|
+
resultString += evalResult !== undefined ? evalResult.replace(/\s+/g,'_') : `${keyToAccess}_value_undefined`;
|
|
78
78
|
} catch (error) {
|
|
79
79
|
console.error(`Error accessing key '${keyToAccess}':`, error);
|
|
80
80
|
resultString += fullMatch; // Keep the original curly braces on error
|
|
@@ -405,6 +405,12 @@ class BTSensor extends EventEmitter {
|
|
|
405
405
|
if (!param.type)
|
|
406
406
|
param.type="string"
|
|
407
407
|
|
|
408
|
+
if (param.isRequired) {
|
|
409
|
+
if (!Object.hasOwn(this._schema.properties.params, "required"))
|
|
410
|
+
this._schema.properties.params.required=[tag]
|
|
411
|
+
else
|
|
412
|
+
this._schema.properties.params.required.push(tag)
|
|
413
|
+
}
|
|
408
414
|
this._schema.properties.params.properties[tag]=param
|
|
409
415
|
return this._schema.properties.params.properties[tag]
|
|
410
416
|
}
|
|
@@ -413,6 +419,13 @@ class BTSensor extends EventEmitter {
|
|
|
413
419
|
if (!path.type)
|
|
414
420
|
path.type="string"
|
|
415
421
|
|
|
422
|
+
if (path.isRequired) {
|
|
423
|
+
if (!Object.hasOwn(this._schema.properties.paths, "required"))
|
|
424
|
+
this._schema.properties.paths.required=[tag]
|
|
425
|
+
else
|
|
426
|
+
this._schema.properties.paths.required.push(tag)
|
|
427
|
+
}
|
|
428
|
+
|
|
416
429
|
if (!path.pattern)
|
|
417
430
|
path.pattern=//"^(?:[^{}\\s]*\\{[a-zA-Z0-9]+\\}[^{}\\s]*|[^{}\\s]*)$"
|
|
418
431
|
"^((\\{[a-zA-Z0-9]+\\}|[a-zA-Z0-9]+))(\\.(\\{[a-zA-Z0-9]+\\}|[a-zA-Z0-9]+))*$"
|
|
@@ -424,7 +437,13 @@ class BTSensor extends EventEmitter {
|
|
|
424
437
|
|
|
425
438
|
if (!param.type)
|
|
426
439
|
param.type="string"
|
|
427
|
-
|
|
440
|
+
|
|
441
|
+
if (param.isRequired) {
|
|
442
|
+
if (!Object.hasOwn(this._schema.properties.gattParams, "required"))
|
|
443
|
+
this._schema.properties.gattParams.required=[tag]
|
|
444
|
+
else
|
|
445
|
+
this._schema.properties.gattParams.required.push(tag)
|
|
446
|
+
}
|
|
428
447
|
return this._schema.properties.gattParams.properties[tag]=param
|
|
429
448
|
}
|
|
430
449
|
|
|
@@ -433,8 +452,10 @@ class BTSensor extends EventEmitter {
|
|
|
433
452
|
return this.addPath(tag,Object.assign({}, path))
|
|
434
453
|
}
|
|
435
454
|
|
|
436
|
-
addDefaultParam(tag){
|
|
437
|
-
|
|
455
|
+
addDefaultParam(tag,required=false){
|
|
456
|
+
const param = Object.assign({}, BTSensor.DEFAULTS.params[tag])
|
|
457
|
+
param.isRequired=required
|
|
458
|
+
return this.addParameter(tag,param)
|
|
438
459
|
}
|
|
439
460
|
|
|
440
461
|
getJSONSchema(){
|
|
@@ -866,26 +887,29 @@ class BTSensor extends EventEmitter {
|
|
|
866
887
|
Object.keys(this.getPaths()).forEach((tag)=>{
|
|
867
888
|
const pathMeta=this.getPath(tag)
|
|
868
889
|
const path = config.paths[tag]
|
|
869
|
-
if (!(path===undefined))
|
|
890
|
+
if (!(path===undefined)) {
|
|
891
|
+
const preparedPath =
|
|
870
892
|
this.app.handleMessage(id,
|
|
871
893
|
{
|
|
872
894
|
updates:
|
|
873
|
-
[{ meta: [{path:
|
|
895
|
+
[{ meta: [{path: preparePath(this, path), value: { units: pathMeta?.unit }}]}]
|
|
874
896
|
})
|
|
897
|
+
}
|
|
875
898
|
})
|
|
876
899
|
}
|
|
877
900
|
|
|
878
901
|
initPaths(deviceConfig, id){
|
|
879
|
-
const source = this.getName()
|
|
902
|
+
const source = `${this.getName()} (bt-sensors-plugin-sk)`
|
|
880
903
|
Object.keys(this.getPaths()).forEach((tag)=>{
|
|
881
904
|
const pathMeta=this.getPath(tag)
|
|
882
905
|
const path = deviceConfig.paths[tag];
|
|
883
906
|
if (!(path === undefined)) {
|
|
907
|
+
let preparedPath = preparePath(this, path)
|
|
884
908
|
this.on(tag, (val)=>{
|
|
885
909
|
if (pathMeta.notify){
|
|
886
910
|
this.app.notify(tag, val, id )
|
|
887
911
|
} else {
|
|
888
|
-
this.updatePath(
|
|
912
|
+
this.updatePath(preparedPath,val, id, source)
|
|
889
913
|
}
|
|
890
914
|
})
|
|
891
915
|
}
|
package/README.md
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
|
+
|
|
3
|
+
## IMPORTANT NOTE TO NEW USERS
|
|
4
|
+
|
|
5
|
+
There's a known issue with saving the configuration after initial installation owing to current ServerAPI limitations.
|
|
6
|
+
|
|
7
|
+
Your device config after you've saved it will appear to be "missing" after restarting. It's in fact saved in the plugin config directory. All you have to do is Submit the main configuration then enable and optionally disable Debug. This, believe it or not, ensures that the config is marked as enabled. You should see your data now and upon restart.
|
|
8
|
+
|
|
9
|
+
## WHAT'S NEW
|
|
10
|
+
|
|
11
|
+
# Version 1.2.4-4
|
|
12
|
+
|
|
13
|
+
Junctek support (tested)
|
|
14
|
+
|
|
15
|
+
# Version 1.2.4-3
|
|
16
|
+
|
|
17
|
+
- Mercury Smartcraft fixes (working now!)
|
|
18
|
+
- Govee 510x regression errors fixed
|
|
19
|
+
|
|
2
20
|
# Version 1.2.4-2
|
|
3
21
|
|
|
4
22
|
- RenogyRoverClient fix to Battery SOC calculation
|
|
@@ -6,9 +24,7 @@
|
|
|
6
24
|
- SwitchBotTH and Meter Plus ::identify errors fixed
|
|
7
25
|
- MercurySmartcraft::identify method fix
|
|
8
26
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# 1.2.4-1
|
|
27
|
+
# Version 1.2.4-1
|
|
12
28
|
|
|
13
29
|
- **NEW SENSOR** [Bank Manager](https://marinedcac.com/pages/bankmanager) (tested)
|
|
14
30
|
- **NEW SENSOR** [Mercury Smartcraft](https://www.mercurymarine.com/us/en/gauges-and-controls/displays/smartcraft-connect) (untested)
|
|
@@ -158,6 +174,10 @@ Finally, restart SK. Plugin should appear in your server plugins list.<br>
|
|
|
158
174
|
|
|
159
175
|
## KNOWN ISSUES
|
|
160
176
|
|
|
177
|
+
### Problems saving the configuration after installing plugin for first time
|
|
178
|
+
|
|
179
|
+
Your device config after you've saved it will appear to be "missing" after restarting. It's in fact saved in the plugin config directory. All you have to do is Submit the main configuration then enable and optionally disable Debug. This, believe it or not, ensures that the config is marked as enabled. You should see your data now and upon restart.
|
|
180
|
+
|
|
161
181
|
### Connected Devices on Raspberry Pi platform (4/4b/5/CM400/CM500)
|
|
162
182
|
|
|
163
183
|
Onboard Raspberry Pi WiFi can cause interference with the onboard Bluetooth resulting in lost connections to GATT connected devices (Renogy, JBD, etc. )
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.2.4-
|
|
3
|
+
"version": "1.2.4-4",
|
|
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
|
-
"dependencies": {
|
|
6
|
+
"dependencies": {
|
|
7
7
|
"@rjsf/bootstrap-4": "^5.24.11",
|
|
8
8
|
"@rjsf/core": "^5.24.11",
|
|
9
9
|
"@rjsf/utils": "^5.24.11",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<meta charset="utf-8">
|
|
3
|
+
<style>
|
|
4
|
+
.line {
|
|
5
|
+
fill: none;
|
|
6
|
+
stroke: steelblue;
|
|
7
|
+
stroke-width: 20px;
|
|
8
|
+
}
|
|
9
|
+
</style>
|
|
10
|
+
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Bluetooth Sensors for SignalK</h1>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
|
|
16
|
+
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
|
|
17
|
+
width = 960 - margin.left - margin.right,
|
|
18
|
+
height = 300 - margin.top - margin.bottom;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
</script>
|
|
23
|
+
<script src="main.js"></script><script src="remoteEntry.js"></script></body>
|
|
@@ -235,7 +235,7 @@ class AbstractBTHomeSensor extends BTSensor {
|
|
|
235
235
|
* Extracts motion detection from the given BTHome data.
|
|
236
236
|
*
|
|
237
237
|
* @param btHomeData {BTHomeServiceData.BthomeServiceData} The BTHome data provided by the device.
|
|
238
|
-
|
|
238
|
+
* @returns {Boolean|null} Wether the device has detected motion.
|
|
239
239
|
*/
|
|
240
240
|
parseMotion(btHomeData) {
|
|
241
241
|
const motion = this.getSensorDataByObjectId(
|
|
@@ -304,6 +304,31 @@ class AbstractBTHomeSensor extends BTSensor {
|
|
|
304
304
|
return null;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Parses the opening (window/door) state from BTHome service data.
|
|
309
|
+
* @param {BTHomeServiceData.BthomeServiceData} btHomeData - The parsed BTHome data object.
|
|
310
|
+
* @returns {string|null} "open", "closed", or null if not present.
|
|
311
|
+
*/
|
|
312
|
+
parseWindowState(btHomeData) {
|
|
313
|
+
const state = this.getSensorDataByObjectId(btHomeData, BTHomeServiceData.BthomeObjectId.BINARY_WINDOW)?.window;
|
|
314
|
+
if (state.intValue === 1) return "open";
|
|
315
|
+
if (state.intValue === 0) return "closed";
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Parses the rotation from BTHome service data.
|
|
321
|
+
* @param {BTHomeServiceData.BthomeServiceData} btHomeData - The parsed BTHome data object.
|
|
322
|
+
* @returns {number|null} rotation in degrees from the closed position, or null if not present.
|
|
323
|
+
*/
|
|
324
|
+
parseRotation(btHomeData) {
|
|
325
|
+
// Use the correct object ID for window event (0x2D, which is 45 in decimal, which is already specified in BthomeObjectId).
|
|
326
|
+
const sensorRotation = this.getSensorDataByObjectId(btHomeData, BTHomeServiceData.BthomeObjectId.SENSOR_ROTATION_0_1)?.rotation;
|
|
327
|
+
if (sensorRotation) {
|
|
328
|
+
return Number.parseFloat(sensorRotation.toFixed(2));
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
307
332
|
|
|
308
333
|
propertiesChanged(props) {
|
|
309
334
|
super.propertiesChanged(props);
|
|
@@ -18,6 +18,7 @@ class GoveeSensor extends BTSensor {
|
|
|
18
18
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
static DATA_ID=0xec88
|
|
21
22
|
getManufacturer(){
|
|
22
23
|
return "Govee"
|
|
23
24
|
}
|
|
@@ -38,7 +39,7 @@ class GoveeSensor extends BTSensor {
|
|
|
38
39
|
super.propertiesChanged(props)
|
|
39
40
|
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
40
41
|
|
|
41
|
-
const buffer = this.getManufacturerData(
|
|
42
|
+
const buffer = this.getManufacturerData(this.constructor.DATA_ID)
|
|
42
43
|
if (buffer) {
|
|
43
44
|
this.emitValuesFrom(buffer)
|
|
44
45
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const GoveeSensor = require("./Govee/GoveeSensor");
|
|
2
2
|
|
|
3
3
|
class GoveeH510x extends GoveeSensor{
|
|
4
|
+
|
|
4
5
|
static getIDRegex(){
|
|
5
6
|
return /^GVH510[0-9]_[a-f,A-F,0-9]{4}$/
|
|
6
7
|
}
|
|
@@ -15,7 +16,7 @@ class GoveeH510x extends GoveeSensor{
|
|
|
15
16
|
sensor.emitValuesFrom(Buffer.from([0x01,0x01,0x03,0x6d,0xcc,0x5c]))
|
|
16
17
|
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
static DATA_ID = 0x0001
|
|
19
20
|
initSchema(){
|
|
20
21
|
super.initSchema()
|
|
21
22
|
this.addDefaultParam("zone")
|
|
@@ -5,7 +5,7 @@ const BTSensor = require("../BTSensor");
|
|
|
5
5
|
function bytesToBase10String(bytes){
|
|
6
6
|
let s = ""
|
|
7
7
|
for (let byte of bytes){
|
|
8
|
-
s+=byte.toString(16)
|
|
8
|
+
s+=byte.toString(16).padStart(2,'0')
|
|
9
9
|
}
|
|
10
10
|
return s
|
|
11
11
|
}
|
|
@@ -22,6 +22,8 @@ class JunctekBMS extends BTSensor{
|
|
|
22
22
|
return null
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
chargeDirection = 1
|
|
26
|
+
|
|
25
27
|
hasGATT(){
|
|
26
28
|
return true
|
|
27
29
|
}
|
|
@@ -41,19 +43,21 @@ class JunctekBMS extends BTSensor{
|
|
|
41
43
|
this.addDefaultPath("cycles",'electrical.batteries.cycles')
|
|
42
44
|
|
|
43
45
|
this.addDefaultPath("soc",'electrical.batteries.capacity.stateOfCharge')
|
|
44
|
-
this.addDefaultPath("
|
|
46
|
+
this.addDefaultPath("remaining",'electrical.batteries.capacity.remaining')
|
|
45
47
|
this.addDefaultPath("timeRemaining",'electrical.batteries.capacity.timeRemaining')
|
|
46
48
|
this.addDefaultPath("discharge",'electrical.batteries.capacity.dischargeSinceFull')
|
|
47
49
|
this.addDefaultPath("charge",'electrical.batteries.capacity.charge')
|
|
48
50
|
this.addDefaultPath("temperature",'electrical.batteries.temperature')
|
|
49
51
|
this.addDefaultPath("actualCapacity",'electrical.batteries.capacity.actual')
|
|
52
|
+
this.addMetadatum('timeToCharged','s', 'time in seconds to battery is fully charged')
|
|
53
|
+
.default='electrical.batteries.{batteryID}.timeToCharged'
|
|
50
54
|
this.addMetadatum('impedance','mOhm', 'measured resistance')
|
|
51
55
|
.default='electrical.batteries.{batteryID}.impedance'
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
emitFrom(buffer){
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
let value=[], emitObject={}
|
|
60
|
+
this.debug(buffer)
|
|
57
61
|
for (let byte of buffer){
|
|
58
62
|
if (byte==0xBB) {
|
|
59
63
|
value=[]
|
|
@@ -63,65 +67,90 @@ class JunctekBMS extends BTSensor{
|
|
|
63
67
|
continue
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (parseInt(byte.toString(16))==NaN){ //not a base-10 number. seriously. that's how Junctek does this.
|
|
70
|
+
if (isNaN(parseInt(byte.toString(16)))){ //not a base-10 number. seriously. that's how Junctek does this.
|
|
69
71
|
const v = parseInt(bytesToBase10String(value))
|
|
72
|
+
//if (byte!==0xd5)
|
|
73
|
+
// this.debug(`0x${(byte).toString(16)}: ${(value).map((v)=>'0x'+v.toString(16))} (${v})`)
|
|
74
|
+
value=[]
|
|
70
75
|
switch (byte){
|
|
71
|
-
case 0xC0:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
case
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
case
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
case
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
76
|
+
case 0xC0:
|
|
77
|
+
emitObject["voltage"]=v/100
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
case 0xC1:
|
|
81
|
+
emitObject["current"]=()=>{return (v/100)*this.chargeDirection}
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
case 0xD1:
|
|
85
|
+
this.debug(v)
|
|
86
|
+
if (v==0)
|
|
87
|
+
this.chargeDirection=-1
|
|
88
|
+
else
|
|
89
|
+
this.chargeDirection= 1
|
|
90
|
+
this.debug(this.chargeDirection)
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
case 0xD2:
|
|
94
|
+
|
|
95
|
+
emitObject["remaining"]=v*3.6
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
case 0xD3:
|
|
99
|
+
emitObject["discharge"]= v/100000
|
|
100
|
+
break
|
|
101
|
+
case 0xD4:
|
|
102
|
+
emitObject["charge"]=v/100000
|
|
103
|
+
break
|
|
104
|
+
case 0xD6:
|
|
105
|
+
if (chargeDirection==-1){
|
|
106
|
+
emitObject["timeToCharged"] = NaN
|
|
107
|
+
emitObject["timeRemaining"] = v*60
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
emitObject["timeRemaining"] = NaN
|
|
111
|
+
emitObject["timeToCharged"] = v*60
|
|
112
|
+
}
|
|
113
|
+
break
|
|
114
|
+
case 0xD7:
|
|
115
|
+
emitObject["impedance"]=v/100
|
|
116
|
+
break
|
|
117
|
+
case 0xD8:
|
|
118
|
+
emitObject["power"]=()=>{
|
|
119
|
+
this.debug(this.chargeDirection)
|
|
120
|
+
return (v/100)*this.chargeDirection
|
|
121
|
+
}
|
|
122
|
+
break
|
|
123
|
+
case 0xD9:
|
|
124
|
+
emitObject["temperature"]=v + 173.15 //assume C not F -- raw value is c - 100
|
|
125
|
+
break
|
|
126
|
+
case 0xB1:
|
|
127
|
+
emitObject["capacityActual"]=v /10
|
|
128
|
+
break
|
|
108
129
|
}
|
|
109
130
|
}
|
|
131
|
+
else{
|
|
132
|
+
value.push(byte)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const [key, value] of Object.entries(emitObject)) {
|
|
136
|
+
this.emit(key,value instanceof Function?value():value)
|
|
110
137
|
}
|
|
138
|
+
emitObject = {}
|
|
111
139
|
}
|
|
112
140
|
emitGATT(){
|
|
113
|
-
this.battCharacteristic.readValue()
|
|
141
|
+
/*this.battCharacteristic.readValue()
|
|
114
142
|
.then((buffer)=>{
|
|
115
143
|
this.emitFrom(buffer)
|
|
116
|
-
})
|
|
144
|
+
})*/
|
|
117
145
|
}
|
|
118
146
|
initGATTConnection(){
|
|
119
147
|
return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
|
|
120
148
|
if (!this.gattServer) {
|
|
121
149
|
this.gattServer = await this.device.gatt()
|
|
122
150
|
this.battService = await this.gattServer.getPrimaryService("0000fff0-0000-1000-8000-00805f9b34fb")
|
|
123
|
-
this.battCharacteristic = await this.battService.getCharacteristic("
|
|
124
|
-
|
|
151
|
+
this.battCharacteristic = await this.battService.getCharacteristic("0000fff1-0000-1000-8000-00805f9b34fb")
|
|
152
|
+
|
|
153
|
+
}
|
|
125
154
|
resolve(this)
|
|
126
155
|
}) .catch((e)=>{ reject(e.message) }) })
|
|
127
156
|
}
|
|
@@ -38,17 +38,17 @@ class MercurySmartcraft extends BTSensor{
|
|
|
38
38
|
emitGATT(){
|
|
39
39
|
}
|
|
40
40
|
initSchema(){
|
|
41
|
-
const bo =
|
|
41
|
+
const bo = 2
|
|
42
42
|
|
|
43
43
|
super.initSchema()
|
|
44
44
|
this.addParameter(
|
|
45
45
|
"id",
|
|
46
46
|
{
|
|
47
47
|
"title": "Engine ID",
|
|
48
|
-
"examples": ["port","starboard","p0","p1"]
|
|
48
|
+
"examples": ["port","starboard","p0","p1"],
|
|
49
|
+
"isRequired": true
|
|
49
50
|
}
|
|
50
51
|
)
|
|
51
|
-
_schema.properties.params.required=["id"]
|
|
52
52
|
|
|
53
53
|
this.addMetadatum("rpm","Hz","engine revolutions per sec",
|
|
54
54
|
(buffer)=>{return buffer.readUInt16LE(bo)/60}
|
|
@@ -67,15 +67,15 @@ class MercurySmartcraft extends BTSensor{
|
|
|
67
67
|
).default='propulsion.{id}.runTime'
|
|
68
68
|
|
|
69
69
|
this.addMetadatum("rate","m3/s","Fuel rate of consumption (cubic meters per second)",
|
|
70
|
-
(buffer)=>{return buffer.readUInt16LE(bo)/
|
|
70
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/100000/3600}
|
|
71
71
|
).default='propulsion.{id}.fuel.rate'
|
|
72
72
|
|
|
73
73
|
this.addMetadatum("pressure","Pa","Fuel pressure",
|
|
74
|
-
(buffer)=>{return buffer.readUInt16LE(bo)
|
|
74
|
+
(buffer)=>{return buffer.readUInt16LE(bo)*10}
|
|
75
75
|
).default='propulsion.{id}.pressure'
|
|
76
76
|
|
|
77
77
|
this.addMetadatum("level","ratio","Level of fluid in tank 0-100%",
|
|
78
|
-
(buffer)=>{return buffer.readUInt16LE(bo)/
|
|
78
|
+
(buffer)=>{return buffer.readUInt16LE(bo)/10000}
|
|
79
79
|
).default='tanks.petrol.currentLevel'
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -100,7 +100,7 @@ class MercurySmartcraft extends BTSensor{
|
|
|
100
100
|
}) .catch((e)=>{ reject(e.message) }) })
|
|
101
101
|
}
|
|
102
102
|
async initGATTNotifications() {
|
|
103
|
-
await this.sdpCharacteristic.
|
|
103
|
+
await this.sdpCharacteristic.writeValue(Buffer.from([0x0D,0x01]))
|
|
104
104
|
for (const c in this.dataCharacteristics){
|
|
105
105
|
Promise.resolve(this.dataCharacteristics[c].startNotifications().then(()=>{
|
|
106
106
|
this.dataCharacteristics[c].on('valuechanged', buffer => {
|
|
@@ -113,7 +113,7 @@ class MercurySmartcraft extends BTSensor{
|
|
|
113
113
|
async stopListening(){
|
|
114
114
|
super.stopListening()
|
|
115
115
|
for (const c in this.dataCharacteristics){
|
|
116
|
-
if (this.dataCharacteristics[c] && await this.dataCharacteristics[
|
|
116
|
+
if (this.dataCharacteristics[c] && await this.dataCharacteristics[c].isNotifying()) {
|
|
117
117
|
await this.dataCharacteristics[c].stopNotifications()
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -280,17 +280,19 @@ class MopekaTankSensor extends BTSensor{
|
|
|
280
280
|
this.addParameter("medium",
|
|
281
281
|
{
|
|
282
282
|
title:"type of liquid in tank",
|
|
283
|
-
enum: Object.keys(Media)
|
|
283
|
+
enum: Object.keys(Media),
|
|
284
|
+
isRequired: true
|
|
284
285
|
}
|
|
285
286
|
)
|
|
286
287
|
this.addParameter("tankHeight",
|
|
287
288
|
{
|
|
288
289
|
title:"height of tank (in mm)",
|
|
289
290
|
type:"number",
|
|
290
|
-
unit:"mm"
|
|
291
|
+
unit:"mm",
|
|
292
|
+
isRequired: true
|
|
291
293
|
}
|
|
292
294
|
)
|
|
293
|
-
this.addDefaultParam("id")
|
|
295
|
+
this.addDefaultParam("id", true)
|
|
294
296
|
|
|
295
297
|
this.addDefaultPath("battVolt","sensors.batteryVoltage")
|
|
296
298
|
.read=((buffer)=>{
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const BTHomeServiceData = require("./BTHome/BTHomeServiceData");
|
|
2
|
+
const AbstractBTHomeSensor = require("./BTHome/AbstractBTHomeSensor");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sensor class representing the Shelly BLU Door/Window.
|
|
6
|
+
* Specification on @see https://shelly-api-docs.shelly.cloud/docs-ble/Devices/dw
|
|
7
|
+
* This sensor is publishing data utilising the BTHome format and inherits from {@link AbstractBTHomeSensor}.
|
|
8
|
+
*/
|
|
9
|
+
class ShellySBDW002C extends AbstractBTHomeSensor {
|
|
10
|
+
static Domain = this.SensorDomains.environmental;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The shortened local name as advertised by the Shelly BLU H&T.
|
|
14
|
+
* @type {string}
|
|
15
|
+
*/
|
|
16
|
+
static SHORTENED_LOCAL_NAME = "SBDW-002C";
|
|
17
|
+
static LOCAL_NAME = "Shelly BLU Door/Window";
|
|
18
|
+
|
|
19
|
+
getDescription() {
|
|
20
|
+
return `For more information about the sensor go here: <a href=https://us.shelly.com/products/shelly-blu-door-window-white target="_blank">Shelly Blu Door/Window</a>.`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
initSchema() {
|
|
24
|
+
super.initSchema();
|
|
25
|
+
this.addDefaultParam("zone", true).default = "cabin";
|
|
26
|
+
|
|
27
|
+
this.addParameter("opening", {
|
|
28
|
+
title: "Name of opening",
|
|
29
|
+
examples: ["companionWay", "porthole", "hatch"],
|
|
30
|
+
isRequired: true,
|
|
31
|
+
default: "companionWay",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
this.addMetadatum("opening", "", "state of opening (door/window) as 'open' or 'closed'; null if not present. ", this.parseWindowState.bind(this)).default =
|
|
35
|
+
"environment.{zone}.{opening}.state";
|
|
36
|
+
|
|
37
|
+
// The second parameter is not the signalk path, but a JSON object in plugin_defaults.json; that provides the default signalk path
|
|
38
|
+
this.addDefaultPath("battery", "sensors.batteryStrength").read = this.parseBatteryLevel.bind(this);
|
|
39
|
+
|
|
40
|
+
this.addMetadatum("illuminance", "lx", "illuminance measured in lux, scaled 0.01", this.parseIlluminance.bind(this)).default =
|
|
41
|
+
"sensors.{macAndName}.illuminance";
|
|
42
|
+
|
|
43
|
+
this.addMetadatum("rotation", "deg", "rotation in degrees from the closed position; null if not present", this.parseRotation.bind(this)).default =
|
|
44
|
+
"sensors.{macAndName}.rotation";
|
|
45
|
+
|
|
46
|
+
this.addMetadatum("button", "", "button 'press' or 'hold_press'; null if not present", this.parseShellyButton.bind(this)).default =
|
|
47
|
+
"sensors.{macAndName}.button";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = ShellySBDW002C;
|
|
@@ -218,7 +218,8 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
218
218
|
this.addParameter(
|
|
219
219
|
"encryptionKey",
|
|
220
220
|
{
|
|
221
|
-
title: "encryptionKey (AKA bindKey) for decryption"
|
|
221
|
+
title: "encryptionKey (AKA bindKey) for decryption",
|
|
222
|
+
|
|
222
223
|
}
|
|
223
224
|
)
|
|
224
225
|
this.addDefaultPath('temp','environment.temperature')
|