diff --git a/src/firmware/actions.ts b/src/firmware/actions.ts
index 051a95e8..1e7dc03f 100644
--- a/src/firmware/actions.ts
+++ b/src/firmware/actions.ts
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
-import { FirmwareReaderError } from '@pybricks/firmware';
+import { FirmwareReaderError, HubType } from '@pybricks/firmware';
import { createAction } from '../actions';
import { Hub } from '../components/hubPicker';
@@ -360,14 +360,14 @@ export const didFailToFinish = createAction(didFailToFinishCreator);
/**
* Low-level action to flash firmware using LEGO's DFU over USB.
- * @param data The firmware zip file data.
- * @param hubName A custom hub name or an empty string to use the default name.
+ * @param firmware The firmware binary blob.
+ * @param hubType The hub type the firmware blob is for.
*/
export const firmwareFlashUsbDfu = createAction(
- (data: ArrayBuffer, hubName: string) => ({
+ (firmware: ArrayBuffer, hubType: HubType) => ({
type: 'firmware.action.flashUsbDfu',
- data,
- hubName,
+ firmware,
+ hubType,
}),
);
diff --git a/src/firmware/alerts/FirmwareMismatch.tsx b/src/firmware/alerts/FirmwareMismatch.tsx
deleted file mode 100644
index dde81080..00000000
--- a/src/firmware/alerts/FirmwareMismatch.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2022 The Pybricks Authors
-
-import { Intent } from '@blueprintjs/core';
-import React from 'react';
-import type { CreateToast } from '../../toasterTypes';
-import { useI18n } from './i18n';
-
-const FirmwareMismatch: React.VoidFunctionComponent = () => {
- const i18n = useI18n();
- return
{i18n.translate('firmwareMismatch.message')}
;
-};
-
-export const firmwareMismatch: CreateToast = (onAction) => {
- return {
- message: ,
- icon: 'error',
- intent: Intent.DANGER,
- onDismiss: () => onAction('dismiss'),
- };
-};
diff --git a/src/firmware/alerts/index.ts b/src/firmware/alerts/index.ts
index 2ae9b719..d25d162c 100644
--- a/src/firmware/alerts/index.ts
+++ b/src/firmware/alerts/index.ts
@@ -2,7 +2,6 @@
// Copyright (c) 2022 The Pybricks Authors
import { dfuError } from './DfuError';
-import { firmwareMismatch } from './FirmwareMismatch';
import { flashProgress } from './FlashProgress';
import { noDfuHub } from './NoDfuHub';
import { noDfuInterface } from './NoDfuInterface';
@@ -11,7 +10,6 @@ import { releaseButton } from './ReleaseButton';
export default {
dfuError,
- firmwareMismatch,
flashProgress,
noDfuHub,
noDfuInterface,
diff --git a/src/firmware/alerts/translations/en.json b/src/firmware/alerts/translations/en.json
index d893bd5f..5784789e 100644
--- a/src/firmware/alerts/translations/en.json
+++ b/src/firmware/alerts/translations/en.json
@@ -20,9 +20,6 @@
"noDfuInterface": {
"message": "This is very unusual. The USB device did not contain the expected interface."
},
- "firmwareMismatch": {
- "message": "Cannot flash firmware. The firmware file is for a different kind of hub."
- },
"flashProgress": {
"erasing": "Erasing internal flash memory: {percent}",
"flashing": "Writing new firmware: {percent}"
diff --git a/src/firmware/sagas.ts b/src/firmware/sagas.ts
index f64bb5ea..da6b6d02 100644
--- a/src/firmware/sagas.ts
+++ b/src/firmware/sagas.ts
@@ -616,13 +616,6 @@ function* handleFlashFirmware(action: ReturnType): Generat
}
}
-/** Maps USB Product ID to LWP3 hub type ID */
-const productIdMap: ReadonlyMap = new Map([
- [LegoUsbProductId.SpikePrimeBootloader, HubType.PrimeHub],
- [LegoUsbProductId.SpikeEssentialBootloader, HubType.EssentialHub],
- [LegoUsbProductId.MindstormsRobotInventorBootloader, HubType.PrimeHub],
-]);
-
// currently all hubs use the same start address
const dfuFirmwareStartAddress = 0x08008000;
@@ -664,6 +657,31 @@ function* handleDfuWriteProcess(event: {
);
}
+function getUsbDeviceFiltersForHub(hubType: HubType): USBDeviceFilter[] {
+ switch (hubType) {
+ case HubType.PrimeHub:
+ return [
+ {
+ vendorId: legoUsbVendorId,
+ productId: LegoUsbProductId.SpikePrimeBootloader,
+ },
+ {
+ vendorId: legoUsbVendorId,
+ productId: LegoUsbProductId.MindstormsRobotInventorBootloader,
+ },
+ ];
+ case HubType.EssentialHub:
+ return [
+ {
+ vendorId: legoUsbVendorId,
+ productId: LegoUsbProductId.SpikeEssentialBootloader,
+ },
+ ];
+ default:
+ throw new Error(`unsupported hub type: ${hubType}`);
+ }
+}
+
function* handleFlashUsbDfu(action: ReturnType): Generator {
const defer = new Array<() => void>();
@@ -678,21 +696,7 @@ function* handleFlashUsbDfu(action: ReturnType): Gen
const device = yield* call(() =>
navigator.usb
.requestDevice({
- filters: [
- {
- vendorId: legoUsbVendorId,
- productId: LegoUsbProductId.SpikePrimeBootloader,
- },
- {
- vendorId: legoUsbVendorId,
- productId: LegoUsbProductId.SpikeEssentialBootloader,
- },
- {
- vendorId: legoUsbVendorId,
- productId:
- LegoUsbProductId.MindstormsRobotInventorBootloader,
- },
- ],
+ filters: getUsbDeviceFiltersForHub(action.hubType),
})
.catch((err) => {
if (err instanceof DOMException && err.name === 'NotFoundError') {
@@ -749,16 +753,8 @@ function* handleFlashUsbDfu(action: ReturnType): Gen
}),
);
- const { firmware, deviceId } = yield* loadFirmware(action.data, action.hubName);
-
- if (deviceId !== productIdMap.get(device.productId)) {
- yield* put(alertsShowAlert('firmware', 'firmwareMismatch'));
- yield* put(firmwareDidFailToFlashUsbDfu());
- return;
- }
-
dfu.dfuseStartAddress = dfuFirmwareStartAddress;
- const writeProc = dfu.write(1024, firmware, true);
+ const writeProc = dfu.write(1024, action.firmware, true);
const eraseProcessChan = eventChannel<{
bytesSent: number;
@@ -817,6 +813,7 @@ function* handleFlashUsbDfu(action: ReturnType): Gen
// errors can happen, e.g. if the USB cable is disconnected while
// flashing the firmware
if (error) {
+ // istanbul ignore if
if (process.env.NODE_ENV !== 'test') {
console.error(error);
}
@@ -844,6 +841,7 @@ function* handleFlashUsbDfu(action: ReturnType): Gen
yield* put(firmwareDidFlashUsbDfu());
} catch (err) {
+ // istanbul ignore if
if (process.env.NODE_ENV !== 'test') {
console.error(err);
}
@@ -878,7 +876,25 @@ function* handleInstallPybricks(): Generator {
yield* put(flashFirmware(accepted.firmwareZip, accepted.hubName));
break;
case 'usb-lego-dfu':
- yield* put(firmwareFlashUsbDfu(accepted.firmwareZip, accepted.hubName));
+ try {
+ const { firmware, deviceId } = yield* loadFirmware(
+ accepted.firmwareZip,
+ accepted.hubName,
+ );
+
+ yield* put(firmwareFlashUsbDfu(firmware, deviceId));
+ } catch (err) {
+ // istanbul ignore if
+ if (process.env.NODE_ENV !== 'test') {
+ console.error(err);
+ }
+
+ yield* put(
+ alertsShowAlert('alerts', 'unexpectedError', {
+ error: ensureError(err),
+ }),
+ );
+ }
break;
}
}