bt-sensors-plugin-sk 1.1.0-beta.2.1.4.1 → 1.1.0-beta.2.1.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/index.js +3 -3
- package/package.json +1 -1
- package/sensor_classes/GoveeTH.js +2 -1
- package/sensor_classes/MopekaTankSensor.js +11 -5
- package/sensor_classes/Renogy/RenogyConstants.js +0 -0
- package/sensor_classes/Renogy/RenogySensor.js +107 -0
- package/sensor_classes/Victron/VictronSensor.js +1 -12
- package/sensor_classes/VictronACCharger.js +2 -2
- package/sensor_classes/VictronSmartLithium.js +13 -4
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@ const util = require('util')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const {createBluetooth} = require('node-ble')
|
|
5
5
|
const {bluetooth, destroy} = createBluetooth()
|
|
6
|
-
|
|
6
|
+
const packageInfo = require("./package.json")
|
|
7
7
|
const BTSensor = require('./BTSensor.js')
|
|
8
8
|
const BLACKLISTED = require('./sensor_classes/BlackListedDevice.js')
|
|
9
9
|
|
|
@@ -152,7 +152,7 @@ module.exports = function (app) {
|
|
|
152
152
|
classMap = utilities_sk.loadClasses(path.join(__dirname, 'sensor_classes'))
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
app.debug(
|
|
155
|
+
app.debug(`Loading plugin ${packageInfo.version}`)
|
|
156
156
|
|
|
157
157
|
plugin.schema = {
|
|
158
158
|
type: "object",
|
|
@@ -414,7 +414,7 @@ module.exports = function (app) {
|
|
|
414
414
|
if (plugin.schema.properties.peripherals.items.dependencies)
|
|
415
415
|
plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf=[]
|
|
416
416
|
} else {
|
|
417
|
-
app.debug('Plugin build
|
|
417
|
+
app.debug('Plugin build Beta 2.4.1.2 started');
|
|
418
418
|
|
|
419
419
|
}
|
|
420
420
|
starts++
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.1.0-beta.2.1.4.
|
|
3
|
+
"version": "1.1.0-beta.2.1.4.4",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird, Ultrasonic, Mopeka tank reader and preliminary support for Govee GVH51xx temp sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -36,6 +36,7 @@ class GoveeTH extends BTSensor{
|
|
|
36
36
|
|
|
37
37
|
async init(){
|
|
38
38
|
await super.init()
|
|
39
|
+
this.initMetadata()
|
|
39
40
|
}
|
|
40
41
|
initMetadata(){
|
|
41
42
|
this.addMetadatum('temp','K', 'temperature')
|
|
@@ -63,4 +64,4 @@ class GoveeTH extends BTSensor{
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
|
-
module.exports=GoveeTH
|
|
67
|
+
module.exports=GoveeTH
|
|
@@ -258,23 +258,28 @@ class MopekaTankSensor extends BTSensor{
|
|
|
258
258
|
const md = this.valueIfVariant(this.getManufacturerData(this.constructor.manufacturerID))
|
|
259
259
|
this.modelID = md[0]
|
|
260
260
|
this.initMetadata()
|
|
261
|
-
|
|
262
261
|
}
|
|
263
262
|
|
|
264
263
|
getMedium(){
|
|
265
264
|
return Media[this?.medium??'PROPANE']
|
|
266
265
|
}
|
|
266
|
+
getTankHeight(){
|
|
267
|
+
return this?.tankHeight??304.8 //Assume a foot
|
|
268
|
+
}
|
|
267
269
|
|
|
268
270
|
_tankLevel( rawLevel ){
|
|
269
271
|
const coefs= this.getMedium().coefficients
|
|
270
|
-
return rawLevel * (coefs[0] + (coefs[1] * this.temp) + (coefs[2] * (this.temp^2)))
|
|
272
|
+
return rawLevel * (coefs[0] + (coefs[1] * (this.temp-233.15)) + (coefs[2] * ((this.temp-233.15)^2)))
|
|
271
273
|
}
|
|
272
274
|
|
|
273
275
|
initMetadata(){
|
|
274
|
-
|
|
276
|
+
var md = this.addMetadatum("medium","","type of liquid in tank")
|
|
275
277
|
md.isParam=true
|
|
276
278
|
md.enum=Object.keys(Media)
|
|
277
279
|
|
|
280
|
+
md = this.addMetadatum("tankHeight","mm","height of tank (in mm)")
|
|
281
|
+
md.isParam=true
|
|
282
|
+
|
|
278
283
|
this.addMetadatum("battVolt","V","sensor battery in volts",
|
|
279
284
|
((buffer)=>{
|
|
280
285
|
this.battVolt = (buffer.readUInt8(1)/32)
|
|
@@ -290,8 +295,8 @@ class MopekaTankSensor extends BTSensor{
|
|
|
290
295
|
return this.temp
|
|
291
296
|
}).bind(this)
|
|
292
297
|
)
|
|
293
|
-
this.addMetadatum("tankLevel","
|
|
294
|
-
(buffer)=>{ return this._tankLevel(((buffer.readUInt16LE(3))&0x3FFF)
|
|
298
|
+
this.addMetadatum("tankLevel","ratio","tank level",
|
|
299
|
+
(buffer)=>{ return (this._tankLevel(((buffer.readUInt16LE(3))&0x3FFF)))/this.getTankHeight()}
|
|
295
300
|
)
|
|
296
301
|
this.addMetadatum("readingQuality","","quality of read",
|
|
297
302
|
(buffer)=>{ return buffer.readUInt8(4)>>6}
|
|
@@ -309,6 +314,7 @@ class MopekaTankSensor extends BTSensor{
|
|
|
309
314
|
if (props.ManufacturerData)
|
|
310
315
|
this.emitValuesFrom( this.getManufacturerData(this.constructor.manufacturerID) )
|
|
311
316
|
}
|
|
317
|
+
|
|
312
318
|
getName(){
|
|
313
319
|
if (this.name)
|
|
314
320
|
return this.name
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const BTSensor = require("../../BTSensor.js");
|
|
2
|
+
const VC = require('./RenogyConstants.js');
|
|
3
|
+
function sleep(x) {
|
|
4
|
+
return new Promise((resolve) => {
|
|
5
|
+
setTimeout(() => {
|
|
6
|
+
resolve(x);
|
|
7
|
+
}, x);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
class RenogySensor extends BTSensor{
|
|
11
|
+
|
|
12
|
+
constructor(device,config,gattConfig){
|
|
13
|
+
super(device,config,gattConfig)
|
|
14
|
+
this.encryptionKey = config?.encryptionKey
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static async identifyMode(device, mode){
|
|
18
|
+
|
|
19
|
+
var md = await this.getDeviceProp(device,'ManufacturerData')
|
|
20
|
+
if (md==undefined || !Object.hasOwn(md,0x2e1))
|
|
21
|
+
return null
|
|
22
|
+
else {
|
|
23
|
+
|
|
24
|
+
if (md[0x2e1].value[0]==0x10) {
|
|
25
|
+
if (md[0x2e1].value[4]==mode)
|
|
26
|
+
return this
|
|
27
|
+
else
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var hasDataPacket=false
|
|
32
|
+
device.helper._prepare()
|
|
33
|
+
device.helper.on("PropertiesChanged",
|
|
34
|
+
(props)=> {
|
|
35
|
+
if (Object.hasOwn(props,'ManufacturerData')){
|
|
36
|
+
md = props['ManufacturerData'].value
|
|
37
|
+
hasDataPacket=md[0x2e1].value[0]==0x10
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
while (!hasDataPacket) {
|
|
41
|
+
await sleep(500)
|
|
42
|
+
}
|
|
43
|
+
device.helper.removeListeners()
|
|
44
|
+
if (md[0x2e1].value[4]==mode)
|
|
45
|
+
return this
|
|
46
|
+
else
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async init(){
|
|
52
|
+
await super.init()
|
|
53
|
+
}
|
|
54
|
+
alarmReason(alarmValue){
|
|
55
|
+
return this.constructor.AlarmReason[alarmValue]
|
|
56
|
+
}
|
|
57
|
+
getModelName(){
|
|
58
|
+
return VC?.MODEL_ID_MAP[this.model_id]??this.constructor.name+" (Model ID:"+this.model_id+")"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
decrypt(data){
|
|
62
|
+
if (!this.encryptionKey)
|
|
63
|
+
throw Error("Unable to decrypt: no encryption key set")
|
|
64
|
+
|
|
65
|
+
const encMethod = 'aes-128-ctr';
|
|
66
|
+
const iv = data.readUInt16LE(5);
|
|
67
|
+
const key = Buffer.from(this.encryptionKey,'hex')
|
|
68
|
+
const ivBuffer = Buffer.alloc(16); // 128 bits = 16 bytes
|
|
69
|
+
|
|
70
|
+
ivBuffer.writeUInt16LE(iv)
|
|
71
|
+
|
|
72
|
+
var encData = Buffer.from([...data.slice(8)])
|
|
73
|
+
|
|
74
|
+
const decipher = crypto.createDecipheriv(encMethod, key, ivBuffer)
|
|
75
|
+
|
|
76
|
+
const decData=decipher.update(encData)
|
|
77
|
+
|
|
78
|
+
return Buffer.from(decData)
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
getName(){
|
|
82
|
+
return `Victron ${this.getModelName()}`
|
|
83
|
+
}
|
|
84
|
+
propertiesChanged(props){
|
|
85
|
+
super.propertiesChanged(props)
|
|
86
|
+
if (this.usingGATT()) return
|
|
87
|
+
|
|
88
|
+
try{
|
|
89
|
+
const md = this.getManufacturerData(0x2e1)
|
|
90
|
+
if (md && md.length && md[0]==0x10){
|
|
91
|
+
const decData=this.decrypt(md)
|
|
92
|
+
this.emitValuesFrom(decData)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new Error(`Unable to read data from ${ this.getDisplayName()}: ${error}` )
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
initGATTConnection(){
|
|
101
|
+
throw new Error( "GATT Connection unimplemented for "+this.getDisplayName())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
module.exports=RenogySensor
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
const BTSensor = require("../../BTSensor.js");
|
|
2
2
|
const crypto = require('node:crypto');
|
|
3
|
-
const int24 = require('int24')
|
|
4
|
-
const util = require('util')
|
|
5
3
|
const VC = require('./VictronConstants.js');
|
|
6
|
-
const BLACKLISTED = require("../BlackListedDevice.js");
|
|
7
|
-
const { resolve } = require("node:path");
|
|
8
|
-
const { setMaxIdleHTTPParsers } = require("node:http");
|
|
9
4
|
function sleep(x) {
|
|
10
5
|
return new Promise((resolve) => {
|
|
11
6
|
setTimeout(() => {
|
|
@@ -20,7 +15,6 @@ function sleep(x) {
|
|
|
20
15
|
this.encryptionKey = config?.encryptionKey
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
|
|
24
18
|
static async identifyMode(device, mode){
|
|
25
19
|
|
|
26
20
|
var md = await this.getDeviceProp(device,'ManufacturerData')
|
|
@@ -54,15 +48,10 @@ function sleep(x) {
|
|
|
54
48
|
return null
|
|
55
49
|
}
|
|
56
50
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
51
|
|
|
60
52
|
async init(){
|
|
61
53
|
await super.init()
|
|
62
|
-
|
|
63
|
-
md.isParam = true
|
|
64
|
-
this.metadata = new Map(super.getMetadata())
|
|
65
|
-
md = this.addMetadatum('encryptionKey','', "Encryption Key")
|
|
54
|
+
const md =this.addMetadatum('encryptionKey','', "Encryption Key")
|
|
66
55
|
md.isParam = true
|
|
67
56
|
this.model_id=this.getManufacturerData(0x2e1).readUInt16LE(2)
|
|
68
57
|
}
|
|
@@ -35,7 +35,7 @@ class VictronACCharger extends VictronSensor{
|
|
|
35
35
|
initMetadata(){
|
|
36
36
|
this.addMetadatum('state','', 'device state',
|
|
37
37
|
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
38
|
-
this.addMetadatum('
|
|
38
|
+
this.addMetadatum('chargerError','', 'charger error code',
|
|
39
39
|
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
40
40
|
|
|
41
41
|
this.addMetadatum('batt1','V', 'battery 1 voltage')
|
|
@@ -70,4 +70,4 @@ class VictronACCharger extends VictronSensor{
|
|
|
70
70
|
this.emit("acCurr", this.NaNif(br.read_unsigned_int(9),0x1FF)/10)
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
module.exports=VictronACCharger
|
|
73
|
+
module.exports=VictronACCharger
|
|
@@ -16,10 +16,19 @@ Start Bit Nr of Bits Meaning Units Range NA Value Remark
|
|
|
16
16
|
152 7 Battery temperature 1°C -40..86 °C 0x7F VE_REG_BAT_TEMPERATURE Temperature = Record value - 40
|
|
17
17
|
159 1 Unused
|
|
18
18
|
VE_REG_BATTERY_CELL_VOLTAGE 0x00 ( 0) when cell voltage < 2.61V 0x01 ( 1) when cell voltage == 2.61V 0x7D (125) when cell voltage == 3.85V 0x7E (126) when cell voltage > 3.85 0x7F (127) when cell voltage is not available / unknown
|
|
19
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const VictronSensor = require ("./Victron/VictronSensor")
|
|
20
22
|
const VC = require("./Victron/VictronConstants")
|
|
21
23
|
const BitReader = require("./_BitReader")
|
|
22
24
|
|
|
25
|
+
const BALANCERSTATUS = {
|
|
26
|
+
0:"Unknown",
|
|
27
|
+
1:"Balanced",
|
|
28
|
+
2:"Balancing",
|
|
29
|
+
3:"Cell imbalance",
|
|
30
|
+
0xF:"Not applicable"
|
|
31
|
+
}
|
|
23
32
|
function _toCellVoltage(val){
|
|
24
33
|
return val==0x7F?NaN:2.6+(val/100)
|
|
25
34
|
}
|
|
@@ -51,15 +60,15 @@ class VictronSmartLithium extends VictronSensor{
|
|
|
51
60
|
this.addMetadatum('batteryVoltage','V', 'battery voltage',
|
|
52
61
|
(buff)=>{return this.NaNif((buff.readUInt16LE(13)&0xFFF),0xFFF)/100})
|
|
53
62
|
this.addMetadatum('balancerStatus','', 'balancer status', //TODO
|
|
54
|
-
(buff)=>{return this.NaNif((buff.readUInt8(14)>>4),0xF)})
|
|
63
|
+
(buff)=>{return BALANCERSTATUS[this.NaNif((buff.readUInt8(14)>>4),0xF)]})
|
|
55
64
|
this.addMetadatum('batteryTemp','K', 'battery temperature',
|
|
56
65
|
(buff)=>{return this.NaNif((buff.readUInt8(15)&0x7F),0x7F)+233.15})
|
|
57
66
|
}
|
|
58
67
|
emitValuesFrom(buffer){
|
|
59
68
|
super.emitValuesFrom(buffer)
|
|
60
69
|
const br = new BitReader(buffer.subarray(6,13))
|
|
61
|
-
for (let i =
|
|
70
|
+
for (let i = 0; i<8; i++)
|
|
62
71
|
this.emit(`cell${i+1}Voltage`,_toCellVoltage(br.read_unsigned_int(7)))
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
|
-
module.exports=VictronSmartLithium
|
|
74
|
+
module.exports=VictronSmartLithium
|