mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
add version page, move off Status to Settings
This commit is contained in:
@@ -15,6 +15,7 @@ import DownloadUpload from 'app/settings/DownloadUpload';
|
|||||||
import MqttSettings from 'app/settings/MqttSettings';
|
import MqttSettings from 'app/settings/MqttSettings';
|
||||||
import NTPSettings from 'app/settings/NTPSettings';
|
import NTPSettings from 'app/settings/NTPSettings';
|
||||||
import Settings from 'app/settings/Settings';
|
import Settings from 'app/settings/Settings';
|
||||||
|
import Version from 'app/settings/Version';
|
||||||
import Network from 'app/settings/network/Network';
|
import Network from 'app/settings/network/Network';
|
||||||
import Security from 'app/settings/security/Security';
|
import Security from 'app/settings/security/Security';
|
||||||
import APStatus from 'app/status/APStatus';
|
import APStatus from 'app/status/APStatus';
|
||||||
@@ -51,6 +52,7 @@ const AuthenticatedRouting = () => {
|
|||||||
{me.admin && (
|
{me.admin && (
|
||||||
<>
|
<>
|
||||||
<Route path="/settings" element={<Settings />} />
|
<Route path="/settings" element={<Settings />} />
|
||||||
|
<Route path="/settings/version" element={<Version />} />
|
||||||
<Route path="/settings/application" element={<ApplicationSettings />} />
|
<Route path="/settings/application" element={<ApplicationSettings />} />
|
||||||
<Route path="/settings/mqtt" element={<MqttSettings />} />
|
<Route path="/settings/mqtt" element={<MqttSettings />} />
|
||||||
<Route path="/settings/ntp" element={<NTPSettings />} />
|
<Route path="/settings/ntp" element={<NTPSettings />} />
|
||||||
|
|||||||
@@ -1,27 +1,13 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
|
||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
import { Box, Button, Typography } from '@mui/material';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
Link,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
import Grid from '@mui/material/Grid2';
|
import Grid from '@mui/material/Grid2';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import { API, callAction } from 'api/app';
|
import { API, callAction } from 'api/app';
|
||||||
import { getDevVersion, getStableVersion } from 'api/system';
|
|
||||||
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
import type { APIcall } from 'app/main/types';
|
import type { APIcall } from 'app/main/types';
|
||||||
import RestartMonitor from 'app/status/RestartMonitor';
|
import RestartMonitor from 'app/status/RestartMonitor';
|
||||||
@@ -38,18 +24,6 @@ const DownloadUpload = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [restarting, setRestarting] = useState<boolean>(false);
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
const [openDialog, setOpenDialog] = useState<boolean>(false);
|
|
||||||
const [useDev, setUseDev] = useState<boolean>(false);
|
|
||||||
const [upgradeAvailable, setUpgradeAvailable] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const { send: sendCheckUpgrade } = useRequest(
|
|
||||||
(version: string) => callAction({ action: 'checkUpgrade', param: version }),
|
|
||||||
{
|
|
||||||
immediate: false
|
|
||||||
}
|
|
||||||
).onSuccess((event) => {
|
|
||||||
setUpgradeAvailable((event.data as { upgradeable: boolean }).upgradeable);
|
|
||||||
});
|
|
||||||
|
|
||||||
const { send: sendExportData } = useRequest(
|
const { send: sendExportData } = useRequest(
|
||||||
(type: string) => callAction({ action: 'export', param: type }),
|
(type: string) => callAction({ action: 'export', param: type }),
|
||||||
@@ -71,13 +45,6 @@ const DownloadUpload = () => {
|
|||||||
|
|
||||||
const { data, send: loadData, error } = useRequest(SystemApi.readSystemStatus);
|
const { data, send: loadData, error } = useRequest(SystemApi.readSystemStatus);
|
||||||
|
|
||||||
const { send: sendUploadURL } = useRequest(
|
|
||||||
(url: string) => callAction({ action: 'uploadURL', param: url }),
|
|
||||||
{
|
|
||||||
immediate: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const doRestart = async () => {
|
const doRestart = async () => {
|
||||||
setRestarting(true);
|
setRestarting(true);
|
||||||
await sendAPI({ device: 'system', cmd: 'restart', id: 0 }).catch(
|
await sendAPI({ device: 'system', cmd: 'restart', id: 0 }).catch(
|
||||||
@@ -87,134 +54,13 @@ const DownloadUpload = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// called immediately to get the latest version, on page load
|
|
||||||
const { data: latestVersion } = useRequest(getStableVersion, {
|
|
||||||
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/v3.6.5/EMS-ESP-3_6_5-ESP32-16MB+.bin
|
|
||||||
// immediate: false,
|
|
||||||
// initialData: '3.6.5'
|
|
||||||
});
|
|
||||||
|
|
||||||
// called immediately to get the latest version, on page load, then check for upgrade
|
|
||||||
const { data: latestDevVersion } = useRequest(getDevVersion, {
|
|
||||||
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_0-dev_31-ESP32-16MB+.bin
|
|
||||||
// immediate: false,
|
|
||||||
// initialData: '3.7.0-dev.32'
|
|
||||||
}).onSuccess((event) => {
|
|
||||||
void sendCheckUpgrade(event.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
|
||||||
const STABLE_RELNOTES_URL =
|
|
||||||
'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
|
|
||||||
|
|
||||||
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
|
||||||
const DEV_RELNOTES_URL =
|
|
||||||
'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
|
|
||||||
|
|
||||||
const getBinURL = (useDevVersion: boolean) => {
|
|
||||||
if (!latestVersion || !latestDevVersion) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const filename =
|
|
||||||
'EMS-ESP-' +
|
|
||||||
(useDevVersion ? latestDevVersion : latestVersion).replaceAll('.', '_') +
|
|
||||||
'-' +
|
|
||||||
getPlatform() +
|
|
||||||
'.bin';
|
|
||||||
return useDevVersion
|
|
||||||
? DEV_URL + filename
|
|
||||||
: STABLE_URL + 'v' + latestVersion + '/' + filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPlatform = () => {
|
|
||||||
return (
|
|
||||||
[data.esp_platform, data.flash_chip_size >= 16384 ? '16MB' : '4MB'].join('-') +
|
|
||||||
(data.psram ? '+' : '')
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const installFirmwareURL = async (url: string) => {
|
|
||||||
await sendUploadURL(url).catch((error: Error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
setRestarting(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
||||||
|
|
||||||
const internet_live =
|
|
||||||
latestDevVersion !== undefined && latestVersion !== undefined;
|
|
||||||
|
|
||||||
const renderUploadDialog = () => {
|
|
||||||
if (!internet_live) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
sx={dialogStyle}
|
|
||||||
open={openDialog}
|
|
||||||
onClose={() => setOpenDialog(false)}
|
|
||||||
>
|
|
||||||
<DialogTitle>
|
|
||||||
{LL.INSTALL('') +
|
|
||||||
' ' +
|
|
||||||
(useDev ? LL.DEVELOPMENT() : LL.STABLE()) +
|
|
||||||
' Firmware'}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent dividers>
|
|
||||||
<Typography mb={2}>
|
|
||||||
{LL.INSTALL_VERSION(useDev ? latestDevVersion : latestVersion)}
|
|
||||||
</Typography>
|
|
||||||
<Link
|
|
||||||
target="_blank"
|
|
||||||
href={useDev ? DEV_RELNOTES_URL : STABLE_RELNOTES_URL}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{LL.RELEASE_NOTES()}
|
|
||||||
</Link>
|
|
||||||
|
|
|
||||||
<Link target="_blank" href={getBinURL(useDev)} color="primary">
|
|
||||||
{LL.DOWNLOAD(1)}
|
|
||||||
</Link>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
startIcon={<CancelIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setOpenDialog(false)}
|
|
||||||
color="secondary"
|
|
||||||
>
|
|
||||||
{LL.CANCEL()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<WarningIcon color="warning" />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => installFirmwareURL(getBinURL(useDev))}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{LL.INSTALL('')}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// useDevVersion = true to force using the dev version
|
|
||||||
const showFirmwareDialog = (useDevVersion: boolean) => {
|
|
||||||
if (useDevVersion || data.emsesp_version.includes('dev')) {
|
|
||||||
setUseDev(true);
|
|
||||||
}
|
|
||||||
setOpenDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDev = data.emsesp_version.includes('dev');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
||||||
@@ -232,7 +78,7 @@ const DownloadUpload = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => sendExportData('settings')}
|
onClick={() => sendExportData('settings')}
|
||||||
>
|
>
|
||||||
{LL.DOWNLOAD(1)} {LL.SETTINGS_OF(LL.APPLICATION())}
|
{LL.SETTINGS_OF(LL.APPLICATION())}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -242,7 +88,7 @@ const DownloadUpload = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => sendExportData('customizations')}
|
onClick={() => sendExportData('customizations')}
|
||||||
>
|
>
|
||||||
{LL.DOWNLOAD(1)} {LL.CUSTOMIZATIONS()}
|
{LL.CUSTOMIZATIONS()}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{ ml: 2 }}
|
sx={{ ml: 2 }}
|
||||||
@@ -251,7 +97,7 @@ const DownloadUpload = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => sendExportData('entities')}
|
onClick={() => sendExportData('entities')}
|
||||||
>
|
>
|
||||||
{LL.DOWNLOAD(1)} {LL.CUSTOM_ENTITIES(0)}
|
{LL.CUSTOM_ENTITIES(0)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{ ml: 2 }}
|
sx={{ ml: 2 }}
|
||||||
@@ -260,7 +106,7 @@ const DownloadUpload = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => sendExportData('schedule')}
|
onClick={() => sendExportData('schedule')}
|
||||||
>
|
>
|
||||||
{LL.DOWNLOAD(1)} {LL.SCHEDULE(0)}
|
{LL.SCHEDULE(0)}
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -273,68 +119,6 @@ const DownloadUpload = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<SingleUpload doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
|
|
||||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
|
||||||
{LL.EMS_ESP_VER()}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Box p={2} mt={2} border="1px solid grey" borderRadius={2}>
|
|
||||||
<Typography>
|
|
||||||
<b>{LL.VERSION() + ':'}</b> {data.emsesp_version}
|
|
||||||
{data.build_flags && (
|
|
||||||
<Typography variant="caption">
|
|
||||||
({data.build_flags})
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
<Typography>
|
|
||||||
<b>Platform:</b> {getPlatform()}
|
|
||||||
</Typography>
|
|
||||||
<Typography>
|
|
||||||
<b>Release:</b> {isDev ? LL.DEVELOPMENT() : LL.STABLE()}
|
|
||||||
{!isDev && (
|
|
||||||
<Button
|
|
||||||
sx={{ ml: 2 }}
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => showFirmwareDialog(true)}
|
|
||||||
>
|
|
||||||
{LL.SWITCH_DEV()}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
<Typography mt={2} color="secondary">
|
|
||||||
<InfoOutlinedIcon color="secondary" sx={{ verticalAlign: 'middle' }} />
|
|
||||||
|
|
||||||
{upgradeAvailable ? LL.UPGRADE_AVAILABLE() : LL.LATEST_VERSION()}
|
|
||||||
{upgradeAvailable &&
|
|
||||||
internet_live &&
|
|
||||||
(data.psram ? (
|
|
||||||
<Button
|
|
||||||
sx={{ ml: 2, textTransform: 'none' }}
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => showFirmwareDialog(false)}
|
|
||||||
>
|
|
||||||
{isDev
|
|
||||||
? LL.INSTALL('v' + latestDevVersion)
|
|
||||||
: LL.INSTALL('v' + latestVersion)}
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
|
|
||||||
<Link target="_blank" href={getBinURL(isDev)} color="primary">
|
|
||||||
{LL.DOWNLOAD(1)} v
|
|
||||||
{isDev ? latestDevVersion : latestVersion}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
{renderUploadDialog()}
|
|
||||||
</Box>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
List
|
List
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import { API } from 'api/app';
|
import { API, callAction } from 'api/app';
|
||||||
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
@@ -39,6 +40,11 @@ const Settings = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// get installed version
|
||||||
|
const { data } = useRequest(() => callAction({ action: 'checkUpgrade' }), {
|
||||||
|
initialData: { emsesp_version: '...' }
|
||||||
|
});
|
||||||
|
|
||||||
const doFormat = async () => {
|
const doFormat = async () => {
|
||||||
await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => {
|
await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => {
|
||||||
setConfirmFactoryReset(false);
|
setConfirmFactoryReset(false);
|
||||||
@@ -77,10 +83,18 @@ const Settings = () => {
|
|||||||
const content = () => (
|
const content = () => (
|
||||||
<>
|
<>
|
||||||
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||||
|
<ListMenuItem
|
||||||
|
icon={BuildIcon}
|
||||||
|
bgcolor="#72caf9"
|
||||||
|
label={LL.EMS_ESP_VER()}
|
||||||
|
text={data.emsesp_version}
|
||||||
|
to="version"
|
||||||
|
/>
|
||||||
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={TuneIcon}
|
icon={TuneIcon}
|
||||||
bgcolor="#134ba2"
|
bgcolor="#134ba2"
|
||||||
label={LL.SETTINGS_OF(LL.APPLICATION())}
|
label={LL.APPLICATION()}
|
||||||
text={LL.APPLICATION_SETTINGS_1()}
|
text={LL.APPLICATION_SETTINGS_1()}
|
||||||
to="application"
|
to="application"
|
||||||
/>
|
/>
|
||||||
@@ -88,7 +102,7 @@ const Settings = () => {
|
|||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={SettingsEthernetIcon}
|
icon={SettingsEthernetIcon}
|
||||||
bgcolor="#40828f"
|
bgcolor="#40828f"
|
||||||
label={LL.SETTINGS_OF(LL.NETWORK(0))}
|
label={LL.NETWORK(0)}
|
||||||
text={LL.CONFIGURE(LL.SETTINGS_OF(LL.NETWORK(1)))}
|
text={LL.CONFIGURE(LL.SETTINGS_OF(LL.NETWORK(1)))}
|
||||||
to="network"
|
to="network"
|
||||||
/>
|
/>
|
||||||
@@ -96,7 +110,7 @@ const Settings = () => {
|
|||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={SettingsInputAntennaIcon}
|
icon={SettingsInputAntennaIcon}
|
||||||
bgcolor="#5f9a5f"
|
bgcolor="#5f9a5f"
|
||||||
label={LL.SETTINGS_OF(LL.ACCESS_POINT(0))}
|
label={LL.ACCESS_POINT(0)}
|
||||||
text={LL.CONFIGURE(LL.ACCESS_POINT(1))}
|
text={LL.CONFIGURE(LL.ACCESS_POINT(1))}
|
||||||
to="ap"
|
to="ap"
|
||||||
/>
|
/>
|
||||||
@@ -104,7 +118,7 @@ const Settings = () => {
|
|||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={AccessTimeIcon}
|
icon={AccessTimeIcon}
|
||||||
bgcolor="#c5572c"
|
bgcolor="#c5572c"
|
||||||
label={LL.SETTINGS_OF('NTP')}
|
label="NTP"
|
||||||
text={LL.CONFIGURE(LL.LOCAL_TIME(1))}
|
text={LL.CONFIGURE(LL.LOCAL_TIME(1))}
|
||||||
to="ntp"
|
to="ntp"
|
||||||
/>
|
/>
|
||||||
@@ -112,14 +126,14 @@ const Settings = () => {
|
|||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={DeviceHubIcon}
|
icon={DeviceHubIcon}
|
||||||
bgcolor="#68374d"
|
bgcolor="#68374d"
|
||||||
label={LL.SETTINGS_OF('MQTT')}
|
label="MQTT"
|
||||||
text={LL.CONFIGURE('MQTT')}
|
text={LL.CONFIGURE('MQTT')}
|
||||||
to="mqtt"
|
to="mqtt"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={LockIcon}
|
icon={LockIcon}
|
||||||
label={LL.SETTINGS_OF(LL.SECURITY(0))}
|
label={LL.SECURITY(0)}
|
||||||
text={LL.SECURITY_1()}
|
text={LL.SECURITY_1()}
|
||||||
to="security"
|
to="security"
|
||||||
/>
|
/>
|
||||||
|
|||||||
261
interface/src/app/settings/Version.tsx
Normal file
261
interface/src/app/settings/Version.tsx
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||||
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Link,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
import { callAction } from 'api/app';
|
||||||
|
import { getDevVersion, getStableVersion } from 'api/system';
|
||||||
|
|
||||||
|
import { dialogStyle } from 'CustomTheme';
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
|
import RestartMonitor from 'app/status/RestartMonitor';
|
||||||
|
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
const Version = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState<boolean>(false);
|
||||||
|
const [useDev, setUseDev] = useState<boolean>(false);
|
||||||
|
const [upgradeAvailable, setUpgradeAvailable] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { send: sendCheckUpgrade } = useRequest(
|
||||||
|
(version: string) => callAction({ action: 'checkUpgrade', param: version }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
).onSuccess((event) => {
|
||||||
|
setUpgradeAvailable((event.data as { upgradeable: boolean }).upgradeable);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, send: loadData, error } = useRequest(SystemApi.readSystemStatus);
|
||||||
|
|
||||||
|
const { send: sendUploadURL } = useRequest(
|
||||||
|
(url: string) => callAction({ action: 'uploadURL', param: url }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// called immediately to get the latest version, on page load
|
||||||
|
const { data: latestVersion } = useRequest(getStableVersion, {
|
||||||
|
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/v3.6.5/EMS-ESP-3_6_5-ESP32-16MB+.bin
|
||||||
|
immediate: false,
|
||||||
|
initialData: '3.6.5'
|
||||||
|
});
|
||||||
|
|
||||||
|
// called immediately to get the latest version, on page load, then check for upgrade
|
||||||
|
const { data: latestDevVersion } = useRequest(getDevVersion, {
|
||||||
|
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_0-dev_31-ESP32-16MB+.bin
|
||||||
|
immediate: false,
|
||||||
|
initialData: '3.7.0-dev.32'
|
||||||
|
}).onSuccess((event) => {
|
||||||
|
void sendCheckUpgrade(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
||||||
|
const STABLE_RELNOTES_URL =
|
||||||
|
'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
|
||||||
|
|
||||||
|
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
||||||
|
const DEV_RELNOTES_URL =
|
||||||
|
'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
|
||||||
|
|
||||||
|
const getBinURL = (useDevVersion: boolean) => {
|
||||||
|
if (!latestVersion || !latestDevVersion) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const filename =
|
||||||
|
'EMS-ESP-' +
|
||||||
|
(useDevVersion ? latestDevVersion : latestVersion).replaceAll('.', '_') +
|
||||||
|
'-' +
|
||||||
|
getPlatform() +
|
||||||
|
'.bin';
|
||||||
|
return useDevVersion
|
||||||
|
? DEV_URL + filename
|
||||||
|
: STABLE_URL + 'v' + latestVersion + '/' + filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPlatform = () => {
|
||||||
|
return (
|
||||||
|
[data.esp_platform, data.flash_chip_size >= 16384 ? '16MB' : '4MB'].join('-') +
|
||||||
|
(data.psram ? '+' : '')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const installFirmwareURL = async (url: string) => {
|
||||||
|
await sendUploadURL(url).catch((error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
});
|
||||||
|
setRestarting(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutTitle(LL.EMS_ESP_VER());
|
||||||
|
|
||||||
|
const internet_live =
|
||||||
|
latestDevVersion !== undefined && latestVersion !== undefined;
|
||||||
|
|
||||||
|
const renderUploadDialog = () => {
|
||||||
|
if (!internet_live) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
sx={dialogStyle}
|
||||||
|
open={openDialog}
|
||||||
|
onClose={() => setOpenDialog(false)}
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
{LL.INSTALL('') +
|
||||||
|
' ' +
|
||||||
|
(useDev ? LL.DEVELOPMENT() : LL.STABLE()) +
|
||||||
|
' Firmware'}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<Typography mb={2}>
|
||||||
|
{LL.INSTALL_VERSION(useDev ? latestDevVersion : latestVersion)}
|
||||||
|
</Typography>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={useDev ? DEV_RELNOTES_URL : STABLE_RELNOTES_URL}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
changelog
|
||||||
|
</Link>
|
||||||
|
|
|
||||||
|
<Link target="_blank" href={getBinURL(useDev)} color="primary">
|
||||||
|
{LL.DOWNLOAD(1)}
|
||||||
|
</Link>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setOpenDialog(false)}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<WarningIcon color="warning" />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => installFirmwareURL(getBinURL(useDev))}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.INSTALL('')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// useDevVersion = true to force using the dev version
|
||||||
|
const showFirmwareDialog = (useDevVersion: boolean) => {
|
||||||
|
if (useDevVersion || data.emsesp_version.includes('dev')) {
|
||||||
|
setUseDev(true);
|
||||||
|
}
|
||||||
|
setOpenDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDev = data.emsesp_version.includes('dev');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Firmware Version Check
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box p={2} mt={2} border="1px solid grey" borderRadius={2}>
|
||||||
|
<Typography>
|
||||||
|
<b>{LL.VERSION() + ':'}</b> {data.emsesp_version}
|
||||||
|
{data.build_flags && (
|
||||||
|
<Typography variant="caption">
|
||||||
|
({data.build_flags})
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
<b>Platform:</b> {getPlatform()}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography>
|
||||||
|
<b>Release:</b>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={useDev ? DEV_RELNOTES_URL : STABLE_RELNOTES_URL}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{isDev ? LL.DEVELOPMENT() : LL.STABLE()}
|
||||||
|
</Link>
|
||||||
|
{!isDev && (
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => showFirmwareDialog(true)}
|
||||||
|
>
|
||||||
|
{LL.SWITCH_DEV()}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography mt={2} color="warning">
|
||||||
|
<InfoOutlinedIcon color="warning" sx={{ verticalAlign: 'middle' }} />
|
||||||
|
|
||||||
|
{upgradeAvailable ? LL.UPGRADE_AVAILABLE() : LL.LATEST_VERSION()}
|
||||||
|
{upgradeAvailable &&
|
||||||
|
internet_live &&
|
||||||
|
(data.psram ? (
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2, textTransform: 'none' }}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => showFirmwareDialog(false)}
|
||||||
|
>
|
||||||
|
{isDev
|
||||||
|
? LL.INSTALL('v' + latestDevVersion)
|
||||||
|
: LL.INSTALL('v' + latestVersion)}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<Link target="_blank" href={getBinURL(isDev)} color="primary">
|
||||||
|
{LL.DOWNLOAD(1)} v
|
||||||
|
{isDev ? latestDevVersion : latestVersion}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{renderUploadDialog()}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Version;
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
import BuildIcon from '@mui/icons-material/Build';
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
||||||
@@ -13,7 +11,6 @@ import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
|||||||
import RouterIcon from '@mui/icons-material/Router';
|
import RouterIcon from '@mui/icons-material/Router';
|
||||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||||
import TimerIcon from '@mui/icons-material/Timer';
|
import TimerIcon from '@mui/icons-material/Timer';
|
||||||
import UpgradeIcon from '@mui/icons-material/Upgrade';
|
|
||||||
import WifiIcon from '@mui/icons-material/Wifi';
|
import WifiIcon from '@mui/icons-material/Wifi';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@@ -46,8 +43,6 @@ import RestartMonitor from './RestartMonitor';
|
|||||||
const SystemStatus = () => {
|
const SystemStatus = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useLayoutTitle(LL.STATUS_OF(''));
|
useLayoutTitle(LL.STATUS_OF(''));
|
||||||
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
const { me } = useContext(AuthenticatedContext);
|
||||||
@@ -248,28 +243,6 @@ const SystemStatus = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar sx={{ bgcolor: '#134ba2', color: 'white' }}>
|
|
||||||
<BuildIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.EMS_ESP_VER()}
|
|
||||||
secondary={'' + data.emsesp_version}
|
|
||||||
/>
|
|
||||||
{me.admin && (
|
|
||||||
<Button
|
|
||||||
startIcon={<UpgradeIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => navigate('/settings/upload')}
|
|
||||||
>
|
|
||||||
{LL.UPDATE()}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar sx={{ bgcolor: '#c5572c', color: 'white' }}>
|
<Avatar sx={{ bgcolor: '#c5572c', color: 'white' }}>
|
||||||
@@ -292,6 +265,15 @@ const SystemStatus = () => {
|
|||||||
)}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={MemoryIcon}
|
||||||
|
bgcolor="#68374d"
|
||||||
|
label={LL.HARDWARE()}
|
||||||
|
text={formatNumber(data.free_heap) + ' KB' + ' ' + LL.FREE_MEMORY()}
|
||||||
|
to="/status/hardwarestatus"
|
||||||
|
/>
|
||||||
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={DirectionsBusIcon}
|
icon={DirectionsBusIcon}
|
||||||
@@ -301,15 +283,6 @@ const SystemStatus = () => {
|
|||||||
to="/status/activity"
|
to="/status/activity"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListMenuItem
|
|
||||||
disabled={!me.admin}
|
|
||||||
icon={MemoryIcon}
|
|
||||||
bgcolor="#68374d"
|
|
||||||
label={LL.STATUS_OF(LL.HARDWARE())}
|
|
||||||
text={formatNumber(data.free_heap) + ' KB' + ' ' + LL.FREE_MEMORY()}
|
|
||||||
to="/status/hardwarestatus"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={
|
icon={
|
||||||
@@ -318,7 +291,7 @@ const SystemStatus = () => {
|
|||||||
: RouterIcon
|
: RouterIcon
|
||||||
}
|
}
|
||||||
bgcolor={networkStatusHighlight()}
|
bgcolor={networkStatusHighlight()}
|
||||||
label={LL.STATUS_OF(LL.NETWORK(1))}
|
label={LL.NETWORK(1)}
|
||||||
text={networkStatus()}
|
text={networkStatus()}
|
||||||
to="/status/network"
|
to="/status/network"
|
||||||
/>
|
/>
|
||||||
@@ -327,7 +300,7 @@ const SystemStatus = () => {
|
|||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={DeviceHubIcon}
|
icon={DeviceHubIcon}
|
||||||
bgcolor={activeHighlight(data.mqtt_status)}
|
bgcolor={activeHighlight(data.mqtt_status)}
|
||||||
label={LL.STATUS_OF('MQTT')}
|
label="MQTT"
|
||||||
text={data.mqtt_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
text={data.mqtt_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
to="/status/mqtt"
|
to="/status/mqtt"
|
||||||
/>
|
/>
|
||||||
@@ -336,7 +309,7 @@ const SystemStatus = () => {
|
|||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={AccessTimeIcon}
|
icon={AccessTimeIcon}
|
||||||
bgcolor={ntpStatusHighlight()}
|
bgcolor={ntpStatusHighlight()}
|
||||||
label={LL.STATUS_OF('NTP')}
|
label="NTP"
|
||||||
text={ntpStatus()}
|
text={ntpStatus()}
|
||||||
to="/status/ntp"
|
to="/status/ntp"
|
||||||
/>
|
/>
|
||||||
@@ -345,7 +318,7 @@ const SystemStatus = () => {
|
|||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={SettingsInputAntennaIcon}
|
icon={SettingsInputAntennaIcon}
|
||||||
bgcolor={activeHighlight(data.ap_status)}
|
bgcolor={activeHighlight(data.ap_status)}
|
||||||
label={LL.STATUS_OF(LL.ACCESS_POINT(0))}
|
label={LL.ACCESS_POINT(0)}
|
||||||
text={data.ap_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
text={data.ap_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
to="/status/ap"
|
to="/status/ap"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user