bt-sensors-plugin-sk 1.1.0-beta.2.1.3.3 → 1.1.0-beta.2.1.4.1

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 CHANGED
@@ -152,7 +152,7 @@ class BTSensor extends EventEmitter {
152
152
  */
153
153
  class Metadatum{
154
154
 
155
- constructor(tag, unit, description, read=()=>{return null}, gatt=null, type){
155
+ constructor(tag, unit, description, read, gatt=null, type){
156
156
  this.tag = tag
157
157
  this.unit = unit
158
158
  this.description = description
package/index.js CHANGED
@@ -90,15 +90,13 @@ module.exports = function (app) {
90
90
  }, x);
91
91
  });
92
92
  }
93
- async function instantiateSensor(device,config){
93
+ async function instantiateSensor(device,config){
94
94
  try{
95
95
  for (var [clsName, cls] of classMap) {
96
+ if (clsName.startsWith("_")) continue
96
97
  const c = await cls.identify(device)
97
98
  if (c) {
98
-
99
- if (c.name.startsWith("_")) continue
100
99
  c.debug=app.debug
101
- c.debug.bind(c)
102
100
  const sensor = new c(device,config?.params, config?.gattParams)
103
101
  sensor.debug=app.debug
104
102
  await sensor.init()
@@ -356,7 +354,6 @@ module.exports = function (app) {
356
354
  })
357
355
  .catch((error)=>
358
356
  {
359
-
360
357
  const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
361
358
  app.debug(msg)
362
359
  app.debug(error)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bt-sensors-plugin-sk",
3
- "version": "1.1.0-beta.2.1.3.3",
4
- "description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird",
3
+ "version": "1.1.0-beta.2.1.4.1",
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": {
7
7
  "dbus-next": "^0.10.2",
@@ -8,16 +8,12 @@ class ATC extends BTSensor{
8
8
  }
9
9
  }
10
10
  static async identify(device){
11
- try{
12
- const regex = /^ATC_[A-Fa-f0-9]{6}$/
13
- const name = await this.getDeviceProp(device,"Name")
14
- if (name && name.match(regex)){
15
- return this
16
- }
17
- } catch (e){
11
+ const regex = /^ATC_[A-Fa-f0-9]{6}$/
12
+ const name = await this.getDeviceProp(device,"Name")
13
+ if (name && name.match(regex))
14
+ return this
15
+ else
18
16
  return null
19
- }
20
- return null
21
17
  }
22
18
 
23
19
  async init() {
@@ -3,7 +3,8 @@ class BLACKLISTED extends BTSensor{
3
3
  static async identify(device){
4
4
  if (await this.getManufacturerID(device)===0x004C) //apple devices use
5
5
  return this //randomised macs and clog up our list
6
- return null
6
+ else
7
+ return null
7
8
  }
8
9
  async init(){
9
10
  await super.init()
@@ -0,0 +1,66 @@
1
+ const BTSensor = require("../BTSensor");
2
+ function decodeTempHumid(tempHumidBytes) {
3
+ // Convert the bytes to a 24-bit integer
4
+ const baseNum = (tempHumidBytes[0] << 16) + (tempHumidBytes[1] << 8) + tempHumidBytes[2];
5
+
6
+ // Check if the temperature is negative
7
+ const isNegative = (baseNum & 0x800000) !== 0;
8
+
9
+ // Extract the temperature and humidity values
10
+ const tempAsInt = baseNum & 0x7FFFFF;
11
+ const tempAsFloat = (tempAsInt / 1000) / 10.0;
12
+ const humid = (tempAsInt % 1000) / 10.0;
13
+
14
+ // Apply the negative sign if necessary
15
+ if (isNegative) {
16
+ return {t:-tempAsFloat, h: humid};
17
+ } else {
18
+ return {t: tempAsFloat, h: humid};
19
+ }
20
+ }
21
+ class GoveeTH extends BTSensor{
22
+
23
+ static async identify(device){
24
+ const regex = /^GVH5[0-9]{3}_[a-f,A-F,0-9]{4}$/
25
+ const name = await this.getDeviceProp(device,"Name")
26
+ const uuids = await this.getDeviceProp(device,'UUIDs')
27
+
28
+ if (name && name.match(regex) &&
29
+ uuids && uuids.length > 0 &&
30
+ uuids[0] == '0000ec88-0000-1000-8000-00805f9b34fb')
31
+ return this
32
+ else
33
+ return null
34
+
35
+ }
36
+
37
+ async init(){
38
+ await super.init()
39
+ }
40
+ initMetadata(){
41
+ this.addMetadatum('temp','K', 'temperature')
42
+ this.addMetadatum('battery','ratio', 'battery strength')
43
+ this.addMetadatum('humidity','ratio', 'humidity')
44
+ }
45
+
46
+ emitValuesFrom(buffer){
47
+ const th = decodeTempHumid(buffer.subarray(2,5))
48
+ this.emit("temp", parseFloat((273.15+th.t).toFixed(2))) ;
49
+ this.emit("humidity", th.h/100 )
50
+ this.emit('battery', buffer[5]/100)
51
+ }
52
+
53
+ getManufacturer(){
54
+ return "Govee"
55
+ }
56
+ async propertiesChanged(props){
57
+ super.propertiesChanged(props)
58
+ if (props.ManufacturerData) {
59
+ const buffer = this.getManufacturerData(0x0001)
60
+ if (buffer) {
61
+ this.emitValuesFrom(buffer)
62
+ }
63
+ }
64
+ }
65
+ }
66
+ module.exports=GoveeTH
@@ -2,18 +2,15 @@ const BTSensor = require("../BTSensor");
2
2
 
3
3
  class Inkbird extends BTSensor{
4
4
 
5
- static async identify(device){
6
- try{
7
- const uuids = await this.getDeviceProp(device,'UUIDs')
8
- const name = await this.getDeviceProp(device,"Name")
9
- if ((name == 'tps' || name=='sps') && (uuids.length > 0 && uuids[0] == '0000fff0-0000-1000-8000-00805f9b34fb')){
10
- return this
11
- }
12
- } catch (e){
13
- this.debug(e)
5
+ static async identify(device){
6
+
7
+ const uuids = await this.getDeviceProp(device,'UUIDs')
8
+ const name = await this.getDeviceProp(device,"Name")
9
+ if ((name == 'tps' || name=='sps') && (uuids.length > 0 && uuids[0] == '0000fff0-0000-1000-8000-00805f9b34fb'))
10
+ return this
11
+ else
14
12
  return null
15
- }
16
- return null
13
+
17
14
  }
18
15
 
19
16
  async init(){
@@ -243,19 +243,14 @@ class MopekaTankSensor extends BTSensor{
243
243
 
244
244
  static manufacturerID = 0x0059
245
245
  static async identify(device){
246
- try{
247
- if (await this.getManufacturerID(device)==this.manufacturerID ){
248
- const uuids = await this.getDeviceProp(device, 'UUIDs')
249
- if (uuids != null && uuids.length>0) {
250
- if (uuids.includes(this.serviceID))
251
- return this
252
- }
253
- }
254
-
255
- } catch (e){
256
- this.debug.log(e)
257
- }
258
- return null
246
+ if (await this.getManufacturerID(device)==this.manufacturerID ){
247
+ const uuids = await this.getDeviceProp(device, 'UUIDs')
248
+ if (uuids && uuids.length>0 && uuids.includes(this.serviceID))
249
+ return this
250
+ else
251
+ return null
252
+ } else
253
+ return null
259
254
  }
260
255
 
261
256
  async init(){
@@ -287,7 +282,7 @@ class MopekaTankSensor extends BTSensor{
287
282
  }).bind(this)
288
283
  )
289
284
  this.addMetadatum("battStrength","ratio","sensor battery strength",
290
- (buffer)=>{ return Math.max(0, Math.min(100, (((this.battVolt / 32.0) - 2.2) / 0.65))) }
285
+ (buffer)=>{ return Math.max(0, Math.min(1, (((this.battVolt) - 2.2) / 0.65))) }
291
286
  )
292
287
  this.addMetadatum("temp","K","temperature",
293
288
  ((buffer)=>{
@@ -1,15 +1,11 @@
1
1
  const BTSensor = require("../BTSensor");
2
2
  class RuuviTag extends BTSensor{
3
3
  static manufacturerID = 0x0499
4
- static async identify(device){
5
- try{
6
- if (await this.getManufacturerID(device)==this.manufacturerID)
7
- return this
8
- } catch (e){
9
- console.log(e)
4
+ static async identify(device){
5
+ if (await this.getManufacturerID(device)==this.manufacturerID)
6
+ return this
7
+ else
10
8
  return null
11
- }
12
- return null
13
9
  }
14
10
 
15
11
  async init(){
@@ -1,22 +1,18 @@
1
1
  const BTSensor = require("../BTSensor");
2
2
  class UltrasonicWindMeter extends BTSensor{
3
3
  static async identify(device){
4
- try{
5
- const uuids = await this.getDeviceProp(device,'UUIDs')
6
- const name = await this.getDeviceProp(device,"Name")
7
- if (name == 'ULTRASONIC'){
8
- return this
9
- }
10
- } catch (e){
11
- this.debug(e)
4
+
5
+ const uuids = await this.getDeviceProp(device,'UUIDs')
6
+ const name = await this.getDeviceProp(device,"Name")
7
+ if (name == 'ULTRASONIC')
8
+ return this
9
+ else
12
10
  return null
13
- }
14
- return null
15
11
  }
16
12
  hasGATT(){
17
13
  return true
18
14
  }
19
- emitGatt(){
15
+ emitGATT(){
20
16
  this.battCharacteristic.readValue()
21
17
  .then((buffer)=>
22
18
  this.emitData("batt", buffer)
@@ -32,6 +28,17 @@ class UltrasonicWindMeter extends BTSensor{
32
28
  )
33
29
 
34
30
  }
31
+ async init(){
32
+ await super.init()
33
+ this.addMetadatum("batt","","Battery strength",
34
+ (buffer)=>{return buffer.readUInt8()})
35
+ this.addMetadatum("awa","","Apparent Wind Angle",
36
+ (buffer)=>{return buffer.readInt16LE()}
37
+ )
38
+ this.addMetadatum("aws","","Apparent Wind Speed",
39
+ (buffer)=>{return buffer.readInt16LE()}
40
+ )
41
+ }
35
42
 
36
43
  initGATTConnection(){
37
44
  return new Promise((resolve,reject )=>{ this.device.connect().then(async ()=>{
@@ -196,6 +196,7 @@ module.exports = {
196
196
  0xA2A2: "Phoenix Inverter 24V 3000VA 230V",
197
197
  0xA2A4: "Phoenix Inverter 48V 3000VA 230V",
198
198
  0xA31A: "Blue Smart AC Charger IP67",
199
+ 0xA33B: "Blue Smart AC Charger IP65",
199
200
  0xA33C: "Blue Smart AC Charger IP65",
200
201
  0xA340: "Phoenix Smart IP43 Charger 12|50 (1+1)",
201
202
  0xA341: "Phoenix Smart IP43 Charger 12|50 (3)",
@@ -4,34 +4,57 @@ const int24 = require('int24')
4
4
  const util = require('util')
5
5
  const VC = require('./VictronConstants.js');
6
6
  const BLACKLISTED = require("../BlackListedDevice.js");
7
-
7
+ const { resolve } = require("node:path");
8
+ const { setMaxIdleHTTPParsers } = require("node:http");
9
+ function sleep(x) {
10
+ return new Promise((resolve) => {
11
+ setTimeout(() => {
12
+ resolve(x);
13
+ }, x);
14
+ });
15
+ }
8
16
  class VictronSensor extends BTSensor{
9
17
 
10
18
  constructor(device,config,gattConfig){
11
19
  super(device,config,gattConfig)
12
20
  this.encryptionKey = config?.encryptionKey
13
21
  }
14
-
22
+
23
+
15
24
  static async identifyMode(device, mode){
25
+
26
+ var md = await this.getDeviceProp(device,'ManufacturerData')
27
+ if (md==undefined || !Object.hasOwn(md,0x2e1))
28
+ return null
29
+ else {
30
+
31
+ if (md[0x2e1].value[0]==0x10) {
32
+ if (md[0x2e1].value[4]==mode)
33
+ return this
34
+ else
35
+ return null
36
+ }
16
37
 
17
- try{
18
- const md = await this.getDeviceProp(device,'ManufacturerData')
19
- if (!md) return null
20
- const data = md[0x2e1]
21
- if (data && data.value[0]==0x2) { //VE.Smart is on
22
- return BLACKLISTED
38
+ var hasDataPacket=false
39
+ device.helper._prepare()
40
+ device.helper.on("PropertiesChanged",
41
+ (props)=> {
42
+ if (Object.hasOwn(props,'ManufacturerData')){
43
+ md = props['ManufacturerData'].value
44
+ hasDataPacket=md[0x2e1].value[0]==0x10
45
+ }
46
+ })
47
+ while (!hasDataPacket) {
48
+ await sleep(500)
23
49
  }
24
- if (data && data.value[0]==0x10 && data.value[4]==mode)
50
+ device.helper.removeListeners()
51
+ if (md[0x2e1].value[4]==mode)
25
52
  return this
26
- else {
53
+ else
27
54
  return null
28
- }
29
- } catch (e){
30
- console.log(e)
31
- return null
32
- }
33
-
55
+ }
34
56
  }
57
+
35
58
 
36
59
 
37
60
  async init(){
@@ -79,9 +102,8 @@ const BLACKLISTED = require("../BlackListedDevice.js");
79
102
 
80
103
  try{
81
104
  const md = this.getManufacturerData(0x2e1)
82
- if (md.length && md[0]==0x10){
83
- const buff = this.getManufacturerData(0x2e1)
84
- const decData=this.decrypt(buff)
105
+ if (md && md.length && md[0]==0x10){
106
+ const decData=this.decrypt(md)
85
107
  this.emitValuesFrom(decData)
86
108
  }
87
109
  }
@@ -20,7 +20,7 @@ Startbit Nr of bits Meaning Units Range NA value Remark
20
20
 
21
21
  const VictronSensor = require ("./Victron/VictronSensor.js")
22
22
  const VC = require("./Victron/VictronConstants.js")
23
- const int24 = require('int24')
23
+ const BitReader = require("./_BitReader")
24
24
 
25
25
  class VictronACCharger extends VictronSensor{
26
26
 
@@ -38,29 +38,36 @@ class VictronACCharger extends VictronSensor{
38
38
  this.addMetadatum('error','', 'error code',
39
39
  (buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
40
40
 
41
- this.addMetadatum('batt1','V', 'battery 1 voltage',
42
- (buff)=>{return this.NaNif((buff.readUInt16LE(2)&0x1FFF), 0x1FFF)/100})
41
+ this.addMetadatum('batt1','V', 'battery 1 voltage')
43
42
 
44
- this.addMetadatum('curr1','A', 'battery 1 current',
45
- (buff)=>{return this.NaNif(buff.readUInt16BE(3)&0x7FF,0x7FF)/10})
43
+ this.addMetadatum('curr1','A', 'battery 1 current')
46
44
 
47
- this.addMetadatum('batt2','V', 'battery 2 voltage',
48
- (buff)=>{return this.NaNif((buff.readUInt16LE(5)&0x1FFF), 0x1FFF)/100})
45
+ this.addMetadatum('batt2','V', 'battery 2 voltage')
49
46
 
50
- this.addMetadatum('curr2','A', 'battery 2 current',
51
- (buff)=>{return this.NaNif(buff.readUInt16BE(7)&0x7FF,0x7FF)/10})
47
+ this.addMetadatum('curr2','A', 'battery 2 current')
52
48
 
53
- this.addMetadatum('batt3','V', 'battery 3 voltage',
54
- (buff)=>{return this.NaNif((buff.readUInt16LE(8)&0x1FFF), 0x1FFF)/100})
49
+ this.addMetadatum('batt3','V', 'battery 3 voltage')
55
50
 
56
- this.addMetadatum('curr3','A', 'battery 3 current',
57
- (buff)=>{return this.NaNif(buff.readUInt16BE(9)&0x7FF,0x7FF)/10})
51
+ this.addMetadatum('curr3','A', 'battery 3 current')
58
52
 
59
- this.addMetadatum('temp', 'K', 'battery temperature',
60
- (buff)=>{return this.NaNif(buff.readUInt8(11)&0x7F,0x7F)+233.15}) //-40 plus K conversion
53
+ this.addMetadatum('temp', 'K', 'battery temperature')
61
54
 
62
- this.addMetadatum('acCurr','A', 'AC current',
63
- (buff)=>{return this.NaNif((buff.readUInt16BE(11)&0x1FF),0x1FF)/10})
55
+ this.addMetadatum('acCurr','A', 'AC current')
56
+ }
57
+ emitValuesFrom(buffer){
58
+ super.emitValuesFrom(buffer)
59
+
60
+ const br = new BitReader(buffer.subarray(2))
61
+ this.emit("batt1", this.NaNif(br.read_unsigned_int(13),0x1FFF)/100)
62
+ this.emit("curr1", this.NaNif(br.read_unsigned_int(11),0x7FF)/10)
63
+
64
+ this.emit("batt2", this.NaNif(br.read_unsigned_int(13),0x1FFF)/100)
65
+ this.emit("curr2", this.NaNif(br.read_unsigned_int(11),0x7FF)/10)
66
+
67
+ this.emit("batt3", this.NaNif(br.read_unsigned_int(13),0x1FFF)/100)
68
+ this.emit("curr3", this.NaNif(br.read_unsigned_int(11),0x7FF)/10)
69
+ this.emit("temp", this.NaNif(br.read_unsigned_int(7),0x7F)+233.15)
70
+ this.emit("acCurr", this.NaNif(br.read_unsigned_int(9),0x1FF)/10)
64
71
  }
65
72
  }
66
73
  module.exports=VictronACCharger
@@ -9,6 +9,9 @@ class VictronDCDCConverter extends VictronSensor{
9
9
 
10
10
  async init(){
11
11
  await super.init()
12
+ this.initMetadata()
13
+ }
14
+ initMetadata(){
12
15
  this.addMetadatum('deviceState','', 'device state',
13
16
  (buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
14
17
  this.addMetadatum('chargerError','', 'charger error',
@@ -1,6 +1,8 @@
1
+
1
2
  const VictronSensor = require("./Victron/VictronSensor");
2
3
  const VC=require("./Victron/VictronConstants.js")
3
-
4
+ const int24 = require("int24");
5
+ const _BitReader = require("./_BitReader.js");
4
6
  class VictronDCEnergyMeter extends VictronSensor{
5
7
 
6
8
  static async identify(device){
@@ -14,9 +16,11 @@ class VictronDCEnergyMeter extends VictronSensor{
14
16
  (buff)=>{return buff.readInt16LE(2)/100})
15
17
  this.addMetadatum('alarm','', 'alarm',
16
18
  (buff)=>{return buff.readUInt16LE(4)})
17
- this.addMetadatum('current','A', 'current')
18
- const modeCurrent = this.getAuxModeAndCurrent()
19
- this.auxMode= modeCurrent.auxMode
19
+ if (this.encryptionKey){
20
+ const decData = this.decrypt(this.getManufacturerData(0x02e1))
21
+ if (decData)
22
+ this.auxMode=decData.readInt8(8)&0x3
23
+ }
20
24
  switch(this.auxMode){
21
25
  case VC.AuxMode.STARTER_VOLTAGE:
22
26
  this.addMetadatum('starterVoltage','V', 'starter battery voltage',
@@ -36,6 +40,8 @@ class VictronDCEnergyMeter extends VictronSensor{
36
40
  default:
37
41
  break
38
42
  }
43
+ this.addMetadatum('current','A', 'current')
44
+
39
45
  }
40
46
 
41
47
  emitValuesFrom(decData){
@@ -56,8 +62,10 @@ class VictronDCEnergyMeter extends VictronSensor{
56
62
  break;
57
63
  default:
58
64
  break
59
- }
60
- this.emit("current", this.getAuxModeAndCurrent(8,decData).current)
65
+ }
66
+
67
+ this.emit("current",
68
+ this.NaNif( new _BitReader(decData.subarray(8,11)).read_signed_int(22),0x3FFFFF)/1000)
61
69
 
62
70
  }
63
71
 
@@ -28,16 +28,19 @@ TBD
28
28
 
29
29
  async init() {
30
30
  await super.init()
31
+ this.initMetadata()
32
+ }
33
+ initMetadata(){
31
34
  this.addMetadatum('voltage','V', 'channel #1 voltage',
32
35
  (buff)=>{return this.NaNif(buff.readInt16LE(0),0xFFFF)/100})
33
36
  this.addMetadatum('pvPower','W','DC input power in watts',
34
- (buff)=>{return this.NaNif(int24.readInt24BE(buff,2)>>4,0xFFFFF)})
37
+ (buff)=>{return this.NaNif(int24.readInt24LE(buff,2)>>4,0xFFFFF)})
35
38
  this.addMetadatum('soc','ratio', 'state of charge',
36
- (buff)=>{ return ((buff.readUInt16BE(4)&0xfff)>>5)/100})
39
+ (buff)=>{ return ((buff.readUInt16LE(4)&0xfff)>>5)/100})
37
40
  this.addMetadatum('batteryPower','W', 'battery power',
38
- (buff)=>{return (this.NaNif(int24.readInt24BE(buff,5)&0x1ffff),0x0FFFFF )})
41
+ (buff)=>{return (this.NaNif(int24.readInt24LE(buff,5)&0x1ffff),0x0FFFFF )})
39
42
  this.addMetadatum('DCPower','W', 'DCpower',
40
- (buff)=>{return this.NaNif(int24.readInt24BE(buff,8)>>3,0x0FFFFF )})
43
+ (buff)=>{return this.NaNif(int24.readInt24LE(buff,8)>>3,0x0FFFFF )})
41
44
  }
42
45
  }
43
46
  module.exports=VictronGXDevice
@@ -1,6 +1,6 @@
1
1
  const VictronSensor = require("./Victron/VictronSensor");
2
2
  const VC=require("./Victron/VictronConstants.js")
3
-
3
+ const BitReader = require('./_BitReader')
4
4
 
5
5
  class VictronInverter extends VictronSensor{
6
6
  static async identify(device){
@@ -9,6 +9,9 @@ class VictronInverter extends VictronSensor{
9
9
 
10
10
  async init() {
11
11
  await super.init()
12
+ this.initMetadata()
13
+ }
14
+ initMetadata(){
12
15
  this.addMetadatum('deviceState','', 'inverter device state',
13
16
  (buff)=>{return VC.OperationMode.get(buff.readIntU8(0))})
14
17
  const md = this.addMetadatum('alarmReason','', 'reason for alarm',
@@ -19,17 +22,19 @@ class VictronInverter extends VictronSensor{
19
22
  (buff)=>{return this.NaNif(buff.readInt16LE(3),0x7FFF)/100})
20
23
  this.addMetadatum('acPower','W', 'AC power (in watts: Apparent Power * Power Factor)',
21
24
  (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
- )
25
+ this.addMetadatum('acVoltage','V','AC Voltage')
26
+ this.addMetadatum('acCurrent','A', 'AC Current')
27
27
 
28
28
  }
29
29
  powerFactor = .8 //parameterize anon
30
30
 
31
31
  emitValuesFrom(decData){
32
32
  super.emitValuesFrom(decData)
33
+ const br = new BitReader(decData.subarray(5))
34
+ this.emit('acVoltage',
35
+ this.NaNif(br.read_unsigned_int(15),0x7FFF)/100)
36
+ this.addMetadatum('acCurrent',
37
+ this.NaNif(br.read_unsigned_int(11),0x7FF)/10)
33
38
  const alarm = this.getMetadatum("alarmReason").read(decData)
34
39
  if (alarm>0){
35
40
  this.emit(
@@ -15,8 +15,7 @@ Start Bit Nr of Bits Meaning Units Range NA Value Remark
15
15
 
16
16
  const VictronSensor = require ("./Victron/VictronSensor.js")
17
17
  const VC = require("./Victron/VictronConstants.js")
18
- const int24 = require('int24')
19
-
18
+ const BitReader = require("./_BitReader")
20
19
  class VictronLynxSmartBMS extends VictronSensor{
21
20
 
22
21
  static async identify(device){
@@ -25,6 +24,9 @@ class VictronLynxSmartBMS extends VictronSensor{
25
24
 
26
25
  async init() {
27
26
  await super.init()
27
+ this.initMetadata()
28
+ }
29
+ initMetadata(){
28
30
  this.addMetadatum('error','', 'error code',
29
31
  (buff)=>{return buff.readUInt8(0)})
30
32
 
@@ -35,15 +37,24 @@ class VictronLynxSmartBMS extends VictronSensor{
35
37
  this.addMetadatum('current','A','channel 1 current',
36
38
  (buff)=>{return this.NaNif(buff.readInt16LE(5),0x7FFF)/10})
37
39
  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})
40
+ (buff)=>{return buff.readUInt16LE(7)})
41
+ this.addMetadatum('warningsAndAlarms','','warnings and alarms')
42
+ this.addMetadatum('soc','','state of charge')
43
+ this.addMetadatum('consumedAh','Ah','amp-hours consumed')
44
+ this.addMetadatum('temp', 'K', 'battery temperature')
45
+ }
46
+ emitValuesFrom(buffer){
47
+ super.emitValuesFrom(buffer)
48
+ const br = new BitReader(buffer.subarray(9))
49
+
50
+ this.emit('warningsAndAlarms',br.read_unsigned_int(18))
51
+ this.emit('soc',
52
+ this.NaNif((br.read_unsigned_int(10),0x3FF)/1000))
53
+ this.emit('consumedAh',
54
+ this.NaNif(br.read_unsigned_int(20),0xFFFFF)/10 )
55
+ this.emit('temp',
56
+ this.NaNif( br.read_unsigned_int(7),0x7f) +233.15)
57
+
47
58
  }
48
59
  }
49
60
  module.exports=VictronLynxSmartBMS
@@ -25,6 +25,9 @@ class VictronSmartBatteryProtect extends VictronSensor{
25
25
 
26
26
  async init() {
27
27
  await super.init()
28
+ this.initMetadata()
29
+ }
30
+ initMetadata(){
28
31
  this.addMetadatum('deviceState','', 'device state',
29
32
  (buff)=>{return VC.OperationMode.get(buff.readUInt8(1))})
30
33
  this.addMetadatum('outputStatus','', 'output status', //TODO
@@ -16,9 +16,13 @@ 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
- */const VictronSensor = require ("./Victron/VictronSensor.js")
20
- const VC = require("./Victron/VictronConstants.js")
19
+ */const VictronSensor = require ("./Victron/VictronSensor")
20
+ const VC = require("./Victron/VictronConstants")
21
+ const BitReader = require("./_BitReader")
21
22
 
23
+ function _toCellVoltage(val){
24
+ return val==0x7F?NaN:2.6+(val/100)
25
+ }
22
26
  class VictronSmartLithium extends VictronSensor{
23
27
 
24
28
  static async identify(device){
@@ -30,36 +34,32 @@ class VictronSmartLithium extends VictronSensor{
30
34
  this.initMetadata()
31
35
  }
32
36
  initMetadata(){
33
- function _toCellVoltage(val){
34
- return val==0x7F?NaN:2.6+(val/100)
35
- }
37
+
36
38
  this.addMetadatum('bmsFlags','', 'BMS Flags',
37
- (buff)=>{return buff.readUInt32BE(0)})
39
+ (buff)=>{return buff.readUInt32LE(0)})
38
40
 
39
41
  this.addMetadatum('smartLithiumErrors','', 'Smart Lithium Errors Flags',
40
- (buff)=>{return buff.readUInt16BE(4)})
41
- this.addMetadatum('cell1Voltage','V', 'cell #1 voltage',
42
- (buff)=>{return _toCellVoltage((buff.readUInt8(6))>>1)})
43
- this.addMetadatum('cell2Voltage','V', 'cell #2 voltage',
44
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(6))&0x01Fe)>>2)})
45
- this.addMetadatum('cell3Voltage','V', 'cell #3 voltage',
46
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(7))&0x03Fe)>>3)})
47
- this.addMetadatum('cell4Voltage','V', 'cell #4 voltage',
48
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(8))&0x07fe)>>4)})
49
- this.addMetadatum('cell5Voltage','V', 'cell #5 voltage',
50
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(9))&0x0ffe)>>5)})
51
- this.addMetadatum('cell6Voltage','V', 'cell #6 voltage',
52
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(10))&0x1ffe)>>6)})
53
- this.addMetadatum('cell7Voltage','V', 'cell #7 voltage',
54
- (buff)=>{return _toCellVoltage(((buff.readUInt16BE(11))&0x3ffe)>>7)})
55
- this.addMetadatum('cell8Voltage','V', 'cell #8 voltage',
56
- (buff)=>{return _toCellVoltage((buff.readUInt8(12))&0x7e)})
42
+ (buff)=>{return buff.readUInt16LE(4)})
43
+ this.addMetadatum('cell1Voltage','V', 'cell #1 voltage')
44
+ this.addMetadatum('cell2Voltage','V', 'cell #2 voltage')
45
+ this.addMetadatum('cell3Voltage','V', 'cell #3 voltage')
46
+ this.addMetadatum('cell4Voltage','V', 'cell #4 voltage')
47
+ this.addMetadatum('cell5Voltage','V', 'cell #5 voltage')
48
+ this.addMetadatum('cell6Voltage','V', 'cell #6 voltage')
49
+ this.addMetadatum('cell7Voltage','V', 'cell #7 voltage')
50
+ this.addMetadatum('cell8Voltage','V', 'cell #8 voltage')
57
51
  this.addMetadatum('batteryVoltage','V', 'battery voltage',
58
52
  (buff)=>{return this.NaNif((buff.readUInt16LE(13)&0xFFF),0xFFF)/100})
59
53
  this.addMetadatum('balancerStatus','', 'balancer status', //TODO
60
- (buff)=>{return this.NaNif((buff.readUInt8(14)&0xf),0xF)})
54
+ (buff)=>{return this.NaNif((buff.readUInt8(14)>>4),0xF)})
61
55
  this.addMetadatum('batteryTemp','K', 'battery temperature',
62
56
  (buff)=>{return this.NaNif((buff.readUInt8(15)&0x7F),0x7F)+233.15})
63
57
  }
58
+ emitValuesFrom(buffer){
59
+ super.emitValuesFrom(buffer)
60
+ const br = new BitReader(buffer.subarray(6,13))
61
+ for (let i = 1; i<8; i++)
62
+ this.emit(`cell${i+1}Voltage`,_toCellVoltage(br.read_unsigned_int(7)))
63
+ }
64
64
  }
65
65
  module.exports=VictronSmartLithium
@@ -24,7 +24,7 @@ class VictronSolarCharger extends VictronSensor{
24
24
  this.addMetadatum('solarPower','W', 'solar power',
25
25
  (buff)=>{return this.NaNif(buff.readUInt16LE(8),0xFFFF)})
26
26
  this.addMetadatum('externalDeviceLoad','A', 'external device load',
27
- (buff)=>{return this.NaNif(buff.readUInt16BE(10)>>7,0x1FF)})
27
+ (buff)=>{return this.NaNif(buff.readUInt16LE(10)&0x1FF,0x1FF)/10})
28
28
  }
29
29
  }
30
30
  module.exports=VictronSolarCharger
@@ -1,6 +1,6 @@
1
1
  const VictronSensor = require ("./Victron/VictronSensor.js")
2
2
  const VC = require("./Victron/VictronConstants.js")
3
- const int24 = require('int24')
3
+ const BitReader = require("./_BitReader")
4
4
  AC_IN_STATE=["AC in 1","AC in 2","NOT CONNECTED", "NA"]
5
5
  ALARM_STATE=["None","warning", "alarm","NA"]
6
6
  class VictronVEBus extends VictronSensor{
@@ -11,7 +11,9 @@ class VictronVEBus extends VictronSensor{
11
11
 
12
12
  async init() {
13
13
  await super.init()
14
-
14
+ this.initMetadata()
15
+ }
16
+ initMetadata(){
15
17
  this.addMetadatum('chargeState','', 'charge state',
16
18
  (buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
17
19
 
@@ -21,27 +23,44 @@ class VictronVEBus extends VictronSensor{
21
23
  this.addMetadatum('current','A','charger battery current',
22
24
  (buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FFF)/10})
23
25
 
24
- this.addMetadatum('voltage','V', 'charger battery voltage',
25
- (buff)=>{return this.NaNif(buff.readUInt16LE(4) & 0x3FFF,0x3FFF)/100})
26
+ this.addMetadatum('voltage','V', 'charger battery voltage')
26
27
 
27
- this.addMetadatum('acInState','', 'AC in state',
28
- (buff)=>{return AC_IN_STATE[readUInt8(5)>>6]})
28
+ this.addMetadatum('acInState','', 'AC in state')
29
29
 
30
- this.addMetadatum('acInPower','W','AC power IN in watts',
31
- (buff)=>{return this.NaNif(int24.readInt24LE(buff,6)>>5,0x3FFFF)*(raw & 0x40000)?-1:1})
30
+ this.addMetadatum('acInPower','W','AC power IN in watts')
32
31
 
33
- this.addMetadatum('acOutPower','W','AC power OUT in watts',
34
- (buff)=>{return this.NaNif((int24.readInt24LE(buff,8) & 0x1FFFF) >> 2,0x3FFFF) * (raw & 0b1)?-1:1})
32
+ this.addMetadatum('acOutPower','W','AC power OUT in watts')
35
33
 
36
- this.addMetadatum('alarm','','alarm state 0=no alarm, 1=warning, 2=alarm',
37
- (buff)=>{return ALARM_STATE[buff.readUInt8(10)&0x3] })
34
+ this.addMetadatum('alarm','','alarm state 0=no alarm, 1=warning, 2=alarm')
38
35
 
39
- this.addMetadatum('batteryTemperature','K', 'battery temperature',
40
- (buff)=>{return this.NaNif((buff.readInt8(10)&0x1FF)>>1,0x7F) +233.15})
36
+ this.addMetadatum('batteryTemperature','K', 'battery temperature')
41
37
 
42
- this.addMetadatum('soc', 'ratio', 'state of charge',
43
- (buff)=>{return this.NaNif((buff.readInt16BE(10)&0x1FF)>>2,0x7F)/100})
38
+ this.addMetadatum('soc', 'ratio', 'state of charge')
44
39
 
45
40
  }
41
+ emitValuesFrom(buffer){
42
+ super.emitValuesFrom(buffer)
43
+ const br = new BitReader(buffer.subarray(4))
44
+ this.emit('voltage',
45
+ this.NaNif(br.read_unsigned_int(14),0x3FFF)/100)
46
+
47
+ this.emit('acInState',
48
+ AC_IN_STATE[br.read_unsigned_int(2)])
49
+
50
+ this.emit('acInPower',
51
+ this.NaNif(br.read_signed_int(19),0x3FFFF))
52
+
53
+ this.emit('acOutPower',
54
+ this.NaNif(br.read_signed_int(19),0x3FFFF))
55
+
56
+ this.emit('alarm', ALARM_STATE[br.read_unsigned_int(2)])
57
+
58
+ this.emit('batteryTemperature',
59
+ this.NaNif((br.read_unsigned_int(7),0x7F))+233.15)
60
+
61
+ this.emit('soc',
62
+ this.NaNif((br.read_unsigned_int(7),0x7F))/100)
63
+
64
+ }
46
65
  }
47
66
  module.exports=VictronVEBus
@@ -80,16 +80,16 @@ class XiaomiMiBeacon extends BTSensor{
80
80
  static SERVICE_MIBEACON = "0000fe95-0000-1000-8000-00805f9b34fb"
81
81
 
82
82
  static async identify(device){
83
- try{
84
- const sd = await this.getDeviceProp(device, 'ServiceData')
85
- if (sd == null || sd.length==0) return null
86
- const keys = Object.keys(sd)
87
- if (keys[0]==this.SERVICE_MIBEACON) return this
88
- } catch (e){
89
- console.log(e)
83
+ var sd = await this.getDeviceProp(device, 'ServiceData')
84
+ if (sd == null || sd.length==0)
90
85
  return null
86
+ else{
87
+ const keys = Object.keys(sd)
88
+ if (keys[0]==this.SERVICE_MIBEACON)
89
+ return this
90
+ else
91
+ return null
91
92
  }
92
- return null
93
93
  }
94
94
 
95
95
  GATTwarning = "WARNING: Xiaomi GATT connections are known to be unreliable on Debian distributions with Bluez 5.55 and up (earlier BlueZ versions are untested). Using GATT on the Xiaomi may put the system Bluetooth stack into an inconsistent state disrupting and disabling other plugin Bluetooth connections. If by some chance you're successful using GATT with the Xiaomi, let the plugin developer know your configuration. Refer to the plugin documentation for getting the Xiamoi bindKey for non-GATT connections and more information on Xiaomi GATT issues."
@@ -0,0 +1,28 @@
1
+ /*
2
+ Ported from Python original by Keshav Varma https://github.com/keshavdv
3
+ */
4
+ class _BitReader{
5
+ constructor( data){
6
+ this._data = data
7
+ this._index = 0
8
+ }
9
+ read_bit(){
10
+ const bit = (this._data[this._index >> 3] >> (this._index & 7)) & 1
11
+ this._index++
12
+ return bit
13
+ }
14
+
15
+ read_unsigned_int(num_bits){
16
+ var value =''
17
+ for (let i = 0; i < num_bits; i++)
18
+ value = this.read_bit().toString()+value
19
+ return parseInt(value,2)
20
+ }
21
+ read_signed_int(num_bits){
22
+ const value = this.read_unsigned_int(num_bits)
23
+ return value & (1 << (num_bits - 1))?value - (1 << num_bits):value
24
+ }
25
+
26
+ }
27
+
28
+ module.exports=_BitReader