From e56d47990581ea3bbace0150f22d3e01671d3539 Mon Sep 17 00:00:00 2001 From: "Valentin D. Pinkman" Date: Thu, 17 May 2018 16:44:54 +0200 Subject: [PATCH] Fix app install and prepare for firmware update --- src/components/ManagerPage/FirmwareUpdate.js | 96 +++++++++++++++++-- src/internals/manager/getFirmwareInfo.js | 26 +++++ .../manager/getLatestFirmwareForDevice.js | 1 - src/internals/manager/helpers.js | 40 ++++++-- src/internals/manager/index.js | 3 + src/internals/manager/installFinalFirmware.js | 24 +++++ src/internals/manager/installFirmware.js | 17 ---- src/internals/manager/installMcu.js | 1 + src/internals/manager/installOsuFirmware.js | 24 +++++ 9 files changed, 195 insertions(+), 37 deletions(-) create mode 100644 src/internals/manager/getFirmwareInfo.js create mode 100644 src/internals/manager/installFinalFirmware.js delete mode 100644 src/internals/manager/installFirmware.js create mode 100644 src/internals/manager/installMcu.js create mode 100644 src/internals/manager/installOsuFirmware.js diff --git a/src/components/ManagerPage/FirmwareUpdate.js b/src/components/ManagerPage/FirmwareUpdate.js index 65e9d7ac..23956556 100644 --- a/src/components/ManagerPage/FirmwareUpdate.js +++ b/src/components/ManagerPage/FirmwareUpdate.js @@ -1,6 +1,7 @@ // @flow import React, { PureComponent } from 'react' +import isEqual from 'lodash/isEqual' import type { Device, T } from 'types/common' @@ -16,6 +17,13 @@ type FirmwareInfos = { notes: string, } +type DeviceInfos = { + final: boolean, + mcu: boolean, + targetId: number, + version: string, +} + type Props = { t: T, device: Device, @@ -23,15 +31,27 @@ type Props = { type State = { latestFirmware: ?FirmwareInfos, + deviceInfos: ?DeviceInfos, + installing: boolean, } class FirmwareUpdate extends PureComponent { state = { latestFirmware: null, + deviceInfos: null, + installing: false, } componentDidMount() { - this.fetchLatestFirmware() + this.fetchLatestFirmware(true) + } + + componentDidUpdate(prevProps: Props, prevState: State) { + if (!isEqual(prevState.deviceInfos, this.state.deviceInfos)) { + this.fetchDeviceInfos() + } else if (this.state.installing) { + this.installFirmware() + } } componentWillUnmount() { @@ -40,7 +60,7 @@ class FirmwareUpdate extends PureComponent { _unmounted = false - fetchLatestFirmware = async () => { + fetchLatestFirmware = async (checkDeviceInfos: boolean = false) => { const { device } = this.props const latestFirmware = CACHED_LATEST_FIRMWARE || @@ -51,25 +71,65 @@ class FirmwareUpdate extends PureComponent { successResponse: 'manager.getLatestFirmwareForDeviceSuccess', errorResponse: 'manager.getLatestFirmwareForDeviceError', })) + + if (checkDeviceInfos) { + await this.fetchDeviceInfos() + } + // CACHED_LATEST_FIRMWARE = latestFirmware - console.log(latestFirmware) if (!this._unmounted) { this.setState({ latestFirmware }) } } - handleInstallFirmware = async () => { + fetchDeviceInfos = async () => { + const { device } = this.props + const deviceInfos = await runJob({ + channel: 'manager', + job: 'getFirmwareInfo', // TODO: RENAME THIS PROCESS DIFFERENTLY (EG: getInstallStep) + data: { devicePath: device.path }, + successResponse: 'device.getFirmwareInfoSuccess', + errorResponse: 'device.getFirmwareInfoError', + }) + + if (!this._unmounted) { + this.setState(state => ({ ...state, deviceInfos })) + } + } + + handleIntallOsuFirmware = async () => { + try { + const { latestFirmware } = this.state + const { + device: { path: devicePath }, + } = this.props + await runJob({ + channel: 'manager', + job: 'installOsuFirmware', + successResponse: 'device.osuFirmwareInstallSuccess', + errorResponse: 'device.osuFirmwareInstallError', + data: { + devicePath, + firmware: latestFirmware, + }, + }) + } catch (err) { + console.log(err) + } + } + + handleIntallFinalFirmware = async () => { try { const { latestFirmware } = this.state - console.log(latestFirmware) + this.setState(state => ({ ...state, installing: true })) const { device: { path: devicePath }, } = this.props await runJob({ channel: 'manager', - job: 'installFirmware', - successResponse: 'device.firmwareInstalled', - errorResponse: 'device.firmwareInstallError', + job: 'installFinalFirmware', + successResponse: 'device.finalFirmwareInstallSuccess', + errorResponse: 'device.finalFirmwareInstallError', data: { devicePath, firmware: latestFirmware, @@ -80,6 +140,22 @@ class FirmwareUpdate extends PureComponent { } } + handleIntallMcu = async () => {} + + installFirmware = async () => { + const { deviceInfos } = this.state + if (deviceInfos) { + const { mcu, final } = deviceInfos + if (mcu) { + this.handleIntallMcu() + } else if (final) { + this.handleIntallFinalFirmware() + } else { + this.handleIntallOsuFirmware() + } + } + } + render() { const { t, ...props } = this.props const { latestFirmware } = this.state @@ -96,8 +172,8 @@ class FirmwareUpdate extends PureComponent { {`Latest firmware: ${latestFirmware.name}`} - ) => + new Promise(async resolve => { + try { + const { targetId, version } = await getFirmwareInfo(transport) + const finalReady = version.endsWith('-osu') + const mcuReady = targetId === 0x01000001 + resolve({ targetId, version, final: finalReady, mcu: mcuReady }) + } catch (err) { + throw err + } + }) + +export default async (send: IPCSend, data: any) => + createTransportHandler(send, { + action: handler, + successResponse: 'device.getFirmwareInfoSuccess', + errorResponse: 'device.getFirmwareInfoError', + })(data) diff --git a/src/internals/manager/getLatestFirmwareForDevice.js b/src/internals/manager/getLatestFirmwareForDevice.js index 1137ff60..0c956458 100644 --- a/src/internals/manager/getLatestFirmwareForDevice.js +++ b/src/internals/manager/getLatestFirmwareForDevice.js @@ -13,7 +13,6 @@ export default async (send: IPCSend, data: any) => { try { const transport = await CommNodeHid.open(data.devicePath) const infos = await getFirmwareInfo(transport) - // Get device infos from targetId const { data: deviceVersion } = await axios.get( `${API_BASE_URL}/device_versions_target_id/${infos.targetId}`, diff --git a/src/internals/manager/helpers.js b/src/internals/manager/helpers.js index fbc42dd5..16d1280d 100644 --- a/src/internals/manager/helpers.js +++ b/src/internals/manager/helpers.js @@ -21,13 +21,15 @@ type Message = { data: any, } -type LedgerAppParams = { +type LedgerScriptParams = { firmware?: string, firmwareKey?: string, delete?: string, deleteKey?: string, } +type FirmwareUpdateType = 'osu' | 'final' + /** * Generate handler which create transport with given * `devicePath` then call action with it @@ -68,7 +70,7 @@ export function createTransportHandler( */ export async function installApp( transport: Transport<*>, - { appParams }: { appParams: LedgerAppParams }, + { appParams }: { appParams: LedgerScriptParams }, ): Promise { return createSocketDialog(transport, '/install', appParams) } @@ -78,7 +80,7 @@ export async function installApp( */ export async function uninstallApp( transport: Transport<*>, - { appParams }: { appParams: LedgerAppParams }, + { appParams }: { appParams: LedgerScriptParams }, ): Promise { return createSocketDialog(transport, '/install', { ...appParams, @@ -105,7 +107,11 @@ function socketSend(ws: WebsocketType, msg: Message) { /** * Exchange data on transport */ -async function exchange(ws: WebsocketType, transport: Transport<*>, msg: Message): Promise { +export async function exchange( + ws: WebsocketType, + transport: Transport<*>, + msg: Message, +): Promise { const { data, nonce } = msg const r: Buffer = await transport.exchange(Buffer.from(data, 'hex')) const status = r.slice(r.length - 2) @@ -121,7 +127,7 @@ async function exchange(ws: WebsocketType, transport: Transport<*>, msg: Message /** * Bulk update on transport */ -async function bulk(ws: WebsocketType, transport: Transport<*>, msg: Message) { +export async function bulk(ws: WebsocketType, transport: Transport<*>, msg: Message) { const { data, nonce } = msg // Execute all apdus and collect last status @@ -146,11 +152,15 @@ async function bulk(ws: WebsocketType, transport: Transport<*>, msg: Message) { * Open socket connection with firmware api, and init a dialog * with the device */ -function createSocketDialog(transport: Transport<*>, endpoint: string, appParams: LedgerAppParams) { +export async function createSocketDialog( + transport: Transport<*>, + endpoint: string, + params: LedgerScriptParams, +) { return new Promise(async (resolve, reject) => { try { let lastData - const url = `${BASE_SOCKET_URL}${endpoint}?${qs.stringify(appParams)}` + const url = `${BASE_SOCKET_URL}${endpoint}?${qs.stringify(params)}` log('WS CONNECTING', url) const ws: WebsocketType = new Websocket(url) @@ -211,7 +221,7 @@ export async function getFirmwareInfo(transport: Transport<*>) { /** * Debug helper */ -function log(namespace: string, str: string = '', color?: string) { +export function log(namespace: string, str: string = '', color?: string) { namespace = namespace.padEnd(15) // $FlowFixMe const coloredNamespace = color ? chalk[color](namespace) : namespace @@ -223,7 +233,7 @@ function log(namespace: string, str: string = '', color?: string) { /** * Log a socket send/receive */ -function logWS(type: string, msg: Message) { +export function logWS(type: string, msg: Message) { const arrow = type === 'SEND' ? '↑' : '↓' const namespace = `${arrow} WS ${type}` const color = type === 'SEND' ? 'blue' : 'red' @@ -243,3 +253,15 @@ function logWS(type: string, msg: Message) { log(namespace, JSON.stringify(msg), color) } } + +/** + * Helpers to build OSU and Final firmware params + */ +export const buildParamsFromFirmware = (type: FirmwareUpdateType): Function => ( + data: any, +): LedgerScriptParams => ({ + firmware: data[`${type}_firmware`], + firmwareKey: data[`${type}_firmware_key`], + perso: data[`${type}_perso`], + targetId: data[`${type}_target_id`], +}) diff --git a/src/internals/manager/index.js b/src/internals/manager/index.js index 40dac9de..13b7093c 100644 --- a/src/internals/manager/index.js +++ b/src/internals/manager/index.js @@ -21,3 +21,6 @@ export { default as installApp } from './installApp' export { default as listApps } from './listApps' export { default as uninstallApp } from './uninstallApp' export { default as getLatestFirmwareForDevice } from './getLatestFirmwareForDevice' +export { default as installOsuFirmware } from './installOsuFirmware' +export { default as installFinalFirmware } from './installFinalFirmware' +export { default as getFirmwareInfo } from './getFirmwareInfo' diff --git a/src/internals/manager/installFinalFirmware.js b/src/internals/manager/installFinalFirmware.js new file mode 100644 index 00000000..74dcc971 --- /dev/null +++ b/src/internals/manager/installFinalFirmware.js @@ -0,0 +1,24 @@ +// @flow + +import CommNodeHid from '@ledgerhq/hw-transport-node-hid' + +import type { IPCSend } from 'types/electron' +import { createSocketDialog, buildParamsFromFirmware } from './helpers' + +type DataType = { + devicePath: string, + firmware: Object, +} + +const buildFinalParams = buildParamsFromFirmware('final') + +export default async (send: IPCSend, data: DataType) => { + try { + const transport = await CommNodeHid.open(data.devicePath) + const finalData = buildFinalParams(data.firmware) + await createSocketDialog(transport, '/install', finalData) + send('device.finalFirmwareInstallSuccess', { success: true }) + } catch (err) { + send('device.finalFirmwareInstallError', { success: false }) + } +} diff --git a/src/internals/manager/installFirmware.js b/src/internals/manager/installFirmware.js deleted file mode 100644 index 708837ba..00000000 --- a/src/internals/manager/installFirmware.js +++ /dev/null @@ -1,17 +0,0 @@ -// @flow - -import type { IPCSend } from 'types/electron' - -export default async (send: IPCSend, data: any) => { - /** - * 1 CREATE TRANSPORT - * 2 GETFIRMWARE INFOS - * 3 SEND - */ - console.log(data) -} -// createTransportHandler(send, { -// action: installFirmware, -// successResponse: 'manager.appInstalled', -// errorResponse: 'manager.appInstallError', -// })(data) diff --git a/src/internals/manager/installMcu.js b/src/internals/manager/installMcu.js new file mode 100644 index 00000000..0f8b7d99 --- /dev/null +++ b/src/internals/manager/installMcu.js @@ -0,0 +1 @@ +// flow diff --git a/src/internals/manager/installOsuFirmware.js b/src/internals/manager/installOsuFirmware.js new file mode 100644 index 00000000..6f877161 --- /dev/null +++ b/src/internals/manager/installOsuFirmware.js @@ -0,0 +1,24 @@ +// @flow + +import CommNodeHid from '@ledgerhq/hw-transport-node-hid' + +import type { IPCSend } from 'types/electron' +import { createSocketDialog, buildParamsFromFirmware } from './helpers' + +type DataType = { + devicePath: string, + firmware: Object, +} + +const buildOsuParams = buildParamsFromFirmware('osu') + +export default async (send: IPCSend, data: DataType) => { + try { + const transport = await CommNodeHid.open(data.devicePath) + const osuData = buildOsuParams(data.firmware) + await createSocketDialog(transport, '/install', osuData) + send('device.osuFirmwareInstallSuccess', { success: true }) + } catch (err) { + send('device.osuFirmwareInstallError', { success: false }) + } +}