mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
refactored restart and format services to be non-blocking
This commit is contained in:
@@ -10,12 +10,6 @@ export const readHardwareStatus = () =>
|
|||||||
export const readSystemStatus = () =>
|
export const readSystemStatus = () =>
|
||||||
alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
||||||
|
|
||||||
// commands
|
|
||||||
export const restart = () => alovaInstance.Post('/rest/restart');
|
|
||||||
export const partition = () => alovaInstance.Post('/rest/partition');
|
|
||||||
export const factoryPartition = () => alovaInstance.Post('/rest/factoryPartition');
|
|
||||||
export const factoryReset = () => alovaInstance.Post('/rest/factoryReset');
|
|
||||||
|
|
||||||
// SystemLog
|
// SystemLog
|
||||||
export const readLogSettings = () =>
|
export const readLogSettings = () =>
|
||||||
alovaInstance.Get<LogSettings>(`/rest/logSettings`);
|
alovaInstance.Get<LogSettings>(`/rest/logSettings`);
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import Grid from '@mui/material/Grid2';
|
import Grid from '@mui/material/Grid2';
|
||||||
|
|
||||||
import { restart } from 'api/system';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Cell,
|
Cell,
|
||||||
@@ -51,6 +49,7 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
API,
|
||||||
readDeviceEntities,
|
readDeviceEntities,
|
||||||
readDevices,
|
readDevices,
|
||||||
resetCustomizations,
|
resetCustomizations,
|
||||||
@@ -61,7 +60,7 @@ import SettingsCustomizationsDialog from './CustomizationsDialog';
|
|||||||
import EntityMaskToggle from './EntityMaskToggle';
|
import EntityMaskToggle from './EntityMaskToggle';
|
||||||
import OptionIcon from './OptionIcon';
|
import OptionIcon from './OptionIcon';
|
||||||
import { DeviceEntityMask } from './types';
|
import { DeviceEntityMask } from './types';
|
||||||
import type { DeviceEntity, DeviceShort } from './types';
|
import type { APIcall, DeviceEntity, DeviceShort } from './types';
|
||||||
|
|
||||||
export const APIURL = window.location.origin + '/api/';
|
export const APIURL = window.location.origin + '/api/';
|
||||||
|
|
||||||
@@ -85,6 +84,10 @@ const Customizations = () => {
|
|||||||
// fetch devices first
|
// fetch devices first
|
||||||
const { data: devices, send: fetchDevices } = useRequest(readDevices);
|
const { data: devices, send: fetchDevices } = useRequest(readDevices);
|
||||||
|
|
||||||
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
const [selectedDevice, setSelectedDevice] = useState<number>(
|
const [selectedDevice, setSelectedDevice] = useState<number>(
|
||||||
Number(useLocation().state) || -1
|
Number(useLocation().state) || -1
|
||||||
);
|
);
|
||||||
@@ -132,9 +135,14 @@ const Customizations = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { send: sendRestart } = useRequest(restart(), {
|
const doRestart = async () => {
|
||||||
immediate: false
|
setRestarting(true);
|
||||||
});
|
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||||
|
(error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const entities_theme = useTheme({
|
const entities_theme = useTheme({
|
||||||
Table: `
|
Table: `
|
||||||
@@ -247,13 +255,6 @@ const Customizations = () => {
|
|||||||
}
|
}
|
||||||
}, [devices, selectedDevice]);
|
}, [devices, selectedDevice]);
|
||||||
|
|
||||||
const doRestart = async () => {
|
|
||||||
await sendRestart().catch((error: Error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
setRestarting(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
function formatValue(value: unknown) {
|
function formatValue(value: unknown) {
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
return new Intl.NumberFormat().format(value);
|
return new Intl.NumberFormat().format(value);
|
||||||
@@ -509,7 +510,7 @@ const Customizations = () => {
|
|||||||
container
|
container
|
||||||
mb={1}
|
mb={1}
|
||||||
mt={0}
|
mt={0}
|
||||||
spacing={1}
|
spacing={2}
|
||||||
direction="row"
|
direction="row"
|
||||||
justifyContent="flex-start"
|
justifyContent="flex-start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const Help = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.HELP());
|
useLayoutTitle(LL.HELP());
|
||||||
|
|
||||||
const { send: getAPI } = useRequest((data: APIcall) => API(data), {
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
immediate: false
|
immediate: false
|
||||||
}).onSuccess((event) => {
|
}).onSuccess((event) => {
|
||||||
const anchor = document.createElement('a');
|
const anchor = document.createElement('a');
|
||||||
@@ -45,8 +45,8 @@ const Help = () => {
|
|||||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||||
});
|
});
|
||||||
|
|
||||||
const callAPI = async (device: string, entity: string) => {
|
const callAPI = async (device: string, cmd: string) => {
|
||||||
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
await sendAPI({ device, cmd, id: 0 }).catch((error: Error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -113,7 +113,7 @@ const Help = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => callAPI('system', 'allvalues')}
|
onClick={() => callAPI('system', 'allvalues')}
|
||||||
>
|
>
|
||||||
{LL.ALLVALUES(0)}
|
{LL.ALLVALUES()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Box border={1} p={1} mt={4}>
|
<Box border={1} p={1} mt={4}>
|
||||||
|
|||||||
@@ -272,8 +272,8 @@ export interface BoardProfile {
|
|||||||
|
|
||||||
export interface APIcall {
|
export interface APIcall {
|
||||||
device: string;
|
device: string;
|
||||||
entity: string;
|
cmd: string;
|
||||||
id: unknown;
|
id: number;
|
||||||
}
|
}
|
||||||
export interface WriteAnalogSensor {
|
export interface WriteAnalogSensor {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import Grid from '@mui/material/Grid2';
|
import Grid from '@mui/material/Grid2';
|
||||||
|
|
||||||
import { readHardwareStatus, restart } from 'api/system';
|
import { readHardwareStatus } from 'api/system';
|
||||||
|
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
import RestartMonitor from 'app/status/RestartMonitor';
|
import RestartMonitor from 'app/status/RestartMonitor';
|
||||||
@@ -35,9 +35,9 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { validate } from 'validators';
|
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 { BOARD_PROFILES } from '../main/types';
|
||||||
import type { Settings } from '../main/types';
|
import type { APIcall, Settings } from '../main/types';
|
||||||
import { createSettingsValidator } from '../main/validators';
|
import { createSettingsValidator } from '../main/validators';
|
||||||
|
|
||||||
export function boardProfileSelectItems() {
|
export function boardProfileSelectItems() {
|
||||||
@@ -80,6 +80,10 @@ const ApplicationSettings = () => {
|
|||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
const { loading: processingBoard, send: readBoardProfile } = useRequest(
|
const { loading: processingBoard, send: readBoardProfile } = useRequest(
|
||||||
(boardProfile: string) => getBoardProfile(boardProfile),
|
(boardProfile: string) => getBoardProfile(boardProfile),
|
||||||
{
|
{
|
||||||
@@ -102,9 +106,14 @@ const ApplicationSettings = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(restart(), {
|
const doRestart = async () => {
|
||||||
immediate: false
|
setRestarting(true);
|
||||||
});
|
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||||
|
(error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const updateBoardProfile = async (board_profile: string) => {
|
const updateBoardProfile = async (board_profile: string) => {
|
||||||
await readBoardProfile(board_profile).catch((error: Error) => {
|
await readBoardProfile(board_profile).catch((error: Error) => {
|
||||||
@@ -158,10 +167,7 @@ const ApplicationSettings = () => {
|
|||||||
|
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
await validateAndSubmit();
|
await validateAndSubmit();
|
||||||
await restartCommand().catch((error: Error) => {
|
await doRestart();
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
setRestarting(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -204,7 +210,7 @@ const ApplicationSettings = () => {
|
|||||||
label={LL.ENABLE_MODBUS()}
|
label={LL.ENABLE_MODBUS()}
|
||||||
/>
|
/>
|
||||||
{data.modbus_enabled && (
|
{data.modbus_enabled && (
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
@@ -258,7 +264,7 @@ const ApplicationSettings = () => {
|
|||||||
label={LL.ENABLE_SYSLOG()}
|
label={LL.ENABLE_SYSLOG()}
|
||||||
/>
|
/>
|
||||||
{data.syslog_enabled && (
|
{data.syslog_enabled && (
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
@@ -351,7 +357,7 @@ const ApplicationSettings = () => {
|
|||||||
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.FORMATTING_OPTIONS()}
|
{LL.FORMATTING_OPTIONS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={2}>
|
||||||
<Grid size={3}>
|
<Grid size={3}>
|
||||||
<TextField
|
<TextField
|
||||||
name="locale"
|
name="locale"
|
||||||
@@ -469,7 +475,7 @@ const ApplicationSettings = () => {
|
|||||||
</TextField>
|
</TextField>
|
||||||
{data.board_profile === 'CUSTOM' && (
|
{data.board_profile === 'CUSTOM' && (
|
||||||
<>
|
<>
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
@@ -555,7 +561,7 @@ const ApplicationSettings = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
{data.phy_type !== 0 && (
|
{data.phy_type !== 0 && (
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
name="eth_power"
|
name="eth_power"
|
||||||
@@ -601,7 +607,7 @@ const ApplicationSettings = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
name="tx_mode"
|
name="tx_mode"
|
||||||
@@ -717,7 +723,7 @@ const ApplicationSettings = () => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Grid container spacing={1} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -740,7 +746,7 @@ const ApplicationSettings = () => {
|
|||||||
disabled={!data.shower_timer}
|
disabled={!data.shower_timer}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid container spacing={1} rowSpacing={2} sx={{ pt: 2 }}>
|
<Grid container spacing={2} sx={{ pt: 2 }}>
|
||||||
{data.shower_timer && (
|
{data.shower_timer && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
checkUpgrade,
|
checkUpgrade,
|
||||||
getDevVersion,
|
getDevVersion,
|
||||||
getStableVersion,
|
getStableVersion,
|
||||||
restart,
|
|
||||||
uploadURL
|
uploadURL
|
||||||
} from 'api/system';
|
} from 'api/system';
|
||||||
|
|
||||||
@@ -76,7 +75,11 @@ const DownloadUpload = () => {
|
|||||||
saveFile(event.data, 'schedule.json');
|
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
|
immediate: false
|
||||||
}).onSuccess((event) => {
|
}).onSuccess((event) => {
|
||||||
saveFile(
|
saveFile(
|
||||||
@@ -98,15 +101,13 @@ const DownloadUpload = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(restart(), {
|
const doRestart = async () => {
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const callRestart = async () => {
|
|
||||||
setRestarting(true);
|
setRestarting(true);
|
||||||
await restartCommand().catch((error: Error) => {
|
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||||
|
(error: Error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { send: sendCheckUpgrade } = useRequest(checkUpgrade, {
|
const { send: sendCheckUpgrade } = useRequest(checkUpgrade, {
|
||||||
@@ -200,8 +201,8 @@ const DownloadUpload = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const callAPI = async (device: string, entity: string) => {
|
const callAPIandSave = async (device: string, cmd: string) => {
|
||||||
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
await sendAPIandSave({ device, cmd, id: 0 }).catch((error: Error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -291,7 +292,7 @@ const DownloadUpload = () => {
|
|||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => callAPI('system', 'info')}
|
onClick={() => callAPIandSave('system', 'info')}
|
||||||
>
|
>
|
||||||
{LL.SUPPORT_INFORMATION(0)}
|
{LL.SUPPORT_INFORMATION(0)}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -300,7 +301,7 @@ const DownloadUpload = () => {
|
|||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => callAPI('system', 'allvalues')}
|
onClick={() => callAPIandSave('system', 'allvalues')}
|
||||||
>
|
>
|
||||||
{LL.ALLVALUES()}
|
{LL.ALLVALUES()}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -420,15 +421,13 @@ const DownloadUpload = () => {
|
|||||||
<Typography variant="body2">{LL.UPLOAD_TEXT()}</Typography>
|
<Typography variant="body2">{LL.UPLOAD_TEXT()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<SingleUpload callRestart={callRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>
|
||||||
{restarting ? <RestartMonitor message={LL.WAIT_FIRMWARE()} /> : content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
|
import { API } from 'api/app';
|
||||||
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
|
import type { APIcall } from 'app/main/types';
|
||||||
import { SectionContent, useLayoutTitle } from 'components';
|
import { SectionContent, useLayoutTitle } from 'components';
|
||||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
@@ -34,13 +36,14 @@ const Settings = () => {
|
|||||||
|
|
||||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||||
|
|
||||||
const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), {
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const factoryReset = async () => {
|
const doFormat = async () => {
|
||||||
await factoryResetCommand();
|
await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => {
|
||||||
setConfirmFactoryReset(false);
|
setConfirmFactoryReset(false);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderFactoryResetDialog = () => (
|
const renderFactoryResetDialog = () => (
|
||||||
@@ -63,7 +66,7 @@ const Settings = () => {
|
|||||||
<Button
|
<Button
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={factoryReset}
|
onClick={doFormat}
|
||||||
color="error"
|
color="error"
|
||||||
>
|
>
|
||||||
{LL.FACTORY_RESET()}
|
{LL.FACTORY_RESET()}
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import * as NetworkApi from 'api/network';
|
import * as NetworkApi from 'api/network';
|
||||||
import * as SystemApi from 'api/system';
|
import { API } from 'api/app';
|
||||||
|
|
||||||
import { updateState, useRequest } from 'alova/client';
|
import { updateState, useRequest } from 'alova/client';
|
||||||
|
import type { APIcall } from 'app/main/types';
|
||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -71,7 +72,7 @@ const NetworkSettings = () => {
|
|||||||
update: NetworkApi.updateNetworkSettings
|
update: NetworkApi.updateNetworkSettings
|
||||||
});
|
});
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -131,11 +132,13 @@ const NetworkSettings = () => {
|
|||||||
await loadData();
|
await loadData();
|
||||||
};
|
};
|
||||||
|
|
||||||
const restart = async () => {
|
const doRestart = async () => {
|
||||||
await restartCommand().catch((error: Error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
setRestarting(true);
|
setRestarting(true);
|
||||||
|
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||||
|
(error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -358,7 +361,7 @@ const NetworkSettings = () => {
|
|||||||
startIcon={<PowerSettingsNewIcon />}
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="error"
|
color="error"
|
||||||
onClick={restart}
|
onClick={doRestart}
|
||||||
>
|
>
|
||||||
{LL.RESTART()}
|
{LL.RESTART()}
|
||||||
</Button>
|
</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 { readHardwareStatus } from 'api/system';
|
||||||
|
|
||||||
import { useRequest } from 'alova/client';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { FormLoader } from 'components';
|
import { useAutoRequest } from 'alova/client';
|
||||||
|
import MessageBox from 'components/MessageBox';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const RESTART_TIMEOUT = 2 * 60 * 1000; // 2 minutes
|
const RestartMonitor = () => {
|
||||||
const POLL_INTERVAL = 2000; // every 2 seconds
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
|
|
||||||
export interface RestartMonitorProps {
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RestartMonitor: FC<RestartMonitorProps> = ({ message }) => {
|
|
||||||
const [failed, setFailed] = useState<boolean>(false);
|
|
||||||
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout>();
|
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const timeoutAt = useRef(new Date().getTime() + RESTART_TIMEOUT);
|
let count = 0;
|
||||||
|
|
||||||
const { send } = useRequest(readHardwareStatus, { immediate: false });
|
const { data } = useAutoRequest(readHardwareStatus, {
|
||||||
|
pollingTime: 1000,
|
||||||
const poll = useRef(async () => {
|
force: true,
|
||||||
try {
|
initialData: { status: 'Getting ready...' },
|
||||||
await send();
|
async middleware(_, next) {
|
||||||
|
if (count++ >= 1) {
|
||||||
|
// skip first request (1 seconds) to allow AsyncWS to send its response
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onSuccess((event) => {
|
||||||
|
console.log(event.data.status); // TODO remove
|
||||||
|
if (event.data.status === 'ready' || event.data.status === undefined) {
|
||||||
document.location.href = '/';
|
document.location.href = '/';
|
||||||
} catch {
|
|
||||||
if (new Date().getTime() < timeoutAt.current) {
|
|
||||||
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
|
|
||||||
} else {
|
|
||||||
setFailed(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.onError((error, _method) => {
|
||||||
|
setErrorMessage(error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => () => timeoutId && clearTimeout(timeoutId), [timeoutId]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormLoader
|
<Dialog fullWidth={true} sx={dialogStyle} open={true}>
|
||||||
message={message ? message : LL.APPLICATION_RESTARTING() + '...'}
|
<DialogContent dividers>
|
||||||
errorMessage={failed ? 'Timed out' : undefined}
|
<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';
|
} from '@mui/material';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
|
import { API } from 'api/app';
|
||||||
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { useAutoRequest, useRequest } from 'alova/client';
|
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 { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
@@ -54,7 +55,7 @@ const SystemStatus = () => {
|
|||||||
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,7 +65,12 @@ const SystemStatus = () => {
|
|||||||
error
|
error
|
||||||
} = useAutoRequest(SystemApi.readSystemStatus, {
|
} = useAutoRequest(SystemApi.readSystemStatus, {
|
||||||
initialData: [],
|
initialData: [],
|
||||||
pollingTime: 5000
|
pollingTime: 5000,
|
||||||
|
async middleware(_, next) {
|
||||||
|
if (!restarting) {
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -195,17 +201,14 @@ const SystemStatus = () => {
|
|||||||
const activeHighlight = (value: boolean) =>
|
const activeHighlight = (value: boolean) =>
|
||||||
value ? theme.palette.success.main : theme.palette.info.main;
|
value ? theme.palette.success.main : theme.palette.info.main;
|
||||||
|
|
||||||
const restart = async () => {
|
const doRestart = async () => {
|
||||||
await restartCommand()
|
|
||||||
.then(() => {
|
|
||||||
setRestarting(true);
|
|
||||||
})
|
|
||||||
.catch((error: Error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setConfirmRestart(false);
|
setConfirmRestart(false);
|
||||||
});
|
setRestarting(true);
|
||||||
|
await sendAPI({ device: 'system', cmd: 'restart', id: -1 }).catch(
|
||||||
|
(error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderRestartDialog = () => (
|
const renderRestartDialog = () => (
|
||||||
@@ -228,7 +231,7 @@ const SystemStatus = () => {
|
|||||||
<Button
|
<Button
|
||||||
startIcon={<PowerSettingsNewIcon />}
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={restart}
|
onClick={doRestart}
|
||||||
color="error"
|
color="error"
|
||||||
>
|
>
|
||||||
{LL.RESTART()}
|
{LL.RESTART()}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager *
|
|||||||
: _securityManager(securityManager)
|
: _securityManager(securityManager)
|
||||||
, _is_firmware(false)
|
, _is_firmware(false)
|
||||||
, _md5() {
|
, _md5() {
|
||||||
// end-points
|
// upload a file via a form
|
||||||
server->on(
|
server->on(
|
||||||
UPLOAD_FILE_PATH,
|
UPLOAD_FILE_PATH,
|
||||||
HTTP_POST,
|
HTTP_POST,
|
||||||
@@ -25,6 +25,7 @@ UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager *
|
|||||||
handleUpload(request, filename, index, data, len, final);
|
handleUpload(request, filename, index, data, len, final);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// upload from a URL
|
||||||
server->on(UPLOAD_URL_PATH,
|
server->on(UPLOAD_URL_PATH,
|
||||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { uploadURL(request, json); },
|
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { uploadURL(request, json); },
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
@@ -123,7 +124,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
|||||||
request->_tempFile.close(); // close the file handle as the upload is now done
|
request->_tempFile.close(); // close the file handle as the upload is now done
|
||||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
request->onDisconnect(RestartService::restartNow);
|
emsesp::EMSESP::system_.restart_pending(true); // will be handled by the main loop
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
|||||||
if (_is_firmware && !request->_tempObject) {
|
if (_is_firmware && !request->_tempObject) {
|
||||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
request->onDisconnect(RestartService::restartNow);
|
emsesp::EMSESP::system_.restart_pending(true); // will be handled by the main loop
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -215,20 +215,6 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
|||||||
string_vector{F_(wifi), F_(reconnect)},
|
string_vector{F_(wifi), F_(reconnect)},
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) { to_app(shell).system_.wifi_reconnect(); });
|
[](Shell & shell, const std::vector<std::string> & arguments) { to_app(shell).system_.wifi_reconnect(); });
|
||||||
|
|
||||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
|
||||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
|
||||||
if (completed) {
|
|
||||||
to_app(shell).esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
|
||||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
|
||||||
to_app(shell).system_.format(shell);
|
|
||||||
} else {
|
|
||||||
shell.println("incorrect password");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SET commands
|
// SET commands
|
||||||
//
|
//
|
||||||
@@ -651,11 +637,7 @@ void EMSESPShell::stopped() {
|
|||||||
void EMSESPShell::display_banner() {
|
void EMSESPShell::display_banner() {
|
||||||
println();
|
println();
|
||||||
printfln("┌───────────────────────────────────────┐");
|
printfln("┌───────────────────────────────────────┐");
|
||||||
#ifndef EMSESP_DEBUG
|
|
||||||
printfln("│ %sEMS-ESP version %-20s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
printfln("│ %sEMS-ESP version %-20s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
||||||
#else
|
|
||||||
printfln("│ %sEMS-ESP version %s%-8s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, " (D)", COLOR_BOLD_OFF);
|
|
||||||
#endif
|
|
||||||
printfln("│ │");
|
printfln("│ │");
|
||||||
printfln("│ %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
printfln("│ %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||||
printfln("│ %ssu%s to access admin commands │", COLOR_UNDERLINE, COLOR_RESET);
|
printfln("│ %ssu%s to access admin commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||||
|
|||||||
@@ -1569,7 +1569,7 @@ void EMSESP::start() {
|
|||||||
// do a quick scan of the filesystem to see if we have a /config folder
|
// do a quick scan of the filesystem to see if we have a /config folder
|
||||||
// so we know if this is a new install or not
|
// so we know if this is a new install or not
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
File root = LittleFS.open("/config");
|
File root = LittleFS.open(EMSESP_FS_CONFIG_DIRECTORY);
|
||||||
bool factory_settings = !root;
|
bool factory_settings = !root;
|
||||||
if (!root) {
|
if (!root) {
|
||||||
LOG_INFO("No config found, assuming factory settings");
|
LOG_INFO("No config found, assuming factory settings");
|
||||||
@@ -1589,9 +1589,9 @@ void EMSESP::start() {
|
|||||||
LOG_DEBUG("NVS device information: %s", system_.getBBQKeesGatewayDetails().c_str());
|
LOG_DEBUG("NVS device information: %s", system_.getBBQKeesGatewayDetails().c_str());
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
LOG_INFO("Starting EMS-ESP version %s from %s partition", EMSESP_APP_VERSION, esp_ota_get_running_partition()->label); // welcome message
|
LOG_INFO("Booting EMS-ESP version %s from %s partition", EMSESP_APP_VERSION, esp_ota_get_running_partition()->label); // welcome message
|
||||||
#else
|
#else
|
||||||
LOG_INFO("Starting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message
|
LOG_INFO("Booting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message
|
||||||
#endif
|
#endif
|
||||||
LOG_DEBUG("System is running in Debug mode");
|
LOG_DEBUG("System is running in Debug mode");
|
||||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||||
@@ -1610,6 +1610,12 @@ void EMSESP::start() {
|
|||||||
system_.system_restart();
|
system_.system_restart();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||||
|
device_library_ = {
|
||||||
|
#include "device_library.h"
|
||||||
|
};
|
||||||
|
LOG_INFO("Loaded EMS device library (%d)", device_library_.size());
|
||||||
|
|
||||||
system_.reload_settings(); // ... and store some of the settings locally
|
system_.reload_settings(); // ... and store some of the settings locally
|
||||||
|
|
||||||
webCustomizationService.begin(); // load the customizations
|
webCustomizationService.begin(); // load the customizations
|
||||||
@@ -1627,31 +1633,22 @@ void EMSESP::start() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start services
|
||||||
if (system_.modbus_enabled()) {
|
if (system_.modbus_enabled()) {
|
||||||
modbus_ = new Modbus;
|
modbus_ = new Modbus;
|
||||||
modbus_->start(1, system_.modbus_port(), system_.modbus_max_clients(), system_.modbus_timeout());
|
modbus_->start(1, system_.modbus_port(), system_.modbus_max_clients(), system_.modbus_timeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
mqtt_.start(); // mqtt init
|
mqtt_.start(); // mqtt init
|
||||||
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
||||||
shower_.start(); // initialize shower timer and shower alert
|
shower_.start(); // initialize shower timer and shower alert
|
||||||
temperaturesensor_.start(); // Temperature external sensors
|
temperaturesensor_.start(); // Temperature external sensors
|
||||||
analogsensor_.start(); // Analog external sensors
|
analogsensor_.start(); // Analog external sensors
|
||||||
|
|
||||||
|
// start web services
|
||||||
webLogService.start(); // apply settings to weblog service
|
webLogService.start(); // apply settings to weblog service
|
||||||
|
|
||||||
webModulesService.begin(); // setup the external library modules
|
webModulesService.begin(); // setup the external library modules
|
||||||
|
|
||||||
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
|
||||||
device_library_ = {
|
|
||||||
#include "device_library.h"
|
|
||||||
};
|
|
||||||
LOG_INFO("Loaded EMS device library (%d records)", device_library_.size());
|
|
||||||
|
|
||||||
#if defined(EMSESP_STANDALONE)
|
|
||||||
Mqtt::on_connect(); // simulate an MQTT connection
|
|
||||||
#endif
|
|
||||||
|
|
||||||
webServer.begin(); // start the web server
|
webServer.begin(); // start the web server
|
||||||
|
LOG_INFO("Starting Web Server");
|
||||||
}
|
}
|
||||||
|
|
||||||
// main loop calling all services
|
// main loop calling all services
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ MAKE_WORD_TRANSLATION(setiovalue_cmd, "set io value", "Setze Wertevorgabe", "ins
|
|||||||
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Sysloglevel", "aanpassen log niveau", "", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "") // TODO translate
|
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Sysloglevel", "aanpassen log niveau", "", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "") // TODO translate
|
||||||
MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Lese alle EMS-Werte neu", "Verversen alle EMS waardes", "", "odśwież wszystkie wartości EMS", "oppfrisk alle EMS verdier", "", "Bütün EMS değerlerini yenile", "aggiornare tutti i valori EMS", "obnoviť všetky hodnoty EMS") // TODO translate
|
MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Lese alle EMS-Werte neu", "Verversen alle EMS waardes", "", "odśwież wszystkie wartości EMS", "oppfrisk alle EMS verdier", "", "Bütün EMS değerlerini yenile", "aggiornare tutti i valori EMS", "obnoviť všetky hodnoty EMS") // TODO translate
|
||||||
MAKE_WORD_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "opnieuw opstarten", "", "uruchom ponownie EMS-ESP", "restart EMS-ESP", "redémarrer EMS-ESP", "EMS-ESPyi yeniden başlat", "riavvia EMS-ESP", "reštart EMS-ESP") // TODO translate
|
MAKE_WORD_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "opnieuw opstarten", "", "uruchom ponownie EMS-ESP", "restart EMS-ESP", "redémarrer EMS-ESP", "EMS-ESPyi yeniden başlat", "riavvia EMS-ESP", "reštart EMS-ESP") // TODO translate
|
||||||
|
MAKE_WORD_TRANSLATION(format_cmd, "factory reset EMS-ESP", "", "", "", "", "", "", "", "", "") // TODO translate
|
||||||
MAKE_WORD_TRANSLATION(watch_cmd, "watch incoming telegrams", "Watch auf eingehende Telegramme", "inkomende telegrammen bekijken", "", "obserwuj przyczodzące telegramy", "se innkommende telegrammer", "", "Gelen telegramları", "guardare i telegrammi in arrivo", "sledovať prichádzajúce telegramy") // TODO translate
|
MAKE_WORD_TRANSLATION(watch_cmd, "watch incoming telegrams", "Watch auf eingehende Telegramme", "inkomende telegrammen bekijken", "", "obserwuj przyczodzące telegramy", "se innkommende telegrammer", "", "Gelen telegramları", "guardare i telegrammi in arrivo", "sledovať prichádzajúce telegramy") // TODO translate
|
||||||
MAKE_WORD_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "publiceer alles naar MQTT", "", "opublikuj wszystko na MQTT", "Publiser alt til MQTT", "", "Hepsini MQTTye gönder", "pubblica tutto su MQTT", "zverejniť všetko na MQTT") // TODO translate
|
MAKE_WORD_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "publiceer alles naar MQTT", "", "opublikuj wszystko na MQTT", "Publiser alt til MQTT", "", "Hepsini MQTTye gönder", "pubblica tutto su MQTT", "zverejniť všetko na MQTT") // TODO translate
|
||||||
MAKE_WORD_TRANSLATION(system_info_cmd, "show system info", "Zeige System-Status", "toon systeemstatus", "", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému") // TODO translate
|
MAKE_WORD_TRANSLATION(system_info_cmd, "show system info", "Zeige System-Status", "toon systeemstatus", "", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému") // TODO translate
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN};
|
|||||||
// init statics
|
// init statics
|
||||||
PButton System::myPButton_;
|
PButton System::myPButton_;
|
||||||
bool System::restart_requested_ = false;
|
bool System::restart_requested_ = false;
|
||||||
|
bool System::restart_pending_ = false;
|
||||||
bool System::test_set_all_active_ = false;
|
bool System::test_set_all_active_ = false;
|
||||||
uint32_t System::max_alloc_mem_;
|
uint32_t System::max_alloc_mem_;
|
||||||
uint32_t System::heap_mem_;
|
uint32_t System::heap_mem_;
|
||||||
@@ -328,7 +329,10 @@ void System::system_restart(const char * partitionname) {
|
|||||||
LOG_INFO("Restarting EMS-ESP...");
|
LOG_INFO("Restarting EMS-ESP...");
|
||||||
}
|
}
|
||||||
|
|
||||||
restart_requested(false); // make sure it's not repeated
|
// make sure it's only executed once
|
||||||
|
restart_requested(false);
|
||||||
|
restart_pending(false);
|
||||||
|
|
||||||
store_nvs_values(); // save any NVS values
|
store_nvs_values(); // save any NVS values
|
||||||
Shell::loop_all(); // flush log to output
|
Shell::loop_all(); // flush log to output
|
||||||
delay(1000); // wait 1 second
|
delay(1000); // wait 1 second
|
||||||
@@ -346,19 +350,6 @@ void System::wifi_reconnect() {
|
|||||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
|
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
|
||||||
}
|
}
|
||||||
|
|
||||||
// format the FS. Wipes everything.
|
|
||||||
void System::format(uuid::console::Shell & shell) {
|
|
||||||
auto msg = ("Formatting file system. This will reset all settings to their defaults");
|
|
||||||
shell.logger().warning(msg);
|
|
||||||
EMSuart::stop();
|
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
LittleFS.format();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
System::system_restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::syslog_init() {
|
void System::syslog_init() {
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
syslog_enabled_ = settings.syslog_enabled;
|
syslog_enabled_ = settings.syslog_enabled;
|
||||||
@@ -538,12 +529,9 @@ void System::button_OnLongPress(PButton & b) {
|
|||||||
EMSESP::system_.system_restart("boot");
|
EMSESP::system_.system_restart("boot");
|
||||||
}
|
}
|
||||||
|
|
||||||
// button indefinite press
|
// button indefinite press - do nothing for now
|
||||||
void System::button_OnVLongPress(PButton & b) {
|
void System::button_OnVLongPress(PButton & b) {
|
||||||
LOG_NOTICE("Button pressed - very long press - factory reset");
|
LOG_NOTICE("Button pressed - very long press");
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
EMSESP::esp8266React.factoryReset();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// push button
|
// push button
|
||||||
@@ -860,9 +848,8 @@ void System::system_check() {
|
|||||||
void System::commands_init() {
|
void System::commands_init() {
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
|
|
||||||
// restart, watch, message (and test) are also exposed as Console commands
|
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(format), System::command_format, FL_(format_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, FL_(watch_cmd));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, FL_(watch_cmd));
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(message), System::command_message, FL_(message_cmd));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(message), System::command_message, FL_(message_cmd));
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_TEST)
|
||||||
@@ -1748,13 +1735,35 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// format command - factory reset, removing all config files
|
||||||
|
bool System::command_format(const char * value, const int8_t id) {
|
||||||
|
LOG_INFO("Removing all config files");
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2+
|
||||||
|
File root = LittleFS.open(EMSESP_FS_CONFIG_DIRECTORY);
|
||||||
|
File file;
|
||||||
|
while ((file = root.openNextFile())) {
|
||||||
|
String path = file.path();
|
||||||
|
file.close();
|
||||||
|
LittleFS.remove(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EMSESP::system_.restart_requested(true); // will be handled by the main loop
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// restart command - perform a hard reset
|
// restart command - perform a hard reset
|
||||||
bool System::command_restart(const char * value, const int8_t id) {
|
bool System::command_restart(const char * value, const int8_t id) {
|
||||||
if (value != nullptr && value[0] != '\0') {
|
if (id != 0) {
|
||||||
EMSESP::system_.system_restart(value);
|
// if it has an id then it's a web call and we need to queue the restart
|
||||||
} else {
|
LOG_INFO("Preparing to restart system");
|
||||||
EMSESP::system_.system_restart();
|
EMSESP::system_.restart_pending(true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
LOG_INFO("Restarting system");
|
||||||
|
EMSESP::system_.restart_requested(true); // will be handled by the main loop
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1908,7 +1917,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
|
|
||||||
LOG_INFO("Firmware uploaded successfully. Restarting...");
|
LOG_INFO("Firmware uploaded successfully. Restarting...");
|
||||||
|
|
||||||
restart_requested(true);
|
restart_pending(true);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
16
src/system.h
16
src/system.h
@@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
using uuid::console::Shell;
|
using uuid::console::Shell;
|
||||||
|
|
||||||
|
#define EMSESP_FS_CONFIG_DIRECTORY "/config"
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 };
|
enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 };
|
||||||
@@ -55,12 +57,14 @@ class System {
|
|||||||
static bool command_publish(const char * value, const int8_t id);
|
static bool command_publish(const char * value, const int8_t id);
|
||||||
static bool command_fetch(const char * value, const int8_t id);
|
static bool command_fetch(const char * value, const int8_t id);
|
||||||
static bool command_restart(const char * value, const int8_t id);
|
static bool command_restart(const char * value, const int8_t id);
|
||||||
static bool command_syslog_level(const char * value, const int8_t id);
|
static bool command_format(const char * value, const int8_t id);
|
||||||
|
// static bool command_syslog_level(const char * value, const int8_t id);
|
||||||
static bool command_watch(const char * value, const int8_t id);
|
static bool command_watch(const char * value, const int8_t id);
|
||||||
static bool command_message(const char * value, const int8_t id);
|
static bool command_message(const char * value, const int8_t id);
|
||||||
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
||||||
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
||||||
static bool command_allvalues(const char * value, const int8_t id, JsonObject output);
|
static bool command_allvalues(const char * value, const int8_t id, JsonObject output);
|
||||||
|
|
||||||
static bool get_value_info(JsonObject root, const char * cmd);
|
static bool get_value_info(JsonObject root, const char * cmd);
|
||||||
static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val);
|
static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val);
|
||||||
|
|
||||||
@@ -72,7 +76,6 @@ class System {
|
|||||||
|
|
||||||
void store_nvs_values();
|
void store_nvs_values();
|
||||||
void system_restart(const char * partition = nullptr);
|
void system_restart(const char * partition = nullptr);
|
||||||
void format(uuid::console::Shell & shell);
|
|
||||||
void upload_status(bool in_progress);
|
void upload_status(bool in_progress);
|
||||||
bool upload_status();
|
bool upload_status();
|
||||||
void show_mem(const char * note);
|
void show_mem(const char * note);
|
||||||
@@ -116,11 +119,17 @@ class System {
|
|||||||
static void restart_requested(bool restart_requested) {
|
static void restart_requested(bool restart_requested) {
|
||||||
restart_requested_ = restart_requested;
|
restart_requested_ = restart_requested;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool restart_requested() {
|
static bool restart_requested() {
|
||||||
return restart_requested_;
|
return restart_requested_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void restart_pending(bool restart_pending) {
|
||||||
|
restart_pending_ = restart_pending;
|
||||||
|
}
|
||||||
|
static bool restart_pending() {
|
||||||
|
return restart_pending_;
|
||||||
|
}
|
||||||
|
|
||||||
bool telnet_enabled() {
|
bool telnet_enabled() {
|
||||||
return telnet_enabled_;
|
return telnet_enabled_;
|
||||||
}
|
}
|
||||||
@@ -291,6 +300,7 @@ class System {
|
|||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
static bool restart_requested_;
|
static bool restart_requested_;
|
||||||
|
static bool restart_pending_; // used in 2-stage process to call restart from Web API
|
||||||
static bool test_set_all_active_; // force all entities in a device to have a value
|
static bool test_set_all_active_; // force all entities in a device to have a value
|
||||||
static uint32_t max_alloc_mem_;
|
static uint32_t max_alloc_mem_;
|
||||||
static uint32_t heap_mem_;
|
static uint32_t heap_mem_;
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques
|
|||||||
if (LittleFS.remove(EMSESP_CUSTOMIZATION_FILE)) {
|
if (LittleFS.remove(EMSESP_CUSTOMIZATION_FILE)) {
|
||||||
AsyncWebServerResponse * response = request->beginResponse(205); // restart needed
|
AsyncWebServerResponse * response = request->beginResponse(205); // restart needed
|
||||||
request->send(response);
|
request->send(response);
|
||||||
EMSESP::system_.restart_requested(true);
|
EMSESP::system_.restart_pending(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
} else {
|
} else {
|
||||||
EMSESP::nvs_.putString("boot", "S32");
|
EMSESP::nvs_.putString("boot", "S32");
|
||||||
}
|
}
|
||||||
ESP.restart();
|
// ESP.restart();
|
||||||
|
EMSESP::system_.restart_requested(true);
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
settings.board_profile = "C3MINI";
|
settings.board_profile = "C3MINI";
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
|||||||
@@ -34,12 +34,6 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
|||||||
|
|
||||||
// /rest/systemStatus
|
// /rest/systemStatus
|
||||||
void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||||
// This is a litle trick for the OTA upload. We don't want the React RestartService to think we're finished
|
|
||||||
// with the upload so we fake it and pretent the /rest/systemStatus is not available. That way the spinner keeps spinning.
|
|
||||||
if (EMSESP::system_.upload_status()) {
|
|
||||||
return; // ignore endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
||||||
|
|
||||||
auto * response = new AsyncJsonResponse(false);
|
auto * response = new AsyncJsonResponse(false);
|
||||||
@@ -85,6 +79,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// /rest/hardwareStatus
|
// /rest/hardwareStatus
|
||||||
|
// This is also used for polling
|
||||||
void WebStatusService::hardwareStatus(AsyncWebServerRequest * request) {
|
void WebStatusService::hardwareStatus(AsyncWebServerRequest * request) {
|
||||||
EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
||||||
|
|
||||||
@@ -144,6 +139,14 @@ void WebStatusService::hardwareStatus(AsyncWebServerRequest * request) {
|
|||||||
root["has_partition"] = false;
|
root["has_partition"] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches RestartMonitor.tsx
|
||||||
|
if (EMSESP::system_.restart_pending()) {
|
||||||
|
root["status"] = "restarting";
|
||||||
|
EMSESP::system_.restart_requested(true); // tell emsesp loop to start restart
|
||||||
|
} else {
|
||||||
|
root["status"] = EMSESP::system_.upload_status() ? "uploading" : "ready";
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
|
|||||||
Reference in New Issue
Block a user