bt-sensors-plugin-sk 1.1.0-beta.2.1.1 → 1.1.0-beta.2.1.3
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 +391 -147
- package/README.md +2 -2
- package/index.js +14 -10
- package/package.json +1 -1
- package/sensor_classes/ATC.js +43 -11
- package/sensor_classes/BlackListedDevice.js +6 -0
- package/sensor_classes/Inkbird.js +1 -1
- package/sensor_classes/RuuviTag.js +3 -3
- package/sensor_classes/Victron/VictronConstants.js +5 -0
- package/sensor_classes/Victron/VictronSensor.js +15 -20
- package/sensor_classes/VictronACCharger.js +8 -5
- package/sensor_classes/VictronBatteryMonitor.js +51 -19
- package/sensor_classes/VictronGXDevice.js +1 -1
- package/sensor_classes/VictronSmartLithium.js +15 -12
- package/sensor_classes/VictronSolarCharger.js +18 -17
- package/sensor_classes/XiaomiMiBeacon.js +5 -5
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
## WHAT IT IS
|
|
5
5
|
|
|
6
|
-
BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for connecting to Bluetooth sensors on your boat and sending deltas to Signalk paths with the values the sensors reports. <br>
|
|
6
|
+
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>
|
|
7
7
|
|
|
8
8
|
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), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags and Inkbird thermometers.
|
|
9
9
|
|
|
@@ -71,7 +71,7 @@ Then press the + button to add a sensor. Your screen should look something like
|
|
|
71
71
|
|
|
72
72
|
<br><br>
|
|
73
73
|
|
|
74
|
-
Select the sensor you want to
|
|
74
|
+
Select the sensor you want Signalk to listen to from the drop down.<br>
|
|
75
75
|
|
|
76
76
|
If you don't see your device and you know that it's on and nearby to the server, it may not be currently supported. But fear not, you can add custom sensor classes yourself. (Check out [the development section](#development).). <br><br>
|
|
77
77
|
|
package/index.js
CHANGED
|
@@ -48,8 +48,8 @@ class MissingSensor {
|
|
|
48
48
|
getDisplayName(){
|
|
49
49
|
return `OUT OF RANGE DEVICE (${this.getName()} ${this.getMacAddress()})`
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
stopListening(){}
|
|
52
|
+
listen(){}
|
|
53
53
|
}
|
|
54
54
|
module.exports = function (app) {
|
|
55
55
|
const adapterID = 'hci0'
|
|
@@ -159,7 +159,7 @@ module.exports = function (app) {
|
|
|
159
159
|
plugin.schema = {
|
|
160
160
|
type: "object",
|
|
161
161
|
description: "NOTE: \n 1) Plugin must be enabled to configure your sensors. \n"+
|
|
162
|
-
"2) You will have to wait until the scanner has found
|
|
162
|
+
"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"+
|
|
163
163
|
"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"+
|
|
164
164
|
"4) If you submit and get errors it may be because the configured devices have not yet all been discovered.",
|
|
165
165
|
required:["discoveryTimeout", "discoveryInterval"],
|
|
@@ -310,7 +310,7 @@ module.exports = function (app) {
|
|
|
310
310
|
})
|
|
311
311
|
.catch((e)=>{
|
|
312
312
|
if (s)
|
|
313
|
-
s.
|
|
313
|
+
s.stopListening()
|
|
314
314
|
app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
|
|
315
315
|
reject( e?.message??e )
|
|
316
316
|
})})
|
|
@@ -346,22 +346,24 @@ module.exports = function (app) {
|
|
|
346
346
|
if (deviceConfig.active) {
|
|
347
347
|
createPaths(deviceConfig)
|
|
348
348
|
initPaths(deviceConfig)
|
|
349
|
-
const result = Promise.resolve(deviceConfig.sensor.
|
|
349
|
+
const result = Promise.resolve(deviceConfig.sensor.listen())
|
|
350
350
|
result.then(() => {
|
|
351
|
-
app.debug(`
|
|
352
|
-
app.setPluginStatus(`Initial scan complete.
|
|
351
|
+
app.debug(`Listening for changes from ${deviceConfig.sensor.getDisplayName()}`);
|
|
352
|
+
app.setPluginStatus(`Initial scan complete. Listening to ${++found} sensors.`);
|
|
353
353
|
})
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
})
|
|
357
357
|
.catch((error)=>
|
|
358
358
|
{
|
|
359
|
+
|
|
359
360
|
const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
|
|
360
361
|
app.debug(msg)
|
|
361
362
|
app.debug(error)
|
|
362
363
|
app.setPluginError(msg)
|
|
363
364
|
deviceConfig.sensor=new MissingSensor(deviceConfig)
|
|
364
365
|
addSensorToList(deviceConfig.sensor) //add sensor to list with known options
|
|
366
|
+
|
|
365
367
|
})
|
|
366
368
|
}
|
|
367
369
|
function findDevices (discoveryTimeout) {
|
|
@@ -439,13 +441,15 @@ module.exports = function (app) {
|
|
|
439
441
|
plugin.stopped=true
|
|
440
442
|
plugin.uiSchema.peripherals['ui:disabled']=true
|
|
441
443
|
if ((sensorMap)){
|
|
444
|
+
plugin.schema.properties.peripherals.items.properties.mac_address.enum=[]
|
|
445
|
+
plugin.schema.properties.peripherals.items.properties.mac_address.enumNames=[]
|
|
442
446
|
sensorMap.forEach(async (sensor, mac)=> {
|
|
443
447
|
try{
|
|
444
|
-
await sensor.
|
|
445
|
-
app.debug(`
|
|
448
|
+
await sensor.stopListening()
|
|
449
|
+
app.debug(`No longer listening to ${mac}`)
|
|
446
450
|
}
|
|
447
451
|
catch (e){
|
|
448
|
-
app.debug(`Error
|
|
452
|
+
app.debug(`Error stopping listening to ${mac}: ${e.message}`)
|
|
449
453
|
}
|
|
450
454
|
})
|
|
451
455
|
}
|
package/package.json
CHANGED
package/sensor_classes/ATC.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const BTSensor = require("../BTSensor");
|
|
2
2
|
class ATC extends BTSensor{
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
constructor(device, config={}){
|
|
5
|
+
super(device,config)
|
|
6
|
+
if (config.parser){
|
|
7
|
+
this.parser=config.parser
|
|
8
|
+
}
|
|
9
|
+
}
|
|
4
10
|
static async identify(device){
|
|
5
11
|
try{
|
|
6
12
|
const regex = /^ATC_[A-Fa-f0-9]{6}$/
|
|
@@ -15,22 +21,48 @@ class ATC extends BTSensor{
|
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
async init() {
|
|
18
|
-
await super.init()
|
|
19
|
-
this.addMetadatum('
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
await super.init()
|
|
25
|
+
const md=this.addMetadatum('parser','','data parsing strategy')
|
|
26
|
+
md.isParam=true
|
|
27
|
+
md.enum=["ATC-LE","ATC-BE"]
|
|
28
|
+
if (!this.parser){
|
|
29
|
+
this.parser="ATC-LE"
|
|
30
|
+
}
|
|
31
|
+
this.initMetadata()
|
|
32
|
+
}
|
|
33
|
+
initMetadata(){
|
|
34
|
+
if (this.parser=="ATC-LE"){
|
|
35
|
+
this.addMetadatum('voltage', 'V', 'sensor battery voltage',
|
|
36
|
+
(buff)=>{return ((buff.readUInt16LE(10))/1000)})
|
|
37
|
+
this.addMetadatum('batteryStrength', 'ratio', 'sensor battery strength',
|
|
38
|
+
(buff)=>{return ((buff.readUInt8(12))/100)})
|
|
39
|
+
this.addMetadatum('temp','K', 'temperature',
|
|
40
|
+
(buff)=>{return parseFloat((273.15+(buff.readInt16LE(6))/100).toFixed(2))})
|
|
41
|
+
this.addMetadatum('humidity','ratio', 'humidity',
|
|
42
|
+
(buff)=>{return ((buff.readUInt16LE(8))/10000)})
|
|
43
|
+
|
|
44
|
+
} else{
|
|
45
|
+
this.addMetadatum('voltage', 'V', 'sensor battery voltage',
|
|
46
|
+
(buff)=>{return ((buff.readUInt16BE(10))/1000)})
|
|
47
|
+
this.addMetadatum('batteryStrength', 'ratio', 'sensor battery strength',
|
|
48
|
+
(buff)=>{return ((buff.readUInt8(9))/100)})
|
|
49
|
+
this.addMetadatum('temp','K', 'temperature',
|
|
50
|
+
(buff)=>{return parseFloat((273.15+(buff.readInt16BE(6))/100).toFixed(2))})
|
|
51
|
+
this.addMetadatum('humidity','ratio', 'humidity',
|
|
52
|
+
(buff)=>{return ((buff.readUInt8(8))/100)})
|
|
53
|
+
|
|
54
|
+
}
|
|
25
55
|
}
|
|
26
56
|
propertiesChanged(props){
|
|
27
57
|
super.propertiesChanged(props)
|
|
28
58
|
const buff = this.getServiceData("0000181a-0000-1000-8000-00805f9b34fb")
|
|
29
59
|
if (!buff)
|
|
30
60
|
throw new Error("Unable to get service data for "+this.getDisplayName())
|
|
31
|
-
this.emitData("temp", buff
|
|
32
|
-
this.emitData("humidity", buff
|
|
33
|
-
this.emitData("voltage", buff
|
|
61
|
+
this.emitData("temp", buff)
|
|
62
|
+
this.emitData("humidity", buff)
|
|
63
|
+
this.emitData("voltage", buff)
|
|
64
|
+
this.emitData("batteryStrength", buff)
|
|
65
|
+
|
|
34
66
|
}
|
|
35
67
|
getManufacturer(){
|
|
36
68
|
return "ATC1441 (custom firmware see: https://github.com/atc1441)"
|
|
@@ -12,7 +12,13 @@ class BLACKLISTED extends BTSensor{
|
|
|
12
12
|
reasonForBlacklisting() {
|
|
13
13
|
switch ( this.getManufacturerID()){
|
|
14
14
|
case (0x004C): return "Randomized MAC address"
|
|
15
|
+
case (0x02e1): return "Device is using VE.Smart" //NOTE: Victron/VictronSensor class
|
|
16
|
+
//determines if a device is using VE.Smart
|
|
17
|
+
//in identify(). If so, identify() returns
|
|
18
|
+
//BlackListedDevice
|
|
19
|
+
|
|
15
20
|
default: return ""
|
|
21
|
+
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
}
|
|
@@ -36,7 +36,7 @@ class Inkbird extends BTSensor{
|
|
|
36
36
|
const data = this.getManufacturerData(key)
|
|
37
37
|
if (!data)
|
|
38
38
|
throw new Error("Unable to get manufacturer data for "+this.getDisplayName())
|
|
39
|
-
this.emit("temp", (parseInt(key)/100)
|
|
39
|
+
this.emit("temp", parseFloat((273.15+parseInt(key)/100).toFixed(2))) ;
|
|
40
40
|
this.emit('battery', data[5]/100)
|
|
41
41
|
if (this.getMetadata().has('humidity')){
|
|
42
42
|
this.emit("temp", data.readUInt16LE(0)/100);
|
|
@@ -40,10 +40,10 @@ class RuuviTag extends BTSensor{
|
|
|
40
40
|
**/
|
|
41
41
|
_initModeV5(){
|
|
42
42
|
this.addMetadatum("temp","K","temperature in Kelvin",
|
|
43
|
-
(buffer)=>{ return (buffer.readInt16BE(1)*.005).toFixed(3)
|
|
43
|
+
(buffer)=>{ return parseFloat((273.15+buffer.readInt16BE(1)*.005).toFixed(3))}
|
|
44
44
|
)
|
|
45
45
|
this.addMetadatum("humidity","ratio","humidity",
|
|
46
|
-
(buffer)=>{ return ((buffer.readUInt16BE(3)*.0025)/100).toFixed(2)}
|
|
46
|
+
(buffer)=>{ return parseFloat(((buffer.readUInt16BE(3)*.0025)/100).toFixed(2))}
|
|
47
47
|
)
|
|
48
48
|
this.addMetadatum("pressure","Pa","atmospheric pressure",
|
|
49
49
|
(buffer)=>{ return buffer.readUInt16BE(5)+50000}
|
|
@@ -58,7 +58,7 @@ class RuuviTag extends BTSensor{
|
|
|
58
58
|
(buffer)=>{ return buffer.readInt16BE(11)}
|
|
59
59
|
)
|
|
60
60
|
this.addMetadatum("battV","V","battery voltage",
|
|
61
|
-
(buffer)=>{ return 1.6+(buffer.readUInt16BE(13)>>5)/1000}
|
|
61
|
+
(buffer)=>{ return parseFloat((1.6+(buffer.readUInt16BE(13)>>5)/1000).toFixed(2))}
|
|
62
62
|
)
|
|
63
63
|
this.addMetadatum("mc","","movement counter",
|
|
64
64
|
(buffer)=>{ return buffer.readUInt16BE(13) && 0x1F}
|
|
@@ -126,6 +126,8 @@ module.exports = {
|
|
|
126
126
|
0xA06F: "BlueSolar MPPT 150|45 rev2",
|
|
127
127
|
0xA070: "BlueSolar MPPT 150|60 rev2",
|
|
128
128
|
0xA071: "BlueSolar MPPT 150|70 rev2",
|
|
129
|
+
0xA0F0: "Smart Lithium Battery 4 cells 12v 330AH",
|
|
130
|
+
0xA0EE: "Smart Lithium Battery",
|
|
129
131
|
0xA102: "SmartSolar MPPT VE.Can 150/70",
|
|
130
132
|
0xA103: "SmartSolar MPPT VE.Can 150/45",
|
|
131
133
|
0xA104: "SmartSolar MPPT VE.Can 150/60",
|
|
@@ -193,6 +195,8 @@ module.exports = {
|
|
|
193
195
|
0xA2A1: "Phoenix Inverter 12V 3000VA 230V",
|
|
194
196
|
0xA2A2: "Phoenix Inverter 24V 3000VA 230V",
|
|
195
197
|
0xA2A4: "Phoenix Inverter 48V 3000VA 230V",
|
|
198
|
+
0xA31A: "Blue Smart AC Charger IP67",
|
|
199
|
+
0xA33C: "Blue Smart AC Charger IP65",
|
|
196
200
|
0xA340: "Phoenix Smart IP43 Charger 12|50 (1+1)",
|
|
197
201
|
0xA341: "Phoenix Smart IP43 Charger 12|50 (3)",
|
|
198
202
|
0xA342: "Phoenix Smart IP43 Charger 24|25 (1+1)",
|
|
@@ -201,6 +205,7 @@ module.exports = {
|
|
|
201
205
|
0xA345: "Phoenix Smart IP43 Charger 12|30 (3)",
|
|
202
206
|
0xA346: "Phoenix Smart IP43 Charger 24|16 (1+1)",
|
|
203
207
|
0xA347: "Phoenix Smart IP43 Charger 24|16 (3)",
|
|
208
|
+
0xA350: "Phoenix Smart IP43 Charger 12|50 (1+1) 120-240V A350",
|
|
204
209
|
0xA381: "BMV-712 Smart",
|
|
205
210
|
0xA382: "BMV-710H Smart",
|
|
206
211
|
0xA383: "BMV-712 Smart Rev2",
|
|
@@ -2,7 +2,8 @@ const BTSensor = require("../../BTSensor.js");
|
|
|
2
2
|
const crypto = require('node:crypto');
|
|
3
3
|
const int24 = require('int24')
|
|
4
4
|
const util = require('util')
|
|
5
|
-
const VC = require('./VictronConstants.js')
|
|
5
|
+
const VC = require('./VictronConstants.js');
|
|
6
|
+
const BLACKLISTED = require("../BlackListedDevice.js");
|
|
6
7
|
|
|
7
8
|
class VictronSensor extends BTSensor{
|
|
8
9
|
|
|
@@ -17,10 +18,14 @@ const VC = require('./VictronConstants.js')
|
|
|
17
18
|
const md = await this.getDeviceProp(device,'ManufacturerData')
|
|
18
19
|
if (!md) return null
|
|
19
20
|
const data = md[0x2e1]
|
|
21
|
+
if (data.value[0]==0x2) { //VE.Smart is on
|
|
22
|
+
return BLACKLISTED
|
|
23
|
+
}
|
|
20
24
|
if (data && data.value[0]==0x10 && data.value[4]==mode)
|
|
21
25
|
return this
|
|
22
|
-
else
|
|
26
|
+
else {
|
|
23
27
|
return null
|
|
28
|
+
}
|
|
24
29
|
} catch (e){
|
|
25
30
|
console.log(e)
|
|
26
31
|
return null
|
|
@@ -28,20 +33,6 @@ const VC = require('./VictronConstants.js')
|
|
|
28
33
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
getAuxModeAndCurrent(offset, decData=null){
|
|
32
|
-
if (decData==null){
|
|
33
|
-
decData=this.getManufacturerData(0x2e1)
|
|
34
|
-
if (this.encryptionKey)
|
|
35
|
-
decData=this.decrypt(decData)
|
|
36
|
-
else
|
|
37
|
-
return {current:NaN, auxMode:NaN}
|
|
38
|
-
}
|
|
39
|
-
const auxModeAndCurrent = int24.readInt24LE(decData,offset)
|
|
40
|
-
return {
|
|
41
|
-
current : (this.NaNif(auxModeAndCurrent >> 2,0x3FFFFF))/1000,
|
|
42
|
-
auxMode : auxModeAndCurrent & 0b11
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
36
|
|
|
46
37
|
async init(){
|
|
47
38
|
await super.init()
|
|
@@ -85,17 +76,21 @@ const VC = require('./VictronConstants.js')
|
|
|
85
76
|
propertiesChanged(props){
|
|
86
77
|
super.propertiesChanged(props)
|
|
87
78
|
if (this.usingGATT()) return
|
|
79
|
+
|
|
88
80
|
try{
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
const md = this.getManufacturerData(0x2e1)
|
|
82
|
+
if (md.length && md[0]==0x10){
|
|
83
|
+
const buff = this.getManufacturerData(0x2e1)
|
|
84
|
+
const decData=this.decrypt(buff)
|
|
85
|
+
this.emitValuesFrom(decData)
|
|
86
|
+
}
|
|
92
87
|
}
|
|
93
88
|
catch (error) {
|
|
94
89
|
throw new Error(`Unable to read data from ${ this.getDisplayName()}: ${error}` )
|
|
95
90
|
}
|
|
96
91
|
}
|
|
97
92
|
|
|
98
|
-
|
|
93
|
+
initGATTConnection(){
|
|
99
94
|
throw new Error( "GATT Connection unimplemented for "+this.getDisplayName())
|
|
100
95
|
}
|
|
101
96
|
|
|
@@ -30,34 +30,37 @@ class VictronACCharger extends VictronSensor{
|
|
|
30
30
|
|
|
31
31
|
async init(){
|
|
32
32
|
await super.init()
|
|
33
|
+
this.initMetadata()
|
|
34
|
+
}
|
|
35
|
+
initMetadata(){
|
|
33
36
|
this.addMetadatum('state','', 'device state',
|
|
34
37
|
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
35
38
|
this.addMetadatum('error','', 'error code',
|
|
36
39
|
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
37
40
|
|
|
38
41
|
this.addMetadatum('batt1','V', 'battery 1 voltage',
|
|
39
|
-
(buff)=>{return this.NaNif((buff.
|
|
42
|
+
(buff)=>{return this.NaNif((buff.readUInt16LE(2)&0x1FFF), 0x1FFF)/100})
|
|
40
43
|
|
|
41
44
|
this.addMetadatum('curr1','A', 'battery 1 current',
|
|
42
45
|
(buff)=>{return this.NaNif(buff.readUInt16BE(3)&0x7FF,0x7FF)/10})
|
|
43
46
|
|
|
44
47
|
this.addMetadatum('batt2','V', 'battery 2 voltage',
|
|
45
|
-
(buff)=>{return this.NaNif((buff.
|
|
48
|
+
(buff)=>{return this.NaNif((buff.readUInt16LE(5)&0x1FFF), 0x1FFF)/100})
|
|
46
49
|
|
|
47
50
|
this.addMetadatum('curr2','A', 'battery 2 current',
|
|
48
51
|
(buff)=>{return this.NaNif(buff.readUInt16BE(7)&0x7FF,0x7FF)/10})
|
|
49
52
|
|
|
50
53
|
this.addMetadatum('batt3','V', 'battery 3 voltage',
|
|
51
|
-
(buff)=>{return this.NaNif((buff.
|
|
54
|
+
(buff)=>{return this.NaNif((buff.readUInt16LE(8)&0x1FFF), 0x1FFF)/100})
|
|
52
55
|
|
|
53
56
|
this.addMetadatum('curr3','A', 'battery 3 current',
|
|
54
57
|
(buff)=>{return this.NaNif(buff.readUInt16BE(9)&0x7FF,0x7FF)/10})
|
|
55
58
|
|
|
56
59
|
this.addMetadatum('temp', 'K', 'battery temperature',
|
|
57
|
-
(buff)=>{return this.NaNif(buff.readUInt8(11)
|
|
60
|
+
(buff)=>{return this.NaNif(buff.readUInt8(11)&0x7F,0x7F)+233.15}) //-40 plus K conversion
|
|
58
61
|
|
|
59
62
|
this.addMetadatum('acCurr','A', 'AC current',
|
|
60
|
-
(buff)=>{return this.NaNif((buff.
|
|
63
|
+
(buff)=>{return this.NaNif((buff.readUInt16BE(11)&0x1FF),0x1FF)/10})
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
module.exports=VictronACCharger
|
|
@@ -1,15 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
const VictronSensor = require("./Victron/VictronSensor.js");
|
|
2
6
|
const VC=require("./Victron/VictronConstants.js")
|
|
7
|
+
const int24 = require('int24')
|
|
3
8
|
class VictronBatteryMonitor extends VictronSensor{
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
static async identify(device){
|
|
7
12
|
return await this.identifyMode(device, 0x02)
|
|
8
13
|
}
|
|
14
|
+
static _test(data, key){
|
|
15
|
+
var b = Buffer.from(data.replaceAll(" ",""),"hex")
|
|
16
|
+
const d = new this()
|
|
17
|
+
|
|
18
|
+
if (key) {
|
|
19
|
+
d.encryptionKey = key
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
d.currentProperties = {}
|
|
23
|
+
d.currentProperties.ManufacturerData={}
|
|
24
|
+
d.currentProperties.ManufacturerData[0x02e1]=b
|
|
25
|
+
d.initMetadata()
|
|
26
|
+
d.getPathMetadata().forEach((datum,tag)=>{
|
|
27
|
+
d.on(tag,(v)=>console.log(`${tag}=${v}`))
|
|
28
|
+
})
|
|
29
|
+
b = d.decrypt(b)
|
|
30
|
+
console.log(b)
|
|
31
|
+
d.emitValuesFrom(b)
|
|
32
|
+
d.removeAllListeners()
|
|
33
|
+
|
|
34
|
+
}
|
|
9
35
|
|
|
10
36
|
characteristics=[]
|
|
11
37
|
async init(){
|
|
12
38
|
await super.init()
|
|
39
|
+
this.initMetadata()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
initMetadata(){
|
|
13
43
|
this.addMetadatum('current', 'A', 'house battery amperage',
|
|
14
44
|
(buff,offset=0)=>{return buff.readInt32LE(offset)/1000},
|
|
15
45
|
'6597ed8c-4bda-4c1e-af4b-551c4cf74769')
|
|
@@ -33,9 +63,13 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
33
63
|
|
|
34
64
|
this.addMetadatum( 'ttg','s','time to go',
|
|
35
65
|
(buff,offset=0)=>{return this.NaNif(buff.readUInt16LE(offset),0xFFFF)*60},
|
|
36
|
-
'65970ffe-4bda-4c1e-af4b-551c4cf74769')
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
'65970ffe-4bda-4c1e-af4b-551c4cf74769')
|
|
67
|
+
if (this.encryptionKey){
|
|
68
|
+
const decData = this.decrypt(this.getManufacturerData(0x02e1))
|
|
69
|
+
if (decData)
|
|
70
|
+
this.auxMode=decData.readInt8(8)&0x3
|
|
71
|
+
}
|
|
72
|
+
|
|
39
73
|
switch(this.auxMode){
|
|
40
74
|
case VC.AuxMode.STARTER_VOLTAGE:
|
|
41
75
|
this.addMetadatum('starterVoltage','V', 'starter battery voltage',
|
|
@@ -50,14 +84,14 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
50
84
|
|
|
51
85
|
case VC.AuxMode.TEMPERATURE:
|
|
52
86
|
this.addMetadatum('temperature','K', 'House battery temperature',
|
|
53
|
-
(buff,offset=0)=>{return buff.readInt16LE(offset)/
|
|
87
|
+
(buff,offset=0)=>{return (buff.readInt16LE(offset)/1000)+273.15},
|
|
54
88
|
'6597ed7d-4bda-4c1e-af4b-551c4cf74769')
|
|
55
89
|
break;
|
|
56
90
|
default:
|
|
57
91
|
break
|
|
58
92
|
}
|
|
59
93
|
}
|
|
60
|
-
|
|
94
|
+
|
|
61
95
|
emitValuesFrom(decData){
|
|
62
96
|
this.emitData("ttg",decData,0)
|
|
63
97
|
this.emitData("voltage",decData,2);
|
|
@@ -65,10 +99,8 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
65
99
|
if (alarm>0){
|
|
66
100
|
this.emit(
|
|
67
101
|
`ALARM #${alarm} from ${this.getDisplayName()})`,
|
|
68
|
-
{ message: AlarmReason.get(alarm), state: 'alert'})
|
|
102
|
+
{ message: VC.AlarmReason.get(alarm), state: 'alert'})
|
|
69
103
|
}
|
|
70
|
-
|
|
71
|
-
this.emit("current", (this.getAuxModeAndCurrent(8,decData)).current)
|
|
72
104
|
switch(this.auxMode){
|
|
73
105
|
case VC.AuxMode.STARTER_VOLTAGE:
|
|
74
106
|
this.emitData("starterVoltage",decData,6);
|
|
@@ -81,17 +113,19 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
81
113
|
break;
|
|
82
114
|
default:
|
|
83
115
|
break
|
|
84
|
-
}
|
|
85
|
-
this.emit("
|
|
86
|
-
|
|
87
|
-
this.emit("soc", ((
|
|
116
|
+
}
|
|
117
|
+
this.emit("current", (this.NaNif(int24.readInt24LE(decData, 8)>>2,0x3FFFFF))/1000)
|
|
118
|
+
this.emit("consumed",(this.NaNif(int24.readInt24LE(decData, 11)&0xFFFFF,0xFFFFF)) / 10) ;
|
|
119
|
+
this.emit("soc", this.NaNif(((decData.readUInt16LE(13)& 0x3FFF)>>4),0x3FF)/1000)
|
|
120
|
+
|
|
88
121
|
}
|
|
89
122
|
|
|
90
|
-
|
|
123
|
+
initGATTConnection() {
|
|
91
124
|
return new Promise((resolve,reject )=>{
|
|
92
125
|
if (!this.valueIfVariant(this.currentProperties.Paired))
|
|
93
126
|
reject(`${this.getName()} must be paired with the Signalk server to use GATT protocol`)
|
|
94
127
|
this.device.connect().then(async ()=>{
|
|
128
|
+
this.debug(`${this.getName()} connected.`)
|
|
95
129
|
if (!this.gattServer) {
|
|
96
130
|
this.gattServer = await this.device.gatt()
|
|
97
131
|
this.gattService= await this.gattServer.getPrimaryService("65970000-4bda-4c1e-af4b-551c4cf74769")
|
|
@@ -133,9 +167,7 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
133
167
|
}})
|
|
134
168
|
resolve(this)})
|
|
135
169
|
}
|
|
136
|
-
|
|
137
|
-
super.propertiesChanged(props)
|
|
138
|
-
}
|
|
170
|
+
|
|
139
171
|
hasGATT(){
|
|
140
172
|
return true
|
|
141
173
|
}
|
|
@@ -144,14 +176,14 @@ class VictronBatteryMonitor extends VictronSensor{
|
|
|
144
176
|
return "To use the GATT connection the SignalK server computer and the Smart Shunt must first be paired."
|
|
145
177
|
}
|
|
146
178
|
|
|
147
|
-
async
|
|
148
|
-
super.
|
|
179
|
+
async stopListening(){
|
|
180
|
+
super.stopListening()
|
|
149
181
|
for (var c of this.characteristics){
|
|
150
182
|
await c.stopNotifications()
|
|
151
183
|
}
|
|
152
184
|
if (await this.device.isConnected()){
|
|
153
|
-
console.log(`Disconnecting from ${ this.getMacAddress()}`)
|
|
154
185
|
await this.device.disconnect()
|
|
186
|
+
this.debug(`Disconnected from ${ this.getName()}`)
|
|
155
187
|
}
|
|
156
188
|
}
|
|
157
189
|
}
|
|
@@ -27,6 +27,9 @@ class VictronSmartLithium extends VictronSensor{
|
|
|
27
27
|
|
|
28
28
|
async init() {
|
|
29
29
|
await super.init()
|
|
30
|
+
this.initMetadata()
|
|
31
|
+
}
|
|
32
|
+
initMetadata(){
|
|
30
33
|
function _toCellVoltage(val){
|
|
31
34
|
return val==0x7F?NaN:2.6+(val/100)
|
|
32
35
|
}
|
|
@@ -34,29 +37,29 @@ class VictronSmartLithium extends VictronSensor{
|
|
|
34
37
|
(buff)=>{return buff.readUInt32BE(0)})
|
|
35
38
|
|
|
36
39
|
this.addMetadatum('smartLithiumErrors','', 'Smart Lithium Errors Flags',
|
|
37
|
-
(buff)=>{return buff.readUInt16BE(
|
|
40
|
+
(buff)=>{return buff.readUInt16BE(4)})
|
|
38
41
|
this.addMetadatum('cell1Voltage','V', 'cell #1 voltage',
|
|
39
|
-
(buff)=>{return _toCellVoltage((buff.readUInt8(
|
|
42
|
+
(buff)=>{return _toCellVoltage((buff.readUInt8(6))>>1)})
|
|
40
43
|
this.addMetadatum('cell2Voltage','V', 'cell #2 voltage',
|
|
41
|
-
(buff)=>{return _toCellVoltage((buff.
|
|
44
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(6))&0x01Fe)>>2)})
|
|
42
45
|
this.addMetadatum('cell3Voltage','V', 'cell #3 voltage',
|
|
43
|
-
(buff)=>{return _toCellVoltage(((buff.
|
|
46
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(7))&0x03Fe)>>3)})
|
|
44
47
|
this.addMetadatum('cell4Voltage','V', 'cell #4 voltage',
|
|
45
|
-
(buff)=>{return _toCellVoltage(((buff.
|
|
48
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(8))&0x07fe)>>4)})
|
|
46
49
|
this.addMetadatum('cell5Voltage','V', 'cell #5 voltage',
|
|
47
|
-
(buff)=>{return _toCellVoltage(((buff.
|
|
50
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(9))&0x0ffe)>>5)})
|
|
48
51
|
this.addMetadatum('cell6Voltage','V', 'cell #6 voltage',
|
|
49
|
-
(buff)=>{return _toCellVoltage(((buff.
|
|
52
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(10))&0x1ffe)>>6)})
|
|
50
53
|
this.addMetadatum('cell7Voltage','V', 'cell #7 voltage',
|
|
51
|
-
(buff)=>{return _toCellVoltage(((buff.
|
|
54
|
+
(buff)=>{return _toCellVoltage(((buff.readUInt16BE(11))&0x3ffe)>>7)})
|
|
52
55
|
this.addMetadatum('cell8Voltage','V', 'cell #8 voltage',
|
|
53
|
-
(buff)=>{return _toCellVoltage((buff.readUInt8(
|
|
56
|
+
(buff)=>{return _toCellVoltage((buff.readUInt8(12))&0x7e)})
|
|
54
57
|
this.addMetadatum('batteryVoltage','V', 'battery voltage',
|
|
55
|
-
(buff)=>{return this.NaNif((buff.
|
|
58
|
+
(buff)=>{return this.NaNif((buff.readUInt16LE(13)&0xFFF),0xFFF)/100})
|
|
56
59
|
this.addMetadatum('balancerStatus','', 'balancer status', //TODO
|
|
57
|
-
(buff)=>{return this.NaNif((buff.
|
|
60
|
+
(buff)=>{return this.NaNif((buff.readUInt8(14)&0xf),0xF)})
|
|
58
61
|
this.addMetadatum('batteryTemp','K', 'battery temperature',
|
|
59
|
-
(buff)=>{return this.NaNif((buff.
|
|
62
|
+
(buff)=>{return this.NaNif((buff.readUInt8(15)&0x7F),0x7F)+233.15})
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
module.exports=VictronSmartLithium
|
|
@@ -8,22 +8,23 @@ class VictronSolarCharger extends VictronSensor{
|
|
|
8
8
|
|
|
9
9
|
async init() {
|
|
10
10
|
await super.init()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
this.initMetadata()
|
|
12
|
+
}
|
|
13
|
+
initMetadata() {
|
|
14
|
+
this.addMetadatum('chargeState','', 'charge state',
|
|
15
|
+
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
16
|
+
this.addMetadatum('chargerError','', 'charger error',
|
|
17
|
+
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
18
|
+
this.addMetadatum('voltage','V', 'charger battery voltage',
|
|
19
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FFF)/100})
|
|
20
|
+
this.addMetadatum('current','A','charger battery current',
|
|
21
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(4),0x7FFF)/10})
|
|
22
|
+
this.addMetadatum('yield','Wh', 'yield today in Watt-hours',
|
|
23
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(6),0xFFFF)*10})
|
|
24
|
+
this.addMetadatum('solarPower','W', 'solar power',
|
|
25
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(8),0xFFFF)})
|
|
26
|
+
this.addMetadatum('externalDeviceLoad','A', 'external device load',
|
|
27
|
+
(buff)=>{return this.NaNif(buff.readUInt16BE(10)>>7,0x1FF)})
|
|
28
|
+
}
|
|
28
29
|
}
|
|
29
30
|
module.exports=VictronSolarCharger
|
|
@@ -105,7 +105,7 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
105
105
|
getGATTDescription() {
|
|
106
106
|
return this.GATTwarning
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
initGATTConnection(){
|
|
109
109
|
if (!this?.gattWarningDelivered) {
|
|
110
110
|
this.debug(this.GATTwarning.toUpperCase())
|
|
111
111
|
this.gattWarningDelivered=true
|
|
@@ -212,15 +212,15 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
212
212
|
return this?.name??`${dt.name} ${dt.model}`
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
async
|
|
215
|
+
async disconnectGATTCharacteristic(){
|
|
216
216
|
if (this.gattCharacteristic && await this.gattCharacteristic.isNotifying()) {
|
|
217
217
|
await this.gattCharacteristic.stopNotifications()
|
|
218
218
|
this.gattCharacteristic=null
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
async
|
|
222
|
-
super.
|
|
223
|
-
await this.
|
|
221
|
+
async stopListening(){
|
|
222
|
+
super.stopListening()
|
|
223
|
+
await this.disconnectGATTCharacteristic()
|
|
224
224
|
|
|
225
225
|
if (await this.device.isConnected()){
|
|
226
226
|
await this.device.disconnect()
|