bt-sensors-plugin-sk 1.2.5 → 1.2.6-beta-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.
Files changed (126) hide show
  1. package/.vscode/settings.json +2 -0
  2. package/BTSensor.js +77 -38
  3. package/DistanceManager.js +249 -0
  4. package/Mixin.js +19 -0
  5. package/OutOfRangeDevice.js +46 -0
  6. package/README.md +26 -6
  7. package/classLoader.js +13 -8
  8. package/connectUUID.exp +26 -0
  9. package/development/FakeBTDevice.js +57 -0
  10. package/development/package.json +5 -0
  11. package/diff.txt +2860 -0
  12. package/index.js +58 -12
  13. package/package.json +8 -6
  14. package/public/698.js +14 -0
  15. package/public/847.js +1 -1
  16. package/public/images/ATC.jpeg +0 -0
  17. package/public/images/Aranet4.webp +0 -0
  18. package/public/images/BP108B.webp +0 -0
  19. package/public/images/GoveeH5074.jpg +0 -0
  20. package/public/images/GoveeH5075.webp +0 -0
  21. package/public/images/GoveeH510x.jpg +0 -0
  22. package/public/images/InkbirdTH3.webp +0 -0
  23. package/public/images/JBDBMS.webp +0 -0
  24. package/public/images/Junctek.webp +0 -0
  25. package/public/images/KilovaultHLXPlus.jpg +0 -0
  26. package/public/images/LancolVoltageMeter.webp +0 -0
  27. package/public/images/LiTimeLiFePo4Battery.avif +0 -0
  28. package/public/images/MercurySmartcraft.jpg +0 -0
  29. package/public/images/MopekaTankSensor.jpg +0 -0
  30. package/public/images/RemoranWave3.jpeg +0 -0
  31. package/public/images/RenogyInverter.jpg +0 -0
  32. package/public/images/RenogyRoverClient.jpg +0 -0
  33. package/public/images/RenogySmartLiFePo4Battery.webp +0 -0
  34. package/public/images/RuuviTag.jpg +0 -0
  35. package/public/images/ShellyBLUHT.webp +0 -0
  36. package/public/images/ShellyBLUMotion.webp +0 -0
  37. package/public/images/ShellyBluDoorWindow.webp +0 -0
  38. package/public/images/Skanbatt.jpg +0 -0
  39. package/public/images/SmartBatteryProtect.webp +0 -0
  40. package/public/images/SmartBatterySense.webp +0 -0
  41. package/public/images/SwitchBotMeterPlus.webp +0 -0
  42. package/public/images/SwitchBotTH.webp +0 -0
  43. package/public/images/TopbandBattery.webp +0 -0
  44. package/public/images/Ultrasonic.jpg +0 -0
  45. package/public/images/VictronBlueSmartACCharger.jpg +0 -0
  46. package/public/images/VictronBlueSolarMPPT.jpeg +0 -0
  47. package/public/images/VictronCerboGX.webp +0 -0
  48. package/public/images/VictronInverterRS.webp +0 -0
  49. package/public/images/VictronLynxSmartBMS.webp +0 -0
  50. package/public/images/VictronMultiPlus-II.webp +0 -0
  51. package/public/images/VictronOrionTrIsolated.webp +0 -0
  52. package/public/images/VictronOrionTrNonIsolated.webp +0 -0
  53. package/public/images/VictronPhoenixInverter.webp +0 -0
  54. package/public/images/VictronPhoenixSmart1600.webp +0 -0
  55. package/public/images/VictronSmartBatteryProtect.jpg +0 -0
  56. package/public/images/VictronSmartIP43.webp +0 -0
  57. package/public/images/VictronSmartLithiumBattery.jpg +0 -0
  58. package/public/images/VictronSmartSolarMPPT.webp +0 -0
  59. package/public/images/VictronVEBus.webp +0 -0
  60. package/public/images/eco-worthy.webp +0 -0
  61. package/public/images/iBeacon.jpg +0 -0
  62. package/public/remoteEntry.js +1 -1
  63. package/readUUID.exp +23 -0
  64. package/sensor_classes/ATC.js +3 -2
  65. package/sensor_classes/Aranet2.js +3 -1
  66. package/sensor_classes/Aranet4.js +1 -2
  67. package/sensor_classes/BankManager.js +1 -1
  68. package/sensor_classes/Beacon/AbstractBeaconMixin.js +85 -0
  69. package/sensor_classes/Beacon/Eddystone.js +77 -0
  70. package/sensor_classes/Beacon/iBeacon.js +58 -0
  71. package/sensor_classes/EcoWorthy.js +160 -0
  72. package/sensor_classes/EctiveBMS.js +269 -0
  73. package/sensor_classes/FeasyComBeacon.js +68 -0
  74. package/sensor_classes/GobiusCTankMeter.js +4 -3
  75. package/sensor_classes/GoveeH5074.js +2 -0
  76. package/sensor_classes/GoveeH5075.js +2 -0
  77. package/sensor_classes/GoveeH510x.js +1 -0
  78. package/sensor_classes/Inkbird.js +1 -0
  79. package/sensor_classes/JBDBMS.js +1 -0
  80. package/sensor_classes/Junctek.js +14 -6
  81. package/sensor_classes/KilovaultHLXPlus.js +1 -0
  82. package/sensor_classes/LancolVoltageMeter.js +2 -0
  83. package/sensor_classes/MercurySmartcraft.js +1 -0
  84. package/sensor_classes/MopekaTankSensor.js +7 -204
  85. package/sensor_classes/RemoranWave3.js +2 -0
  86. package/sensor_classes/Renogy/RenogySensor.js +1 -0
  87. package/sensor_classes/RenogyBattery.js +3 -4
  88. package/sensor_classes/RenogyInverter.js +3 -6
  89. package/sensor_classes/RenogyRoverClient.js +3 -0
  90. package/sensor_classes/RuuviTag.js +11 -8
  91. package/sensor_classes/ShellySBDW002C.js +3 -1
  92. package/sensor_classes/ShellySBHT003C.js +7 -0
  93. package/sensor_classes/ShellySBMO003Z.js +3 -2
  94. package/sensor_classes/ShenzhenLiOnBMS.js +4 -0
  95. package/sensor_classes/SwitchBotMeterPlus.js +1 -1
  96. package/sensor_classes/SwitchBotTH.js +2 -1
  97. package/sensor_classes/UNKNOWN.js +2 -1
  98. package/sensor_classes/UltrasonicWindMeter.js +3 -0
  99. package/sensor_classes/Victron/VictronConstants.js +2 -0
  100. package/sensor_classes/Victron/VictronIdentifier.js +24 -0
  101. package/sensor_classes/Victron/VictronSensor.js +59 -49
  102. package/sensor_classes/VictronACCharger.js +1 -6
  103. package/sensor_classes/VictronBatteryMonitor.js +39 -28
  104. package/sensor_classes/VictronDCDCConverter.js +1 -4
  105. package/sensor_classes/VictronDCEnergyMeter.js +1 -4
  106. package/sensor_classes/VictronGXDevice.js +1 -4
  107. package/sensor_classes/VictronInverter.js +2 -3
  108. package/sensor_classes/VictronInverterRS.js +2 -4
  109. package/sensor_classes/VictronLynxSmartBMS.js +1 -4
  110. package/sensor_classes/VictronOrionXS.js +1 -3
  111. package/sensor_classes/VictronSmartBatteryProtect.js +1 -4
  112. package/sensor_classes/VictronSmartLithium.js +1 -4
  113. package/sensor_classes/VictronSolarCharger.js +1 -3
  114. package/sensor_classes/VictronVEBus.js +1 -4
  115. package/sensor_classes/XiaomiMiBeacon.js +5 -2
  116. package/sensor_classes/iBeaconSensor.js +40 -0
  117. package/src/components/PluginConfigurationPanel.js +179 -184
  118. package/test.txt +805 -0
  119. package/public/681.js +0 -14
  120. package/sensor_classes/IBeacon.js +0 -45
  121. /package/public/{681.js.LICENSE.txt → 698.js.LICENSE.txt} +0 -0
  122. /package/public/images/{Aranet2_HOME_F_900x900_90OVA5J.original.webp → Aranet2.webp} +0 -0
  123. /package/public/images/{Bank Manager All-in-onewc.webp → BankManager.webp} +0 -0
  124. /package/public/images/{Gobius_C.png → GobiusCTankMeter.png} +0 -0
  125. /package/public/images/{Victron-SmartShunt.jpg → VictronSmartShunt.jpg} +0 -0
  126. /package/public/images/{smartsolarMPPT7515.png → VictronSmartSolarMPPT7515.png} +0 -0
@@ -0,0 +1,160 @@
1
+ const BTSensor = require("../BTSensor");
2
+ ({FakeDevice,FakeGATTService,FakeGATTCharacteristic }=require( "../development/FakeBTDevice.js"))
3
+ //a1000000650000000000180103440018004800640531ff8000002710000100010000000000000000000100020000ffff00000000000000000000000000000000000000000000000000000000000000000000418b
4
+ //a20000006500000000001801035600040cfb0cfd0cfb0cfaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000300cd00c000befc18fc18fc18fc18fc18fc18976a
5
+
6
+ /*
7
+ BMSdp("battery_level", 16, 2, False, lambda x: x, 0xA1),
8
+ BMSdp("voltage", 20, 2, False, lambda x: x / 100, 0xA1),
9
+ BMSdp("current", 22, 2, True, lambda x: x / 100, 0xA1),
10
+ BMSdp("problem_code", 51, 2, False, lambda x: x, 0xA1),
11
+ BMSdp("design_capacity", 26, 2, False, lambda x: x // 100, 0xA1),
12
+ BMSdp("cell_count", _CELL_POS, 2, False, lambda x: x, 0xA2),
13
+ BMSdp("temp_sensors", _TEMP_POS, 2, False, lambda x: x, 0xA2),
14
+ # ("cycles", 0xA1, 8, 2, False, lambda x: x),
15
+ */
16
+
17
+ function waitForVariable(obj, variableName, interval = 100) {
18
+ return new Promise((resolve) => {
19
+ if (obj[variableName] !== undefined) {
20
+ return resolve(obj[variableName]);
21
+ }
22
+
23
+ const intervalId = setInterval(() => {
24
+ if (obj[variableName] !== undefined) {
25
+ clearInterval(intervalId);
26
+ resolve(obj[variableName]);
27
+ }
28
+ }, interval);
29
+ });
30
+ }
31
+
32
+ class EcoWorthy extends BTSensor {
33
+ static Domain = BTSensor.SensorDomains.electrical
34
+
35
+ static TX_RX_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"
36
+ static NOTIFY_CHAR_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"
37
+
38
+ static async test(data=["a1000000650000000000180103440018004800640531ff8000002710000100010000000000000000000100020000ffff00000000000000000000000000000000000000000000000000000000000000000000418b",
39
+ "a20000006500000000001801035600040cfb0cfd0cfb0cfaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000300cd00c000befc18fc18fc18fc18fc18fc18976a"
40
+ ]){
41
+ const device = new FakeDevice(
42
+ [new FakeGATTService("0000fff0-0000-1000-8000-00805f9b34fb",
43
+ [new FakeGATTCharacteristic("0000fff1-0000-1000-8000-00805f9b34fb",
44
+ data
45
+ )]
46
+ )]
47
+ )
48
+ const obj = new EcoWorthy(device)
49
+ obj.currentProperties={Name:"Fake EcoWorthy", Address:"<mac>"}
50
+ obj.debug=(m)=>{console.log(m)}
51
+ obj.deviceConnect=()=>{}
52
+ await obj.initSchema()
53
+ for (const [tag,path] of Object.entries(obj._schema.properties.paths.properties)) {
54
+ obj.on(tag, val=>{console.log(`${tag} => ${val} `)})
55
+ }
56
+
57
+ }
58
+
59
+ static identify(device){
60
+ return null
61
+ }
62
+ static ImageFile = "EcoWorthy.webp"
63
+
64
+
65
+ async initSchema(){
66
+ super.initSchema()
67
+ this.addDefaultParam("batteryID")
68
+
69
+ this.addDefaultPath('voltage','electrical.batteries.voltage')
70
+ .read=
71
+ (buffer)=>{return buffer.readUInt16BE(20) / 100}
72
+
73
+ this.addDefaultPath('current','electrical.batteries.current')
74
+ .read=
75
+ (buffer)=>{return buffer.readInt16BE(22) / 100}
76
+
77
+ this.addDefaultPath('remainingCapacity','electrical.batteries.capacity.remaining')
78
+ //.read=(buffer)=>{return (buffer.readUInt16BE(8) / 100)*3600}
79
+
80
+ this.addDefaultPath('capacity','electrical.batteries.capacity.actual')
81
+ .read=(buffer)=>{return (buffer.readUInt16BE(26) / 100)*3600}
82
+
83
+ this.addDefaultPath('SOC','electrical.batteries.capacity.stateOfCharge')
84
+ .read=(buffer)=>{return buffer.readUInt16BE(16)/100}
85
+
86
+ await this._initGATTConnection()
87
+ await this._initGATTNotifications()
88
+ await waitForVariable(this,"numberOfTemps")
89
+ await waitForVariable(this,"numberOfCells")
90
+
91
+ for (let i=0; i<this.numberOfTemps; i++){
92
+ this.addMetadatum(`temp${i}`, 'K', `Temperature${i+1} reading`,
93
+ (buffer)=>{
94
+ return buffer.readUInt16BE(82+(i*2))/10
95
+ })
96
+ .default=`electrical.batteries.{batteryID}.Temperature${i+1}`
97
+ }
98
+
99
+ for (let i=0; i<this.numberOfCells; i++){
100
+ this.addMetadatum(`cell${i}Voltage`, 'V', `Cell ${i+1} voltage`,
101
+ (buffer)=>{return buffer.readUInt16BE((16+(i*2)))/1000} )
102
+ .default=`electrical.batteries.{batteryID}.cell${i+1}.voltage`
103
+ }
104
+ }
105
+
106
+ hasGATT(){
107
+ return true
108
+ }
109
+
110
+ async _initGATTNotifications(){
111
+ await this.rxChar.startNotifications()
112
+
113
+ this.rxChar.on("valuechanged", (buffer)=>{
114
+ if (buffer[0]==0xA1){
115
+ (["current", "voltage", "remainingCapacity", "capacity", "SOC" ]).forEach((tag) =>
116
+ this.emitData( tag, buffer )
117
+ )
118
+ } else
119
+ if (buffer[0]==0xA2){
120
+
121
+ if (!this.numberOfCells){
122
+ this.numberOfCells=buffer[15]
123
+ }
124
+ if (!this.numberOfTemps){
125
+ this.numberOfTemps=buffer[81]
126
+ }
127
+ for (let i=0;i<this.numberOfCells;i++){
128
+ this.emitData(`cell${i+1}Voltage`, buffer)
129
+ }
130
+ for (let i=0;i<this.numberOfTemps;i++){
131
+ this.emitData(`temp${i+1}`, buffer)
132
+
133
+ }
134
+ }
135
+ })
136
+ }
137
+ async _initGATTConnection() {
138
+ await this.deviceConnect()
139
+ const gattServer = await this.device.gatt()
140
+ const txRxService= await gattServer.getPrimaryService(this.constructor.TX_RX_SERVICE)
141
+ this.rxChar = await txRxService.getCharacteristic(this.constructor.NOTIFY_CHAR_UUID)
142
+ return this
143
+ }
144
+
145
+ async init(){
146
+
147
+ return await super.init()
148
+
149
+ }
150
+
151
+ async stopListening(){
152
+ super.stopListening()
153
+ if (this.rxChar)
154
+ this.rxChar.stopNotifications()
155
+ if (this.device)
156
+ await this.device.disconnect()
157
+ }
158
+
159
+ }
160
+ module.exports = EcoWorthy;
@@ -0,0 +1,269 @@
1
+ const BTSensor = require("../BTSensor");
2
+ const EventEmitter = require('node:events');
3
+
4
+
5
+ const testData=[
6
+ ['5e 43',
7
+ '34',
8
+ '33 33 30 30 30 30 44 32 46 37 46 46 46 46 34 38',
9
+ '45 38 30 31 30 30 30 45 30 30 34 41',
10
+ '30 30 39 30 30 42 30 30 38 30 30 37 42 34 45 44',
11
+ '30 43 46 30 30 43 46 32 30 43 46 35 30 43 30 30',
12
+ '30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30',
13
+ '30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30',
14
+ '30 30 30 30 30 30 30 30 30 30 30 30',
15
+ '30 30 30 43 31 31 00 00 00 00 00 00 00 00'],
16
+ ["5e 35 34 33 37 30 30 30 30 30 30 30 30 30 30 30 30",
17
+ "34 38 45 38 30 31 30 30 31 30 30 30 36 33 30 30",
18
+ "43 30 30 42 30 30 38 30 38 37 46 34",
19
+ "33 41 30 45 30 35 30 45 30 33 30 45 30 46 30 44",
20
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
21
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
22
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
23
+ "30 35 37 44 00 00 00 00 00 00 00 00"
24
+ ],
25
+ //bad data (!=0x21)
26
+ ["5e 35 34 33 37 30 30 30 30 30 30 30 30 30 30 30 30",
27
+ "34 38 45 38 30 21 30 30 31 30 30 30 36 33 30 30",
28
+ "43 30 30 42 30 30 38 30 38 37 46 34",
29
+ "33 41 30 45 30 35 30 45 30 33 30 45 30 46 30 44",
30
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
31
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
32
+ "30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30",
33
+ "30 35 37 44 00 00 00 00 00 00 00 00"
34
+ ]
35
+ ]
36
+ class EctiveDataManager extends EventEmitter{
37
+ static expectedLength = 120
38
+ static ImageFile = "TopbandBattery.webp"
39
+ buffer = Buffer.from([])
40
+
41
+ delimitedAt(data){
42
+ const delimiters = [0x5e,0xaf]
43
+ for (const d of delimiters){
44
+ if (data.includes(d)) return data.indexOf(d)
45
+ }
46
+ return -1
47
+ }
48
+
49
+ add(buff) {
50
+
51
+ const delimiterIndex = this.delimitedAt(buff)
52
+ if (delimiterIndex == 0)
53
+ this.buffer= buff.subarray(1)
54
+ else {
55
+ if (delimiterIndex==-1)
56
+ this.buffer=Buffer.concat([this.buffer, buff])
57
+ else {
58
+ this.buffer=Buffer.concat([this.buffer,buff.subarray(0, delimiterIndex)])
59
+ }
60
+ }
61
+ if ( this.buffer.length==this.constructor.expectedLength) {
62
+ const _data = new EctiveData (this.buffer)
63
+ if (_data.error) {
64
+ this.buffer=Buffer.from([])
65
+ throw new Error (`malformed packet: ${JSON.parse(JSON.stringify(_data)).ascii}`)
66
+ }
67
+ else {
68
+ this.emit("valuechanged", _data.hex )
69
+ this.buffer=Buffer.from([])
70
+ }
71
+ } else {
72
+ if (delimiterIndex>0) {
73
+ this.buffer=buff.subarray(delimiterIndex+1)
74
+ }
75
+ }
76
+
77
+
78
+ }
79
+ }
80
+
81
+ class EctiveData{
82
+ constructor(data){
83
+ this.ascii = data.toString("utf8").slice(0,data.indexOf(0))
84
+ this.hex = Buffer.from(this.ascii,"hex")
85
+ this.error = !this.verify()
86
+ }
87
+
88
+ verify(){
89
+ return (this.hex.length==this.ascii.toString("utf8").length/2)
90
+ }
91
+ }
92
+ class EctiveBMS extends BTSensor {
93
+ static Domain = BTSensor.SensorDomains.electrical
94
+
95
+ static RX_SERVICE = "0000ffe0-0000-1000-8000-00805f9b34fb"
96
+ static NOTIFY_CHAR_UUID = "0000ffe4-0000-1000-8000-00805f9b34fb"
97
+ static test(data, numCells=4){
98
+ const obj = new EctiveBMS()
99
+ obj.currentProperties={Name:"Topbrand BMS", Address:"<mac>"}
100
+ obj.numberOfCells=numCells
101
+ obj.initSchema()
102
+ obj.debug=(m)=>{console.log(m)}
103
+ for (const [tag,path] of Object.entries(obj._schema.properties.paths.properties)) {
104
+ obj.on(tag, val=>{console.log(`${tag} => ${val} `)})
105
+ }
106
+ obj._dataManager.on("valuechanged", (b)=>{
107
+ obj.emitValuesFrom(b)
108
+ })
109
+ data.forEach(d => {
110
+ const _d=d.replaceAll(" ","").toLowerCase()
111
+ const b=Buffer.from(_d,"hex")
112
+ try {
113
+ obj._dataManager.add(b)
114
+ } catch (e){
115
+ obj.debug(`(${obj.getName()}): ${e.message}`)
116
+ }
117
+ });
118
+ }
119
+ static testStream(filename, numCells=4){
120
+ const obj = new EctiveBMS()
121
+ obj.currentProperties={Name:"Topbrand BMS", Address:"<mac>"}
122
+ obj.numberOfCells=numCells
123
+ obj.initSchema()
124
+ obj.debug=(m)=>{console.log(m)}
125
+ for (const [tag,path] of Object.entries(obj._schema.properties.paths.properties)) {
126
+ obj.on(tag, val=>{console.log(`${tag} => ${val} `)})
127
+ }
128
+ obj._dataManager.on("valuechanged", (b)=>{
129
+ obj.emitValuesFrom(b)
130
+ })
131
+ const lineReader = require('readline').createInterface({ input: require('fs').createReadStream(filename)});
132
+ // Each line in input.txt will be successively available here as `line`.
133
+
134
+ (async ()=>{for await (const line of lineReader) {
135
+ const _d = line.slice(0,50).replaceAll(" ","").toLowerCase()
136
+ const b=Buffer.from(_d,"hex")
137
+ try {
138
+ obj._dataManager.add(b)
139
+ } catch (e){
140
+ obj.debug(`(${obj.getName()}): ${e.message}`)
141
+ }
142
+ }})()
143
+ }
144
+
145
+ static DisplayName(){
146
+ return "EctiveBMS: Topbrand, Skanbatt and other LiFePo4 batteries"
147
+ }
148
+ static identify(device){
149
+ return null
150
+ }
151
+
152
+ _dataManager = new EctiveDataManager()
153
+ initSchema(){
154
+ super.initSchema()
155
+ this.addDefaultParam("batteryID")
156
+ this.addParameter(
157
+ "numberOfCells",
158
+ {
159
+ title:'number of cells in battery',
160
+ type: 'integer',
161
+ default: 4,
162
+ isRequired: true
163
+ }
164
+ )
165
+
166
+ this.addDefaultPath('voltage','electrical.batteries.voltage')
167
+ .read=
168
+ (buffer)=>{return buffer.readUInt32LE(0) / 1000}
169
+
170
+ this.addDefaultPath('current','electrical.batteries.current')
171
+ .read=
172
+ (buffer)=>{return buffer.readInt32LE(4) / 1000}
173
+
174
+ this.addMetadatum(`cycleCharge`, 'C', `cycle charge`,
175
+ (buffer)=>{
176
+ return 3600*(buffer.readUInt32LE(8)/1000)
177
+ })
178
+ .default='electrical.batteries.{batteryID}.cycleCharge'
179
+
180
+
181
+ this.addMetadatum('cycleCount', '', 'number of cycles',
182
+ (buffer)=>{
183
+ return buffer.readUInt16LE(12)
184
+ })
185
+ .default='electrical.batteries.{batteryID}.cycles'
186
+
187
+ this.addDefaultPath('SOC','electrical.batteries.capacity.stateOfCharge')
188
+ .read=(buffer)=>{return buffer.readUInt16LE(14)/100}
189
+
190
+ this.addMetadatum('temp', 'K', 'Temperature reading',
191
+ (buffer)=>{
192
+ return buffer.readUInt16LE(16)/10
193
+ })
194
+ .default='electrical.batteries.{batteryID}.temperature'
195
+
196
+
197
+ this.addMetadatum('flags', '', 'Problem flags',
198
+ (buffer)=>{
199
+ return buffer.readUInt16LE(18).toString('2').padStart(16,'0')
200
+ })
201
+ .default='electrical.batteries.{batteryID}.problemFlags'
202
+
203
+
204
+ for (let i=0; i<this?.numberOfCells??4; i++){
205
+ this.addMetadatum(`cell${i+1}Voltage`, 'V', `Cell ${i+1} voltage`,
206
+ (buffer)=>{return buffer.readUInt16LE((22+(i*2)))/1000} )
207
+ .default=`electrical.batteries.{batteryID}.cell${i+1}.voltage`
208
+ }
209
+ }
210
+
211
+ hasGATT(){
212
+ return true
213
+ }
214
+
215
+
216
+ usingGATT(){
217
+ return true
218
+ }
219
+
220
+ emitGATT(){
221
+ //do nothing
222
+ }
223
+
224
+ verifyData(d1,d2){
225
+ if (d1.length!==d2.toString("utf8").length/2) {
226
+ this._dataError=true
227
+ this.debug(`(${this.getName()}) malformed packet received: ${d2.toString()}`)
228
+ }
229
+ return !this._dataError
230
+ }
231
+
232
+
233
+ async initGATTNotifications(){
234
+ await this.rxChar.startNotifications()
235
+ this.debug(`(${this.getName()}) Notifications started`)
236
+
237
+ this.rxChar.on("valuechanged", (buffer)=>{
238
+ try {
239
+ this._dataManager.add(buffer)
240
+ } catch (e){
241
+ this.debug(`(${this.getName()}): ${e.message}`)
242
+ }
243
+ })
244
+ this._dataManager.on("valuechanged", (b)=>{
245
+ this.emitValuesFrom(b)
246
+ } )
247
+ }
248
+
249
+ async initGATTConnection() {
250
+
251
+ await this.deviceConnect()
252
+ const gattServer = await this.device.gatt()
253
+ const rxService= await gattServer.getPrimaryService(this.constructor.RX_SERVICE)
254
+ this.rxChar = await rxService.getCharacteristic(this.constructor.NOTIFY_CHAR_UUID)
255
+
256
+ }
257
+
258
+
259
+ async stopListening(){
260
+ super.stopListening()
261
+ if (this.rxChar)
262
+ this.rxChar.stopNotifications()
263
+ if (this.device)
264
+ await this.device.disconnect()
265
+ }
266
+
267
+ }
268
+
269
+ module.exports = EctiveBMS;
@@ -0,0 +1,68 @@
1
+
2
+ const BTSensor = require("../BTSensor");
3
+ const Eddystone = require("./Beacon/Eddystone")
4
+ const iBeacon = require("./Beacon/iBeacon")
5
+
6
+ class FeasyComBeacon extends BTSensor {
7
+
8
+ static async identify (device){
9
+ return null
10
+ }
11
+ static IsRoaming = true;
12
+ static Domain = BTSensor.SensorDomains.beacons
13
+ static ImageFile = "BP108B.webp"
14
+
15
+ initSchema(){
16
+ super.initSchema()
17
+ this.addParameter("beaconType",
18
+ {
19
+ title:"type of beacon",
20
+ enum: ["Eddystone", "iBeacon"],
21
+ isRequired: true,
22
+ default:"EddyStone"
23
+ }
24
+ )
25
+ this.beacon.initSchema()
26
+ }
27
+
28
+ async init(){
29
+ const bt = this?.beaconType??"Eddystone"
30
+ if (bt=="Eddystone") {
31
+
32
+ this.beacon=new Eddystone(this)
33
+
34
+ }
35
+ else
36
+ if (bt=="iBeacon")
37
+ this.beacon=new iBeacon(this)
38
+ else
39
+ throw new Error (`(${this.getName()}) Unknown Beacon Type: ${bt}`)
40
+
41
+ await super.init()
42
+
43
+ }
44
+
45
+ initListen(){
46
+ super.initListen()
47
+ this.beacon.initListen()
48
+
49
+ }
50
+ elapsedTimeSinceLastContact(){
51
+
52
+ return this.beacon.elapsedTimeSinceLastContact(super.elapsedTimeSinceLastContact())
53
+ }
54
+
55
+ propertiesChanged(props){
56
+ super.propertiesChanged(props);
57
+ this.beacon.propertiesChanged(props)
58
+ }
59
+
60
+ getManufacturer(){
61
+ return "FeasyCom Inc.";
62
+ }
63
+ getTextDescription(){
64
+ return `${this.getName()} iBeacon/Eddystone device`
65
+ }
66
+ }
67
+
68
+ module.exports = FeasyComBeacon;
@@ -261,6 +261,8 @@ class GobiusCTankMeter extends BTSensor{
261
261
  else
262
262
  return null
263
263
  }
264
+ static ImageFile = "GobiusCTankMeter.png"
265
+
264
266
  hasGATT(){
265
267
  return true
266
268
  }
@@ -274,6 +276,7 @@ class GobiusCTankMeter extends BTSensor{
274
276
  )
275
277
 
276
278
  }
279
+
277
280
  initSchema(){
278
281
  super.initSchema()
279
282
  this.getGATTParams()["useGATT"].default=true
@@ -351,9 +354,7 @@ class GobiusCTankMeter extends BTSensor{
351
354
  })
352
355
  }))
353
356
  }
354
- getDescription(){
355
- return '<a href="https://gobiusc.com/"><img src="../bt-sensors-plugin-sk/images/Gobius_C.png" alt="Gobius C Tank Measure" "></a>'
356
- }
357
+
357
358
  async stopListening(){
358
359
  super.stopListening()
359
360
  if (this.characteristic && await this.characteristic.isNotifying()) {
@@ -4,6 +4,8 @@ class GoveeH5074 extends GoveeSensor {
4
4
  static getIDRegex() {
5
5
  return /^Govee_H5074_[a-f,A-F,0-9]{4}$/
6
6
  }
7
+ static ImageFile = "GoveeH5074.jpg"
8
+
7
9
  initSchema(){
8
10
  super.initSchema()
9
11
  this.addDefaultParam("zone")
@@ -15,6 +15,8 @@ class GoveeH5075 extends GoveeSensor {
15
15
  sensor.emitValuesFrom(Buffer.from([0x00,0x03,0xbb,0x94,0x64,0x00]))
16
16
 
17
17
  }
18
+ static ImageFile = "GoveeH5075.webp"
19
+
18
20
  initSchema(){
19
21
  super.initSchema()
20
22
  this.addDefaultParam("zone")
@@ -17,6 +17,7 @@ class GoveeH510x extends GoveeSensor{
17
17
 
18
18
  }
19
19
  static DATA_ID = 0x0001
20
+ static ImageFile = "GoveeH510x.jpg"
20
21
  initSchema(){
21
22
  super.initSchema()
22
23
  this.addDefaultParam("zone")
@@ -12,6 +12,7 @@ class Inkbird extends BTSensor{
12
12
  return null
13
13
 
14
14
  }
15
+ static ImageFile = "InkbirdTH3.jpg"
15
16
 
16
17
  initSchema() {
17
18
  super.initSchema()
@@ -29,6 +29,7 @@ class JBDBMS extends BTSensor {
29
29
  static identify(device){
30
30
  return null
31
31
  }
32
+ static ImageFile = "JBDBMS.webp"
32
33
 
33
34
  jbdCommand(command) {
34
35
  return [0xDD, 0xA5, command, 0x00, 0xFF, 0xFF - (command - 1), 0x77]
@@ -21,19 +21,27 @@ class JunctekBMS extends BTSensor{
21
21
 
22
22
  return null
23
23
  }
24
+ static ImageFile = "Junctek.webp"
24
25
 
25
- chargeDirection = 1
26
+ chargeDirection = 0
26
27
 
27
28
  hasGATT(){
28
29
  return true
29
30
  }
30
31
 
31
-
32
-
33
32
  async initSchema(){
34
33
  super.initSchema()
35
34
  this.addDefaultParam("batteryID")
36
35
 
36
+ this.addParameter("capacityAh",
37
+ {
38
+ title:"battery capacity in Ah",
39
+ type:"integer",
40
+ isRequired: true,
41
+ default: 200
42
+ }
43
+ )
44
+
37
45
  this.addDefaultPath("voltage","electrical.batteries.voltage")
38
46
 
39
47
  this.addDefaultPath("current","electrical.batteries.current")
@@ -78,16 +86,15 @@ class JunctekBMS extends BTSensor{
78
86
  break
79
87
 
80
88
  case 0xC1:
81
- emitObject["current"]=()=>{return (v/100)*this.chargeDirection}
89
+ if (this.chargeDirection)
90
+ emitObject["current"]=()=>{return (v/100)*this.chargeDirection}
82
91
  break
83
92
 
84
93
  case 0xD1:
85
- this.debug(v)
86
94
  if (v==0)
87
95
  this.chargeDirection=-1
88
96
  else
89
97
  this.chargeDirection= 1
90
- this.debug(this.chargeDirection)
91
98
  break
92
99
 
93
100
  case 0xD2:
@@ -125,6 +132,7 @@ class JunctekBMS extends BTSensor{
125
132
  break
126
133
  case 0xB1:
127
134
  emitObject["capacityActual"]=v /10
135
+ emitObject["soc"]=(this.capacityAh/(emitObject["capacityActual"]))/100
128
136
  break
129
137
  }
130
138
  }
@@ -41,6 +41,7 @@ class KilovaultHLXPlus extends BTSensor{
41
41
  super(device, config, gattConfig)
42
42
  this.accumulated_buffer = Buffer.alloc(0)
43
43
  }
44
+ static ImageFile = "KilovaultHLXPlus.jpg"
44
45
 
45
46
  static async identify(device){
46
47
  const regex = /^\d\d\-(12|24|36)00HLX\+\d{4}/
@@ -3,6 +3,8 @@ const BTSensor = require("../BTSensor");
3
3
  class LancolVoltageMeter extends BTSensor{
4
4
 
5
5
  static Domain = BTSensor.SensorDomains.electrical
6
+ static ImageFile = "LancolVoltageMeter.webp"
7
+
6
8
  static async identify(device){
7
9
 
8
10
  const name = await this.getDeviceProp(device,"Name")
@@ -26,6 +26,7 @@ class MercurySmartcraft extends BTSensor{
26
26
  else
27
27
  return null
28
28
  }
29
+ static ImageFile = "MercurySmartcraft.jpg"
29
30
 
30
31
 
31
32