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 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! 🔋⚡