Skip to content

Commit

Permalink
bootloaderInstructions: add recovery variant
Browse files Browse the repository at this point in the history
For official firmware recovery, we have separate videos for non-USB
hubs.

Closes: pybricks/support#728
  • Loading branch information
dlech committed Dec 9, 2022
1 parent 0f871c6 commit 91802f7
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 37 deletions.
3 changes: 0 additions & 3 deletions src/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
65 changes: 61 additions & 4 deletions src/firmware/bootloaderInstructions/BootloaderInstructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<Hub, string> = new Map([
Expand All @@ -48,13 +63,30 @@ const metadataFileMap: ReadonlyMap<Hub, string> = new Map([
[Hub.Technic, technicHubVtt],
]);

const recoveryVideoFileMap: ReadonlyMap<Hub, string> = new Map([
[Hub.City, cityHubRecoveryMp4],
[Hub.Essential, essentialHubMp4],
[Hub.Inventor, inventorHubMp4],
[Hub.Move, moveHubRecoveryMp4],
[Hub.Prime, primeHubMp4],
[Hub.Technic, technicHubRecoveryMp4],
]);

const recoveryMetadataFileMap: ReadonlyMap<Hub, string> = 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(() => {
Expand Down Expand Up @@ -139,10 +171,21 @@ const BootloaderInstructions: React.VoidFunctionComponent<
disablePictureInPicture
className="pb-bootloader-video"
>
<source src={videoFileMap.get(hubType)} type="video/mp4" />
<source
src={
recovery
? recoveryVideoFileMap.get(hubType)
: videoFileMap.get(hubType)
}
type="video/mp4"
/>
<track
kind="metadata"
src={metadataFileMap.get(hubType)}
src={
recovery
? recoveryMetadataFileMap.get(hubType)
: metadataFileMap.get(hubType)
}
ref={metadataTrackRef}
/>
</video>
Expand All @@ -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'),
})}
</p>
Expand Down Expand Up @@ -210,8 +257,18 @@ const BootloaderInstructions: React.VoidFunctionComponent<
</li>
)}

{recovery && !hubHasUSB(hubType) && (
<li
className={classNames(
activeStep === 'wait-app-connect' && 'pb-active-step',
)}
>
{i18n.translate('step.waitAppConnect')}
</li>
)}

{/* hubs with USB will keep the power on, but other hubs won't */}
{hubHasUSB(hubType) ? (
{recovery || hubHasUSB(hubType) ? (
<li
className={classNames(
activeStep === 'release-button' && 'pb-active-step',
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
WEBVTT
disconnect-io
00:00.100 --> 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
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

.pb-bootloader-video {
width: 90%;
align-self: center;
display: block;
margin: 0 auto;
}
6 changes: 4 additions & 2 deletions src/firmware/bootloaderInstructions/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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}."
}
}
28 changes: 3 additions & 25 deletions src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -51,7 +47,7 @@ const RestoreFirmwarePanel: React.VoidFunctionComponent = () => {

return (
<div className={classNames(Classes.DIALOG_BODY, Classes.RUNNING_TEXT)}>
<BootloaderInstructions hubType={hubType} />
<BootloaderInstructions hubType={hubType} recovery />
{hubHasUSB(hubType) ? (
<>
<p>
Expand All @@ -73,25 +69,7 @@ const RestoreFirmwarePanel: React.VoidFunctionComponent = () => {
</Button>
</>
) : (
<p>
{i18n.translate('restoreFirmwarePanel.instruction2.ble.message', {
lego: legoRegisteredTrademark,
thisVideo: (
<>
<a
href={pybricksBleFirmwareRestoreVideoUrl}
target="_blank"
rel="noreferrer"
>
{i18n.translate(
'restoreFirmwarePanel.instruction2.ble.thisVideo',
)}
</a>
<ExternalLinkIcon />
</>
),
})}
</p>
<p>{i18n.translate('restoreFirmwarePanel.instruction2.ble.message')}</p>
)}
</div>
);
Expand Down
3 changes: 1 addition & 2 deletions src/firmware/restoreOfficialDialog/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
},
Expand Down

0 comments on commit 91802f7

Please sign in to comment.