node-zwave-js/docs/examples/ota-update.md

193 lines
6.1 KiB
Markdown

# ☁️ Perform an OTA update {docsify-ignore-all}
> **NOTE:** The following example expects that you have worked through the [Quick Start](getting-started/quickstart.md) guide and the [Blink a light with Basic CC](examples/basic-on-off.md) example. Make sure to do that before continuing.
This example assumes that you have a file with the OTA update on disk. To read it, we need to import the `fs/promises` module. Add this to the top of your script:
```ts
import fs from "node:fs/promises";
```
Then read the file before the firmware update:
```ts
const filename = "path/to/firmware.bin";
const rawData = await fs.readFile(filename);
```
There are lots of different file formats for Z-Wave firmware updates. Some are encoded as [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX), some are binary. Some are compressed, some are encrypted in a format that the end node can decrypt, some are encrypted in a format that has to be decrypted first, some are just plaintext.
Z-Wave JS can help figure out the format of the firmware file and extract the data that needs to be transmitted to the end device. To have Z-Wave JS "guess" the format of the firmware file, use the `guessFirmwareFileFormat` method. This takes conventions about the filename into account, e.g. `.otz` files are typically encoded as Intel HEX, but can also be binary, `.gbl` files are to be transmitted as-is.
To do so, add this import to the top of your script:
```ts
import { extractFirmwareAsync, guessFirmwareFileFormat } from "@zwave-js/core";
```
and determine the firmware format:
```ts
const format = guessFirmwareFileFormat(filename);
```
Alternatively, you can provide the format yourself, see the [`updateFirmware` documentation](api/node#updatefirmware) for details. For example, if you know that the firmware file is in the `gbl` format, you can specify it like this:
```ts
const format: FirmwareFileFormat = "gbl";
```
Now extract the firmware data that should be transmitted to the end device:
```ts
const firmware = await extractFirmwareAsync(rawData, format);
```
Most file formats do not contain information about the firmware target that should be updated. If you plan on updating a different target than the Z-Wave chip (0), set it manually:
```ts
firmware.firmwareTarget = 1;
```
Likewise, there is no way to determine the firmware ID from firmware update files. If your end device validates the firmware ID, you need to set it manuall, e.g.:
```ts
firmware.firmwareId = 0xcafe;
```
Afterwards, start the firmware update:
```ts
try {
const result = await node.updateFirmware([firmware]);
if (result.success) {
console.log("Firmware update finished successfully");
} else {
console.error("Firmware update failed with status:", result.status);
}
} catch (error) {
console.error("Failed to start firmware update:", error);
}
```
If you know that your firmware data can be transmitted as-is, you can also skip the extraction part and provide the raw data directly:
```ts
const result = await node.updateFirmware([{
data: firmwareData,
// optionally specify target and firmare ID:
firmwareTarget: 1,
firmwareId: 0xcafe,
}]);
```
To be notified of the progress, two driver events exist. Listening to progress can be done like this:
```ts
driver.on("firmware update progress", (node, progress) => {
console.log(
`Firmware update progress: ${progress.sentFragments}/${progress.totalFragments}`,
);
});
```
To be notified about the outcome, either `await` the `updateFirmware` call like we did above, or use the following event:
```ts
driver.on("firmware update finished", (node, result) => {
if (result.success) {
console.log("Firmware update finished successfully");
} else {
console.error("Firmware update failed with status:", result.status);
}
});
```
## Putting it all together
Here's a complete example where you can provide the node ID, firmware file, target and firmware ID as command line arguments
```ts
import { extractFirmwareAsync, guessFirmwareFileFormat } from "@zwave-js/core";
import fs from "node:fs/promises";
import { Driver } from "zwave-js";
// ...driver initialization, see Quick Start guide
async function main() {
const node = driver.controller.nodes.getOrThrow(2);
// Expects the script to be called like
// node ota-update.js <nodeId> <path/to/firmware> [<target> <firmwareIdHex>]
// where the target and firmware id are optional
const nodeId = process.argv[2] ? parseInt(process.argv[2], 10) : undefined;
const filename = process.argv[3];
if (!nodeId || !filename) {
console.error(
"Usage: node ota-update.js <nodeId> <path/to/firmware> [<target> <firmwareIdHex>]",
);
process.exit(1);
}
const node = driver.controller.nodes.get(nodeId);
if (!node) {
console.error(`Node ${nodeId} not found`);
process.exit(1);
}
const firmwareTarget = process.argv[4] ? parseInt(process.argv[4], 10) : 0;
const firmwareId = process.argv[5]
? parseInt(process.argv[5], 16)
: undefined;
const rawData = await fs.readFile(filename);
let firmware;
try {
const format = guessFirmwareFileFormat(filename);
firmware = await extractFirmwareAsync(rawData, format);
} catch (e) {
console.error("Failed to extract firmware", e);
process.exit(1);
}
if (firmwareTarget !== 0) {
firmware.firmwareTarget = firmwareTarget;
}
if (firmwareId != undefined) {
firmware.firmwareId = firmwareId;
}
try {
const result = await node.updateFirmware([firmware]);
if (result.success) {
console.log("Firmware update finished successfully");
process.exit(0);
} else {
console.error("Firmware update failed with status:", result.status);
process.exit(1);
}
} catch (error) {
console.error("Failed to start firmware update:", error);
process.exit(1);
}
}
driver.on("firmware update progress", (node, progress) => {
console.log(
`Firmware update progress: ${progress.sentFragments}/${progress.totalFragments}`,
);
});
```
To update node 2 with `path/to/firmware.bin`, simply run the script with
```bash
node ota-update.js 2 path/to/firmware.bin
```
If you want to update a different target (1) or firmware ID (`0xcafe`), you can provide them as additional arguments:
```bash
node ota-update.js 2 path/to/firmware.bin 1 0xcafe
```