Skip to content

Commit

Permalink
[CP-3128] Flashing Harmony on MacOS (#2111)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Karski <[email protected]>
  • Loading branch information
MateuszMudita and dkarski committed Oct 21, 2024
1 parent 9ef6302 commit eeba5fc
Show file tree
Hide file tree
Showing 50 changed files with 1,525 additions and 247 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum IconType {
ArrowLongLeft,
ArrowLongRight,
AttachContact,
BackArrowIcon,
BackupFolder,
Battery,
BorderCheckIcon,
Expand All @@ -16,6 +17,7 @@ export enum IconType {
ChargingBattery,
Check,
CheckCircle,
CheckCircleBlack,
CheckIndeterminate,
Cloud,
Close,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import Arrow from "Core/__deprecated__/renderer/svg/arrow.svg"
import ArrowLongLeft from "Core/__deprecated__/renderer/svg/arrow-long-left.svg"
import ArrowLongRight from "Core/__deprecated__/renderer/svg/arrow-long-right.svg"
import AttachContact from "Core/__deprecated__/renderer/svg/attach-contact.svg"
import BackArrowIcon from "Core/__deprecated__/renderer/svg/back-arrow-icon.svg"
import BackupFolder from "Core/__deprecated__/renderer/svg/backup-folder.svg"
import Battery from "Core/__deprecated__/renderer/svg/battery.svg"
import BorderCheck from "Core/__deprecated__/renderer/svg/border-check-icon.svg"
import ChargingBattery from "Core/__deprecated__/renderer/svg/charging-battery.svg"
import Check from "Core/__deprecated__/renderer/svg/check-icon.svg"
import CheckCircle from "Core/__deprecated__/renderer/svg/check-circle.svg"
import CheckCircleBlack from "Core/__deprecated__/renderer/svg/check-circle-black.svg"
import CheckIndeterminate from "Core/__deprecated__/renderer/svg/check-indeterminate.svg"
import Close from "Core/__deprecated__/renderer/svg/close.svg"
import CloseWhite from "Core/__deprecated__/renderer/svg/close-white.svg"
Expand Down Expand Up @@ -146,6 +148,9 @@ const typeToIcon: Partial<Record<IconType, typeof Arrow>> = {
[IconType.AttachContact]: AttachContact,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
[IconType.BackArrowIcon]: BackArrowIcon,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
[IconType.BackupFolder]: BackupFolder,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
Expand All @@ -170,6 +175,9 @@ const typeToIcon: Partial<Record<IconType, typeof Arrow>> = {
[IconType.CheckCircle]: CheckCircle,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
[IconType.CheckCircleBlack]: CheckCircleBlack,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
[IconType.CheckIndeterminate]: CheckIndeterminate,
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
Expand Down
12 changes: 11 additions & 1 deletion libs/core/__deprecated__/renderer/locales/default/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@
"module.recoveryMode.harmony.description": "Please read the instructions carefully. Not following the instructions may void the\nwarranty!",
"module.recoveryMode.harmony.warning1": "Make sure your device is running <b>OS 1.9.0 or later.</b>",
"module.recoveryMode.harmony.warning2": "Once you start the recovery, it must not be cancelled or interrupted.",
"module.recoveryMode.harmony.warning3": "Do not disconnect your Harmony during the recovery process.",
"module.recoveryMode.harmony.warning3": "Connect your Harmony directly to your computer using the USB C cable and do not disconnect it until the process is complete.",
"module.recoveryMode.harmony.warning4": "Before starting the recovery, charge your device for 1 hour (or more) from a suitable power outlet.",
"module.recoveryMode.harmony.warningLinux": "Make sure your computer is running <b>Ubuntu 22.04 or later.</b>",
"module.recoveryMode.harmony.confirmation": "I understand that not following these instructions may void the warranty",
Expand All @@ -1016,6 +1016,16 @@
"module.recoveryMode.modal.step4": "Flashing process... (4 of 4)",
"module.recoveryMode.modal.restarting.subtitle": "Restarting Harmony...",
"module.recoveryMode.modal.restarting.message": "Please do not disconnect your Harmony.",
"module.recoveryMode.modal.error.subtitle": "Recovery failed",
"module.recoveryMode.modal.error.message": "The process was interrupted. Please try again.",
"module.recoveryMode.modal.waitingForBackButton.subtitle": "Almost finished...",
"module.recoveryMode.modal.waitingForBackButton.description": "Press the back button on your device to complete the recovery process.",
"module.recoveryMode.mocal.terminalInfo.subtitle": "Just a few more steps...",
"module.recoveryMode.mocal.terminalInfo.description": "The Terminal was opened in the background",
"module.recoveryMode.mocal.terminalInfo.step1": "1. Switch to the open terminal",
"module.recoveryMode.mocal.terminalInfo.step2": "2. Press enter to start the process",
"module.recoveryMode.mocal.terminalInfo.step3": "3. Enter your computer password when prompted",
"module.recoveryMode.mocal.terminalInfo.step4": "4. Repeat your computer password when prompted",
"module.genericViews.dataMigration.cancelConfirm.title": "Cancel data transfer?",
"module.genericViews.dataMigration.cancelConfirm.description": "We’ll stop the transfer but some data may already be on your Kompakt.",
"module.genericViews.dataMigration.cancelConfirm.cancelButtonLabel": "Cancel transfer",
Expand Down
4 changes: 4 additions & 0 deletions libs/core/__deprecated__/renderer/svg/back-arrow-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions libs/core/__deprecated__/renderer/svg/check-circle-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useDeviceLockedEffect } from "Core/core/hooks/use-device-locked-effect"
import { useDeviceDetachedEffect } from "Core/core/hooks/use-device-detached-effect"
import { useDeviceConnectFailedEffect } from "Core/core/hooks/use-device-connect-failed-effect"
import { useDiscoveryRedirectEffect } from "Core/core/hooks/use-discovery-redirect-effect"
import { useMscDeviceDetachedEffect } from "Core/core/hooks/use-msc-device-detached-effect"
import { useRouterListener } from "Core/core/hooks"
import {
OutboxWrapper,
Expand Down Expand Up @@ -46,6 +47,9 @@ const BaseApp: FunctionComponent = () => {
useFileDialogEventListener()
useHelp()

// MSC
useMscDeviceDetachedEffect()

return (
<>
<OutboxWrapper />
Expand Down
1 change: 0 additions & 1 deletion libs/core/core/hooks/use-device-detached-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const useDeviceDetachedEffect = () => {
}

const useHandleDevicesDetached = () => {
const dispatch = useDispatch<Dispatch>()
const processActiveDevicesDetachment = useProcessActiveDevicesDetachment()
const processSingleDeviceRemaining = useProcessSingleDeviceRemaining()

Expand Down
66 changes: 66 additions & 0 deletions libs/core/core/hooks/use-msc-device-detached-effect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) Mudita sp. z o.o. All rights reserved.
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
*/

import { useCallback, useEffect } from "react"
import { useDispatch, useSelector } from "react-redux"
import { answerMain, useDebouncedEventsHandler } from "shared/utils"
import {
DeviceBaseProperties,
DeviceProtocolMainEvent,
DeviceType,
} from "device-protocol/models"
import {
abortMscFlashing,
FlashingProcessState,
selectIsFlashingInActivePhases,
selectIsFlashingInState,
setMscFlashingInitialState,
} from "msc-flash-harmony"
import { Dispatch } from "Core/__deprecated__/renderer/store"

export const useMscDeviceDetachedEffect = () => {
const handleDevicesDetached = useHandleDevicesDetached()

const batchDeviceDetachedEvents =
useDebouncedEventsHandler<DeviceBaseProperties>(handleDevicesDetached)

useEffect(() => {
return answerMain(
DeviceProtocolMainEvent.DeviceDetached,
batchDeviceDetachedEvents
)
}, [batchDeviceDetachedEvents])
}

const useHandleDevicesDetached = () => {
const dispatch = useDispatch<Dispatch>()
const flashingInActivePhases = useSelector(selectIsFlashingInActivePhases)
const flashingInWaitingForBackButtonState = useSelector(
selectIsFlashingInState(FlashingProcessState.WaitingForBackButton)
)

return useCallback(
(deviceDetachedEvents: DeviceBaseProperties[]) => {
const mscEvents = deviceDetachedEvents.filter(
({ deviceType }) => deviceType === DeviceType.MuditaHarmonyMsc
)

if (mscEvents.length === 0) {
return
}

if (flashingInWaitingForBackButtonState) {
dispatch(setMscFlashingInitialState())
return
}

const reason = flashingInActivePhases
? FlashingProcessState.Failed
: undefined
dispatch(abortMscFlashing({ reason }))
},
[dispatch, flashingInActivePhases, flashingInWaitingForBackButtonState]
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,22 @@
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
*/

import { ProductID, VendorID } from "Core/device/constants"
import { execPromise } from "shared/utils"
import { PortInfo } from "serialport"

interface DeviceDetails {
[key: string]: string | undefined
name?: string
ProductID?: string
VendorID?: string
Version?: string
SerialNumber?: string
Speed?: string
Manufacturer?: string
LocationID?: string
"CurrentAvailable(mA)"?: string
"CurrentRequired(mA)"?: string
"ExtraOperatingCurrent(mA)"?: string
}

export const getHarmonyMSCDevice = (output: string): DeviceDetails | null => {
const devices: Array<DeviceDetails> = []
const lines = output.trim().split("\n")
let currentDevice: DeviceDetails = {}
let currentKey = ""

lines.forEach((line) => {
if (line.trim() && !line.trim().endsWith(":")) {
const [keyParts, ...valueParts] = line.trim().split(":")
const key = keyParts.replaceAll(" ", "")
const value = valueParts.join(":").trim()
currentDevice[key] = value
} else if (line.trim().endsWith(":")) {
if (Object.keys(currentDevice).length) {
devices.push({ name: currentKey, ...currentDevice })
currentDevice = {}
}
currentKey = line.trim().slice(0, -1)
}
})

if (Object.keys(currentDevice).length) {
devices.push({ name: currentKey, ...currentDevice })
}

return (
devices.find((device) => {
if (device.VendorID && device.ProductID) {
return (
device.VendorID.includes(VendorID.MuditaHarmony) &&
device.ProductID.includes(ProductID.MuditaHarmonyMsc)
)
}
return null
}) || null
)
}

export const parseToPortInfo = (device: DeviceDetails): PortInfo => {
const vendorId = device.VendorID!.replace("0x", "")
const productId = device.ProductID!.replace("0x", "")
const serialNumber = device.SerialNumber

return {
path: `${vendorId}/${productId}/${serialNumber}`,
manufacturer: device.Manufacturer,
serialNumber: device.SerialNumber,
productId,
vendorId,
locationId: device.LocationID?.split(" ")[0],
}
}
import { MacosUSBPortDeviceParser } from "shared/utils"
import { ProductID, VendorID } from "Core/device/constants"

export const getUsbDevicesMacOS = async (): Promise<PortInfo | void> => {
try {
const stdout = await execPromise("system_profiler SPUSBDataType")
if (stdout) {
const harmonyDevice = getHarmonyMSCDevice(stdout)
if (harmonyDevice) {
return parseToPortInfo(harmonyDevice)
}
const devices = await MacosUSBPortDeviceParser.getUSBPortDevices()
const device = devices.find((device) => {
return (
device.vendorId?.includes(VendorID.MuditaHarmony) &&
device.productId?.includes(ProductID.MuditaHarmonyMsc)
)
})

if (device !== undefined) {
return device
}
} catch (error) {
console.error(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import React from "react"
import { useSelector } from "react-redux"
import { FlashingErrorModal } from "msc-flash-harmony"
import { FunctionComponent } from "Core/core/types/function-component.interface"
import ContactSupportFlow from "Core/contact-support/containers/contact-support-flow.container"
import { UpdateOsInterruptedFlowContainer } from "Core/update/components/update-os-interrupted-flow"
Expand All @@ -25,6 +26,7 @@ const ModalsManager: FunctionComponent = () => {
<UpdateOsInterruptedFlowContainer />
<ConnectingLoaderModal />
<DetachedDuringUploadErrorModal />
<FlashingErrorModal />
{appUpdateVisible && <AppUpdateFlow />}
</>
)
Expand Down
3 changes: 3 additions & 0 deletions libs/generic-view/store/src/lib/action-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export enum ActionName {

MscFlashingGetFilesDetails = "msc-flashing/get-files-details",
MscFlashingSetProcessState = "msc-flashing/set-process-state",
MscFlashingSetAbort = "msc-flashing/set-abort",
MscFlashingAbort = "msc-flashing/abort",
MscFlashingSetInitialState = "msc-flashing/set-initial-state",

GetEntitiesConfig = "entities/get-entities-config",
SetEntitiesConfig = "entities/set-entities-config",
Expand Down
Loading

0 comments on commit eeba5fc

Please sign in to comment.