11import { ref , unref , computed , reactive } from 'vue'
22import { Platform } from 'quasar'
3+
34import { defineStore } from 'pinia'
45import { FlipperWeb , FlipperElectron } from 'shared/lib/flipperJs'
56
67import { showNotif } from 'shared/lib/utils/useShowNotif'
7- import { logger } from 'shared/lib/utils/useLog'
8+ import { logger , type LogLevel } from 'shared/lib/utils/useLog'
89
910import { AppsModel } from 'entity/Apps'
1011import {
@@ -13,6 +14,7 @@ import {
1314 DataFlipperElectron ,
1415 DataDfuFlipperElectron
1516} from './types'
17+ import { FlipperApi } from 'entity/Flipper'
1618import { useRoute , useRouter , type RouteLocationRaw } from 'vue-router'
1719
1820// import type { Emitter, DefaultEvents } from 'nanoevents'
@@ -55,10 +57,10 @@ export const useFlipperStore = defineStore('flipper', () => {
5557 mobileDetected : false ,
5658 serialUnsupported : false ,
5759 logs : false ,
58- downloadPath : false
60+ downloadPath : false ,
61+ recovery : false
5962 } )
6063
61- const recoveringFlipperName = ref ( '' )
6264 const oldFlipper = ref < FlipperElectron | FlipperWeb > ( )
6365 const flipper = ref < FlipperElectron | FlipperWeb > ( )
6466
@@ -518,14 +520,218 @@ export const useFlipperStore = defineStore('flipper', () => {
518520 }
519521 }
520522
523+ const recoveringFlipperName = ref ( '' )
524+ const recoveryError = ref ( false )
525+ const recoveryUpdateStage = ref ( '' )
526+ const recoveryProgress = ref ( 0 )
527+ const recoveryLogs = ref < string [ ] > ( [ ] )
528+
529+ const retry = ( ) => {
530+ resetRecovery ( true )
531+ dialogs . recovery = false
532+ dialogs . multiflipper = true
533+ }
534+ const resetRecovery = ( clearLogs = false ) => {
535+ recoveryUpdateStage . value = ''
536+ recoveryProgress . value = 0
537+ if ( clearLogs ) {
538+ recoveryLogs . value = [ ]
539+ }
540+ }
541+ const recovery = async ( info : DataDfuFlipperElectron [ 'info' ] ) => {
542+ if ( ! isElectron ) {
543+ return
544+ }
545+
546+ if ( flipper . value ) {
547+ flipper . value . disconnect ( )
548+ flipper . value = undefined
549+ }
550+
551+ recoveryError . value = false
552+ dialogs . multiflipper = false
553+ dialogs . recovery = true
554+ flags . recovering = true
555+ recoveringFlipperName . value = info . name
556+ flags . waitForReconnect = true
557+ // setAutoReconnectCondition.value(flags.value.autoReconnect)
558+ // flags.value.autoReconnect = false
559+ // autoReconnectFlipperName.value = info.name
560+
561+ // console.log(info)
562+ recoveryUpdateStage . value = 'Loading firmware bundle...'
563+ const firmwareTar = await FlipperApi . fetchFirmwareTar (
564+ `https://update.flipperzero.one/firmware/release/f${ info . target } /update_tgz`
565+ )
566+
567+ const saved = await window . fs . saveToTemp ( {
568+ filename : 'update.tar' ,
569+ buffer : firmwareTar
570+ } )
571+
572+ if ( saved . status !== 'ok' ) {
573+ return
574+ }
575+
576+ let inactivityTimeout : NodeJS . Timeout
577+ const onTimeout = ( ) => {
578+ const messageLong =
579+ 'Error: Operation timed out. Please check USB connection and try again.'
580+ const messageShort = `Failed to repair ${ info . name } : Repair timeout`
581+ showNotif ( {
582+ message : messageShort ,
583+ color : 'negative'
584+ } )
585+ logger . error ( {
586+ context : componentName ,
587+ message : messageShort
588+ } )
589+ unbindLogs ( )
590+ unbindStatus ( )
591+ recoveryUpdateStage . value = messageLong
592+ recoveryError . value = true
593+ }
594+ const updateInactivityTimeout = ( stop = false ) => {
595+ if ( inactivityTimeout ) {
596+ clearTimeout ( inactivityTimeout )
597+ }
598+
599+ if ( stop ) {
600+ return
601+ }
602+
603+ inactivityTimeout = setTimeout ( onTimeout , 60 * 1000 )
604+ }
605+
606+ window . bridge . send ( {
607+ type : 'repair' ,
608+ name : info . name ,
609+ data : {
610+ file : saved . path
611+ }
612+ } )
613+
614+ updateInactivityTimeout ( )
615+
616+ const unbindLogs = bridgeEmitter . on ( 'log' , ( stderr ) => {
617+ const logLines = stderr . data . split ( '\n' )
618+ logLines . pop ( )
619+ logLines . forEach ( ( line : string ) => {
620+ recoveryLogs . value . push ( line )
621+ let level : LogLevel = 'debug'
622+ if ( line . includes ( '[E]' ) ) {
623+ level = 'error'
624+ } else if ( line . includes ( '[W]' ) ) {
625+ level = 'warn'
626+ } else if ( line . includes ( '[I]' ) ) {
627+ level = 'info'
628+ }
629+ logger [ level ] ( {
630+ context : componentName ,
631+ message : line
632+ } )
633+ } )
634+ } )
635+
636+ const unbindStatus = bridgeEmitter . on ( 'status' , async ( status ) => {
637+ if ( status . error ) {
638+ updateInactivityTimeout ( true )
639+ let messageLong = `Failed to repair ${ info . name } : ${ status . error . message } `
640+ let messageShort = messageLong
641+ switch ( status . error . message ) {
642+ case 'UnknownError' :
643+ messageLong =
644+ 'Unknown error! Please try again. If the error persists, please contact support.'
645+ messageShort = `Failed to repair ${ info . name } : Unknown error`
646+ break
647+ case 'InvalidDevice' :
648+ messageLong =
649+ 'Error: Cannot determine device type. Please try again.'
650+ messageShort = `Failed to repair ${ info . name } : Invalid device`
651+ break
652+ case 'DiskError' :
653+ messageLong =
654+ 'Error: Cannot read/write to disk. The app may be missing permissions.'
655+ messageShort = `Failed to repair ${ info . name } : Disk error`
656+ break
657+ case 'DataError' :
658+ messageLong =
659+ 'Error: Necessary files are corrupted. Please try again.'
660+ messageShort = `Failed to repair ${ info . name } : Data error`
661+ break
662+ case 'SerialAccessError' :
663+ messageLong =
664+ 'Error: Cannot access device in Serial mode. Please check USB connection and permissions and try again.'
665+ messageShort = `Failed to repair ${ info . name } : Serial access error`
666+ break
667+ case 'RecoveryAccessError' :
668+ messageLong =
669+ 'Error: Cannot access device in Recovery mode. Please check USB connection and permissions and try again.'
670+ messageShort = `Failed to repair ${ info . name } : Recovery access error`
671+ break
672+ case 'OperationError' :
673+ messageLong =
674+ 'Error: Current operation was interrupted. Please try again.'
675+ messageShort = `Failed to repair ${ info . name } : Operation error`
676+ break
677+ case 'SerialError' :
678+ messageLong =
679+ 'Error: Serial port error. Please check USB connection and try again.'
680+ messageShort = `Failed to repair ${ info . name } : Serial error`
681+ break
682+ case 'RecoveryError' :
683+ messageLong =
684+ 'Error: Recovery mode error. Please check USB connection and try again.'
685+ messageShort = `Failed to repair ${ info . name } : Recovery error`
686+ break
687+ case 'ProtocolError' :
688+ messageLong =
689+ 'Error: Protocol error. Please try again. If the error persists, please contact support.'
690+ messageShort = `Failed to repair ${ info . name } : Protocol error`
691+ break
692+ case 'TimeoutError' :
693+ messageLong =
694+ 'Error: Operation timed out. Please check USB connection and try again.'
695+ messageShort = `Failed to repair ${ info . name } : Timeout error`
696+ break
697+ }
698+ showNotif ( {
699+ message : messageShort ,
700+ color : 'negative'
701+ } )
702+ unbindLogs ( )
703+ unbindStatus ( )
704+ recoveryUpdateStage . value = messageLong
705+ recoveryError . value = true
706+ }
707+ if ( status . message ) {
708+ updateInactivityTimeout ( )
709+ recoveryUpdateStage . value = status . message
710+ }
711+ if ( status . progress ) {
712+ updateInactivityTimeout ( )
713+ recoveryProgress . value = status . progress / 100
714+ }
715+ if ( status . finished ) {
716+ updateInactivityTimeout ( true )
717+ unbindLogs ( )
718+ unbindStatus ( )
719+
720+ dialogs . multiflipper = false
721+ dialogs . recovery = false
722+
723+ recoveryUpdateStage . value = 'Finished'
724+ }
725+ } )
726+ }
727+
521728 return {
522729 isElectron,
523730
524731 flags,
525732 dialogs,
526733 connect,
527734 disconnect,
528- recoveringFlipperName,
529735 oldFlipper,
530736 flipper,
531737 flipperReady,
@@ -545,6 +751,15 @@ export const useFlipperStore = defineStore('flipper', () => {
545751 availableDfuFlippers,
546752 availableBridgeFlippers,
547753 connectFlipper,
548- findMicroSd
754+ findMicroSd,
755+
756+ recoveringFlipperName,
757+ retry,
758+ recovery,
759+ resetRecovery,
760+ recoveryUpdateStage,
761+ recoveryProgress,
762+ recoveryError,
763+ recoveryLogs
549764 }
550765} )
0 commit comments