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.
- package/AI-DEV.md +784 -0
- package/BTSensor.js +10 -7
- package/README.md +17 -0
- package/index.js +36 -14
- package/package.json +2 -2
- package/pairing.md +147 -0
- package/sensor_classes/HumsienkBMS.js +385 -0
- package/sensor_classes/JBDBMS.js +1 -0
- package/sensor_classes/JikongBMS.js +1 -1
- package/sensor_classes/Renogy/RenogySensor.js +15 -6
- package/sensor_classes/RenogyBattery.js +28 -14
- package/sensor_classes/RenogyInverter.js +17 -3
- package/sensor_classes/RenogyRoverClient.js +9 -18
- package/sensor_classes/ShenzhenLiOnBMS.js +3 -2
- package/sensor_classes/VictronBatteryMonitor.js +1 -4
- package/sensor_classes/VictronDCEnergyMeter.js +0 -1
- package/sensor_classes/VictronInverter.js +0 -1
- package/sensor_classes/VictronInverterRS.js +0 -1
- package/sensor_classes/VictronSmartBatteryProtect.js +2 -3
- package/sensor_classes/XiaomiMiBeacon.js +52 -24
package/AI-DEV.md
ADDED
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
# AI-Assisted Development Guide for Bluetooth Battery Sensors
|
|
2
|
+
|
|
3
|
+
This guide documents the AI-assisted development process used to create Bluetooth battery sensor implementations for the SignalK bt-sensors-plugin-sk. Follow this process to add support for new Bluetooth battery devices.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin architecture allows adding new Bluetooth battery sensors by creating sensor class files that inherit from [`BTSensor.js`](BTSensor.js:1). Each sensor class handles device-specific Bluetooth communication and data parsing, then maps the data to SignalK paths.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
Before starting, gather the following information about your Bluetooth battery:
|
|
12
|
+
|
|
13
|
+
### 1. Device Documentation
|
|
14
|
+
- Manufacturer's name and device model
|
|
15
|
+
- Bluetooth specifications (BLE services, characteristics, UUIDs)
|
|
16
|
+
- Data packet format and protocol documentation
|
|
17
|
+
- Available metrics (voltage, current, SOC, temperature, etc.)
|
|
18
|
+
|
|
19
|
+
### 2. Development Tools
|
|
20
|
+
- Access to the physical device for testing
|
|
21
|
+
- **Android device with Bluetooth debugging enabled** (CRITICAL - see section below)
|
|
22
|
+
- Bluetooth scanning tools (`bluetoothctl`, nRF Connect app, etc.)
|
|
23
|
+
- **Vendor's mobile app** for the battery (if available)
|
|
24
|
+
- Sample data packets (advertising data, characteristic reads)
|
|
25
|
+
|
|
26
|
+
### 3. Technical Details
|
|
27
|
+
- Service UUIDs the device advertises
|
|
28
|
+
- Characteristic UUIDs for reading data
|
|
29
|
+
- Data encoding format (byte order, scaling factors, units)
|
|
30
|
+
- Any CRC/checksum algorithms used
|
|
31
|
+
- Whether device uses advertising data or GATT connection
|
|
32
|
+
|
|
33
|
+
## Critical First Step: Capture Real Bluetooth Data
|
|
34
|
+
|
|
35
|
+
### Using Android Bluetooth HCI Snoop Log (ESSENTIAL)
|
|
36
|
+
|
|
37
|
+
**This is the most important step** - Before writing any code, you need to capture actual Bluetooth communication from the device. The most effective method is using Android's built-in Bluetooth debugging with the vendor's app.
|
|
38
|
+
|
|
39
|
+
**Why this matters:**
|
|
40
|
+
- Reveals the exact protocol the device uses
|
|
41
|
+
- Shows actual commands and responses
|
|
42
|
+
- Eliminates guesswork about byte layouts
|
|
43
|
+
- Provides test data for validation
|
|
44
|
+
|
|
45
|
+
**Step-by-step process:**
|
|
46
|
+
|
|
47
|
+
1. **Enable Developer Options on Android:**
|
|
48
|
+
- Go to Settings → About Phone
|
|
49
|
+
- Tap "Build Number" 7 times
|
|
50
|
+
- Go back to Settings → Developer Options
|
|
51
|
+
|
|
52
|
+
2. **Enable Bluetooth HCI Snoop Log:**
|
|
53
|
+
- In Developer Options, enable "Bluetooth HCI snoop log"
|
|
54
|
+
- This captures ALL Bluetooth traffic to a file
|
|
55
|
+
|
|
56
|
+
3. **Use the Vendor's App:**
|
|
57
|
+
- Install the battery manufacturer's official app
|
|
58
|
+
- Connect to your battery
|
|
59
|
+
- Navigate through all screens showing battery data
|
|
60
|
+
- Trigger all functions (refresh, settings, etc.)
|
|
61
|
+
- Let it run for 30-60 seconds
|
|
62
|
+
|
|
63
|
+
4. **Extract the Log File:**
|
|
64
|
+
```bash
|
|
65
|
+
# Pull the Bluetooth log from Android device via ADB
|
|
66
|
+
adb pull /sdcard/Android/data/btsnoop_hci.log
|
|
67
|
+
|
|
68
|
+
# Or from newer Android versions:
|
|
69
|
+
adb bugreport
|
|
70
|
+
# Then extract: FS/data/misc/bluetooth/logs/btsnoop_hci.log
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
5. **Analyze with Wireshark:**
|
|
74
|
+
- Open btsnoop_hci.log in Wireshark
|
|
75
|
+
- Filter for your device's MAC address: `bluetooth.addr == XX:XX:XX:XX:XX:XX`
|
|
76
|
+
- Look for:
|
|
77
|
+
- **ATT (Attribute Protocol) packets** - GATT read/write/notify operations
|
|
78
|
+
- **Advertising packets** - Manufacturer/Service data broadcasts
|
|
79
|
+
- **Command/response patterns** - Protocol structure
|
|
80
|
+
|
|
81
|
+
**What to look for in Wireshark:**
|
|
82
|
+
|
|
83
|
+
For GATT devices:
|
|
84
|
+
- `ATT Write Request` - Commands sent by app to device
|
|
85
|
+
- `ATT Handle Value Notification` - Data pushed from device
|
|
86
|
+
- `ATT Read Response` - Data read from characteristics
|
|
87
|
+
- Note the characteristic handles (0x00XX) and UUIDs
|
|
88
|
+
|
|
89
|
+
For Advertising devices:
|
|
90
|
+
- `Advertising Data` packets
|
|
91
|
+
- Manufacturer Data field with ID and hex bytes
|
|
92
|
+
- Service Data field with UUID and hex bytes
|
|
93
|
+
|
|
94
|
+
**Real Example - HSC14F Battery:**
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Wireshark Filter: bluetooth.addr == AA:BB:CC:DD:EE:FF
|
|
98
|
+
|
|
99
|
+
Frame 142: ATT Write Request, Handle: 0x000e
|
|
100
|
+
Data: A5 5A 00 FF 01 03
|
|
101
|
+
(This is the "read battery info" command)
|
|
102
|
+
|
|
103
|
+
Frame 145: ATT Handle Value Notification, Handle: 0x000b
|
|
104
|
+
Data: A5 5A 00 01 01 0C E4 10 0E 55 19 [...]
|
|
105
|
+
(Device's response with battery data)
|
|
106
|
+
|
|
107
|
+
Vendor app displayed at this moment:
|
|
108
|
+
- Voltage: 13.2V (bytes 6-7: 0x0CE4 = 3300 * 0.01 / 2 cells)
|
|
109
|
+
- Current: 10.5A (bytes 8-9: 0x100E = 4110 * 0.01 / 4)
|
|
110
|
+
- SOC: 85% (byte 10: 0x55 = 85)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Prompt to AI after capturing:**
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
I've captured Bluetooth HCI snoop log from my [DEVICE_NAME] using the vendor's
|
|
117
|
+
Android app. Here's what Wireshark shows:
|
|
118
|
+
|
|
119
|
+
[FOR GATT DEVICES:]
|
|
120
|
+
Service UUID: 0000ffe0-0000-1000-8000-00805f9b34fb
|
|
121
|
+
Characteristic UUID: 0000ffe1-0000-1000-8000-00805f9b34fb
|
|
122
|
+
|
|
123
|
+
Write Request to device:
|
|
124
|
+
A5 5A 00 FF 01 03
|
|
125
|
+
|
|
126
|
+
Notification Response:
|
|
127
|
+
A5 5A 00 01 01 0C E4 10 0E 55 19 00 01 02 [...]
|
|
128
|
+
|
|
129
|
+
When this packet arrived, the vendor app displayed:
|
|
130
|
+
- Voltage: 13.2V
|
|
131
|
+
- Current: 10.5A
|
|
132
|
+
- SOC: 85%
|
|
133
|
+
- Temperature: 25°C
|
|
134
|
+
|
|
135
|
+
Please help me:
|
|
136
|
+
1. Understand the protocol structure
|
|
137
|
+
2. Map hex bytes to displayed values
|
|
138
|
+
3. Determine byte positions, endianness, and scaling
|
|
139
|
+
4. Create the sensor class implementation
|
|
140
|
+
|
|
141
|
+
[FOR ADVERTISING DEVICES:]
|
|
142
|
+
Advertising Data shows:
|
|
143
|
+
Manufacturer Data (0x1234): AB CD 0C E4 10 0E 55 01 F4
|
|
144
|
+
|
|
145
|
+
When this was captured, device display showed:
|
|
146
|
+
- Voltage: 13.2V
|
|
147
|
+
- Current: 10.5A
|
|
148
|
+
- SOC: 85%
|
|
149
|
+
|
|
150
|
+
Please decode this packet format.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Alternative: Using nRF Connect (If No Vendor App Available)
|
|
154
|
+
|
|
155
|
+
If there's no vendor app:
|
|
156
|
+
|
|
157
|
+
1. **Install nRF Connect** (Nordic Semiconductor app for Android/iOS)
|
|
158
|
+
2. **Scan and connect** to your battery
|
|
159
|
+
3. **Explore all services and characteristics**
|
|
160
|
+
4. **Read values** from readable characteristics
|
|
161
|
+
5. **Enable notifications** on notifiable characteristics
|
|
162
|
+
6. **Screenshot hex values** and note what they represent
|
|
163
|
+
|
|
164
|
+
**Prompt to AI:**
|
|
165
|
+
```
|
|
166
|
+
Using nRF Connect, I found on [DEVICE_NAME]:
|
|
167
|
+
|
|
168
|
+
Service: 0000ffe0-0000-1000-8000-00805f9b34fb
|
|
169
|
+
Characteristics:
|
|
170
|
+
- 0000ffe1-... (Read, Notify)
|
|
171
|
+
Current value: A5 5A 00 01 01 0C E4 10 0E 55
|
|
172
|
+
|
|
173
|
+
Battery label shows: 13.2V, 10.5A, 85%
|
|
174
|
+
|
|
175
|
+
How do I decode this to get those values?
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Step-by-Step AI-Assisted Development Process
|
|
179
|
+
|
|
180
|
+
### Step 1: Analyze Existing Similar Implementations
|
|
181
|
+
|
|
182
|
+
**Prompt to AI:**
|
|
183
|
+
```
|
|
184
|
+
I want to add support for a [DEVICE_NAME] Bluetooth battery to the bt-sensors-plugin-sk.
|
|
185
|
+
Please review the sensor_classes directory and identify existing battery sensor
|
|
186
|
+
implementations that are similar. Show me the key patterns used for:
|
|
187
|
+
- Device identification (matchManufacturerData or identify methods)
|
|
188
|
+
- Bluetooth service/characteristic UUIDs
|
|
189
|
+
- Data packet parsing
|
|
190
|
+
- SignalK path mapping
|
|
191
|
+
- GATT vs advertising data approaches
|
|
192
|
+
|
|
193
|
+
Focus on these example files:
|
|
194
|
+
- sensor_classes/VictronSmartLithium.js
|
|
195
|
+
- sensor_classes/JBDBMS.js
|
|
196
|
+
- sensor_classes/RenogyBattery.js
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**What AI should examine:**
|
|
200
|
+
- [`VictronSmartLithium.js`](sensor_classes/VictronSmartLithium.js:1) - Advertising data approach with encryption
|
|
201
|
+
- [`JBDBMS.js`](sensor_classes/JBDBMS.js:1) - GATT connection with command/response protocol
|
|
202
|
+
- [`RenogyBattery.js`](sensor_classes/RenogyBattery.js:1) - GATT with Modbus-style registers
|
|
203
|
+
- [`BTSensor.js`](BTSensor.js:1) - Base class showing required methods
|
|
204
|
+
|
|
205
|
+
### Step 2: Determine Communication Method
|
|
206
|
+
|
|
207
|
+
**Prompt to AI:**
|
|
208
|
+
```
|
|
209
|
+
My device [DOES/DOES NOT] require a GATT connection. It provides data via:
|
|
210
|
+
- [X] Advertising packets (manufacturer data or service data)
|
|
211
|
+
- [ ] GATT characteristic notifications
|
|
212
|
+
- [ ] GATT characteristic reads (polling)
|
|
213
|
+
|
|
214
|
+
Here's the Bluetooth scan data I captured:
|
|
215
|
+
[Paste output from bluetoothctl or nRF Connect]
|
|
216
|
+
|
|
217
|
+
Which approach should I use: advertising-based like VictronSmartLithium,
|
|
218
|
+
or GATT-based like JBDBMS or RenogyBattery?
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Step 3: Create Initial Sensor Class File
|
|
222
|
+
|
|
223
|
+
**Prompt to AI:**
|
|
224
|
+
```
|
|
225
|
+
Create a new sensor class file for [DEVICE_NAME] based on the following specifications:
|
|
226
|
+
|
|
227
|
+
Device: [DEVICE_NAME]
|
|
228
|
+
Manufacturer: [MANUFACTURER]
|
|
229
|
+
Communication method: [Advertising/GATT]
|
|
230
|
+
|
|
231
|
+
[IF ADVERTISING:]
|
|
232
|
+
Manufacturer ID: [0xXXXX]
|
|
233
|
+
The device advertises data in manufacturer data with this format:
|
|
234
|
+
[Describe byte layout]
|
|
235
|
+
|
|
236
|
+
[IF GATT:]
|
|
237
|
+
Service UUID: [UUID]
|
|
238
|
+
Characteristics:
|
|
239
|
+
- Read: [UUID] - [description]
|
|
240
|
+
- Notify: [UUID] - [description]
|
|
241
|
+
- Write: [UUID] - [description if applicable]
|
|
242
|
+
|
|
243
|
+
The device provides these metrics:
|
|
244
|
+
- Voltage (range: X-Y V)
|
|
245
|
+
- Current (range: -X to +Y A)
|
|
246
|
+
- State of Charge (0-100%)
|
|
247
|
+
- Temperature (range: X-Y °C)
|
|
248
|
+
- [Other metrics...]
|
|
249
|
+
|
|
250
|
+
Please create sensor_classes/[DeviceName].js following the pattern used in
|
|
251
|
+
[VictronSmartLithium.js/JBDBMS.js/RenogyBattery.js], including:
|
|
252
|
+
1. Class definition extending BTSensor
|
|
253
|
+
2. Static Domain property set to BTSensor.SensorDomains.electrical
|
|
254
|
+
3. Static identify() method for device identification
|
|
255
|
+
4. Static ImageFile property
|
|
256
|
+
5. initSchema() method with metadata
|
|
257
|
+
6. Data parsing methods
|
|
258
|
+
7. SignalK path defaults
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Expected AI actions:**
|
|
262
|
+
- Creates new file in `sensor_classes/`
|
|
263
|
+
- Implements basic class structure
|
|
264
|
+
- Sets up UUID constants (if GATT)
|
|
265
|
+
- Defines device identification logic
|
|
266
|
+
- Creates default SignalK path mappings
|
|
267
|
+
|
|
268
|
+
### Step 4: Implement Device Identification
|
|
269
|
+
|
|
270
|
+
**For Advertising-based Devices:**
|
|
271
|
+
|
|
272
|
+
**Prompt to AI:**
|
|
273
|
+
```
|
|
274
|
+
I have Bluetooth advertising data from [DEVICE_NAME]:
|
|
275
|
+
Manufacturer Data: [paste hex dump, e.g., "AB CD 01 02 03 04..."]
|
|
276
|
+
or
|
|
277
|
+
Service Data (UUID [UUID]): [paste hex dump]
|
|
278
|
+
|
|
279
|
+
The manufacturer ID should be: [0xXXXX]
|
|
280
|
+
|
|
281
|
+
Please implement the static identify(device) method to correctly identify this device.
|
|
282
|
+
Use the pattern from VictronSensor.js which checks manufacturer ID and additional
|
|
283
|
+
identifying bytes if needed.
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**For GATT Devices:**
|
|
287
|
+
|
|
288
|
+
**Prompt to AI:**
|
|
289
|
+
```
|
|
290
|
+
My GATT device advertises the service UUID: [UUID]
|
|
291
|
+
|
|
292
|
+
Please implement the static identify(device) method that:
|
|
293
|
+
1. Checks if the device advertises this service UUID
|
|
294
|
+
2. Returns the class if matched, null otherwise
|
|
295
|
+
|
|
296
|
+
Use this pattern:
|
|
297
|
+
static async identify(device){
|
|
298
|
+
const serviceUUIDs = await BTSensor.getDeviceProp(device, 'UUIDs')
|
|
299
|
+
if (serviceUUIDs && serviceUUIDs.includes('[UUID]'))
|
|
300
|
+
return this
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Step 5: Implement Data Parsing (Advertising Method)
|
|
306
|
+
|
|
307
|
+
**Prompt to AI:**
|
|
308
|
+
```
|
|
309
|
+
The [DEVICE_NAME] sends data in manufacturer data packets with this format:
|
|
310
|
+
|
|
311
|
+
Byte 0-1: Manufacturer ID (0xXXXX, little-endian)
|
|
312
|
+
Byte 2-3: Voltage (0.01V units, unsigned 16-bit LE)
|
|
313
|
+
Byte 4-5: Current (0.01A units, signed 16-bit LE, positive=charging)
|
|
314
|
+
Byte 6: State of Charge (%, unsigned 8-bit, 0-100)
|
|
315
|
+
Byte 7-8: Temperature (0.1°C units, signed 16-bit LE)
|
|
316
|
+
[Continue for all fields...]
|
|
317
|
+
|
|
318
|
+
Sample packet: [provide actual hex dump, e.g., "CD AB 10 0D E8 03 64 5A 01"]
|
|
319
|
+
|
|
320
|
+
Please implement the propertiesChanged() method that:
|
|
321
|
+
1. Calls super.propertiesChanged(props) first
|
|
322
|
+
2. Extracts manufacturer data
|
|
323
|
+
3. Parses the buffer using the read functions defined in initSchema()
|
|
324
|
+
4. Calls emitValuesFrom(buffer) to emit all values
|
|
325
|
+
|
|
326
|
+
Also update initSchema() with the correct read functions for each path using:
|
|
327
|
+
.read = (buffer) => { return buffer.readUInt16LE(2) / 100 } // for voltage at byte 2
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Common Buffer Reading Methods:**
|
|
331
|
+
- `buffer.readUInt16LE(offset)` - Unsigned 16-bit little-endian
|
|
332
|
+
- `buffer.readInt16LE(offset)` - Signed 16-bit little-endian
|
|
333
|
+
- `buffer.readUInt8(offset)` - Unsigned 8-bit
|
|
334
|
+
- `buffer.readInt8(offset)` - Signed 8-bit
|
|
335
|
+
- `buffer.readUInt32LE(offset)` - Unsigned 32-bit little-endian
|
|
336
|
+
- Use `BE` suffix for big-endian variants
|
|
337
|
+
|
|
338
|
+
### Step 6: Implement GATT Communication (GATT Method)
|
|
339
|
+
|
|
340
|
+
**Prompt to AI:**
|
|
341
|
+
```
|
|
342
|
+
My GATT device uses this protocol:
|
|
343
|
+
Service UUID: [UUID]
|
|
344
|
+
Notify Characteristic: [UUID] - Sends battery data
|
|
345
|
+
Write Characteristic: [UUID] - Accepts commands
|
|
346
|
+
Read Characteristic: [UUID] - Returns data on request
|
|
347
|
+
|
|
348
|
+
Command format: [describe, e.g., "0xDD 0xA5 CMD 0x00 0xFF CHECKSUM 0x77"]
|
|
349
|
+
Response format: [describe structure]
|
|
350
|
+
|
|
351
|
+
Please implement:
|
|
352
|
+
1. initSchema() that connects to the device and sets up characteristics
|
|
353
|
+
2. initGATTConnection() override if needed
|
|
354
|
+
3. initGATTNotifications() or initGATTInterval() depending on polling vs notifications
|
|
355
|
+
4. emitGATT() method that requests data and parses responses
|
|
356
|
+
5. Helper methods like sendReadFunctionRequest() if needed
|
|
357
|
+
|
|
358
|
+
Follow the pattern from JBDBMS.js which uses command/response protocol.
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**For devices that need polling:**
|
|
362
|
+
|
|
363
|
+
**Prompt to AI:**
|
|
364
|
+
```
|
|
365
|
+
Add GATT parameters to support polling:
|
|
366
|
+
- In initSchema(), call this.addGATTParameter() for pollFreq
|
|
367
|
+
- Set hasGATT() to return true
|
|
368
|
+
- Set usingGATT() to return this.useGATT
|
|
369
|
+
- Implement initGATTInterval() to poll at specified frequency
|
|
370
|
+
- Implement emitGATT() to read and emit all values
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Step 7: Define SignalK Path Mappings
|
|
374
|
+
|
|
375
|
+
**Prompt to AI:**
|
|
376
|
+
```
|
|
377
|
+
Please implement proper SignalK path defaults in initSchema() for this battery.
|
|
378
|
+
Map the parsed values to these SignalK paths:
|
|
379
|
+
|
|
380
|
+
Use addDefaultPath() for standard paths:
|
|
381
|
+
- Voltage → electrical.batteries.voltage
|
|
382
|
+
- Current → electrical.batteries.current
|
|
383
|
+
- SOC → electrical.batteries.capacity.stateOfCharge
|
|
384
|
+
- Temperature → electrical.batteries.temperature
|
|
385
|
+
- Remaining capacity → electrical.batteries.capacity.remaining
|
|
386
|
+
- Cycles → electrical.batteries.cycles
|
|
387
|
+
|
|
388
|
+
Use addMetadatum() for device-specific paths:
|
|
389
|
+
- Cell voltages → electrical.batteries.{batteryID}.cell[N].voltage
|
|
390
|
+
- Protection status → electrical.batteries.{batteryID}.protectionStatus
|
|
391
|
+
- [Other custom metrics]
|
|
392
|
+
|
|
393
|
+
Add parameter for battery ID:
|
|
394
|
+
this.addDefaultParam("batteryID").default="house"
|
|
395
|
+
|
|
396
|
+
Ensure all paths use proper template variables like {batteryID} where appropriate.
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**SignalK Path Reference:**
|
|
400
|
+
```
|
|
401
|
+
electrical.batteries.{id}.voltage // V
|
|
402
|
+
electrical.batteries.{id}.current // A (positive = charging)
|
|
403
|
+
electrical.batteries.{id}.temperature // K (Kelvin)
|
|
404
|
+
electrical.batteries.{id}.capacity.stateOfCharge // ratio (0-1)
|
|
405
|
+
electrical.batteries.{id}.capacity.remaining // C (coulombs/amp-hours)
|
|
406
|
+
electrical.batteries.{id}.capacity.actual // C (total capacity)
|
|
407
|
+
electrical.batteries.{id}.cycles // count
|
|
408
|
+
electrical.batteries.{id}.lifetimeDischarge // C
|
|
409
|
+
electrical.batteries.{id}.lifetimeRecharge // C
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Step 8: Add Device Image and Description
|
|
413
|
+
|
|
414
|
+
**Prompt to AI:**
|
|
415
|
+
```
|
|
416
|
+
I have an image for [DEVICE_NAME] saved as: public/images/[DeviceName].webp
|
|
417
|
+
|
|
418
|
+
Please update the sensor class to include:
|
|
419
|
+
1. static ImageFile = "[DeviceName].webp"
|
|
420
|
+
2. static Description = "[Brief description of device]"
|
|
421
|
+
3. static Manufacturer = "[Manufacturer Name]"
|
|
422
|
+
|
|
423
|
+
Follow the pattern from other sensor classes.
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Step 9: Register the Sensor Class
|
|
427
|
+
|
|
428
|
+
**Prompt to AI:**
|
|
429
|
+
```
|
|
430
|
+
Please help me register the new [DeviceName] sensor class in the plugin:
|
|
431
|
+
1. Check classLoader.js to see the pattern for importing and registering classes
|
|
432
|
+
2. Add the appropriate require() statement
|
|
433
|
+
3. Add it to the class map
|
|
434
|
+
4. Ensure it's in the correct order for identification
|
|
435
|
+
|
|
436
|
+
Show me the exact changes needed to classLoader.js.
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Step 10: Test with Real Device
|
|
440
|
+
|
|
441
|
+
**Prompt to AI:**
|
|
442
|
+
```
|
|
443
|
+
I'm testing with the actual [DEVICE_NAME] device. Here's what I'm seeing:
|
|
444
|
+
|
|
445
|
+
Debug output:
|
|
446
|
+
[Paste logs, errors, or unexpected behavior]
|
|
447
|
+
|
|
448
|
+
Expected values from device display:
|
|
449
|
+
- Voltage: [X]V
|
|
450
|
+
- Current: [X]A
|
|
451
|
+
- SOC: [X]%
|
|
452
|
+
- Temperature: [X]°C
|
|
453
|
+
|
|
454
|
+
Actual parsed values:
|
|
455
|
+
- Voltage: [Y]V
|
|
456
|
+
- Current: [Y]A
|
|
457
|
+
- SOC: [Y]%
|
|
458
|
+
- Temperature: [Y]°C
|
|
459
|
+
|
|
460
|
+
Please help debug:
|
|
461
|
+
1. Are the byte offsets correct?
|
|
462
|
+
2. Is the byte order (endianness) correct?
|
|
463
|
+
3. Are scaling factors correct?
|
|
464
|
+
4. Are signed vs unsigned interpretations correct?
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Common Issues & Fixes:**
|
|
468
|
+
- **Values 256x too large/small**: Wrong endianness (LE vs BE)
|
|
469
|
+
- **Negative values where shouldn't be**: Using UInt instead of Int
|
|
470
|
+
- **Values in wrong range**: Incorrect scaling factor
|
|
471
|
+
- **Values always same**: Wrong byte offset or not updating
|
|
472
|
+
|
|
473
|
+
### Step 11: Add Error Handling
|
|
474
|
+
|
|
475
|
+
**Prompt to AI:**
|
|
476
|
+
```
|
|
477
|
+
Please add robust error handling to the [DeviceName] class for:
|
|
478
|
+
|
|
479
|
+
For advertising-based devices:
|
|
480
|
+
1. Check manufacturer data exists before parsing
|
|
481
|
+
2. Validate buffer length before reading
|
|
482
|
+
3. Handle out-of-range values gracefully
|
|
483
|
+
4. Add try-catch in propertiesChanged()
|
|
484
|
+
|
|
485
|
+
For GATT devices:
|
|
486
|
+
1. Add timeouts for response waits
|
|
487
|
+
2. Validate checksums if protocol uses them
|
|
488
|
+
3. Handle disconnections gracefully
|
|
489
|
+
4. Add retry logic for failed reads
|
|
490
|
+
5. Implement deactivateGATT() cleanup
|
|
491
|
+
|
|
492
|
+
Follow the error handling patterns in JBDBMS.js and BTSensor.js.
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Step 12: Documentation and Testing
|
|
496
|
+
|
|
497
|
+
**Prompt to AI:**
|
|
498
|
+
```
|
|
499
|
+
Please add comprehensive documentation to the [DeviceName] class:
|
|
500
|
+
|
|
501
|
+
1. Add JSDoc comments for all methods explaining:
|
|
502
|
+
- Parameters and return values
|
|
503
|
+
- Protocol details
|
|
504
|
+
- Data format specifics
|
|
505
|
+
|
|
506
|
+
2. Add a comment block at the top documenting the data format:
|
|
507
|
+
/*
|
|
508
|
+
[DeviceName] Data Format
|
|
509
|
+
Byte X-Y: [Field] (units, format, range)
|
|
510
|
+
...
|
|
511
|
+
*/
|
|
512
|
+
|
|
513
|
+
3. Create a _test() usage example showing how to test parsing with sample data
|
|
514
|
+
|
|
515
|
+
4. Document any device-specific quirks or limitations
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Complete Example Workflow
|
|
519
|
+
|
|
520
|
+
Here's a condensed example conversation for adding an "AcmeBattery BT-100":
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
User: I want to add support for the Acme BT-100 Bluetooth battery. It uses
|
|
524
|
+
advertising data with manufacturer ID 0x1234. Here's a sample packet:
|
|
525
|
+
34 12 10 0D E8 03 64 5A 01
|
|
526
|
+
|
|
527
|
+
AI: [Analyzes structure, creates initial class file]
|
|
528
|
+
|
|
529
|
+
User: The packet format is:
|
|
530
|
+
Bytes 0-1: Manufacturer ID (0x1234)
|
|
531
|
+
Bytes 2-3: Voltage in 0.01V (little-endian, so 0x0D10 = 3344 = 33.44V)
|
|
532
|
+
Bytes 4-5: Current in 0.01A (signed LE, 0x03E8 = 1000 = 10.00A)
|
|
533
|
+
Byte 6: SOC (0x64 = 100 = 100%)
|
|
534
|
+
Bytes 7-8: Temp in 0.1°C (signed LE, 0x015A = 346 = 34.6°C)
|
|
535
|
+
|
|
536
|
+
AI: [Implements propertiesChanged() and read functions with correct parsing]
|
|
537
|
+
|
|
538
|
+
User: Testing shows voltage correct but current is backwards (negative when charging)
|
|
539
|
+
|
|
540
|
+
AI: [Fixes current sign interpretation]
|
|
541
|
+
|
|
542
|
+
User: Perfect! Now add the image reference and register it.
|
|
543
|
+
|
|
544
|
+
AI: [Updates ImageFile, modifies classLoader.js]
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## Bluetooth Scanning Commands
|
|
548
|
+
|
|
549
|
+
### Using bluetoothctl
|
|
550
|
+
```bash
|
|
551
|
+
# Start scanning
|
|
552
|
+
bluetoothctl
|
|
553
|
+
scan on
|
|
554
|
+
|
|
555
|
+
# Wait for device to appear, then:
|
|
556
|
+
info [MAC_ADDRESS]
|
|
557
|
+
|
|
558
|
+
# Look for:
|
|
559
|
+
# - ManufacturerData: key [ID] value [hex bytes]
|
|
560
|
+
# - ServiceData: key [UUID] value [hex bytes]
|
|
561
|
+
# - UUIDs: [list of service UUIDs]
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Using hcitool/hcidump
|
|
565
|
+
```bash
|
|
566
|
+
# Terminal 1: Start scan
|
|
567
|
+
sudo hcitool lescan
|
|
568
|
+
|
|
569
|
+
# Terminal 2: Capture advertising data
|
|
570
|
+
sudo hcidump --raw
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Testing Your Implementation
|
|
574
|
+
|
|
575
|
+
### Static Testing (No Device Required)
|
|
576
|
+
```javascript
|
|
577
|
+
const MyBattery = require('./sensor_classes/MyBattery.js')
|
|
578
|
+
|
|
579
|
+
// Test with sample packet
|
|
580
|
+
const sampleData = "34 12 10 0D E8 03 64 5A 01"
|
|
581
|
+
MyBattery._test(sampleData)
|
|
582
|
+
|
|
583
|
+
// Should output:
|
|
584
|
+
// voltage=33.44
|
|
585
|
+
// current=10.00
|
|
586
|
+
// SOC=1.00
|
|
587
|
+
// temperature=307.75
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Live Testing Checklist
|
|
591
|
+
- [ ] Device correctly identified during scan
|
|
592
|
+
- [ ] Connection establishes (if GATT)
|
|
593
|
+
- [ ] All metrics parse with correct values
|
|
594
|
+
- [ ] **Values match vendor app exactly** (critical!)
|
|
595
|
+
- [ ] **Parsed values match HCI snoop captured data**
|
|
596
|
+
- [ ] Values also match device's physical display (if available)
|
|
597
|
+
- [ ] Reconnection works after going out of range
|
|
598
|
+
- [ ] SignalK paths appear in data browser
|
|
599
|
+
- [ ] Units are correct (Kelvin for temp, etc.)
|
|
600
|
+
- [ ] No memory leaks over extended operation
|
|
601
|
+
|
|
602
|
+
### Validating Against Captured HCI Data
|
|
603
|
+
|
|
604
|
+
After implementing your sensor class, verify it parses the same packets you captured:
|
|
605
|
+
|
|
606
|
+
**Extract specific packets from your capture:**
|
|
607
|
+
```bash
|
|
608
|
+
# Use tshark to extract hex data from your captured log
|
|
609
|
+
tshark -r btsnoop_hci.log -Y "btatt && bluetooth.addr == XX:XX:XX:XX:XX:XX" \
|
|
610
|
+
-T fields -e btatt.value
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Test with captured data:**
|
|
614
|
+
```javascript
|
|
615
|
+
const MyBattery = require('./sensor_classes/MyBattery.js')
|
|
616
|
+
|
|
617
|
+
// Use actual packet from your HCI capture
|
|
618
|
+
const capturedPacket = "A5 5A 00 01 01 0C E4 10 0E 55 19 00 01 02"
|
|
619
|
+
MyBattery._test(capturedPacket)
|
|
620
|
+
|
|
621
|
+
// Output should match what vendor app showed:
|
|
622
|
+
// voltage=13.2
|
|
623
|
+
// current=10.5
|
|
624
|
+
// SOC=0.85
|
|
625
|
+
// temperature=298.15
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**Prompt to AI if values don't match:**
|
|
629
|
+
```
|
|
630
|
+
My implementation parses this HCI-captured packet:
|
|
631
|
+
A5 5A 00 01 01 0C E4 10 0E 55
|
|
632
|
+
|
|
633
|
+
And produces:
|
|
634
|
+
- voltage=33.0V (WRONG)
|
|
635
|
+
- current=10.5A (CORRECT)
|
|
636
|
+
|
|
637
|
+
But the vendor app showed voltage=13.2V when this packet was sent.
|
|
638
|
+
|
|
639
|
+
Bytes 6-7 are: 0C E4 (hex)
|
|
640
|
+
0x0CE4 = 3300 decimal
|
|
641
|
+
|
|
642
|
+
The device is a 2-cell battery. Help me debug the voltage parsing.
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**AI will likely identify:** You need to divide by number of cells, or the scaling factor is different, or byte order is reversed.
|
|
646
|
+
|
|
647
|
+
## Common Data Parsing Patterns
|
|
648
|
+
|
|
649
|
+
### Temperature Conversions
|
|
650
|
+
```javascript
|
|
651
|
+
// Celsius to Kelvin
|
|
652
|
+
.read = (buffer) => { return buffer.readInt16LE(offset) / 10 + 273.15 }
|
|
653
|
+
|
|
654
|
+
// Handle "not available" values
|
|
655
|
+
.read = (buffer) => {
|
|
656
|
+
const val = buffer.readInt8(offset)
|
|
657
|
+
return val === 0x7F ? NaN : val + 273.15
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Current Direction
|
|
662
|
+
```javascript
|
|
663
|
+
// Positive = charging, negative = discharging
|
|
664
|
+
.read = (buffer) => { return buffer.readInt16LE(offset) / 100 }
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### State of Charge
|
|
668
|
+
```javascript
|
|
669
|
+
// Percent to ratio (0-1)
|
|
670
|
+
.read = (buffer) => { return buffer.readUInt8(offset) / 100 }
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Bit Flags
|
|
674
|
+
```javascript
|
|
675
|
+
.read = (buffer) => {
|
|
676
|
+
const byte = buffer.readUInt8(offset)
|
|
677
|
+
return {
|
|
678
|
+
charging: (byte & 0x01) !== 0,
|
|
679
|
+
discharging: (byte & 0x02) !== 0,
|
|
680
|
+
balancing: (byte & 0x04) !== 0,
|
|
681
|
+
fault: (byte & 0x80) !== 0
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
## Advanced Topics
|
|
687
|
+
|
|
688
|
+
### Encryption
|
|
689
|
+
Some devices (like Victron) encrypt advertising data. If your device uses encryption:
|
|
690
|
+
|
|
691
|
+
**Prompt to AI:**
|
|
692
|
+
```
|
|
693
|
+
My device uses AES-CTR encryption with a 128-bit key. The encrypted data starts
|
|
694
|
+
at byte 8 of the manufacturer data. The nonce/counter is in bytes 0-7.
|
|
695
|
+
|
|
696
|
+
Please implement:
|
|
697
|
+
1. A decrypt() method using Node.js crypto module
|
|
698
|
+
2. Update propertiesChanged() to decrypt before parsing
|
|
699
|
+
3. Add encryptionKey parameter to configuration
|
|
700
|
+
|
|
701
|
+
Follow the pattern from VictronSensor.js.
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Multiple Cell Voltages
|
|
705
|
+
For batteries with multiple cells:
|
|
706
|
+
|
|
707
|
+
**Prompt to AI:**
|
|
708
|
+
```
|
|
709
|
+
The battery has [N] cells. Cell voltages are packed into the data:
|
|
710
|
+
- Starting at byte X
|
|
711
|
+
- Each cell is 2 bytes, little-endian
|
|
712
|
+
- Values are in millivolts (1mV = 0.001V)
|
|
713
|
+
|
|
714
|
+
Please:
|
|
715
|
+
1. Add a loop in initSchema() to create cell voltage paths dynamically
|
|
716
|
+
2. Create read functions for each cell
|
|
717
|
+
3. Use template {batteryID} in paths like:
|
|
718
|
+
electrical.batteries.{batteryID}.cell[N].voltage
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Checksum Validation
|
|
722
|
+
For protocols with checksums:
|
|
723
|
+
|
|
724
|
+
**Prompt to AI:**
|
|
725
|
+
```
|
|
726
|
+
The protocol includes a checksum at bytes [X-Y]:
|
|
727
|
+
- Type: [CRC-16/sum/XOR]
|
|
728
|
+
- Calculated over bytes [A-B]
|
|
729
|
+
- [Describe algorithm]
|
|
730
|
+
|
|
731
|
+
Please implement a validateChecksum() function and call it before parsing data.
|
|
732
|
+
Reject invalid packets with this.debug() message.
|
|
733
|
+
|
|
734
|
+
See JBDBMS.js checkSum() function as reference.
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
## Troubleshooting Guide
|
|
738
|
+
|
|
739
|
+
| Problem | Likely Cause | Solution Prompt |
|
|
740
|
+
|---------|-------------|-----------------|
|
|
741
|
+
| Device not appearing in scan | UUID filter, not advertising | "Check if identify() method is correct. Show me how to debug device detection." |
|
|
742
|
+
| Values are NaN | Wrong byte offsets, invalid data | "Values showing NaN. Help me verify byte offsets and add validation." |
|
|
743
|
+
| Connection fails | Wrong service UUID, device busy | "GATT connection failing with error: [error]. What should I check?" |
|
|
744
|
+
| Values incorrect magnitude | Wrong scaling factor | "Values are 100x too large. Check my scaling factors." |
|
|
745
|
+
| Negative when should be positive | Unsigned vs signed | "Current showing negative when charging. Fix my Int/UInt usage." |
|
|
746
|
+
| Updates stop after a while | Memory leak, no cleanup | "Device stops updating after 5 minutes. Check my cleanup in stopListening()." |
|
|
747
|
+
|
|
748
|
+
## Resources
|
|
749
|
+
|
|
750
|
+
- [SignalK Specification](http://signalk.org/specification/latest/doc/vesselsBranch.html)
|
|
751
|
+
- [Bluetooth GATT Specifications](https://www.bluetooth.com/specifications/specs/)
|
|
752
|
+
- [Node.js Buffer Documentation](https://nodejs.org/api/buffer.html)
|
|
753
|
+
- Plugin README: [`README.md`](README.md:1)
|
|
754
|
+
- Development Guide: [`sensor_classes/DEVELOPMENT.md`](sensor_classes/DEVELOPMENT.md:1)
|
|
755
|
+
- Base Sensor Class: [`BTSensor.js`](BTSensor.js:1)
|
|
756
|
+
|
|
757
|
+
## Summary
|
|
758
|
+
|
|
759
|
+
The AI-assisted development process follows this pattern:
|
|
760
|
+
|
|
761
|
+
1. **Research** - Analyze similar implementations
|
|
762
|
+
2. **Identify** - Determine communication method (advertising vs GATT)
|
|
763
|
+
3. **Create** - Generate skeleton sensor class
|
|
764
|
+
4. **Implement** - Add device identification logic
|
|
765
|
+
5. **Parse** - Implement data parsing with correct byte operations
|
|
766
|
+
6. **Map** - Create SignalK path mappings with defaults
|
|
767
|
+
7. **Register** - Add to classLoader.js
|
|
768
|
+
8. **Test** - Verify with real hardware
|
|
769
|
+
9. **Debug** - Fix scaling, offsets, byte order issues
|
|
770
|
+
10. **Refine** - Add error handling and documentation
|
|
771
|
+
11. **Complete** - Test thoroughly and contribute back
|
|
772
|
+
|
|
773
|
+
By providing clear, specific prompts with actual device data (hex dumps, protocol specs, test results), AI can effectively assist in creating robust, maintainable sensor implementations that follow the established patterns in this codebase.
|
|
774
|
+
|
|
775
|
+
## Key Success Factors
|
|
776
|
+
|
|
777
|
+
1. **Have real device data** - Actual advertising packets or GATT responses
|
|
778
|
+
2. **Know the protocol** - Byte layout, scaling factors, data types
|
|
779
|
+
3. **Test iteratively** - Make small changes, test each one
|
|
780
|
+
4. **Compare values** - Match against device's own display
|
|
781
|
+
5. **Use existing patterns** - Don't reinvent, follow proven implementations
|
|
782
|
+
6. **Document thoroughly** - Help the next developer
|
|
783
|
+
|
|
784
|
+
Good luck adding your Bluetooth battery! 🔋⚡
|