diff --git a/src/app/constants.ts b/src/app/constants.ts index b6639a30..1870b11d 100644 --- a/src/app/constants.ts +++ b/src/app/constants.ts @@ -51,9 +51,6 @@ export const pybricksUsbDfuWindowsDriverInstallUrl = export const pybricksUsbLinuxUdevRulesUrl = 'https://github.com/pybricks/support/discussions/688#discussioncomment-3239099'; -export const pybricksBleFirmwareRestoreVideoUrl = - 'https://pybricks.com/install/technic-boost-city/#restoring-the-original-firmware'; - /** Pybricks copyright statement. */ export const pybricksCopyright = 'Copyright (c) 2020-2023 The Pybricks Authors'; diff --git a/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx index 87db2f4f..706f1cce 100644 --- a/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx +++ b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx @@ -6,6 +6,7 @@ import { Callout, Intent } from '@blueprintjs/core'; import classNames from 'classnames'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { + legoRegisteredTrademark, pybricksUsbDfuWindowsDriverInstallUrl, pybricksUsbLinuxUdevRulesUrl, } from '../../app/constants'; @@ -24,10 +25,24 @@ import primeHubMp4 from './assets/bootloader-primehub-540.mp4'; import primeHubVtt from './assets/bootloader-primehub-metadata.vtt'; import technicHubMp4 from './assets/bootloader-technichub-540.mp4'; import technicHubVtt from './assets/bootloader-technichub-metadata.vtt'; +import cityHubRecoveryMp4 from './assets/recover-cityhub-540.mp4'; +import cityHubRecoveryVtt from './assets/recover-cityhub-metadata.vtt'; +import moveHubRecoveryMp4 from './assets/recover-movehub-540.mp4'; +import moveHubRecoveryVtt from './assets/recover-movehub-metadata.vtt'; +import technicHubRecoveryMp4 from './assets/recover-technichub-540.mp4'; +import technicHubRecoveryVtt from './assets/recover-technichub-metadata.vtt'; import { useI18n } from './i18n'; type BootloaderInstructionsProps = { + /** + * The instructions and video will be customized for this hub. + */ hubType: Hub; + /** + * If true, show official firmware recovery video and steps for supported hubs. + * @default false + */ + recovery?: boolean; }; const videoFileMap: ReadonlyMap = new Map([ @@ -48,13 +63,30 @@ const metadataFileMap: ReadonlyMap = new Map([ [Hub.Technic, technicHubVtt], ]); +const recoveryVideoFileMap: ReadonlyMap = new Map([ + [Hub.City, cityHubRecoveryMp4], + [Hub.Essential, essentialHubMp4], + [Hub.Inventor, inventorHubMp4], + [Hub.Move, moveHubRecoveryMp4], + [Hub.Prime, primeHubMp4], + [Hub.Technic, technicHubRecoveryMp4], +]); + +const recoveryMetadataFileMap: ReadonlyMap = new Map([ + [Hub.City, cityHubRecoveryVtt], + [Hub.Essential, essentialHubVtt], + [Hub.Inventor, inventorHubVtt], + [Hub.Move, moveHubRecoveryVtt], + [Hub.Prime, primeHubVtt], + [Hub.Technic, technicHubRecoveryVtt], +]); /** * Provides customized instructions on how to enter bootloader mode based * on the hub type. */ const BootloaderInstructions: React.VoidFunctionComponent< BootloaderInstructionsProps -> = ({ hubType }) => { +> = ({ hubType, recovery }) => { const i18n = useI18n(); const { button, light, lightPattern } = useMemo(() => { @@ -139,10 +171,21 @@ const BootloaderInstructions: React.VoidFunctionComponent< disablePictureInPicture className="pb-bootloader-video" > - + @@ -153,6 +196,10 @@ const BootloaderInstructions: React.VoidFunctionComponent< {i18n.translate('instruction', { startPoweredOff: hubHasUSB(hubType) ? i18n.translate('startPoweredOff.usb') + : recovery + ? i18n.translate('startPoweredOff.recovery', { + lego: legoRegisteredTrademark, + }) : i18n.translate('startPoweredOff.default'), })}

@@ -210,8 +257,18 @@ const BootloaderInstructions: React.VoidFunctionComponent< )} + {recovery && !hubHasUSB(hubType) && ( +
  • + {i18n.translate('step.waitAppConnect')} +
  • + )} + {/* hubs with USB will keep the power on, but other hubs won't */} - {hubHasUSB(hubType) ? ( + {recovery || hubHasUSB(hubType) ? (
  • 00:07.000 + +hold-button +00:07.000 --> 00:11.000 + +wait-for-light +00:11.000 --> 00:19.000 + +wait-app-connect +00:19.000 --> 00:25.000 + +release-button +00:25.000 --> 00:28.000 diff --git a/src/firmware/bootloaderInstructions/assets/recover-movehub-540.mp4 b/src/firmware/bootloaderInstructions/assets/recover-movehub-540.mp4 new file mode 100644 index 00000000..8d893483 Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/recover-movehub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/recover-movehub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/recover-movehub-metadata.vtt new file mode 100644 index 00000000..574c78a7 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/recover-movehub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +hold-button +00:00.100 --> 00:05.000 + +wait-for-light +00:05.000 --> 00:11.000 + +wait-app-connect +00:11.000 --> 00:15.000 + +release-button +00:15.000 --> 00:18.000 diff --git a/src/firmware/bootloaderInstructions/assets/recover-technichub-540.mp4 b/src/firmware/bootloaderInstructions/assets/recover-technichub-540.mp4 new file mode 100644 index 00000000..7c727160 Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/recover-technichub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/recover-technichub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/recover-technichub-metadata.vtt new file mode 100644 index 00000000..12a5eda5 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/recover-technichub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +hold-button +00:05.000 --> 00:08.000 + +wait-for-light +00:08.000 --> 00:14.000 + +wait-app-connect +00:14.000 --> 00:21.000 + +release-button +00:21.000 --> 00:24.000 diff --git a/src/firmware/bootloaderInstructions/bootloaderInstructions.scss b/src/firmware/bootloaderInstructions/bootloaderInstructions.scss index 540b4dfb..1bfe0556 100644 --- a/src/firmware/bootloaderInstructions/bootloaderInstructions.scss +++ b/src/firmware/bootloaderInstructions/bootloaderInstructions.scss @@ -8,5 +8,6 @@ .pb-bootloader-video { width: 90%; - align-self: center; + display: block; + margin: 0 auto; } diff --git a/src/firmware/bootloaderInstructions/translations/en.json b/src/firmware/bootloaderInstructions/translations/en.json index f9cc8a58..5113176f 100644 --- a/src/firmware/bootloaderInstructions/translations/en.json +++ b/src/firmware/bootloaderInstructions/translations/en.json @@ -7,7 +7,8 @@ "instruction": "To flash the firmware, the hub must be placed in bootloader mode. {startPoweredOff}, then follow the instructions below:", "startPoweredOff": { "default": "Start with the hub powered off", - "usb": "Start with the hub powered off and the USB cable disconnected" + "usb": "Start with the hub powered off and the USB cable disconnected", + "recovery": "Start with the hub powered off and the {lego} PoweredUp app opened to any 'Create' program" }, "button": { "bluetooth": "Bluetooth button", @@ -26,7 +27,8 @@ "holdButton": "Press and hold the {button} on the hub.", "connectUsb": "Connect the USB cable.", "waitForLight": "Keep holding the {button} and wait for the {light} to start flashing {lightPattern}. This takes about 5 seconds.", - "releaseButton": "Release the {button}", + "waitAppConnect": "The app will automatically connect and start flashing the firmware.", + "releaseButton": "Release the {button}.", "keepHolding": "Keep holding the {button}." } } diff --git a/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx b/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx index 8989a40f..a2d1f344 100644 --- a/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx +++ b/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx @@ -11,11 +11,7 @@ import { import classNames from 'classnames'; import React from 'react'; import { useDispatch } from 'react-redux'; -import { - legoRegisteredTrademark, - pybricksBleFirmwareRestoreVideoUrl, -} from '../../app/constants'; -import ExternalLinkIcon from '../../components/ExternalLinkIcon'; +import { legoRegisteredTrademark } from '../../app/constants'; import { hubHasUSB } from '../../components/hubPicker'; import { HubPicker } from '../../components/hubPicker/HubPicker'; import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks'; @@ -51,7 +47,7 @@ const RestoreFirmwarePanel: React.VoidFunctionComponent = () => { return (
    - + {hubHasUSB(hubType) ? ( <>

    @@ -73,25 +69,7 @@ const RestoreFirmwarePanel: React.VoidFunctionComponent = () => { ) : ( -

    - {i18n.translate('restoreFirmwarePanel.instruction2.ble.message', { - lego: legoRegisteredTrademark, - thisVideo: ( - <> - - {i18n.translate( - 'restoreFirmwarePanel.instruction2.ble.thisVideo', - )} - - - - ), - })} -

    +

    {i18n.translate('restoreFirmwarePanel.instruction2.ble.message')}

    )}
    ); diff --git a/src/firmware/restoreOfficialDialog/translations/en.json b/src/firmware/restoreOfficialDialog/translations/en.json index 95727a3e..1a091da9 100644 --- a/src/firmware/restoreOfficialDialog/translations/en.json +++ b/src/firmware/restoreOfficialDialog/translations/en.json @@ -12,8 +12,7 @@ "title": "Restore firmware", "instruction2": { "ble": { - "message": "Then use the official {lego} app for this hub to connect to the hub and restore the firmware. Watch {thisVideo} to see how.", - "thisVideo": "this video" + "message": "When the progress bar is done, the hub will reboot and the app will connect. The official firmware has been restored." }, "dfu": "Then click the {flashFirmware} button below to connect to the hub and flash the firmware. After flashing is complete, connect the hub to one of the official {lego} apps and use it to update to the latest official firmware." },