mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 17:45:22 +00:00
bootloaderInstructions: add recovery variant
For official firmware recovery, we have separate videos for non-USB hubs. Closes: https://github.com/pybricks/support/issues/728
This commit is contained in:
committed by
David Lechner
parent
0f871c6b2c
commit
4fcb82e361
@@ -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';
|
||||
|
||||
|
||||
@@ -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<Hub, string> = new Map([
|
||||
@@ -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(() => {
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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',
|
||||
|
||||
Binary file not shown.
@@ -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.
@@ -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.
@@ -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
|
||||
@@ -8,5 +8,6 @@
|
||||
|
||||
.pb-bootloader-video {
|
||||
width: 90%;
|
||||
align-self: center;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -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}."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className={classNames(Classes.DIALOG_BODY, Classes.RUNNING_TEXT)}>
|
||||
<BootloaderInstructions hubType={hubType} />
|
||||
<BootloaderInstructions hubType={hubType} recovery />
|
||||
{hubHasUSB(hubType) ? (
|
||||
<>
|
||||
<p>
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user