mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
refactored restart and format services to be non-blocking
This commit is contained in:
@@ -26,8 +26,6 @@ import {
|
||||
} from '@mui/material';
|
||||
import Grid from '@mui/material/Grid2';
|
||||
|
||||
import { restart } from 'api/system';
|
||||
|
||||
import {
|
||||
Body,
|
||||
Cell,
|
||||
@@ -51,6 +49,7 @@ import {
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import {
|
||||
API,
|
||||
readDeviceEntities,
|
||||
readDevices,
|
||||
resetCustomizations,
|
||||
@@ -61,7 +60,7 @@ import SettingsCustomizationsDialog from './CustomizationsDialog';
|
||||
import EntityMaskToggle from './EntityMaskToggle';
|
||||
import OptionIcon from './OptionIcon';
|
||||
import { DeviceEntityMask } from './types';
|
||||
import type { DeviceEntity, DeviceShort } from './types';
|
||||
import type { APIcall, DeviceEntity, DeviceShort } from './types';
|
||||
|
||||
export const APIURL = window.location.origin + '/api/';
|
||||
|
||||
@@ -85,6 +84,10 @@ const Customizations = () => {
|
||||
// fetch devices first
|
||||
const { data: devices, send: fetchDevices } = useRequest(readDevices);
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const [selectedDevice, setSelectedDevice] = useState<number>(
|
||||
Number(useLocation().state) || -1
|
||||
);
|
||||
@@ -132,9 +135,14 @@ const Customizations = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const { send: sendRestart } = useRequest(restart(), {
|
||||
immediate: false
|
||||
});
|
||||
const doRestart = async () => {
|
||||
setRestarting(true);
|
||||
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||
(error: Error) => {
|
||||
toast.error(error.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const entities_theme = useTheme({
|
||||
Table: `
|
||||
@@ -247,13 +255,6 @@ const Customizations = () => {
|
||||
}
|
||||
}, [devices, selectedDevice]);
|
||||
|
||||
const doRestart = async () => {
|
||||
await sendRestart().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
setRestarting(true);
|
||||
};
|
||||
|
||||
function formatValue(value: unknown) {
|
||||
if (typeof value === 'number') {
|
||||
return new Intl.NumberFormat().format(value);
|
||||
@@ -509,7 +510,7 @@ const Customizations = () => {
|
||||
container
|
||||
mb={1}
|
||||
mt={0}
|
||||
spacing={1}
|
||||
spacing={2}
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
|
||||
@@ -28,7 +28,7 @@ const Help = () => {
|
||||
const { LL } = useI18nContext();
|
||||
useLayoutTitle(LL.HELP());
|
||||
|
||||
const { send: getAPI } = useRequest((data: APIcall) => API(data), {
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
}).onSuccess((event) => {
|
||||
const anchor = document.createElement('a');
|
||||
@@ -45,8 +45,8 @@ const Help = () => {
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
});
|
||||
|
||||
const callAPI = async (device: string, entity: string) => {
|
||||
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
||||
const callAPI = async (device: string, cmd: string) => {
|
||||
await sendAPI({ device, cmd, id: 0 }).catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
@@ -113,7 +113,7 @@ const Help = () => {
|
||||
color="primary"
|
||||
onClick={() => callAPI('system', 'allvalues')}
|
||||
>
|
||||
{LL.ALLVALUES(0)}
|
||||
{LL.ALLVALUES()}
|
||||
</Button>
|
||||
|
||||
<Box border={1} p={1} mt={4}>
|
||||
|
||||
@@ -272,8 +272,8 @@ export interface BoardProfile {
|
||||
|
||||
export interface APIcall {
|
||||
device: string;
|
||||
entity: string;
|
||||
id: unknown;
|
||||
cmd: string;
|
||||
id: number;
|
||||
}
|
||||
export interface WriteAnalogSensor {
|
||||
id: number;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import Grid from '@mui/material/Grid2';
|
||||
|
||||
import { readHardwareStatus, restart } from 'api/system';
|
||||
import { readHardwareStatus } from 'api/system';
|
||||
|
||||
import { useRequest } from 'alova/client';
|
||||
import RestartMonitor from 'app/status/RestartMonitor';
|
||||
@@ -35,9 +35,9 @@ import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
|
||||
import { getBoardProfile, readSettings, writeSettings } from '../../api/app';
|
||||
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
|
||||
import { BOARD_PROFILES } from '../main/types';
|
||||
import type { Settings } from '../main/types';
|
||||
import type { APIcall, Settings } from '../main/types';
|
||||
import { createSettingsValidator } from '../main/validators';
|
||||
|
||||
export function boardProfileSelectItems() {
|
||||
@@ -80,6 +80,10 @@ const ApplicationSettings = () => {
|
||||
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const { loading: processingBoard, send: readBoardProfile } = useRequest(
|
||||
(boardProfile: string) => getBoardProfile(boardProfile),
|
||||
{
|
||||
@@ -102,9 +106,14 @@ const ApplicationSettings = () => {
|
||||
});
|
||||
});
|
||||
|
||||
const { send: restartCommand } = useRequest(restart(), {
|
||||
immediate: false
|
||||
});
|
||||
const doRestart = async () => {
|
||||
setRestarting(true);
|
||||
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||
(error: Error) => {
|
||||
toast.error(error.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const updateBoardProfile = async (board_profile: string) => {
|
||||
await readBoardProfile(board_profile).catch((error: Error) => {
|
||||
@@ -158,10 +167,7 @@ const ApplicationSettings = () => {
|
||||
|
||||
const restart = async () => {
|
||||
await validateAndSubmit();
|
||||
await restartCommand().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
setRestarting(true);
|
||||
await doRestart();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -204,7 +210,7 @@ const ApplicationSettings = () => {
|
||||
label={LL.ENABLE_MODBUS()}
|
||||
/>
|
||||
{data.modbus_enabled && (
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
@@ -258,7 +264,7 @@ const ApplicationSettings = () => {
|
||||
label={LL.ENABLE_SYSLOG()}
|
||||
/>
|
||||
{data.syslog_enabled && (
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
@@ -351,7 +357,7 @@ const ApplicationSettings = () => {
|
||||
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||
{LL.FORMATTING_OPTIONS()}
|
||||
</Typography>
|
||||
<Grid container spacing={1}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={3}>
|
||||
<TextField
|
||||
name="locale"
|
||||
@@ -469,7 +475,7 @@ const ApplicationSettings = () => {
|
||||
</TextField>
|
||||
{data.board_profile === 'CUSTOM' && (
|
||||
<>
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
@@ -555,7 +561,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
{data.phy_type !== 0 && (
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="eth_power"
|
||||
@@ -601,7 +607,7 @@ const ApplicationSettings = () => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="tx_mode"
|
||||
@@ -717,7 +723,7 @@ const ApplicationSettings = () => {
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Grid container spacing={1} rowSpacing={0}>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
@@ -740,7 +746,7 @@ const ApplicationSettings = () => {
|
||||
disabled={!data.shower_timer}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid container spacing={1} rowSpacing={2} sx={{ pt: 2 }}>
|
||||
<Grid container spacing={2} sx={{ pt: 2 }}>
|
||||
{data.shower_timer && (
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
checkUpgrade,
|
||||
getDevVersion,
|
||||
getStableVersion,
|
||||
restart,
|
||||
uploadURL
|
||||
} from 'api/system';
|
||||
|
||||
@@ -76,7 +75,11 @@ const DownloadUpload = () => {
|
||||
saveFile(event.data, 'schedule.json');
|
||||
});
|
||||
|
||||
const { send: getAPI } = useRequest((data: APIcall) => API(data), {
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const { send: sendAPIandSave } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
}).onSuccess((event) => {
|
||||
saveFile(
|
||||
@@ -98,15 +101,13 @@ const DownloadUpload = () => {
|
||||
}
|
||||
);
|
||||
|
||||
const { send: restartCommand } = useRequest(restart(), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const callRestart = async () => {
|
||||
const doRestart = async () => {
|
||||
setRestarting(true);
|
||||
await restartCommand().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||
(error: Error) => {
|
||||
toast.error(error.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const { send: sendCheckUpgrade } = useRequest(checkUpgrade, {
|
||||
@@ -200,8 +201,8 @@ const DownloadUpload = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const callAPI = async (device: string, entity: string) => {
|
||||
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
||||
const callAPIandSave = async (device: string, cmd: string) => {
|
||||
await sendAPIandSave({ device, cmd, id: 0 }).catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
@@ -291,7 +292,7 @@ const DownloadUpload = () => {
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => callAPI('system', 'info')}
|
||||
onClick={() => callAPIandSave('system', 'info')}
|
||||
>
|
||||
{LL.SUPPORT_INFORMATION(0)}
|
||||
</Button>
|
||||
@@ -300,7 +301,7 @@ const DownloadUpload = () => {
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => callAPI('system', 'allvalues')}
|
||||
onClick={() => callAPIandSave('system', 'allvalues')}
|
||||
>
|
||||
{LL.ALLVALUES()}
|
||||
</Button>
|
||||
@@ -420,15 +421,13 @@ const DownloadUpload = () => {
|
||||
<Typography variant="body2">{LL.UPLOAD_TEXT()}</Typography>
|
||||
</Box>
|
||||
|
||||
<SingleUpload callRestart={callRestart} />
|
||||
<SingleUpload doRestart={doRestart} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent>
|
||||
{restarting ? <RestartMonitor message={LL.WAIT_FIRMWARE()} /> : content()}
|
||||
</SectionContent>
|
||||
<SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ import {
|
||||
} from '@mui/material';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { API } from 'api/app';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova/client';
|
||||
import type { APIcall } from 'app/main/types';
|
||||
import { SectionContent, useLayoutTitle } from 'components';
|
||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
@@ -34,13 +36,14 @@ const Settings = () => {
|
||||
|
||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||
|
||||
const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), {
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const factoryReset = async () => {
|
||||
await factoryResetCommand();
|
||||
setConfirmFactoryReset(false);
|
||||
const doFormat = async () => {
|
||||
await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => {
|
||||
setConfirmFactoryReset(false);
|
||||
});
|
||||
};
|
||||
|
||||
const renderFactoryResetDialog = () => (
|
||||
@@ -63,7 +66,7 @@ const Settings = () => {
|
||||
<Button
|
||||
startIcon={<SettingsBackupRestoreIcon />}
|
||||
variant="outlined"
|
||||
onClick={factoryReset}
|
||||
onClick={doFormat}
|
||||
color="error"
|
||||
>
|
||||
{LL.FACTORY_RESET()}
|
||||
|
||||
@@ -22,9 +22,10 @@ import {
|
||||
} from '@mui/material';
|
||||
|
||||
import * as NetworkApi from 'api/network';
|
||||
import * as SystemApi from 'api/system';
|
||||
import { API } from 'api/app';
|
||||
|
||||
import { updateState, useRequest } from 'alova/client';
|
||||
import type { APIcall } from 'app/main/types';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
@@ -71,7 +72,7 @@ const NetworkSettings = () => {
|
||||
update: NetworkApi.updateNetworkSettings
|
||||
});
|
||||
|
||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
@@ -131,11 +132,13 @@ const NetworkSettings = () => {
|
||||
await loadData();
|
||||
};
|
||||
|
||||
const restart = async () => {
|
||||
await restartCommand().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
const doRestart = async () => {
|
||||
setRestarting(true);
|
||||
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||
(error: Error) => {
|
||||
toast.error(error.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -358,7 +361,7 @@ const NetworkSettings = () => {
|
||||
startIcon={<PowerSettingsNewIcon />}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={restart}
|
||||
onClick={doRestart}
|
||||
>
|
||||
{LL.RESTART()}
|
||||
</Button>
|
||||
|
||||
@@ -1,52 +1,80 @@
|
||||
import { type FC, useEffect, useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
CircularProgress,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
|
||||
import { readHardwareStatus } from 'api/system';
|
||||
|
||||
import { useRequest } from 'alova/client';
|
||||
import { FormLoader } from 'components';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useAutoRequest } from 'alova/client';
|
||||
import MessageBox from 'components/MessageBox';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const RESTART_TIMEOUT = 2 * 60 * 1000; // 2 minutes
|
||||
const POLL_INTERVAL = 2000; // every 2 seconds
|
||||
|
||||
export interface RestartMonitorProps {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const RestartMonitor: FC<RestartMonitorProps> = ({ message }) => {
|
||||
const [failed, setFailed] = useState<boolean>(false);
|
||||
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout>();
|
||||
const RestartMonitor = () => {
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
const timeoutAt = useRef(new Date().getTime() + RESTART_TIMEOUT);
|
||||
let count = 0;
|
||||
|
||||
const { send } = useRequest(readHardwareStatus, { immediate: false });
|
||||
|
||||
const poll = useRef(async () => {
|
||||
try {
|
||||
await send();
|
||||
document.location.href = '/';
|
||||
} catch {
|
||||
if (new Date().getTime() < timeoutAt.current) {
|
||||
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
|
||||
} else {
|
||||
setFailed(true);
|
||||
const { data } = useAutoRequest(readHardwareStatus, {
|
||||
pollingTime: 1000,
|
||||
force: true,
|
||||
initialData: { status: 'Getting ready...' },
|
||||
async middleware(_, next) {
|
||||
if (count++ >= 1) {
|
||||
// skip first request (1 seconds) to allow AsyncWS to send its response
|
||||
await next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
|
||||
}, []);
|
||||
|
||||
useEffect(() => () => timeoutId && clearTimeout(timeoutId), [timeoutId]);
|
||||
})
|
||||
.onSuccess((event) => {
|
||||
console.log(event.data.status); // TODO remove
|
||||
if (event.data.status === 'ready' || event.data.status === undefined) {
|
||||
document.location.href = '/';
|
||||
}
|
||||
})
|
||||
.onError((error, _method) => {
|
||||
setErrorMessage(error.message);
|
||||
});
|
||||
|
||||
return (
|
||||
<FormLoader
|
||||
message={message ? message : LL.APPLICATION_RESTARTING() + '...'}
|
||||
errorMessage={failed ? 'Timed out' : undefined}
|
||||
/>
|
||||
<Dialog fullWidth={true} sx={dialogStyle} open={true}>
|
||||
<DialogContent dividers>
|
||||
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
|
||||
<Typography
|
||||
color="secondary"
|
||||
variant="h6"
|
||||
fontWeight={400}
|
||||
textAlign="center"
|
||||
>
|
||||
{data?.status === 'uploading'
|
||||
? LL.WAIT_FIRMWARE()
|
||||
: data?.status === 'restarting'
|
||||
? LL.APPLICATION_RESTARTING()
|
||||
: data?.status === 'ready'
|
||||
? 'Reloading'
|
||||
: 'Preparing'}
|
||||
</Typography>
|
||||
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center">
|
||||
{LL.PLEASE_WAIT()}…
|
||||
</Typography>
|
||||
|
||||
{errorMessage ? (
|
||||
<MessageBox my={2} level="error" message={errorMessage} />
|
||||
) : (
|
||||
<Box py={2}>
|
||||
<CircularProgress size={48} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -30,10 +30,11 @@ import {
|
||||
} from '@mui/material';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { API } from 'api/app';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useAutoRequest, useRequest } from 'alova/client';
|
||||
import { busConnectionStatus } from 'app/main/types';
|
||||
import { type APIcall, busConnectionStatus } from 'app/main/types';
|
||||
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
@@ -54,7 +55,7 @@ const SystemStatus = () => {
|
||||
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
||||
const [restarting, setRestarting] = useState<boolean>();
|
||||
|
||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
@@ -64,7 +65,12 @@ const SystemStatus = () => {
|
||||
error
|
||||
} = useAutoRequest(SystemApi.readSystemStatus, {
|
||||
initialData: [],
|
||||
pollingTime: 5000
|
||||
pollingTime: 5000,
|
||||
async middleware(_, next) {
|
||||
if (!restarting) {
|
||||
await next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const theme = useTheme();
|
||||
@@ -195,17 +201,14 @@ const SystemStatus = () => {
|
||||
const activeHighlight = (value: boolean) =>
|
||||
value ? theme.palette.success.main : theme.palette.info.main;
|
||||
|
||||
const restart = async () => {
|
||||
await restartCommand()
|
||||
.then(() => {
|
||||
setRestarting(true);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
const doRestart = async () => {
|
||||
setConfirmRestart(false);
|
||||
setRestarting(true);
|
||||
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||
(error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setConfirmRestart(false);
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const renderRestartDialog = () => (
|
||||
@@ -228,7 +231,7 @@ const SystemStatus = () => {
|
||||
<Button
|
||||
startIcon={<PowerSettingsNewIcon />}
|
||||
variant="outlined"
|
||||
onClick={restart}
|
||||
onClick={doRestart}
|
||||
color="error"
|
||||
>
|
||||
{LL.RESTART()}
|
||||
|
||||
Reference in New Issue
Block a user