@hangtime/grip-connect 0.10.9 → 0.13.0
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/README.md +14 -3
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interfaces/command.interface.d.ts +120 -20
- package/dist/cjs/interfaces/command.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device/climbro.interface.d.ts +25 -0
- package/dist/cjs/interfaces/device/climbro.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device/cts500.interface.d.ts +96 -0
- package/dist/cjs/interfaces/device/cts500.interface.d.ts.map +1 -0
- package/dist/cjs/interfaces/device/cts500.interface.js +3 -0
- package/dist/cjs/interfaces/device/cts500.interface.js.map +1 -0
- package/dist/cjs/interfaces/device/forceboard.interface.d.ts +2 -2
- package/dist/cjs/interfaces/device/forceboard.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device/progressor.interface.d.ts +17 -7
- package/dist/cjs/interfaces/device/progressor.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/index.d.ts +2 -0
- package/dist/cjs/interfaces/index.d.ts.map +1 -1
- package/dist/cjs/interfaces/nordic.interface.d.ts +47 -0
- package/dist/cjs/interfaces/nordic.interface.d.ts.map +1 -0
- package/dist/cjs/interfaces/nordic.interface.js +3 -0
- package/dist/cjs/interfaces/nordic.interface.js.map +1 -0
- package/dist/cjs/models/device/climbro.model.d.ts +25 -0
- package/dist/cjs/models/device/climbro.model.d.ts.map +1 -1
- package/dist/cjs/models/device/climbro.model.js +93 -1
- package/dist/cjs/models/device/climbro.model.js.map +1 -1
- package/dist/cjs/models/device/cts500.model.d.ts +173 -0
- package/dist/cjs/models/device/cts500.model.d.ts.map +1 -0
- package/dist/cjs/models/device/cts500.model.js +588 -0
- package/dist/cjs/models/device/cts500.model.js.map +1 -0
- package/dist/cjs/models/device/entralpi.model.d.ts.map +1 -1
- package/dist/cjs/models/device/entralpi.model.js +3 -5
- package/dist/cjs/models/device/entralpi.model.js.map +1 -1
- package/dist/cjs/models/device/forceboard.model.d.ts +2 -2
- package/dist/cjs/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/forceboard.model.js +3 -14
- package/dist/cjs/models/device/forceboard.model.js.map +1 -1
- package/dist/cjs/models/device/progressor.model.d.ts +25 -12
- package/dist/cjs/models/device/progressor.model.d.ts.map +1 -1
- package/dist/cjs/models/device/progressor.model.js +82 -31
- package/dist/cjs/models/device/progressor.model.js.map +1 -1
- package/dist/cjs/models/device.model.d.ts +7 -0
- package/dist/cjs/models/device.model.d.ts.map +1 -1
- package/dist/cjs/models/device.model.js +52 -32
- package/dist/cjs/models/device.model.js.map +1 -1
- package/dist/cjs/models/index.d.ts +2 -0
- package/dist/cjs/models/index.d.ts.map +1 -1
- package/dist/cjs/models/index.js +6 -1
- package/dist/cjs/models/index.js.map +1 -1
- package/dist/cjs/models/nordic.model.d.ts +128 -0
- package/dist/cjs/models/nordic.model.d.ts.map +1 -0
- package/dist/cjs/models/nordic.model.js +405 -0
- package/dist/cjs/models/nordic.model.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/command.interface.d.ts +120 -20
- package/dist/interfaces/command.interface.d.ts.map +1 -1
- package/dist/interfaces/device/climbro.interface.d.ts +25 -0
- package/dist/interfaces/device/climbro.interface.d.ts.map +1 -1
- package/dist/interfaces/device/cts500.interface.d.ts +96 -0
- package/dist/interfaces/device/cts500.interface.d.ts.map +1 -0
- package/dist/interfaces/device/cts500.interface.js +2 -0
- package/dist/interfaces/device/cts500.interface.js.map +1 -0
- package/dist/interfaces/device/forceboard.interface.d.ts +2 -2
- package/dist/interfaces/device/forceboard.interface.d.ts.map +1 -1
- package/dist/interfaces/device/progressor.interface.d.ts +17 -7
- package/dist/interfaces/device/progressor.interface.d.ts.map +1 -1
- package/dist/interfaces/index.d.ts +2 -0
- package/dist/interfaces/index.d.ts.map +1 -1
- package/dist/interfaces/nordic.interface.d.ts +47 -0
- package/dist/interfaces/nordic.interface.d.ts.map +1 -0
- package/dist/interfaces/nordic.interface.js +2 -0
- package/dist/interfaces/nordic.interface.js.map +1 -0
- package/dist/models/device/climbro.model.d.ts +25 -0
- package/dist/models/device/climbro.model.d.ts.map +1 -1
- package/dist/models/device/climbro.model.js +93 -1
- package/dist/models/device/climbro.model.js.map +1 -1
- package/dist/models/device/cts500.model.d.ts +173 -0
- package/dist/models/device/cts500.model.d.ts.map +1 -0
- package/dist/models/device/cts500.model.js +584 -0
- package/dist/models/device/cts500.model.js.map +1 -0
- package/dist/models/device/entralpi.model.d.ts.map +1 -1
- package/dist/models/device/entralpi.model.js +3 -5
- package/dist/models/device/entralpi.model.js.map +1 -1
- package/dist/models/device/forceboard.model.d.ts +2 -2
- package/dist/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/models/device/forceboard.model.js +3 -14
- package/dist/models/device/forceboard.model.js.map +1 -1
- package/dist/models/device/progressor.model.d.ts +25 -12
- package/dist/models/device/progressor.model.d.ts.map +1 -1
- package/dist/models/device/progressor.model.js +82 -31
- package/dist/models/device/progressor.model.js.map +1 -1
- package/dist/models/device.model.d.ts +7 -0
- package/dist/models/device.model.d.ts.map +1 -1
- package/dist/models/device.model.js +51 -32
- package/dist/models/device.model.js.map +1 -1
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/nordic.model.d.ts +128 -0
- package/dist/models/nordic.model.d.ts.map +1 -0
- package/dist/models/nordic.model.js +393 -0
- package/dist/models/nordic.model.js.map +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/index.ts +2 -0
- package/src/interfaces/command.interface.ts +143 -20
- package/src/interfaces/device/climbro.interface.ts +30 -0
- package/src/interfaces/device/cts500.interface.ts +113 -0
- package/src/interfaces/device/forceboard.interface.ts +2 -2
- package/src/interfaces/device/progressor.interface.ts +19 -7
- package/src/interfaces/index.ts +4 -0
- package/src/interfaces/nordic.interface.ts +47 -0
- package/src/models/device/climbro.model.ts +98 -1
- package/src/models/device/cts500.model.ts +702 -0
- package/src/models/device/entralpi.model.ts +3 -5
- package/src/models/device/forceboard.model.ts +3 -14
- package/src/models/device/progressor.model.ts +86 -31
- package/src/models/device.model.ts +60 -32
- package/src/models/index.ts +4 -0
- package/src/models/nordic.model.ts +468 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { Device } from "./device.model.js";
|
|
2
|
+
const NORDIC_DFU_SERVICE_UUID = "0000fe59-0000-1000-8000-00805f9b34fb";
|
|
3
|
+
const DFU_PACKET_SIZE = 20;
|
|
4
|
+
// Keep CRC values in signed int32 form so they compare directly with DataView.getInt32() responses from the bootloader.
|
|
5
|
+
const CRC32_TABLE = (() => {
|
|
6
|
+
const table = new Int32Array(256);
|
|
7
|
+
for (let index = 0; index < 256; index++) {
|
|
8
|
+
let crc = index;
|
|
9
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
10
|
+
crc = (crc & 1) !== 0 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;
|
|
11
|
+
}
|
|
12
|
+
table[index] = crc | 0;
|
|
13
|
+
}
|
|
14
|
+
return table;
|
|
15
|
+
})();
|
|
16
|
+
/**
|
|
17
|
+
* Creates a fresh Nordic Secure DFU service definition.
|
|
18
|
+
* Characteristics are mutable at runtime, so each device instance needs its own copy.
|
|
19
|
+
* @returns {Service} A new DFU service descriptor with control, packet, and buttonless characteristics.
|
|
20
|
+
*/
|
|
21
|
+
export function createNordicDfuService() {
|
|
22
|
+
return {
|
|
23
|
+
name: "Nordic Device Firmware Update (DFU) Service",
|
|
24
|
+
id: "dfu",
|
|
25
|
+
uuid: NORDIC_DFU_SERVICE_UUID,
|
|
26
|
+
characteristics: [
|
|
27
|
+
{
|
|
28
|
+
name: "DFU Control Point",
|
|
29
|
+
id: "control",
|
|
30
|
+
uuid: "8ec90001-f315-4f60-9fb8-838830daea50",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "DFU Packet",
|
|
34
|
+
id: "packet",
|
|
35
|
+
uuid: "8ec90002-f315-4f60-9fb8-838830daea50",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "Buttonless DFU",
|
|
39
|
+
id: "buttonless",
|
|
40
|
+
uuid: "8ec90003-f315-4f60-9fb8-838830daea50",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Shared Nordic Secure DFU implementation for devices exposing the FE59 service.
|
|
47
|
+
*/
|
|
48
|
+
export class NordicDfuDevice extends Device {
|
|
49
|
+
/**
|
|
50
|
+
* Returns a cached DFU characteristic discovered during the current GATT session.
|
|
51
|
+
* @param {"control" | "packet" | "buttonless"} characteristicId - The DFU characteristic identifier.
|
|
52
|
+
* @returns {BluetoothRemoteGATTCharacteristic | undefined} The discovered characteristic, if available.
|
|
53
|
+
*/
|
|
54
|
+
getDfuCharacteristic(characteristicId) {
|
|
55
|
+
return this.services
|
|
56
|
+
.find((service) => service.id === "dfu")
|
|
57
|
+
?.characteristics.find((characteristic) => characteristic.id === characteristicId)?.characteristic;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks whether the connected device is already exposing the DFU bootloader characteristics.
|
|
61
|
+
* @returns {boolean} `true` when both control and packet characteristics are available.
|
|
62
|
+
*/
|
|
63
|
+
hasDfuBootloaderCharacteristics() {
|
|
64
|
+
return this.getDfuCharacteristic("control") != null && this.getDfuCharacteristic("packet") != null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Wraps the shared connect flow so DFU callers get a rejected promise when connection setup fails.
|
|
68
|
+
* @returns {Promise<void>} Resolves when discovery is complete.
|
|
69
|
+
*/
|
|
70
|
+
async connectForDfu() {
|
|
71
|
+
await new Promise((resolve, reject) => {
|
|
72
|
+
void this.connect(() => resolve(), (error) => reject(error));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Prompts for a Bluetooth device matching the provided filters, then runs the normal service discovery flow.
|
|
77
|
+
* @param {BluetoothLEScanFilter[]} filters - Alternative device filters to pass to `requestDevice`.
|
|
78
|
+
* @returns {Promise<void>} Resolves after the selected device is connected and characteristics are cached.
|
|
79
|
+
*/
|
|
80
|
+
async requestAndConnectDfuDevice(filters) {
|
|
81
|
+
const bluetooth = await this.getBluetooth();
|
|
82
|
+
// Clear any stale GATT state before replacing the selected device with the bootloader identity.
|
|
83
|
+
this.disconnect();
|
|
84
|
+
delete this.bluetooth;
|
|
85
|
+
this.bluetooth = await bluetooth.requestDevice({
|
|
86
|
+
filters,
|
|
87
|
+
optionalServices: this.getAllServiceUUIDs(),
|
|
88
|
+
});
|
|
89
|
+
await this.connectForDfu();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Ensures there is an active connection to either the application or DFU bootloader variant of the device.
|
|
93
|
+
* @returns {Promise<void>} Resolves after a DFU-capable device has been connected and discovered.
|
|
94
|
+
*/
|
|
95
|
+
async ensureDfuCapableConnection() {
|
|
96
|
+
if (this.bluetooth?.gatt) {
|
|
97
|
+
try {
|
|
98
|
+
await this.connectForDfu();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// If the previously granted device no longer reconnects, fall back to a fresh picker.
|
|
103
|
+
this.disconnect();
|
|
104
|
+
delete this.bluetooth;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
await this.requestAndConnectDfuDevice([...this.filters, { services: [NORDIC_DFU_SERVICE_UUID] }]);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Prompts for the rebooted Nordic DFU bootloader after the application switches into buttonless DFU mode.
|
|
111
|
+
* @returns {Promise<void>} Resolves after the bootloader device is selected and connected.
|
|
112
|
+
*/
|
|
113
|
+
async connectDfuBootloader() {
|
|
114
|
+
try {
|
|
115
|
+
await this.requestAndConnectDfuDevice([{ services: [NORDIC_DFU_SERVICE_UUID] }]);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
119
|
+
const wrappedError = new Error(`Device entered DFU mode. Select the Nordic DFU bootloader to continue. ${message}`);
|
|
120
|
+
wrappedError.cause = error;
|
|
121
|
+
throw wrappedError;
|
|
122
|
+
}
|
|
123
|
+
if (!this.hasDfuBootloaderCharacteristics()) {
|
|
124
|
+
throw new Error("Selected device did not expose the Nordic DFU control and packet characteristics.");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Normalizes DFU payload inputs to `Uint8Array` so packet slicing and CRC calculation use one byte representation.
|
|
129
|
+
* @param {Uint8Array | ArrayBuffer} data - Raw DFU payload bytes.
|
|
130
|
+
* @returns {Uint8Array} The payload as a `Uint8Array`.
|
|
131
|
+
*/
|
|
132
|
+
toDfuBytes(data) {
|
|
133
|
+
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Calculates the Nordic Secure DFU CRC32 for the given payload prefix.
|
|
137
|
+
* @param {Uint8Array} data - The bytes to checksum.
|
|
138
|
+
* @returns {number} The signed 32-bit CRC value returned by Nordic DFU checksum responses.
|
|
139
|
+
*/
|
|
140
|
+
dfuCrc32(data) {
|
|
141
|
+
let crc = 0xffffffff;
|
|
142
|
+
for (const byte of data) {
|
|
143
|
+
const tableEntry = CRC32_TABLE[(crc ^ byte) & 0xff];
|
|
144
|
+
if (tableEntry === undefined) {
|
|
145
|
+
throw new Error("CRC32 lookup index out of range");
|
|
146
|
+
}
|
|
147
|
+
crc = tableEntry ^ (crc >>> 8);
|
|
148
|
+
}
|
|
149
|
+
return (crc ^ 0xffffffff) | 0;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Formats a signed CRC value as an unsigned hexadecimal string for error messages.
|
|
153
|
+
* @param {number} crc - The CRC value to format.
|
|
154
|
+
* @returns {string} The CRC formatted as `0x????????`.
|
|
155
|
+
*/
|
|
156
|
+
formatDfuCrc(crc) {
|
|
157
|
+
return `0x${(crc >>> 0).toString(16).padStart(8, "0")}`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Transfers one Nordic Secure DFU object type, handling resume, chunking, checksum validation, and execute steps.
|
|
161
|
+
* @param {"command" | "data"} objectType - The DFU object type to transfer.
|
|
162
|
+
* @param {Uint8Array | ArrayBuffer} data - The full payload for that object type.
|
|
163
|
+
* @returns {Promise<void>} Resolves when the payload has been fully transferred and validated.
|
|
164
|
+
*/
|
|
165
|
+
async dfuTransferObject(objectType, data) {
|
|
166
|
+
const bytes = this.toDfuBytes(data);
|
|
167
|
+
if (bytes.byteLength === 0) {
|
|
168
|
+
throw new Error(`DFU ${objectType.toUpperCase()} payload is required`);
|
|
169
|
+
}
|
|
170
|
+
const { maxSize, offset, crc } = await this.dfuSelect(objectType);
|
|
171
|
+
if (maxSize <= 0) {
|
|
172
|
+
throw new Error(`DFU ${objectType.toUpperCase()} maxSize ${maxSize} is invalid`);
|
|
173
|
+
}
|
|
174
|
+
if (offset > bytes.byteLength) {
|
|
175
|
+
throw new Error(`DFU ${objectType.toUpperCase()} offset ${offset} exceeds payload size ${bytes.byteLength}`);
|
|
176
|
+
}
|
|
177
|
+
// Validate the bootloader's resume point before sending more bytes; otherwise a resumed transfer could continue from a corrupt state.
|
|
178
|
+
if (offset > 0) {
|
|
179
|
+
const expectedCrc = this.dfuCrc32(bytes.slice(0, offset));
|
|
180
|
+
if (expectedCrc !== crc) {
|
|
181
|
+
throw new Error(`DFU ${objectType.toUpperCase()} resume CRC mismatch at offset ${offset}: expected ${this.formatDfuCrc(expectedCrc)}, got ${this.formatDfuCrc(crc)}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (offset === bytes.byteLength) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// The bootloader may report an offset in the middle of an object; restart from that object's boundary.
|
|
188
|
+
for (let objectStart = offset - (offset % maxSize); objectStart < bytes.byteLength;) {
|
|
189
|
+
const objectEnd = Math.min(objectStart + maxSize, bytes.byteLength);
|
|
190
|
+
await this.dfuCreate(objectType, objectEnd - objectStart);
|
|
191
|
+
// Packet writes stay at 20 bytes for Web Bluetooth compatibility with the default ATT payload size.
|
|
192
|
+
for (let packetStart = objectStart; packetStart < objectEnd; packetStart += DFU_PACKET_SIZE) {
|
|
193
|
+
await this.dfuWritePacket(bytes.slice(packetStart, Math.min(packetStart + DFU_PACKET_SIZE, objectEnd)));
|
|
194
|
+
}
|
|
195
|
+
// Nordic reports checksum state for the whole transferred prefix, not just the current object chunk.
|
|
196
|
+
const state = await this.dfuChecksum();
|
|
197
|
+
if (state.offset !== objectEnd) {
|
|
198
|
+
throw new Error(`DFU ${objectType.toUpperCase()} checksum offset ${state.offset} did not match ${objectEnd}`);
|
|
199
|
+
}
|
|
200
|
+
const expectedCrc = this.dfuCrc32(bytes.slice(0, state.offset));
|
|
201
|
+
if (state.crc !== expectedCrc) {
|
|
202
|
+
throw new Error(`DFU ${objectType.toUpperCase()} checksum CRC mismatch at offset ${state.offset}: expected ${this.formatDfuCrc(expectedCrc)}, got ${this.formatDfuCrc(state.crc)}`);
|
|
203
|
+
}
|
|
204
|
+
await this.dfuExecute();
|
|
205
|
+
objectStart = state.offset;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Switches the device from application mode into the Nordic DFU bootloader.
|
|
210
|
+
* @returns {Promise<void>} Resolves after the device reboots into DFU mode and reconnects to the bootloader.
|
|
211
|
+
*/
|
|
212
|
+
dfuSwitch = async () => {
|
|
213
|
+
// Reuse the existing connect/onConnected path, but subscribe to the buttonless DFU notifier.
|
|
214
|
+
this.notifyCharacteristicId = "buttonless";
|
|
215
|
+
try {
|
|
216
|
+
await this.ensureDfuCapableConnection();
|
|
217
|
+
if (this.hasDfuBootloaderCharacteristics()) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (!this.getDfuCharacteristic("buttonless")) {
|
|
221
|
+
throw new Error('Characteristic "buttonless" not found in service "dfu".');
|
|
222
|
+
}
|
|
223
|
+
const device = this.bluetooth;
|
|
224
|
+
if (!device?.gatt?.connected) {
|
|
225
|
+
throw new Error("Device must be connected before entering DFU mode");
|
|
226
|
+
}
|
|
227
|
+
await new Promise((resolve, reject) => {
|
|
228
|
+
const cleanup = () => {
|
|
229
|
+
device.removeEventListener("gattserverdisconnected", onDisconnected);
|
|
230
|
+
};
|
|
231
|
+
const onDisconnected = () => {
|
|
232
|
+
// Entering buttonless DFU reboots the device, so disconnect is the success signal here.
|
|
233
|
+
cleanup();
|
|
234
|
+
resolve();
|
|
235
|
+
};
|
|
236
|
+
device.addEventListener("gattserverdisconnected", onDisconnected, { once: true });
|
|
237
|
+
// Opcode 0x01 requests a switch from application mode into the Nordic DFU bootloader.
|
|
238
|
+
this.write("dfu", "buttonless", new Uint8Array([0x01])).catch((error) => {
|
|
239
|
+
cleanup();
|
|
240
|
+
reject(error);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
// After the reboot, prompt for the bootloader explicitly instead of assuming the browser will reconnect to the same BLE identity.
|
|
244
|
+
await this.connectDfuBootloader();
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
// Restore the normal application notify characteristic after the DFU transition attempt.
|
|
248
|
+
this.notifyCharacteristicId = "rx";
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Sends a raw Nordic Secure DFU control operation and resolves with the response payload bytes.
|
|
253
|
+
* Call after dfuSwitch() has reconnected to the DFU bootloader.
|
|
254
|
+
* @param {Uint8Array} operation - The DFU control opcode bytes to send.
|
|
255
|
+
* @param {ArrayBuffer} [payload] - Optional payload appended to the opcode.
|
|
256
|
+
* @returns {Promise<Uint8Array>} Resolves with the response payload bytes after the 3-byte Nordic response header.
|
|
257
|
+
*/
|
|
258
|
+
dfuControl = async (operation, payload) => {
|
|
259
|
+
if (operation.length === 0) {
|
|
260
|
+
throw new Error("DFU control operation is required");
|
|
261
|
+
}
|
|
262
|
+
const control = this.getDfuCharacteristic("control");
|
|
263
|
+
if (!control) {
|
|
264
|
+
throw new Error('Characteristic "control" not found in service "dfu". Call dfuSwitch() first.');
|
|
265
|
+
}
|
|
266
|
+
const value = new Uint8Array(operation.length + (payload?.byteLength ?? 0));
|
|
267
|
+
value.set(operation);
|
|
268
|
+
if (payload) {
|
|
269
|
+
value.set(new Uint8Array(payload), operation.length);
|
|
270
|
+
}
|
|
271
|
+
await control.startNotifications();
|
|
272
|
+
return await new Promise((resolve, reject) => {
|
|
273
|
+
const cleanup = () => {
|
|
274
|
+
control.removeEventListener("characteristicvaluechanged", onNotification);
|
|
275
|
+
};
|
|
276
|
+
const onNotification = (event) => {
|
|
277
|
+
const target = event.target;
|
|
278
|
+
const view = target.value;
|
|
279
|
+
// Control responses are framed as 0x60 <opcode> <status> [...payload].
|
|
280
|
+
if (!view || view.getUint8(0) !== 0x60 || view.getUint8(1) !== operation[0]) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
cleanup();
|
|
284
|
+
const status = view.getUint8(2);
|
|
285
|
+
if (status === 0x01) {
|
|
286
|
+
const response = new Uint8Array(view.buffer, view.byteOffset + 3, view.byteLength - 3);
|
|
287
|
+
resolve(Uint8Array.from(response));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (status === 0x0b && view.byteLength > 3) {
|
|
291
|
+
reject(new Error(`DFU control failed with extended error 0x${view.getUint8(3).toString(16).padStart(2, "0")}`));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
reject(new Error(`DFU control failed with status 0x${status.toString(16).padStart(2, "0")}`));
|
|
295
|
+
};
|
|
296
|
+
control.addEventListener("characteristicvaluechanged", onNotification);
|
|
297
|
+
control.writeValue(value).catch((error) => {
|
|
298
|
+
cleanup();
|
|
299
|
+
reject(error);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Sends Nordic Secure DFU SELECT for command or data objects and returns the bootloader state.
|
|
305
|
+
* @param {"command" | "data"} objectType - The object type to query.
|
|
306
|
+
* @returns {Promise<{ maxSize: number; offset: number; crc: number }>} The bootloader's object size, offset, and CRC state.
|
|
307
|
+
*/
|
|
308
|
+
dfuSelect = async (objectType) => {
|
|
309
|
+
const response = await this.dfuControl(new Uint8Array([0x06, objectType === "command" ? 0x01 : 0x02]));
|
|
310
|
+
if (response.byteLength < 12) {
|
|
311
|
+
throw new Error("DFU SELECT response was shorter than expected");
|
|
312
|
+
}
|
|
313
|
+
const view = new DataView(response.buffer, response.byteOffset, response.byteLength);
|
|
314
|
+
return {
|
|
315
|
+
maxSize: view.getUint32(0, true),
|
|
316
|
+
offset: view.getUint32(4, true),
|
|
317
|
+
crc: view.getInt32(8, true),
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* Sends Nordic Secure DFU CREATE for command or data objects.
|
|
322
|
+
* @param {"command" | "data"} objectType - The object type to create.
|
|
323
|
+
* @param {number} size - The size of the object chunk to allocate in bytes.
|
|
324
|
+
* @returns {Promise<void>} Resolves when the bootloader accepts the object allocation request.
|
|
325
|
+
*/
|
|
326
|
+
dfuCreate = async (objectType, size) => {
|
|
327
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
328
|
+
throw new Error("DFU CREATE size must be a non-negative number");
|
|
329
|
+
}
|
|
330
|
+
const payload = new ArrayBuffer(4);
|
|
331
|
+
new DataView(payload).setUint32(0, size, true);
|
|
332
|
+
await this.dfuControl(new Uint8Array([0x01, objectType === "command" ? 0x01 : 0x02]), payload);
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Writes raw bytes to the Nordic Secure DFU packet characteristic.
|
|
336
|
+
* @param {Uint8Array | ArrayBuffer} data - The packet payload bytes to send.
|
|
337
|
+
* @returns {Promise<void>} Resolves after the packet has been written.
|
|
338
|
+
*/
|
|
339
|
+
dfuWritePacket = async (data) => {
|
|
340
|
+
await this.write("dfu", "packet", data instanceof Uint8Array ? data : new Uint8Array(data), 0);
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Sends Nordic Secure DFU CALCULATE_CHECKSUM and returns the bootloader state.
|
|
344
|
+
* @returns {Promise<{ offset: number; crc: number }>} The bootloader's transferred offset and CRC for the current object stream.
|
|
345
|
+
*/
|
|
346
|
+
dfuChecksum = async () => {
|
|
347
|
+
const response = await this.dfuControl(new Uint8Array([0x03]));
|
|
348
|
+
if (response.byteLength < 8) {
|
|
349
|
+
throw new Error("DFU CHECKSUM response was shorter than expected");
|
|
350
|
+
}
|
|
351
|
+
const view = new DataView(response.buffer, response.byteOffset, response.byteLength);
|
|
352
|
+
return {
|
|
353
|
+
offset: view.getUint32(0, true),
|
|
354
|
+
crc: view.getInt32(4, true),
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Sends Nordic Secure DFU EXECUTE for the currently created object.
|
|
359
|
+
* @returns {Promise<void>} Resolves when the bootloader executes the current DFU object.
|
|
360
|
+
*/
|
|
361
|
+
dfuExecute = async () => {
|
|
362
|
+
await this.dfuControl(new Uint8Array([0x04]));
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* Runs a complete Nordic Secure DFU upload: switch to bootloader, send init packet, then send firmware.
|
|
366
|
+
* @param {Uint8Array | ArrayBuffer} initPacket - The Nordic Secure DFU init packet bytes.
|
|
367
|
+
* @param {Uint8Array | ArrayBuffer} firmware - The firmware image bytes to upload.
|
|
368
|
+
* @returns {Promise<void>} Resolves after the firmware upload completes and the bootloader disconnects to reboot.
|
|
369
|
+
*/
|
|
370
|
+
dfuUpload = async (initPacket, firmware) => {
|
|
371
|
+
await this.dfuSwitch();
|
|
372
|
+
await this.dfuTransferObject("command", initPacket);
|
|
373
|
+
const device = this.bluetooth;
|
|
374
|
+
if (!device?.gatt?.connected) {
|
|
375
|
+
throw new Error("Device disconnected before firmware transfer started");
|
|
376
|
+
}
|
|
377
|
+
// Attach the listener before the final data phase so a fast reboot cannot disconnect before we start waiting.
|
|
378
|
+
const waitForDisconnect = new Promise((resolve) => {
|
|
379
|
+
const onDisconnected = () => {
|
|
380
|
+
device.removeEventListener("gattserverdisconnected", onDisconnected);
|
|
381
|
+
resolve();
|
|
382
|
+
};
|
|
383
|
+
device.addEventListener("gattserverdisconnected", onDisconnected, { once: true });
|
|
384
|
+
});
|
|
385
|
+
await this.dfuTransferObject("data", firmware);
|
|
386
|
+
// Some browsers observe the disconnect before the awaited transfer returns, so avoid waiting twice.
|
|
387
|
+
if (!device.gatt?.connected) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
await waitForDisconnect;
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=nordic.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nordic.model.js","sourceRoot":"","sources":["../../src/models/nordic.model.ts"],"names":[],"mappings":"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"}
|