bt-sensors-plugin-sk 1.0.3 → 1.1.0-beta.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 +334 -39
- package/bt_co.json +14520 -0
- package/index.js +385 -177
- package/package.json +4 -3
- package/sensor_classes/ATC.js +31 -22
- package/sensor_classes/BlackListedDevice.js +20 -0
- package/sensor_classes/Inkbird.js +47 -0
- package/sensor_classes/RuuviTag.js +117 -0
- package/sensor_classes/UNKNOWN.js +12 -0
- package/sensor_classes/Victron/VictronConstants.js +328 -0
- package/sensor_classes/Victron/VictronSensor.js +105 -0
- package/sensor_classes/VictronACCharger.js +63 -0
- package/sensor_classes/VictronBatteryMonitor.js +158 -0
- package/sensor_classes/VictronDCDCConverter.js +26 -0
- package/sensor_classes/VictronDCEnergyMeter.js +66 -0
- package/sensor_classes/VictronGXDevice.js +43 -0
- package/sensor_classes/VictronInverter.js +43 -0
- package/sensor_classes/VictronInverterRS.js +44 -0
- package/sensor_classes/VictronLynxSmartBMS.js +49 -0
- package/sensor_classes/VictronOrionXS.js +30 -0
- package/sensor_classes/VictronSmartBatteryProtect.js +48 -0
- package/sensor_classes/VictronSmartLithium.js +62 -0
- package/sensor_classes/VictronSolarCharger.js +29 -0
- package/sensor_classes/VictronVEBus.js +47 -0
- package/sensor_classes/XiaomiMiBeacon.js +227 -0
- package/test.js +32 -0
- package/sensor_classes/LYWSD03MMC.js +0 -39
- package/sensor_classes/SmartShunt.js +0 -300
- package/sensor_classes/SmartShunt_GATT.js +0 -64
- package/sensor_classes/TPS.js +0 -49
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const BTSensor = require("../../BTSensor.js");
|
|
2
|
+
const crypto = require('node:crypto');
|
|
3
|
+
const int24 = require('int24')
|
|
4
|
+
const util = require('util')
|
|
5
|
+
const VC = require('./VictronConstants.js')
|
|
6
|
+
|
|
7
|
+
class VictronSensor extends BTSensor{
|
|
8
|
+
|
|
9
|
+
constructor(device,config,gattConfig){
|
|
10
|
+
super(device,config,gattConfig)
|
|
11
|
+
this.encryptionKey = config?.encryptionKey
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static async identifyMode(device, mode){
|
|
15
|
+
|
|
16
|
+
try{
|
|
17
|
+
const md = await this.getDeviceProp(device,'ManufacturerData')
|
|
18
|
+
if (!md) return null
|
|
19
|
+
const data = md[0x2e1]
|
|
20
|
+
if (data && data.value[0]==0x10 && data.value[4]==mode)
|
|
21
|
+
return this
|
|
22
|
+
else
|
|
23
|
+
return null
|
|
24
|
+
} catch (e){
|
|
25
|
+
console.log(e)
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
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
|
+
|
|
46
|
+
async init(){
|
|
47
|
+
await super.init()
|
|
48
|
+
var md = this.addMetadatum('encryptionKey','', "Encryption Key")
|
|
49
|
+
md.isParam = true
|
|
50
|
+
this.metadata = new Map(super.getMetadata())
|
|
51
|
+
md = this.addMetadatum('encryptionKey','', "Encryption Key")
|
|
52
|
+
md.isParam = true
|
|
53
|
+
this.model_id=this.getManufacturerData(0x2e1).readUInt16LE(2)
|
|
54
|
+
}
|
|
55
|
+
alarmReason(alarmValue){
|
|
56
|
+
return this.constructor.AlarmReason[alarmValue]
|
|
57
|
+
}
|
|
58
|
+
getModelName(){
|
|
59
|
+
return VC.MODEL_ID_MAP[this.model_id]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
decrypt(data){
|
|
63
|
+
if (!this.encryptionKey)
|
|
64
|
+
throw Error("Unable to decrypt: no encryption key set")
|
|
65
|
+
|
|
66
|
+
const encMethod = 'aes-128-ctr';
|
|
67
|
+
const iv = data.readUInt16LE(5);
|
|
68
|
+
const key = Buffer.from(this.encryptionKey,'hex')
|
|
69
|
+
const ivBuffer = Buffer.alloc(16); // 128 bits = 16 bytes
|
|
70
|
+
|
|
71
|
+
ivBuffer.writeUInt16LE(iv)
|
|
72
|
+
|
|
73
|
+
var encData = Buffer.from([...data.slice(8)])
|
|
74
|
+
|
|
75
|
+
const decipher = crypto.createDecipheriv(encMethod, key, ivBuffer)
|
|
76
|
+
|
|
77
|
+
const decData=decipher.update(encData)
|
|
78
|
+
|
|
79
|
+
return Buffer.from(decData)
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
getName(){
|
|
83
|
+
return `Victron ${this.getModelName()}`
|
|
84
|
+
}
|
|
85
|
+
propertiesChanged(props){
|
|
86
|
+
super.propertiesChanged(props)
|
|
87
|
+
if (this.usingGATT()) return
|
|
88
|
+
try{
|
|
89
|
+
const buff = this.getManufacturerData(0x2e1)
|
|
90
|
+
const decData=this.decrypt(buff)
|
|
91
|
+
this.emitValuesFrom(decData)
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw new Error(`Unable to read data from ${ this.getDisplayName()}: ${error}` )
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
initGATT(){
|
|
99
|
+
throw new Error( "GATT Connection unimplemented for "+this.getDisplayName())
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
module.exports=VictronSensor
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*AC Charger
|
|
2
|
+
Record layout is still to be determined and might change.
|
|
3
|
+
Last update: 2022/12/14 13:25 rend:ble:extra_manufacturer_data https://wiki.victronenergy.com/rend/ble/extra_manufacturer_data
|
|
4
|
+
https://wiki.victronenergy.com/ Printed on 2022/12/14 13:27
|
|
5
|
+
Startbit Nr of bits Meaning Units Range NA value Remark
|
|
6
|
+
32 8 Device state 0 .. 0xFE 0xFF VE_REG_DEVICE_STATE
|
|
7
|
+
40 8 Charger Error 0 .. 0xFE 0xFF VE_REG_CHR_ERROR_CODE
|
|
8
|
+
48 13 Battery voltage 1 0.01 V 0 .. 81.90V 0x1FFF VE_REG_DC_CHANNEL1_VOLTAGE
|
|
9
|
+
61 11 Battery current 1 0.1A 0 .. 204.6A 0x7FF VE_REG_DC_CHANNEL1_CURRENT
|
|
10
|
+
72 13 Battery voltage 2 0.01 V 0 .. 81.90V 0x1FFF VE_REG_DC_CHANNEL2_VOLTAGE
|
|
11
|
+
85 11 Battery current 2 0.1A 0 .. 204.6A 0x7FF VE_REG_DC_CHANNEL2_CURRENT
|
|
12
|
+
96 13 Battery voltage 3 0.01 V 0 .. 81.90V 0x1FFF VE_REG_DC_CHANNEL3_VOLTAGE
|
|
13
|
+
109 11 Battery current 3 0.1A 0 .. 204.6A 0x7FF VE_REG_DC_CHANNEL3_CURRENT
|
|
14
|
+
120 7 Temperature °C -40 .. 86°C 0x7F VE_REG_BAT_TEMPERATURE Temperature = Record value - 40
|
|
15
|
+
127 9 AC current 0.1A 0 .. 51.0 A 0x1FF VE_REG_AC_ACTIVE_INPUT_L1_CURRENT
|
|
16
|
+
136 24 Unused
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const VictronSensor = require ("./Victron/VictronSensor.js")
|
|
22
|
+
const VC = require("./Victron/VictronConstants.js")
|
|
23
|
+
const int24 = require('int24')
|
|
24
|
+
|
|
25
|
+
class VictronACCharger extends VictronSensor{
|
|
26
|
+
|
|
27
|
+
static async identify(device){
|
|
28
|
+
return await this.identifyMode(device, 0x0A)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async init(){
|
|
32
|
+
await super.init()
|
|
33
|
+
this.addMetadatum('state','', 'device state',
|
|
34
|
+
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
35
|
+
this.addMetadatum('error','', 'error code',
|
|
36
|
+
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
37
|
+
|
|
38
|
+
this.addMetadatum('batt1','V', 'battery 1 voltage',
|
|
39
|
+
(buff)=>{return this.NaNif((buff.readUInt16BE(2)>>3), 0x1FFF)/100})
|
|
40
|
+
|
|
41
|
+
this.addMetadatum('curr1','A', 'battery 1 current',
|
|
42
|
+
(buff)=>{return this.NaNif(buff.readUInt16BE(3)&0x7FF,0x7FF)/10})
|
|
43
|
+
|
|
44
|
+
this.addMetadatum('batt2','V', 'battery 2 voltage',
|
|
45
|
+
(buff)=>{return this.NaNif((buff.readUInt16BE(5)>>3), 0x1FFF)/100})
|
|
46
|
+
|
|
47
|
+
this.addMetadatum('curr2','A', 'battery 2 current',
|
|
48
|
+
(buff)=>{return this.NaNif(buff.readUInt16BE(7)&0x7FF,0x7FF)/10})
|
|
49
|
+
|
|
50
|
+
this.addMetadatum('batt3','V', 'battery 3 voltage',
|
|
51
|
+
(buff)=>{return this.NaNif((buff.readUInt16BE(8)>>3), 0x1FFF)/100})
|
|
52
|
+
|
|
53
|
+
this.addMetadatum('curr3','A', 'battery 3 current',
|
|
54
|
+
(buff)=>{return this.NaNif(buff.readUInt16BE(9)&0x7FF,0x7FF)/10})
|
|
55
|
+
|
|
56
|
+
this.addMetadatum('temp', 'K', 'battery temperature',
|
|
57
|
+
(buff)=>{return this.NaNif(buff.readUInt8(11)>>1,0x7F)+233.15}) //-40 plus K conversion
|
|
58
|
+
|
|
59
|
+
this.addMetadatum('acCurr','A', 'AC current',
|
|
60
|
+
(buff)=>{return this.NaNif((buff.readInt16BE(11))&0x1FFF,0x1FFF)})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
module.exports=VictronACCharger
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const VictronSensor = require("./Victron/VictronSensor.js");
|
|
2
|
+
const VC=require("./Victron/VictronConstants.js")
|
|
3
|
+
class VictronBatteryMonitor extends VictronSensor{
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
static async identify(device){
|
|
7
|
+
return await this.identifyMode(device, 0x02)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
characteristics=[]
|
|
11
|
+
async init(){
|
|
12
|
+
await super.init()
|
|
13
|
+
this.addMetadatum('current', 'A', 'house battery amperage',
|
|
14
|
+
(buff,offset=0)=>{return buff.readInt32LE(offset)/1000},
|
|
15
|
+
'6597ed8c-4bda-4c1e-af4b-551c4cf74769')
|
|
16
|
+
this.addMetadatum('power','W', 'house battery wattage',
|
|
17
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)},
|
|
18
|
+
'6597ed8e-4bda-4c1e-af4b-551c4cf74769')
|
|
19
|
+
this.addMetadatum('voltage','V', 'house battery voltage',
|
|
20
|
+
(buff,offset=0)=>{return this.NaNif(buff.readInt16LE(offset), 0x7FFF)/100},
|
|
21
|
+
'6597ed8d-4bda-4c1e-af4b-551c4cf74769',)
|
|
22
|
+
const alarmMD = this.addMetadatum('alarm','', 'alarm',
|
|
23
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)})
|
|
24
|
+
alarmMD.notify=true
|
|
25
|
+
|
|
26
|
+
this.addMetadatum( 'consumed','C', 'amp-hours consumed',
|
|
27
|
+
(buff,offset=0)=>{return buff.readInt32LE(offset)/10},
|
|
28
|
+
'6597eeff-4bda-4c1e-af4b-551c4cf74769',)
|
|
29
|
+
|
|
30
|
+
this.addMetadatum( 'soc','ratio', 'state of charge',
|
|
31
|
+
(buff,offset=0)=>{return buff.readUInt16LE(offset)/10000},
|
|
32
|
+
'65970fff-4bda-4c1e-af4b-551c4cf74769')
|
|
33
|
+
|
|
34
|
+
this.addMetadatum( 'ttg','s','time to go',
|
|
35
|
+
(buff,offset=0)=>{return this.NaNif(buff.readUInt16LE(offset),0xFFFF)*60},
|
|
36
|
+
'65970ffe-4bda-4c1e-af4b-551c4cf74769')
|
|
37
|
+
const modecurrent = this.getAuxModeAndCurrent()
|
|
38
|
+
this.auxMode= modecurrent.auxMode
|
|
39
|
+
switch(this.auxMode){
|
|
40
|
+
case VC.AuxMode.STARTER_VOLTAGE:
|
|
41
|
+
this.addMetadatum('starterVoltage','V', 'starter battery voltage',
|
|
42
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)/100},
|
|
43
|
+
'6597ed7d-4bda-4c1e-af4b-551c4cf74769')
|
|
44
|
+
break;
|
|
45
|
+
case VC.AuxMode.MIDPOINT_VOLTAGE:
|
|
46
|
+
this.addMetadatum('midpointVoltage','V', 'midpoint battery voltage',
|
|
47
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)/100},
|
|
48
|
+
'6597ed7d-4bda-4c1e-af4b-551c4cf74769')
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case VC.AuxMode.TEMPERATURE:
|
|
52
|
+
this.addMetadatum('temperature','K', 'House battery temperature',
|
|
53
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)/100},
|
|
54
|
+
'6597ed7d-4bda-4c1e-af4b-551c4cf74769')
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
break
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
emitValuesFrom(decData){
|
|
62
|
+
this.emitData("ttg",decData,0)
|
|
63
|
+
this.emitData("voltage",decData,2);
|
|
64
|
+
const alarm = this.getMetadatum("alarm").read(decData,4)
|
|
65
|
+
if (alarm>0){
|
|
66
|
+
this.emit(
|
|
67
|
+
`ALARM #${alarm} from ${this.getDisplayName()})`,
|
|
68
|
+
{ message: AlarmReason.get(alarm), state: 'alert'})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.emit("current", (this.getAuxModeAndCurrent(8,decData)).current)
|
|
72
|
+
switch(this.auxMode){
|
|
73
|
+
case VC.AuxMode.STARTER_VOLTAGE:
|
|
74
|
+
this.emitData("starterVoltage",decData,6);
|
|
75
|
+
break;
|
|
76
|
+
case VC.AuxMode.MIDPOINT_VOLTAGE:
|
|
77
|
+
this.emitData("midpointVoltage",decData,6);
|
|
78
|
+
break;
|
|
79
|
+
case VC.AuxMode.TEMPERATURE:
|
|
80
|
+
this.emitData("temperature",decData,6);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
this.emit("consumed",decData.readInt16LE(11) / 10 ); //TODO this ain't right
|
|
86
|
+
var soc = decData.readUInt16LE(13)
|
|
87
|
+
this.emit("soc", ((soc & 0x3FFF) >> 4) / 1000)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
initGATT() {
|
|
91
|
+
return new Promise((resolve,reject )=>{
|
|
92
|
+
if (!this.valueIfVariant(this.currentProperties.Paired))
|
|
93
|
+
reject(`${this.getName()} must be paired with the Signalk server to use GATT protocol`)
|
|
94
|
+
this.device.connect().then(async ()=>{
|
|
95
|
+
if (!this.gattServer) {
|
|
96
|
+
this.gattServer = await this.device.gatt()
|
|
97
|
+
this.gattService= await this.gattServer.getPrimaryService("65970000-4bda-4c1e-af4b-551c4cf74769")
|
|
98
|
+
const keepAlive = await this.gattService.getCharacteristic('6597ffff-4bda-4c1e-af4b-551c4cf74769')
|
|
99
|
+
await keepAlive.writeValue(Buffer.from([0xFF,0xFF]), { offset: 0, type: 'request' })
|
|
100
|
+
}
|
|
101
|
+
resolve(this)
|
|
102
|
+
}).catch((e)=>reject(e.message))
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
emitGATT(){
|
|
106
|
+
this.getPathMetadata().forEach( (datum, tag)=> {
|
|
107
|
+
if (datum.gatt) {
|
|
108
|
+
this.gattService.getCharacteristic(datum.gatt).then((gattCharacteristic)=>{
|
|
109
|
+
gattCharacteristic.readValue().then((buffer)=>{
|
|
110
|
+
this.emitData(tag, buffer)
|
|
111
|
+
})
|
|
112
|
+
}).catch((e)=>{
|
|
113
|
+
throw new Error(e)
|
|
114
|
+
})}
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
initGATTNotifications(){
|
|
118
|
+
return new Promise((resolve,reject )=>{
|
|
119
|
+
|
|
120
|
+
this.getPathMetadata().forEach((datum, tag)=> {
|
|
121
|
+
if (datum.gatt) {
|
|
122
|
+
this.gattService.getCharacteristic(datum.gatt).then(async (gattCharacteristic)=>{
|
|
123
|
+
const buffer = await gattCharacteristic.readValue()
|
|
124
|
+
this.emitData(tag, buffer)
|
|
125
|
+
|
|
126
|
+
gattCharacteristic.startNotifications().then(()=>{
|
|
127
|
+
gattCharacteristic.on('valuechanged', buffer => {
|
|
128
|
+
this.emitData(tag, buffer)
|
|
129
|
+
})
|
|
130
|
+
this.characteristics.push(gattCharacteristic)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
}})
|
|
134
|
+
resolve(this)})
|
|
135
|
+
}
|
|
136
|
+
propertiesChanged(props){
|
|
137
|
+
super.propertiesChanged(props)
|
|
138
|
+
}
|
|
139
|
+
hasGATT(){
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getGATTDescription(){
|
|
144
|
+
return "To use the GATT connection the SignalK server computer and the Smart Shunt must first be paired."
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async disconnect(){
|
|
148
|
+
super.disconnect()
|
|
149
|
+
for (var c of this.characteristics){
|
|
150
|
+
await c.stopNotifications()
|
|
151
|
+
}
|
|
152
|
+
if (await this.device.isConnected()){
|
|
153
|
+
console.log(`Disconnecting from ${ this.getMacAddress()}`)
|
|
154
|
+
await this.device.disconnect()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
module.exports=VictronBatteryMonitor
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const VictronSensor = require("./Victron/VictronSensor");
|
|
2
|
+
const VC=require("./Victron/VictronConstants.js")
|
|
3
|
+
|
|
4
|
+
class VictronDCDCConverter extends VictronSensor{
|
|
5
|
+
|
|
6
|
+
static async identify(device){
|
|
7
|
+
return await this.identifyMode(device,0x04)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async init(){
|
|
11
|
+
await super.init()
|
|
12
|
+
this.addMetadatum('deviceState','', 'device state',
|
|
13
|
+
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
14
|
+
this.addMetadatum('chargerError','', 'charger error',
|
|
15
|
+
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
16
|
+
this.addMetadatum('inputVoltage','V', 'input voltage',
|
|
17
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(2),0xFFFF)/100})
|
|
18
|
+
this.addMetadatum('outputVoltage','V', 'output voltage',
|
|
19
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(4),0x7FFF)/100 })
|
|
20
|
+
this.addMetadatum('offReason','', 'reason unit is off',
|
|
21
|
+
(buff)=>{return VC.OffReasons.get(buff.readUInt32LE(6))})
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
module.exports=VictronDCDCConverter
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const VictronSensor = require("./Victron/VictronSensor");
|
|
2
|
+
const VC=require("./Victron/VictronConstants.js")
|
|
3
|
+
|
|
4
|
+
class VictronDCEnergyMeter extends VictronSensor{
|
|
5
|
+
|
|
6
|
+
static async identify(device){
|
|
7
|
+
return await this.identifyMode(device, 0x0D)
|
|
8
|
+
}
|
|
9
|
+
async init(){
|
|
10
|
+
await super.init()
|
|
11
|
+
this.addMetadatum('meterType','', 'meter type',
|
|
12
|
+
(buff)=>{return VC.MeterType.get( buff.readInt16LE(0))})
|
|
13
|
+
this.addMetadatum('voltage','','voltage',
|
|
14
|
+
(buff)=>{return buff.readInt16LE(2)/100})
|
|
15
|
+
this.addMetadatum('alarm','', 'alarm',
|
|
16
|
+
(buff)=>{return buff.readUInt16LE(4)})
|
|
17
|
+
this.addMetadatum('current','A', 'current')
|
|
18
|
+
const modeCurrent = this.getAuxModeAndCurrent()
|
|
19
|
+
this.auxMode= modeCurrent.auxMode
|
|
20
|
+
switch(this.auxMode){
|
|
21
|
+
case VC.AuxMode.STARTER_VOLTAGE:
|
|
22
|
+
this.addMetadatum('starterVoltage','V', 'starter battery voltage',
|
|
23
|
+
(buff,offset=0)=>{return buff.readInt16LE(offset)/100})
|
|
24
|
+
break;
|
|
25
|
+
|
|
26
|
+
case VC.AuxMode.TEMPERATURE:
|
|
27
|
+
this.addMetadatum('temperature','K','House battery temperature',
|
|
28
|
+
(buff,offset=0)=>{
|
|
29
|
+
const temp = buff.readUInt16LE(offset)
|
|
30
|
+
if (temp==0xffff)
|
|
31
|
+
return null
|
|
32
|
+
else
|
|
33
|
+
return temp / 100
|
|
34
|
+
})
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
emitValuesFrom(decData){
|
|
42
|
+
this.emitData("meterType",decData,0)
|
|
43
|
+
this.emitData("voltage",decData,2);
|
|
44
|
+
const alarm = this.getMetadatum("alarm").read(decData,4)
|
|
45
|
+
if (alarm>0){
|
|
46
|
+
this.emit(
|
|
47
|
+
`ALARM #${alarm} from ${this.getDisplayName()})`,
|
|
48
|
+
{ message: AlarmReason(alarm), state: 'alert'})
|
|
49
|
+
}
|
|
50
|
+
switch(this.auxMode){
|
|
51
|
+
case VC.AuxMode.STARTER_VOLTAGE:
|
|
52
|
+
this.emitData("starterVoltage",decData,6);
|
|
53
|
+
break;
|
|
54
|
+
case VC.AuxMode.TEMPERATURE:
|
|
55
|
+
this.emitData("temperature",decData,6);
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
this.emit("current", this.getAuxModeAndCurrent(8,decData).current)
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
module.exports=VictronDCEnergyMeter
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const VictronSensor = require ("./Victron/VictronSensor.js")
|
|
2
|
+
const VC = require("./Victron/VictronConstants.js")
|
|
3
|
+
const int24 = require('int24')
|
|
4
|
+
class VictronGXDevice extends VictronSensor{
|
|
5
|
+
/*
|
|
6
|
+
Record layout is still to be determined and might change.
|
|
7
|
+
Start
|
|
8
|
+
bit
|
|
9
|
+
Nr of
|
|
10
|
+
bits Meaning Units Range NA value Remark
|
|
11
|
+
0 0 32 16 Battery voltage 0.01 V 0 .. 655.34
|
|
12
|
+
V
|
|
13
|
+
0xFFFF VE_REG_DC_CHANNEL1_VOLTAGE
|
|
14
|
+
2 16 48 20 PV power W 0 .. 1 MW 0xFFFFF VE_REG_DC_INPUT_POWER
|
|
15
|
+
4 36 68 7 SOC 1% 0 .. 100% 0x7F VE_REG_SOC
|
|
16
|
+
5 43 75 21 Battery power W -1 .. 1 MW 0x0FFFFF VE_REG_DC_CHANNEL1_POWER
|
|
17
|
+
96 21 DC power W -1 .. 1 MW 0x0FFFFF
|
|
18
|
+
TBD - AC in power
|
|
19
|
+
TBD - AC out power
|
|
20
|
+
TBD - Warnings /
|
|
21
|
+
Alarms
|
|
22
|
+
TBD
|
|
23
|
+
117 43 Unuse
|
|
24
|
+
*/
|
|
25
|
+
static async identify(device){
|
|
26
|
+
return await this.identifyMode(device, 0x07)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async init() {
|
|
30
|
+
super.init()
|
|
31
|
+
this.addMetadatum('voltage','V', 'channel #1 voltage',
|
|
32
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(0),0xFFFF)/100})
|
|
33
|
+
this.addMetadatum('pvPower','W','DC input power in watts',
|
|
34
|
+
(buff)=>{return this.NaNif(int24.readInt24BE(buff,2)>>4,0xFFFFF)})
|
|
35
|
+
this.addMetadatum('soc','ratio', 'state of charge',
|
|
36
|
+
(buff)=>{ return ((buff.readUInt16BE(4)&0xfff)>>5)/100})
|
|
37
|
+
this.addMetadatum('batteryPower','W', 'battery power',
|
|
38
|
+
(buff)=>{return (this.NaNif(int24.readInt24BE(buff,5)&0x1ffff),0x0FFFFF )})
|
|
39
|
+
this.addMetadatum('DCPower','W', 'DCpower',
|
|
40
|
+
(buff)=>{return this.NaNif(int24.readInt24BE(buff,8)>>3,0x0FFFFF )})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
module.exports=VictronGXDevice
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const VictronSensor = require("./Victron/VictronSensor");
|
|
2
|
+
const VC=require("./Victron/VictronConstants.js")
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class VictronInverter extends VictronSensor{
|
|
6
|
+
static async identify(device){
|
|
7
|
+
return await this.identifyMode(device, 0x03)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async init() {
|
|
11
|
+
await super.init()
|
|
12
|
+
this.addMetadatum('deviceState','', 'inverter device state',
|
|
13
|
+
(buff)=>{return VC.OperationMode.get(buff.readIntU8(0))})
|
|
14
|
+
const md = this.addMetadatum('alarmReason','', 'reason for alarm',
|
|
15
|
+
(buff)=>{return buff.readIntU16LE(1)})
|
|
16
|
+
md.notify=true
|
|
17
|
+
|
|
18
|
+
this.addMetadatum('batteryVoltage','V', 'battery voltage',
|
|
19
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(3),0x7FFF)/100})
|
|
20
|
+
this.addMetadatum('acPower','W', 'AC power (in watts: Apparent Power * Power Factor)',
|
|
21
|
+
(buff)=>{return this.NaNif( buff.readUInt16LE(5), 0xFFFF )*this.powerFactor})
|
|
22
|
+
this.addMetadatum('acVoltage','V','AC Voltage',
|
|
23
|
+
(buff)=>{return this.NaNif((buff.readUInt16BE(5)>>1),0x7FFF)/10})
|
|
24
|
+
this.addMetadatum('acCurrent','A', 'AC Current',
|
|
25
|
+
(buff)=>{return this.NaNif(((buff.readUInt32BE(5)&0x7ffff)>>6),0x7FF)/10}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
powerFactor = .8 //parameterize anon
|
|
30
|
+
|
|
31
|
+
emitValuesFrom(decData){
|
|
32
|
+
super.emitValuesFrom(decData)
|
|
33
|
+
const alarm = this.getMetadatum("alarmReason").read(decData)
|
|
34
|
+
if (alarm>0){
|
|
35
|
+
this.emit(
|
|
36
|
+
`ALARM #${alarm} from ${this.getDisplayName()})`,
|
|
37
|
+
{ message: VC.AlarmReason.get(alarm), state: 'alert'})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
module.exports=VictronInverter
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const VictronSensor = require("./Victron/VictronSensor");
|
|
2
|
+
const VC=require("./Victron/VictronConstants.js")
|
|
3
|
+
|
|
4
|
+
function toBinaryString(buff){
|
|
5
|
+
return [...buff].map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
6
|
+
}
|
|
7
|
+
class VictronInverterRS extends VictronSensor{
|
|
8
|
+
static async identify(device){
|
|
9
|
+
return await this.identifyMode(device, 0x06)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async init() {
|
|
14
|
+
await super.init()
|
|
15
|
+
|
|
16
|
+
this.addMetadatum('deviceState','', 'inverter device state',
|
|
17
|
+
(buff)=>{return VC.OperationMode.get(buff.readIntU8(0))})
|
|
18
|
+
const md = this.addMetadatum('chargerError','', 'charger error',
|
|
19
|
+
(buff)=>{return VC.ChargerError(buff.readIntU8(1))})
|
|
20
|
+
md.notify=true
|
|
21
|
+
|
|
22
|
+
this.addMetadatum('batteryVoltage','V', 'battery voltage',
|
|
23
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FFF)/100})
|
|
24
|
+
this.addMetadatum('pvPower','W', 'PV power',
|
|
25
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(4), 0xffff)})
|
|
26
|
+
this.addMetadatum('yieldToday','W', 'yield yoday in watts',
|
|
27
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(6), 0xffff)*10})
|
|
28
|
+
this.addMetadatum('acOutPower','W', 'AC out power in watts',
|
|
29
|
+
(buff)=>{this.NaNif(buff.readInt16LE(8), 0x7fff)})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
emitValuesFrom(decData){
|
|
33
|
+
super.emitValuesFrom(decData)
|
|
34
|
+
const error = this.getMetadatum("chargerError").read(decData)
|
|
35
|
+
if (error>0){
|
|
36
|
+
this.emit(
|
|
37
|
+
`Charger Error #${error} from ${this.getDisplayName()})`,
|
|
38
|
+
{ message: VC.ChargerError.get(error), state: 'error'})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
module.exports=VictronInverterRS
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*(Lynx Smart) BMS (0x0A)
|
|
2
|
+
Start Bit Nr of Bits Meaning Units Range NA Value Remark
|
|
3
|
+
32 8 Error 0x0 VE_REG_BMS_ERROR
|
|
4
|
+
40 16 TTG 1min 0..45.5 days 0xFFFF VE_REG_TTG
|
|
5
|
+
56 16 Battery voltage 0.01V -327.68..327.66 V 0x7FFF VE_REG_DC_CHANNEL1_VOLTAGE
|
|
6
|
+
72 16 Battery current 0.1A -3276.8..3276.6 0x7FFF VE_REG_DC_CHANNEL1_CURRENT
|
|
7
|
+
88 16 IO status 0x0 VE_REG_BMS_IO
|
|
8
|
+
104 18 Warnings/Alarms 0x0 VE_REG_BMS_WARNINGS_ALARMS
|
|
9
|
+
122 10 SOC 0.1% 0..100.0% 0x3FF VE_REG_SOC
|
|
10
|
+
132 20 Consumed Ah 0.1Ah -104,857..0 Ah 0xFFFFF VE_REG_CAH Consumed Ah = -Record value
|
|
11
|
+
152 7 Temperature °C -40..86 °C 0x7F VE_REG_BAT_TEMPERATURE Temperature = Record value - 40
|
|
12
|
+
159 1 Unused*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const VictronSensor = require ("./Victron/VictronSensor.js")
|
|
17
|
+
const VC = require("./Victron/VictronConstants.js")
|
|
18
|
+
const int24 = require('int24')
|
|
19
|
+
|
|
20
|
+
class VictronLynxSmartBMS extends VictronSensor{
|
|
21
|
+
|
|
22
|
+
static async identify(device){
|
|
23
|
+
return await this.identifyMode(device, 0x0A)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async init() {
|
|
27
|
+
await super.init()
|
|
28
|
+
this.addMetadatum('error','', 'error code',
|
|
29
|
+
(buff)=>{return buff.readUInt8(0)})
|
|
30
|
+
|
|
31
|
+
this.addMetadatum('ttg','s', 'time to go (in seconds)',
|
|
32
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(1),0xFFFF)*60})
|
|
33
|
+
this.addMetadatum('voltage','V', 'channel 1 voltage',
|
|
34
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(3),0x7FFF)/100})
|
|
35
|
+
this.addMetadatum('current','A','channel 1 current',
|
|
36
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(5),0x7FFF)/10})
|
|
37
|
+
this.addMetadatum('ioStatus','','IO Status', //TODO
|
|
38
|
+
(buff)=>{return buff.readInt16LE(7)})
|
|
39
|
+
this.addMetadatum('warningsAndAlarms','','warnings and alarms', //TODO
|
|
40
|
+
(buff)=>{return (int24.readUInt24BE(buff,9)>>6)})
|
|
41
|
+
this.addMetadatum('soc','','state of charge',
|
|
42
|
+
(buff)=>{return this.NaNif(((buff.readUInt16BE(11)&0x3fff)>>4),0x3FF)/1000})
|
|
43
|
+
this.addMetadatum('consumedAh','Ah','amp-hours consumed',
|
|
44
|
+
(buff)=>{return this.NaNif((int24.readUInt24BE(13)>>4),0xFFFFF)/10} )
|
|
45
|
+
this.addMetadatum('temp', 'K', 'battery temperature',
|
|
46
|
+
(buff)=>{this.NaNif( buff.readUInt8(15)>>1,0x7f) +233.15})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
module.exports=VictronLynxSmartBMS
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
*/
|
|
4
|
+
const VictronSensor = require ("./Victron/VictronSensor.js")
|
|
5
|
+
const VC = require("./Victron/VictronConstants.js")
|
|
6
|
+
class VictronOrionXS extends VictronSensor{
|
|
7
|
+
|
|
8
|
+
static async identify(device){
|
|
9
|
+
return await this.identifyMode(device, 0x0F)
|
|
10
|
+
}
|
|
11
|
+
async init() {
|
|
12
|
+
await super.init()
|
|
13
|
+
this.addMetadatum('deviceState','', 'device state',
|
|
14
|
+
(buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
|
|
15
|
+
this.addMetadatum('chargerError','', 'charger error',
|
|
16
|
+
(buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
|
|
17
|
+
this.addMetadatum('outputVoltage','V', 'output voltage',
|
|
18
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FF)/100})
|
|
19
|
+
this.addMetadatum('current','A','output current',
|
|
20
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(4),0xFFFF)/10})
|
|
21
|
+
this.addMetadatum('inputVoltage','V', 'input voltage',
|
|
22
|
+
(buff)=>{return this.NaNif(buff.readInt16LE(6),0x07FF)/100})
|
|
23
|
+
this.addMetadatum('inputCurrent','A','input current',
|
|
24
|
+
(buff)=>{return this.NaNif(buff.readUInt16LE(8),0xFFFF)/10})
|
|
25
|
+
this.addMetadatum('deviceOffReason','', 'device off reason',
|
|
26
|
+
(buff)=>{return VC.OffReasons(buff.readUInt32(10))})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
module.exports=VictronOrionXS
|