bt-sensors-plugin-sk 1.3.2 → 1.3.4-beta

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.
@@ -0,0 +1,385 @@
1
+ const BTSensor = require("../BTSensor");
2
+
3
+ /**
4
+ * HSC14F Battery Management System Sensor Class
5
+ *
6
+ * Manufacturer: BMC (HumsiENK branded)
7
+ * Protocol: Custom BLE protocol using AA prefix commands
8
+ *
9
+ * Discovered protocol details:
10
+ * - TX Handle: 0x000c (write commands to battery)
11
+ * - RX Handle: 0x000e (receive notifications from battery)
12
+ * - Command format: aa [CMD] 00 [CMD] 00
13
+ * - Multi-part responses (some commands send 2-3 notifications)
14
+ *
15
+ * Key Commands:
16
+ * - 0x00: Handshake
17
+ * - 0x21: Real-time battery data (voltage, current, SOC, temps) - PRIMARY
18
+ * - 0x22: Individual cell voltages
19
+ * - 0x23: Battery status/warnings
20
+ * - 0x20: Configuration data
21
+ * - 0x10: Manufacturer name
22
+ * - 0x11: Model number
23
+ */
24
+
25
+ class HumsienkBMS extends BTSensor {
26
+ static Domain = BTSensor.SensorDomains.electrical;
27
+
28
+ // Discovered actual UUIDs from device
29
+ static TX_RX_SERVICE = "00000001-0000-1000-8000-00805f9b34fb";
30
+ static WRITE_CHAR_UUID = "00000002-0000-1000-8000-00805f9b34fb";
31
+ static NOTIFY_CHAR_UUID = "00000003-0000-1000-8000-00805f9b34fb";
32
+
33
+ static identify(device) {
34
+ // HSC14F batteries advertise with this service UUID
35
+ // Further identification would require GATT connection to read manufacturer/model
36
+ return null;
37
+ }
38
+
39
+ static ImageFile = "JBDBMS.webp"; // Using similar BMS image for now
40
+ static Manufacturer = "BMC (HumsiENK)";
41
+ static Description = "HSC14F LiFePO4 Battery Management System";
42
+
43
+ /**
44
+ * Create HSC14F command
45
+ * Format: aa [CMD] 00 [CMD] 00
46
+ */
47
+ hsc14fCommand(command) {
48
+ return [0xaa, command, 0x00, command, 0x00];
49
+ }
50
+
51
+ /**
52
+ * Send command to battery
53
+ * HSC14F requires Write Request (0x12) not Write Command (0x52)
54
+ */
55
+ async sendCommand(command) {
56
+ this.debug(`Sending command 0x${command.toString(16)} to ${this.getName()}`);
57
+ return await this.txChar.writeValue(
58
+ Buffer.from(this.hsc14fCommand(command))
59
+ );
60
+ }
61
+
62
+ async initSchema() {
63
+ super.initSchema();
64
+ this.addDefaultParam("batteryID");
65
+
66
+ // Number of cells parameter - configurable (default 4 for LiFePO4)
67
+ if (this.numberOfCells === undefined) {
68
+ this.numberOfCells = 4;
69
+ }
70
+
71
+ this.addParameter("numberOfCells", {
72
+ title: "Number of cells",
73
+ description: "Number of cells in the battery (typically 4 for 12V LiFePO4)",
74
+ type: "number",
75
+ isRequired: true,
76
+ default: this.numberOfCells,
77
+ minimum: 1,
78
+ maximum: 16,
79
+ multipleOf: 1
80
+ });
81
+
82
+ // Voltage
83
+ this.addDefaultPath("voltage", "electrical.batteries.voltage").read = (
84
+ buffer
85
+ ) => {
86
+ // Bytes 3-4: voltage in mV, little-endian
87
+ return buffer.readUInt16LE(3) / 1000;
88
+ };
89
+
90
+ // Current - CORRECTED based on buffer analysis
91
+ this.addDefaultPath("current", "electrical.batteries.current").read = (
92
+ buffer
93
+ ) => {
94
+ // Bytes 7-8: current in milliamps, signed little-endian
95
+ // Negative = charging, Positive = discharging
96
+ return buffer.readInt16LE(7) / 1000;
97
+ };
98
+
99
+ // State of Charge
100
+ this.addDefaultPath(
101
+ "SOC",
102
+ "electrical.batteries.capacity.stateOfCharge"
103
+ ).read = (buffer) => {
104
+ // Byte 11: SOC percentage (0-100)
105
+ return buffer.readUInt8(11) / 100;
106
+ };
107
+
108
+ // Temperature 1 (ENV temperature) - CORRECTED byte position
109
+ this.addMetadatum("temp1", "K", "Battery Environment Temperature", (buffer) => {
110
+ // Byte 27: Environment temperature in °C
111
+ const tempC = buffer.readUInt8(27);
112
+ return 273.15 + tempC; // Convert to Kelvin
113
+ }).default = "electrical.batteries.{batteryID}.temperature";
114
+
115
+ // Temperature 2 (MOS temperature) - CORRECTED byte position
116
+ this.addMetadatum("temp2", "K", "Battery MOS Temperature", (buffer) => {
117
+ // Byte 28: MOS (MOSFET) temperature in °C
118
+ const tempC = buffer.readUInt8(28);
119
+ return 273.15 + tempC;
120
+ }).default = "electrical.batteries.{batteryID}.mosfetTemperature";
121
+
122
+ // Temperature 3 (Sensor) - CORRECTED byte position
123
+ this.addMetadatum("temp3", "K", "Battery Sensor Temperature", (buffer) => {
124
+ // Byte 29: Additional temperature sensor in °C
125
+ const tempC = buffer.readUInt8(29);
126
+ return 273.15 + tempC;
127
+ }).default = "electrical.batteries.{batteryID}.sensorTemperature";
128
+
129
+ // Manufacturer (from command 0x10)
130
+ this.addMetadatum(
131
+ "manufacturer",
132
+ "",
133
+ "Battery Manufacturer",
134
+ (buffer) => {
135
+ // Response: aa 10 03 42 4d 43 ...
136
+ // ASCII bytes starting at position 3
137
+ const len = buffer.readUInt8(2);
138
+ return buffer.toString("ascii", 3, 3 + len);
139
+ }
140
+ ).default = "electrical.batteries.{batteryID}.manufacturer";
141
+
142
+ // Model (from command 0x11)
143
+ this.addMetadatum("model", "", "Battery Model", (buffer) => {
144
+ // Response: aa 11 0a 42 4d 43 2d 30 34 53 30 30 31 ...
145
+ const len = buffer.readUInt8(2);
146
+ return buffer.toString("ascii", 3, 3 + len);
147
+ }).default = "electrical.batteries.{batteryID}.model";
148
+
149
+ // Cell voltages (from command 0x22)
150
+ // Number of cells is configurable via numberOfCells parameter
151
+ for (let i = 0; i < this.numberOfCells; i++) {
152
+ this.addMetadatum(
153
+ `cell${i}Voltage`,
154
+ "V",
155
+ `Cell ${i + 1} voltage`,
156
+ (buffer) => {
157
+ // Cell voltages: aa 22 30 6a 0d 58 0d 8f 0d 34 0d ...
158
+ // Starting at byte 3, each cell is 2 bytes little-endian in mV
159
+ return buffer.readUInt16LE(3 + i * 2) / 1000;
160
+ }
161
+ ).default = `electrical.batteries.{batteryID}.cell${i}.voltage`;
162
+ }
163
+ }
164
+
165
+ hasGATT() {
166
+ return true;
167
+ }
168
+
169
+ usingGATT() {
170
+ return true;
171
+ }
172
+
173
+ async initGATTNotifications() {
174
+ // Don't use notifications for polling mode
175
+ // The parent class initGATTInterval will handle periodic connections
176
+ }
177
+
178
+ async emitGATT() {
179
+ try {
180
+ await this.getAndEmitBatteryInfo();
181
+ } catch (e) {
182
+ this.debug(`Failed to emit battery info for ${this.getName()}: ${e}`);
183
+ throw e; // Re-throw so parent can handle reconnection
184
+ }
185
+
186
+ // Get cell voltages after a short delay
187
+ await new Promise(resolve => setTimeout(resolve, 2000));
188
+
189
+ try {
190
+ await this.getAndEmitCellVoltages();
191
+ } catch (e) {
192
+ this.debug(`Failed to emit cell voltages for ${this.getName()}: ${e}`);
193
+ throw e; // Re-throw so parent can handle reconnection
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Get buffer response from battery command
199
+ * HSC14F sends multi-part responses for some commands
200
+ */
201
+ async getBuffer(command) {
202
+ let result = Buffer.alloc(256);
203
+ let offset = 0;
204
+ let lastPacketTime = Date.now();
205
+
206
+ // Set up listener first
207
+ const responsePromise = new Promise((resolve, reject) => {
208
+ const timer = setTimeout(() => {
209
+ clearTimeout(timer);
210
+ if (completionTimer) clearTimeout(completionTimer);
211
+ this.rxChar.removeAllListeners("valuechanged");
212
+ reject(
213
+ new Error(
214
+ `Response timed out (+10s) from HSC14F device ${this.getName()}.`
215
+ )
216
+ );
217
+ }, 10000);
218
+
219
+ let completionTimer = null;
220
+
221
+ const valChanged = (buffer) => {
222
+ // HSC14F responses start with 0xaa followed by command byte
223
+ if (offset === 0 && (buffer[0] !== 0xaa || buffer[1] !== command)) {
224
+ this.debug(
225
+ `Invalid buffer from ${this.getName()}, expected command 0x${command.toString(
226
+ 16
227
+ )}, got 0x${buffer[0].toString(16)} 0x${buffer[1].toString(16)}`
228
+ );
229
+ return;
230
+ }
231
+
232
+ buffer.copy(result, offset);
233
+ offset += buffer.length;
234
+ lastPacketTime = Date.now();
235
+
236
+ // Clear any existing completion timer
237
+ if (completionTimer) clearTimeout(completionTimer);
238
+
239
+ // Wait 200ms after last packet to consider response complete
240
+ // This allows multi-packet responses to assemble properly
241
+ completionTimer = setTimeout(() => {
242
+ result = Uint8Array.prototype.slice.call(result, 0, offset);
243
+ this.rxChar.removeAllListeners("valuechanged");
244
+ clearTimeout(timer);
245
+ resolve(result);
246
+ }, 200);
247
+ };
248
+
249
+ // Set up listener BEFORE sending command
250
+ this.rxChar.on("valuechanged", valChanged);
251
+ });
252
+
253
+ // Small delay to ensure listener is attached
254
+ await new Promise(r => setTimeout(r, 100));
255
+
256
+ // Send the command
257
+ try {
258
+ await this.sendCommand(command);
259
+ } catch (err) {
260
+ this.rxChar.removeAllListeners("valuechanged");
261
+ throw err;
262
+ }
263
+
264
+ // Wait for response
265
+ return responsePromise;
266
+ }
267
+
268
+ async initGATTConnection(isReconnecting = false) {
269
+ await super.initGATTConnection(isReconnecting);
270
+
271
+ // Set up GATT characteristics
272
+ const gattServer = await this.device.gatt();
273
+ const txRxService = await gattServer.getPrimaryService(
274
+ this.constructor.TX_RX_SERVICE
275
+ );
276
+ this.rxChar = await txRxService.getCharacteristic(
277
+ this.constructor.NOTIFY_CHAR_UUID
278
+ );
279
+ this.txChar = await txRxService.getCharacteristic(
280
+ this.constructor.WRITE_CHAR_UUID
281
+ );
282
+ await this.rxChar.startNotifications();
283
+
284
+ return this;
285
+ }
286
+
287
+ /**
288
+ * Get and emit main battery data (voltage, current, SOC, temp)
289
+ * Uses command 0x21
290
+ */
291
+ async getAndEmitBatteryInfo() {
292
+ return this.getBuffer(0x21).then((buffer) => {
293
+ // Debug logging to verify buffer received
294
+ this.debug(`Command 0x21 response: ${buffer.length} bytes, hex: ${buffer.slice(0, 30).toString('hex')}`);
295
+
296
+ ["voltage", "current", "SOC", "temp1", "temp2", "temp3"].forEach((tag) => {
297
+ this.emitData(tag, buffer);
298
+ });
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Get and emit individual cell voltages
304
+ * Uses command 0x22
305
+ */
306
+ async getAndEmitCellVoltages() {
307
+ return this.getBuffer(0x22).then((buffer) => {
308
+ // Debug logging to verify buffer received
309
+ this.debug(`Command 0x22 response: ${buffer.length} bytes, hex: ${buffer.slice(0, 30).toString('hex')}`);
310
+
311
+ for (let i = 0; i < this.numberOfCells; i++) {
312
+ this.emitData(`cell${i}Voltage`, buffer);
313
+ }
314
+ });
315
+ }
316
+
317
+ async initGATTInterval() {
318
+ // Get static info once (manufacturer, model) at first connection
319
+ try {
320
+ const mfgBuffer = await this.getBuffer(0x10);
321
+ this.emitData("manufacturer", mfgBuffer);
322
+
323
+ const modelBuffer = await this.getBuffer(0x11);
324
+ this.emitData("model", modelBuffer);
325
+ } catch (e) {
326
+ this.debug(`Failed to get device info: ${e.message}`);
327
+ }
328
+
329
+ // Get first poll data before disconnecting
330
+ try {
331
+ await this.emitGATT();
332
+ } catch (error) {
333
+ this.debug(`Initial poll failed: ${error.message}`);
334
+ }
335
+
336
+ // Disconnect after initial data collection
337
+ await this.deactivateGATT().catch((e) => {
338
+ this.debug(`Error deactivating GATT Connection: ${e.message}`);
339
+ });
340
+ this.setState("WAITING");
341
+
342
+ // Set up polling interval - reconnect, poll, disconnect
343
+ this.intervalID = setInterval(async () => {
344
+ try {
345
+ this.setState("CONNECTING");
346
+ await this.initGATTConnection(true);
347
+ await this.emitGATT();
348
+ } catch (error) {
349
+ // Check if device has been removed from BlueZ cache
350
+ if (error.message && error.message.includes("interface not found in proxy object")) {
351
+ this.debug(`Device removed from BlueZ cache. Clearing stale connection state.`);
352
+ this.setError(`Device out of range or removed from Bluetooth cache. Waiting for rediscovery...`);
353
+
354
+ // Clear the interval to stop futile reconnection attempts
355
+ if (this.intervalID) {
356
+ clearInterval(this.intervalID);
357
+ this.intervalID = null;
358
+ }
359
+
360
+ // Set state to indicate waiting for rediscovery
361
+ this.setState("OUT_OF_RANGE");
362
+ return;
363
+ }
364
+
365
+ this.debug(error);
366
+ this.setError(`Unable to emit values for device: ${error.message}`);
367
+ } finally {
368
+ await this.deactivateGATT().catch((e) => {
369
+ // Suppress errors when device is already removed from BlueZ
370
+ if (!e.message || !e.message.includes("interface not found")) {
371
+ this.debug(`Error deactivating GATT Connection: ${e.message}`);
372
+ }
373
+ });
374
+ this.setState("WAITING");
375
+ }
376
+ }, this.pollFreq * 1000);
377
+ }
378
+
379
+ async deactivateGATT() {
380
+ await this.stopGATTNotifications(this.rxChar);
381
+ await super.deactivateGATT();
382
+ }
383
+ }
384
+
385
+ module.exports = HumsienkBMS;
@@ -197,6 +197,7 @@ class JBDBMS extends BTSensor {
197
197
  }
198
198
 
199
199
  async initGATTConnection() {
200
+ this.setConnected(await this.device.isConnected())
200
201
  return this
201
202
  }
202
203
 
@@ -257,7 +257,7 @@ class JikongBMS extends BTSensor {
257
257
 
258
258
  this.addMetadatum("cycleCapacity", "number", "Cycle capacity", (buffer) => {
259
259
  return buffer.readUInt32LE(154 + this.offset * 2) / 1000;
260
- }).default = "electrical.batteries.{batteryID}.discharging";
260
+ }).default = "electrical.batteries.{batteryID}.cycleCapacity";
261
261
 
262
262
  this.addMetadatum(
263
263
  "balanceAction",
@@ -3,7 +3,7 @@ ported from https://github.com/cyrils/renogy-bt
3
3
  */
4
4
  const BTSensor = require("../../BTSensor.js");
5
5
  const VC = require('./RenogyConstants.js');
6
- const crc16Modbus = require('./CRC.js')
6
+ const crc16Modbus = require('./CRC.js');
7
7
  class RenogySensor extends BTSensor{
8
8
  static Domain=BTSensor.SensorDomains.electrical
9
9
  static ALIAS_PREFIX = 'BT-TH'
@@ -34,20 +34,27 @@ class RenogySensor extends BTSensor{
34
34
  b.writeUInt16BE(words,4)
35
35
  b.writeUInt16BE(crc16Modbus(b.subarray(0,6)),6)
36
36
 
37
- await writeCharacteristic.writeValueWithResponse(b, 0)
37
+ await writeCharacteristic.writeValueWithoutResponse(b, 0)
38
38
 
39
39
  }
40
40
  static identify(device){
41
41
  return null
42
42
  }
43
-
43
+ pollFreq=30
44
44
  async initSchema(){
45
45
  await super.initSchema()
46
-
46
+ this.getGATTParams().pollFreq.default=this.pollFreq
47
47
  this.addParameter(
48
48
  "deviceID",
49
49
  {
50
- title: 'ID of device'
50
+ title: 'ID of device',
51
+ description: 'only modify if device is in hub mode or daisy chained',
52
+ default:255,
53
+ type: 'number',
54
+ minimum: 0,
55
+ maximum: 255,
56
+ multipleOf:1,
57
+ isRequired: true
51
58
  }
52
59
  )
53
60
  }
@@ -73,15 +80,17 @@ class RenogySensor extends BTSensor{
73
80
  }
74
81
 
75
82
  async sendReadFunctionRequest(writeReq, words){
76
- this.constructor.sendReadFunctionRequest(
83
+ await this.constructor.sendReadFunctionRequest(
77
84
  this.writeChar, this.getDeviceID(), writeReq, words)
78
85
  }
79
86
 
80
87
  initGATTInterval(){
88
+ this.emitGATT()
81
89
  this.intervalID = setInterval(()=>{
82
90
  this.emitGATT()
83
91
  }, 1000*(this?.pollFreq??60) )
84
92
  }
93
+
85
94
  emitGATT(){
86
95
  this.getAllEmitterFunctions().forEach(async (emitter)=>
87
96
  await emitter()
@@ -12,9 +12,21 @@ class RenogyBattery extends RenogySensor {
12
12
  this.getAndEmitCellTemperatures.bind(this),
13
13
  this.getAndEmitCellVoltages.bind(this)]
14
14
  }
15
+ numberOfCells=4
15
16
  initSchema(){
16
-
17
- this.addMetadatum('numberOfCells','', 'number of cells')
17
+ this.addDefaultParam("batteryID").default="house"
18
+ this.addParameter(
19
+ "numberOfCells",
20
+ {
21
+ title:"Number of cells",
22
+ type: "number",
23
+ isRequired: true,
24
+ default: this.numberOfCells,
25
+ minimum: 1,
26
+ maximum: 16,
27
+ multipleOf:1
28
+ }
29
+ )
18
30
  this.addDefaultPath('current','electrical.batteries.current')
19
31
  .read=(buffer)=>{return buffer.readInt16BE(3)/100}
20
32
 
@@ -41,31 +53,33 @@ class RenogyBattery extends RenogySensor {
41
53
  async initGATTConnection() {
42
54
  await super.initGATTConnection()
43
55
  this.numberOfCells = await this.retrieveNumberOfCells()
44
- this.deviceID = await this.retrieveDeviceID()
45
- this.emit('numberOfCells', this.numberOfCells)
46
56
  }
47
57
 
48
- retrieveNumberOfCells(){
49
-
58
+ retrieveModelID(){
50
59
  return new Promise( async ( resolve, reject )=>{
51
- await this.sendReadFunctionRequest(0x1388,0x11)
52
60
 
53
- const valChanged = async (buffer) => {
54
- resolve(buffer.readUInt16(3))
55
- }
56
- this.readChar.once('valuechanged', valChanged )
61
+ await this.sendReadFunctionRequest(0x1402,0x08)
62
+
63
+ this.readChar.once('valuechanged', async (buffer) => {
64
+ if (buffer[2]!=0x10)
65
+ reject("Unknown error retrieving model ID") //???
66
+ const model = buffer.subarray(3,17).toString().trim()
67
+ resolve(model)
57
68
  })
69
+ })
58
70
  }
59
- retrieveDeviceID(){
71
+ retrieveNumberOfCells(){
72
+
60
73
  return new Promise( async ( resolve, reject )=>{
61
- this.sendFunctionRequest(0x14, 0x67)
74
+ await this.sendReadFunctionRequest(0x1388,0x11)
62
75
 
63
76
  const valChanged = async (buffer) => {
64
- resolve((buffer.readUInt8(4)))
77
+ resolve(buffer.readUInt16(3))
65
78
  }
66
79
  this.readChar.once('valuechanged', valChanged )
67
80
  })
68
81
  }
82
+
69
83
 
70
84
  getAndEmitBatteryInfo(){
71
85
  return new Promise( async ( resolve, reject )=>{
@@ -54,10 +54,24 @@ class RenogyInverter extends RenogySensor {
54
54
  })
55
55
  }
56
56
 
57
+ retrieveModelID(){
58
+ return new Promise( async ( resolve, reject )=>{
59
+
60
+ await this.sendReadFunctionRequest(0x10d7,0x08)
61
+
62
+ this.readChar.once('valuechanged', async (buffer) => {
63
+ if (buffer[2]!=0x10)
64
+ reject("Unknown error retrieving model ID") //???
65
+ const model = buffer.subarray(3,17).toString().trim()
66
+ resolve(model)
67
+ })
68
+ })
69
+ }
70
+
57
71
  getAndEmitInverterStats(){
58
72
  return new Promise( async ( resolve, reject )=>{
59
73
 
60
- await this.sendReadFunctionRequest(0xfa0, 0x8)
74
+ await this.sendReadFunctionRequest(0xfa0, 0xA)
61
75
 
62
76
  this.readChar.once('valuechanged', (buffer) => {
63
77
  ["ueiVoltage","ueiCurrent", "voltage", "loadCurrent", "frequency","temperature"].forEach((tag)=>
@@ -71,7 +85,7 @@ class RenogyInverter extends RenogySensor {
71
85
  getAndEmitSolarCharging(){
72
86
  return new Promise( async ( resolve, reject )=>{
73
87
 
74
- await this.sendReadFunctionRequest(0x10e9, 0x5)
88
+ await this.sendReadFunctionRequest(0x10e9, 0x7)
75
89
 
76
90
  this.readChar.once('valuechanged', (buffer) => {
77
91
  ["solarVoltage","solarCurrent", "solarPower", "solarChargingStatus", "solarChargingPower"].forEach((tag)=>
@@ -85,7 +99,7 @@ class RenogyInverter extends RenogySensor {
85
99
  getAndEmitInverterLoad(){
86
100
  return new Promise( async ( resolve, reject )=>{
87
101
 
88
- await this.sendReadFunctionRequest(0x113a, 0x2)
102
+ await this.sendReadFunctionRequest(0x113a, 0x6)
89
103
 
90
104
  this.readChar.once('valuechanged', (buffer) => {
91
105
  ["loadPower", "chargingCurrent"].forEach((tag)=>
@@ -34,6 +34,10 @@ class RenogyRoverClient extends RenogySensor {
34
34
  initSchema(){
35
35
  //Buffer(73) [1, 3, 68, 32, 32, 82, 78, 71, 45, 67, 84, 82, 76, 45, 87, 78, 68, 51, 48, 7, 140, 0, 132, 0, 126, 0, 120, 0, 111, 0, 106, 100, 50, 0, 5, 0, 120, 0, 120, 0, 28, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 5, 0, 5, 2, 148, 0, 5, 206, 143, 34, 228, buffer: ArrayBuffer(8192), byteLength: 73, byteOffset: 6144, length: 73, Symbol(Symbol.toStringTag): 'Uint8Array']
36
36
  super.initSchema()
37
+
38
+ this.addDefaultParam("id")
39
+ .default="solar"
40
+
37
41
  this.addMetadatum('batteryType', '', "battery type")
38
42
  .default="electrical.chargers.{id}.battery.type"
39
43
  this.addMetadatum('batteryPercentage', 'ratio', "battery percentage",
@@ -118,22 +122,12 @@ class RenogyRoverClient extends RenogySensor {
118
122
 
119
123
  }
120
124
 
121
- retrieveDeviceID(){
122
- return new Promise( async ( resolve, reject )=>{
123
- this.sendReadFunctionRequest(0x1A, 0x1)
124
-
125
- const valChanged = async (buffer) => {
126
- resolve((buffer.readUInt8(4)))
127
- }
128
- this.readChar.once('valuechanged', valChanged )
129
- })
130
- }
131
-
125
+
132
126
  retrieveBatteryType(){
133
127
  return new Promise( async ( resolve, reject )=>{
134
128
  //Buffer(7) [255, 3, 2, 0, 1, 80, 80, buffer: ArrayBuffer(8192), byteLength: 7, byteOffset: 864, length: 7, Symbol(Symbol.toStringTag): 'Uint8Array']
135
129
 
136
- this.sendReadFunctionRequest(0xe004, 0x01)
130
+ await this.sendReadFunctionRequest(0xe004, 0x01)
137
131
 
138
132
  const valChanged = async (buffer) => {
139
133
  resolve(RC.BATTERY_TYPE[(buffer.readUInt8(4))])
@@ -157,13 +151,10 @@ class RenogyRoverClient extends RenogySensor {
157
151
  }
158
152
  async initGATTConnection() {
159
153
  await super.initGATTConnection()
160
- if (!this.deviceID)
161
- this.deviceID = await this.retrieveDeviceID()
162
- this.modelID=await this.retrieveModelID()
163
154
 
155
+ this.modelID=await this.retrieveModelID()
164
156
  this.batteryType = await this.retrieveBatteryType()
165
- this.emit('batteryType', this.batteryType)
166
-
157
+ this.emit('batteryType', this.batteryType)
167
158
 
168
159
  }
169
160
 
@@ -175,7 +166,7 @@ class RenogyRoverClient extends RenogySensor {
175
166
  async getAndEmitChargeInfo(){
176
167
  return new Promise( async ( resolve, reject )=>{
177
168
  try {
178
- this.sendReadFunctionRequest(0x100, 0x22)
169
+ await this.sendReadFunctionRequest(0x100, 0x22)
179
170
 
180
171
  this.readChar.once('valuechanged', buffer => {
181
172
  this.emitValuesFrom(buffer)
@@ -71,11 +71,12 @@ class ShenzhenLiONBMS extends BTSensor{
71
71
  "numberOfCells",
72
72
  {
73
73
  title:"Number of cells",
74
- type: "integer",
74
+ type: "number",
75
75
  isRequired: true,
76
76
  default: 4,
77
77
  minimum: 1,
78
- maximum: 16
78
+ maximum: 16,
79
+ multipleOf:1
79
80
  }
80
81
  )
81
82
 
@@ -26,7 +26,7 @@ class VictronBatteryMonitor extends VictronSensor{
26
26
  d.debug.bind(d)
27
27
  d.initSchema()
28
28
  Object.keys(d.getPaths()).forEach((tag)=>{
29
- d.on(tag,(v)=>console.log(`${tag}=${v}`))
29
+ d.on(tag,(v)=>console.log(`${tag}=${JSON.stringify(v)}`))
30
30
  })
31
31
  if (key)
32
32
  b = d.decrypt(b)
@@ -62,7 +62,6 @@ class VictronBatteryMonitor extends VictronSensor{
62
62
  const alarmMD = this.addMetadatum('alarm','', 'alarm',
63
63
  (buff,offset=0)=>{return buff.readInt16LE(offset)})
64
64
  alarmMD.default='electrical.batteries.{batteryID}.alarm'
65
- alarmMD.notify=true
66
65
 
67
66
  this.addMetadatum( 'consumed','Ah', 'amp-hours consumed',
68
67
  (buff,offset=0)=>{return buff.readInt32LE(offset)/10},
@@ -78,8 +77,6 @@ class VictronBatteryMonitor extends VictronSensor{
78
77
  .read=(buff,offset=0)=>{return this.NaNif(buff.readUInt16LE(offset),0xFFFF)*60}
79
78
  this.getPath("ttg").gatt='65970ffe-4bda-4c1e-af4b-551c4cf74769';
80
79
 
81
- this.auxMode=VC.AuxMode.STARTER_VOLTAGE
82
-
83
80
  if (this.auxMode==undefined){
84
81
  const md=await this.constructor.getDataPacket(this.device, this.getManufacturerData(this.constructor.ManufacturerID))
85
82
  try {
@@ -55,7 +55,6 @@ class VictronDCEnergyMeter extends VictronSensor{
55
55
  this.addMetadatum('alarm','', 'alarm',
56
56
  (buff)=>{return buff.readUInt16LE(4)})
57
57
  .default="electrical.meters.{id}.alarm"
58
- this.getPath("alarm").notify=true
59
58
  this.addMetadatum('current','A', 'current')
60
59
  .default="electrical.meters.{id}.current"
61
60