mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
status screen updates
This commit is contained in:
@@ -4,6 +4,7 @@ import CastIcon from '@mui/icons-material/Cast';
|
||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import MemoryIcon from '@mui/icons-material/Memory';
|
||||
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
|
||||
@@ -24,7 +25,8 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Box
|
||||
Box,
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useState, type FC } from 'react';
|
||||
@@ -294,6 +296,26 @@ const Settings: FC = () => {
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<ListItem
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
<ListItemIcon style={{ justifyContent: 'right', color: 'lightblue', verticalAlign: 'middle' }}>
|
||||
<NavigateNextIcon />
|
||||
</ListItemIcon>
|
||||
}
|
||||
>
|
||||
<ListItemButton component={Link} to="espsystemstatus">
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ color: 'white' }}>
|
||||
<MemoryIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.STATUS_OF('ESP32')} secondary="ESP32 Information" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
@@ -312,8 +334,10 @@ const Settings: FC = () => {
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
{renderRestartDialog()}
|
||||
{renderFactoryResetDialog()}
|
||||
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
||||
<ButtonRow>
|
||||
|
||||
@@ -205,7 +205,7 @@ const APSettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF(LL.ACCESS_POINT(1))}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -99,7 +99,7 @@ const APStatusForm: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent title={LL.STATUS_OF(LL.ACCESS_POINT(1))}>{content()}</SectionContent>;
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default APStatusForm;
|
||||
|
||||
@@ -449,7 +449,7 @@ const MqttSettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF('MQTT')}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -146,7 +146,7 @@ const MqttStatusForm: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent title={LL.STATUS_OF('MQTT')}>{content()}</SectionContent>;
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default MqttStatusForm;
|
||||
|
||||
@@ -360,7 +360,7 @@ const WiFiSettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF(LL.NETWORK(1))}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{restarting ? <RestartMonitor /> : content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -193,7 +193,7 @@ const NetworkStatusForm: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent title={LL.STATUS_OF(LL.NETWORK(1))}>{content()}</SectionContent>;
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default NetworkStatusForm;
|
||||
|
||||
@@ -56,7 +56,7 @@ const WiFiNetworkScanner: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.NETWORK_SCANNER()}>
|
||||
<SectionContent>
|
||||
{renderNetworkScanner()}
|
||||
<ButtonRow>
|
||||
<Button
|
||||
|
||||
@@ -130,7 +130,7 @@ const NTPSettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF('NTP')}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -214,7 +214,7 @@ const NTPStatusForm: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent title={LL.STATUS_OF('NTP')}>{content()}</SectionContent>;
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default NTPStatusForm;
|
||||
|
||||
@@ -120,7 +120,7 @@ const OTASettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF('OTA')}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -232,7 +232,7 @@ const ManageUsersForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.MANAGE_USERS()}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -96,7 +96,7 @@ const SecuritySettingsForm: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.SETTINGS_OF(LL.SECURITY(1))}>
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import AppsIcon from '@mui/icons-material/Apps';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard';
|
||||
import DevicesIcon from '@mui/icons-material/Devices';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
@@ -7,24 +6,25 @@ import MemoryIcon from '@mui/icons-material/Memory';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import SdCardAlertIcon from '@mui/icons-material/SdCardAlert';
|
||||
import SdStorageIcon from '@mui/icons-material/SdStorage';
|
||||
import TimerIcon from '@mui/icons-material/Timer';
|
||||
import { Avatar, Box, Button, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@mui/material';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
function formatNumber(num: number) {
|
||||
return new Intl.NumberFormat().format(num);
|
||||
}
|
||||
|
||||
const SystemStatusForm: FC = () => {
|
||||
const ESPSystemStatus: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||
useLayoutTitle(LL.STATUS_OF('ESP32'));
|
||||
|
||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readESPSystemStatus, { force: true });
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
@@ -34,24 +34,6 @@ const SystemStatusForm: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||
<BuildIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.EMS_ESP_VER()} secondary={data.emsesp_version} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||
<TimerIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.UPTIME()} secondary={data.uptime} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||
@@ -151,7 +133,6 @@ const SystemStatusForm: FC = () => {
|
||||
secondary={formatNumber(data.fs_used) + ' KB / ' + formatNumber(data.fs_free) + ' KB'}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
</List>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
||||
@@ -166,7 +147,7 @@ const SystemStatusForm: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent title={LL.STATUS_OF(LL.SYSTEM(1))}>{content()}</SectionContent>;
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default SystemStatusForm;
|
||||
export default ESPSystemStatus;
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Tab } from '@mui/material';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
import SystemLog from './SystemLog';
|
||||
import SystemStatusForm from './SystemStatusForm';
|
||||
import SystemStatus from './SystemStatus';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { useRouterTab, RouterTabs, useLayoutTitle } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import EMSStatus from 'project/EMSStatus';
|
||||
import Activity from 'project/Activity';
|
||||
|
||||
const Status: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
@@ -20,12 +20,12 @@ const Status: FC = () => {
|
||||
<>
|
||||
<RouterTabs value={routerTab}>
|
||||
<Tab value="status" label={LL.SYSTEM(1)} />
|
||||
<Tab value="emsesp-status" label="EMS-ESP" />
|
||||
<Tab value="log" label={LL.LOG_OF(LL.SYSTEM(2))} />
|
||||
<Tab value="activity" label={LL.ACTIVITY()} />
|
||||
<Tab value="log" label={LL.LOG_OF('')} />
|
||||
</RouterTabs>
|
||||
<Routes>
|
||||
<Route path="status" element={<SystemStatusForm />} />
|
||||
<Route path="emsesp-status" element={<EMSStatus />} />
|
||||
<Route path="status" element={<SystemStatus />} />
|
||||
<Route path="activity" element={<Activity />} />
|
||||
<Route path="log" element={<SystemLog />} />
|
||||
<Route path="*" element={<Navigate replace to="status" />} />
|
||||
</Routes>
|
||||
|
||||
@@ -232,7 +232,7 @@ const SystemLog: FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.LOG_OF(LL.SYSTEM(2))} id="log-window">
|
||||
<SectionContent id="log-window">
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
|
||||
212
interface/src/framework/system/SystemStatus.tsx
Normal file
212
interface/src/framework/system/SystemStatus.tsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
||||
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import TimerIcon from '@mui/icons-material/Timer';
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { useContext, type FC, useState } from 'react';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import * as SystemApi from 'api/system';
|
||||
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import * as EMSESP from 'project/api';
|
||||
import { busConnectionStatus } from 'project/types';
|
||||
|
||||
const SystemStatus: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
useLayoutTitle(LL.STATUS_OF(''));
|
||||
|
||||
const { me } = useContext(AuthenticatedContext);
|
||||
|
||||
const [confirmScan, setConfirmScan] = useState<boolean>(false);
|
||||
|
||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||
|
||||
const { send: scanDevices } = useRequest(EMSESP.scanDevices, {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const formatDurationSec = (duration_sec: number) => {
|
||||
const days = Math.trunc((duration_sec * 1000) / 86400000);
|
||||
const hours = Math.trunc((duration_sec * 1000) / 3600000) % 24;
|
||||
const minutes = Math.trunc((duration_sec * 1000) / 60000) % 60;
|
||||
const seconds = Math.trunc((duration_sec * 1000) / 1000) % 60;
|
||||
|
||||
let formatted = '';
|
||||
if (days) {
|
||||
formatted += LL.NUM_DAYS({ num: days }) + ' ';
|
||||
}
|
||||
if (hours) {
|
||||
formatted += LL.NUM_HOURS({ num: hours }) + ' ';
|
||||
}
|
||||
if (minutes) {
|
||||
formatted += LL.NUM_MINUTES({ num: minutes }) + ' ';
|
||||
}
|
||||
formatted += LL.NUM_SECONDS({ num: seconds });
|
||||
return formatted;
|
||||
};
|
||||
|
||||
const busStatus = () => {
|
||||
if (data) {
|
||||
switch (data.status) {
|
||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||
return LL.CONNECTED(0);
|
||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||
return LL.TX_ISSUES();
|
||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||
return LL.DISCONNECTED();
|
||||
}
|
||||
}
|
||||
return 'Unknown';
|
||||
};
|
||||
|
||||
// export const isConnected = ({ status }: Status) => status !== busConnectionStatus.BUS_STATUS_OFFLINE;
|
||||
|
||||
const busStatusHighlight = () => {
|
||||
switch (data.status) {
|
||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||
return theme.palette.warning.main;
|
||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||
return theme.palette.success.main;
|
||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||
return theme.palette.error.main;
|
||||
default:
|
||||
return theme.palette.warning.main;
|
||||
}
|
||||
};
|
||||
|
||||
const scan = async () => {
|
||||
await scanDevices()
|
||||
.then(() => {
|
||||
toast.info(LL.SCANNING() + '...');
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
});
|
||||
setConfirmScan(false);
|
||||
};
|
||||
|
||||
const renderScanDialog = () => (
|
||||
<Dialog sx={dialogStyle} open={confirmScan} onClose={() => setConfirmScan(false)}>
|
||||
<DialogTitle>{LL.SCAN_DEVICES()}</DialogTitle>
|
||||
<DialogContent dividers>{LL.EMS_SCAN()}</DialogContent>
|
||||
<DialogActions>
|
||||
<Button startIcon={<CancelIcon />} variant="outlined" onClick={() => setConfirmScan(false)} color="secondary">
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button startIcon={<PermScanWifiIcon />} variant="outlined" onClick={scan} color="primary">
|
||||
{LL.SCAN()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<BuildIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.EMS_ESP_VER()} secondary={data.emsesp_version} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||
<TimerIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.UPTIME()} secondary={formatDurationSec(data.uptime)} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: busStatusHighlight() }}>
|
||||
<DirectionsBusIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.EMS_BUS_STATUS()} secondary={busStatus()} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: theme.palette.success.main }}>
|
||||
<DeviceHubIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={LL.ACTIVE_DEVICES()}
|
||||
secondary={
|
||||
LL.NUM_DEVICES({ num: data.num_devices }) +
|
||||
', ' +
|
||||
LL.NUM_TEMP_SENSORS({ num: data.num_sensors }) +
|
||||
', ' +
|
||||
LL.NUM_ANALOG_SENSORS({ num: data.num_analogs })
|
||||
}
|
||||
/>
|
||||
{me.admin && (
|
||||
<Button
|
||||
startIcon={<PermScanWifiIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => setConfirmScan(true)}
|
||||
>
|
||||
{LL.SCAN_DEVICES()}
|
||||
</Button>
|
||||
)}
|
||||
</ListItem>
|
||||
|
||||
<Divider variant="inset" component="li" />
|
||||
</List>
|
||||
|
||||
{renderScanDialog()}
|
||||
|
||||
<Box mt={2} display="flex" flexWrap="wrap">
|
||||
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
||||
{LL.REFRESH()}
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return <SectionContent>{content()}</SectionContent>;
|
||||
};
|
||||
|
||||
export default SystemStatus;
|
||||
@@ -32,12 +32,13 @@ const UploadDownload: FC = () => {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readESPSystemStatus, { force: true });
|
||||
|
||||
const { data: latestVersion } = useRequest(SystemApi.getStableVersion, {
|
||||
immediate: true,
|
||||
force: true
|
||||
});
|
||||
|
||||
const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion, {
|
||||
immediate: true,
|
||||
force: true
|
||||
|
||||
Reference in New Issue
Block a user