updates to web pages

This commit is contained in:
proddy
2024-03-17 23:23:09 +01:00
parent 20ddbeb709
commit 1af103d5ee
22 changed files with 121 additions and 142 deletions

View File

@@ -84,5 +84,8 @@
"**/*.map", "**/*.map",
"**/ArduinoJson/**" "**/ArduinoJson/**"
], ],
"cSpell.enableFiletypes": ["!cpp"] "cSpell.enableFiletypes": [
"!cpp",
"!typescript"
]
} }

View File

@@ -1,7 +1,8 @@
import { useContext, type FC } from 'react';
import { Navigate, Routes, Route } from 'react-router-dom'; import { Navigate, Routes, Route } from 'react-router-dom';
import Help from './project/Help'; import Help from './project/Help';
import type { FC } from 'react'; import { Layout } from 'components';
import { Layout, RequireAdmin } from 'components'; import { AuthenticatedContext } from 'contexts/authentication';
import Settings from 'framework/Settings'; import Settings from 'framework/Settings';
import AccessPoint from 'framework/ap/AccessPoint'; import AccessPoint from 'framework/ap/AccessPoint';
import Mqtt from 'framework/mqtt/Mqtt'; import Mqtt from 'framework/mqtt/Mqtt';
@@ -18,49 +19,35 @@ import Devices from 'project/Devices';
import Scheduler from 'project/Scheduler'; import Scheduler from 'project/Scheduler';
import Sensors from 'project/Sensors'; import Sensors from 'project/Sensors';
const AuthenticatedRouting: FC = () => ( const AuthenticatedRouting: FC = () => {
<Layout> const { me } = useContext(AuthenticatedContext);
<Routes> return (
<Route path="/devices/*" element={<Devices />} /> <Layout>
<Route path="/sensors/*" element={<Sensors />} /> <Routes>
<Route path="/devices/*" element={<Devices />} />
<Route path="/customizations/*" element={<Customization />} /> <Route path="/sensors/*" element={<Sensors />} />
<Route path="/scheduler/*" element={<Scheduler />} /> <Route path="/status/*" element={<Status />} />
<Route path="/customentities/*" element={<CustomEntities />} /> <Route path="/help/*" element={<Help />} />
<Route path="/*" element={<Navigate to="/" />} />
{/* TODO only show the rest here if admin */} {me.admin && (
<>
<Route path="/status/*" element={<Status />} /> <Route path="/customizations/*" element={<Customization />} />
<Route path="/scheduler/*" element={<Scheduler />} />
<Route <Route path="/customentities/*" element={<CustomEntities />} />
path="settings/*" <Route path="/settings/*" element={<Settings />} />
element={ <Route path="/settings/network/*" element={<NetworkConnection />} />
<RequireAdmin> <Route path="/settings/ems-esp/*" element={<ApplicationSettings />} />
<Settings /> <Route path="/settings/ap/*" element={<AccessPoint />} />
</RequireAdmin> <Route path="/settings/ntp/*" element={<NetworkTime />} />
} <Route path="/settings/mqtt/*" element={<Mqtt />} />
/> <Route path="/settings/ota/*" element={<OTASettingsForm />} />
<Route path="/settings/network/*" element={<NetworkConnection />} /> <Route path="/settings/security/*" element={<Security />} />
<Route path="/settings/ems-esp/*" element={<ApplicationSettings />} /> <Route path="/settings/upload/*" element={<UploadDownload />} />
<Route path="/settings/ap/*" element={<AccessPoint />} /> </>
<Route path="/settings/ntp/*" element={<NetworkTime />} /> )}
<Route path="/settings/mqtt/*" element={<Mqtt />} /> </Routes>
<Route path="/settings/ota/*" element={<OTASettingsForm />} /> </Layout>
<Route );
path="/settings/security/*" };
element={
<RequireAdmin>
<Security />
</RequireAdmin>
}
/>
<Route path="/settings/upload/*" element={<UploadDownload />} />
<Route path="/help/*" element={<Help />} />
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Layout>
);
export default AuthenticatedRouting; export default AuthenticatedRouting;

View File

@@ -2,7 +2,7 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import AssessmentIcon from '@mui/icons-material/Assessment'; import AssessmentIcon from '@mui/icons-material/Assessment';
import CategoryIcon from '@mui/icons-material/Category'; import CategoryIcon from '@mui/icons-material/Category';
import ConstructionIcon from '@mui/icons-material/Construction'; import ConstructionIcon from '@mui/icons-material/Construction';
import InfoIcon from '@mui/icons-material/Info'; import LiveHelpIcon from '@mui/icons-material/LiveHelp';
import MoreTimeIcon from '@mui/icons-material/MoreTime'; import MoreTimeIcon from '@mui/icons-material/MoreTime';
import PersonIcon from '@mui/icons-material/Person'; import PersonIcon from '@mui/icons-material/Person';
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd'; import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
@@ -73,14 +73,24 @@ const LayoutMenu: FC = () => {
<LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} /> <LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} />
<LayoutMenuItem icon={SensorsIcon} label={LL.SENSORS()} to={`/sensors`} /> <LayoutMenuItem icon={SensorsIcon} label={LL.SENSORS()} to={`/sensors`} />
<Divider /> <Divider />
<LayoutMenuItem icon={ConstructionIcon} label={LL.CUSTOMIZATIONS()} to={`/customizations`} /> <LayoutMenuItem
<LayoutMenuItem icon={MoreTimeIcon} label={LL.SCHEDULER()} to={`/scheduler`} /> icon={ConstructionIcon}
<LayoutMenuItem icon={PlaylistAddIcon} label={LL.CUSTOM_ENTITIES(0)} to={`/customentities`} /> label={LL.CUSTOMIZATIONS()}
disabled={!me.admin}
to={`/customizations`}
/>
<LayoutMenuItem icon={MoreTimeIcon} label={LL.SCHEDULER()} disabled={!me.admin} to={`/scheduler`} />
<LayoutMenuItem
icon={PlaylistAddIcon}
label={LL.CUSTOM_ENTITIES(0)}
disabled={!me.admin}
to={`/customentities`}
/>
</List> </List>
<List style={{ marginTop: `auto` }}> <List style={{ marginTop: `auto` }}>
<LayoutMenuItem icon={AssessmentIcon} label={LL.STATUS_OF('')} to="/status" /> <LayoutMenuItem icon={AssessmentIcon} label={LL.STATUS_OF('')} to="/status" />
<LayoutMenuItem icon={SettingsIcon} label={LL.SETTINGS(0)} disabled={!me.admin} to="/settings" /> <LayoutMenuItem icon={SettingsIcon} label={LL.SETTINGS(0)} disabled={!me.admin} to="/settings" />
<LayoutMenuItem icon={InfoIcon} label={LL.HELP_OF('')} to={`/help`} /> <LayoutMenuItem icon={LiveHelpIcon} label={LL.HELP_OF('')} to={`/help`} />
</List> </List>
<Divider /> <Divider />
<List> <List>

View File

@@ -1,12 +1,10 @@
import { Tab } from '@mui/material'; import { Tab } from '@mui/material';
import { useContext } from 'react';
import { Navigate, Routes, Route } from 'react-router-dom'; import { Navigate, Routes, Route } from 'react-router-dom';
import APSettingsForm from './APSettingsForm'; import APSettingsForm from './APSettingsForm';
import APStatusForm from './APStatusForm'; import APStatusForm from './APStatusForm';
import type { FC } from 'react'; import type { FC } from 'react';
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
@@ -15,14 +13,12 @@ const AccessPoint: FC = () => {
useLayoutTitle(LL.ACCESS_POINT(0)); useLayoutTitle(LL.ACCESS_POINT(0));
const authenticatedContext = useContext(AuthenticatedContext);
const { routerTab } = useRouterTab(); const { routerTab } = useRouterTab();
return ( return (
<> <>
<RouterTabs value={routerTab}> <RouterTabs value={routerTab}>
<Tab value="settings" label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} disabled={!authenticatedContext.me.admin} /> <Tab value="settings" label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} />
<Tab value="status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} /> <Tab value="status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} />
</RouterTabs> </RouterTabs>
<Routes> <Routes>

View File

@@ -1,12 +1,10 @@
import { Tab } from '@mui/material'; import { Tab } from '@mui/material';
import { useContext } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import MqttSettingsForm from './MqttSettingsForm'; import MqttSettingsForm from './MqttSettingsForm';
import MqttStatusForm from './MqttStatusForm'; import MqttStatusForm from './MqttStatusForm';
import type { FC } from 'react'; import type { FC } from 'react';
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
@@ -15,13 +13,12 @@ const Mqtt: FC = () => {
useLayoutTitle('MQTT'); useLayoutTitle('MQTT');
const authenticatedContext = useContext(AuthenticatedContext);
const { routerTab } = useRouterTab(); const { routerTab } = useRouterTab();
return ( return (
<> <>
<RouterTabs value={routerTab}> <RouterTabs value={routerTab}>
<Tab value="settings" label={LL.SETTINGS_OF('MQTT')} disabled={!authenticatedContext.me.admin} /> <Tab value="settings" label={LL.SETTINGS_OF('MQTT')} />
<Tab value="status" label={LL.STATUS_OF('MQTT')} /> <Tab value="status" label={LL.STATUS_OF('MQTT')} />
</RouterTabs> </RouterTabs>
<Routes> <Routes>

View File

@@ -1,5 +1,5 @@
import { Tab } from '@mui/material'; import { Tab } from '@mui/material';
import { useCallback, useContext, useState } from 'react'; import { useCallback, useState } from 'react';
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom'; import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
import NetworkSettingsForm from './NetworkSettingsForm'; import NetworkSettingsForm from './NetworkSettingsForm';
import NetworkStatusForm from './NetworkStatusForm'; import NetworkStatusForm from './NetworkStatusForm';
@@ -9,7 +9,6 @@ import type { FC } from 'react';
import type { WiFiNetwork } from 'types'; import type { WiFiNetwork } from 'types';
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
const NetworkConnection: FC = () => { const NetworkConnection: FC = () => {
@@ -18,7 +17,6 @@ const NetworkConnection: FC = () => {
const { routerTab } = useRouterTab(); const { routerTab } = useRouterTab();
const authenticatedContext = useContext(AuthenticatedContext);
const navigate = useNavigate(); const navigate = useNavigate();
const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>(); const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>();
@@ -44,8 +42,8 @@ const NetworkConnection: FC = () => {
}} }}
> >
<RouterTabs value={routerTab}> <RouterTabs value={routerTab}>
<Tab value="settings" label={LL.SETTINGS_OF(LL.NETWORK(1))} disabled={!authenticatedContext.me.admin} /> <Tab value="settings" label={LL.SETTINGS_OF(LL.NETWORK(1))} />
<Tab value="scan" label={LL.NETWORK_SCAN()} disabled={!authenticatedContext.me.admin} /> <Tab value="scan" label={LL.NETWORK_SCAN()} />
<Tab value="status" label={LL.STATUS_OF(LL.NETWORK(1))} /> <Tab value="status" label={LL.STATUS_OF(LL.NETWORK(1))} />
</RouterTabs> </RouterTabs>
<Routes> <Routes>

View File

@@ -22,7 +22,7 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import { useRequest } from 'alova'; import { useRequest } from 'alova';
import { useContext, useState } from 'react'; import { useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import type { Theme } from '@mui/material'; import type { Theme } from '@mui/material';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -31,7 +31,6 @@ import type { NTPStatus } from 'types';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import * as NTPApi from 'api/ntp'; import * as NTPApi from 'api/ntp';
import { ButtonRow, FormLoader, SectionContent } from 'components'; import { ButtonRow, FormLoader, SectionContent } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { NTPSyncStatus } from 'types'; import { NTPSyncStatus } from 'types';
@@ -59,7 +58,6 @@ const NTPStatusForm: FC = () => {
const [localTime, setLocalTime] = useState<string>(''); const [localTime, setLocalTime] = useState<string>('');
const [settingTime, setSettingTime] = useState<boolean>(false); const [settingTime, setSettingTime] = useState<boolean>(false);
const [processing, setProcessing] = useState<boolean>(false); const [processing, setProcessing] = useState<boolean>(false);
const { me } = useContext(AuthenticatedContext);
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -201,7 +199,7 @@ const NTPStatusForm: FC = () => {
</Button> </Button>
</ButtonRow> </ButtonRow>
</Box> </Box>
{me.admin && data && !isNtpActive(data) && ( {data && !isNtpActive(data) && (
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box flexWrap="nowrap" whiteSpace="nowrap">
<ButtonRow> <ButtonRow>
<Button onClick={openSetTime} variant="outlined" color="primary" startIcon={<AccessTimeIcon />}> <Button onClick={openSetTime} variant="outlined" color="primary" startIcon={<AccessTimeIcon />}>

View File

@@ -1,12 +1,10 @@
import { Tab } from '@mui/material'; import { Tab } from '@mui/material';
import { useContext } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import NTPSettingsForm from './NTPSettingsForm'; import NTPSettingsForm from './NTPSettingsForm';
import NTPStatusForm from './NTPStatusForm'; import NTPStatusForm from './NTPStatusForm';
import type { FC } from 'react'; import type { FC } from 'react';
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
@@ -14,13 +12,12 @@ const NetworkTime: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
useLayoutTitle('NTP'); useLayoutTitle('NTP');
const authenticatedContext = useContext(AuthenticatedContext);
const { routerTab } = useRouterTab(); const { routerTab } = useRouterTab();
return ( return (
<> <>
<RouterTabs value={routerTab}> <RouterTabs value={routerTab}>
<Tab value="settings" label={LL.SETTINGS_OF('NTP')} disabled={!authenticatedContext.me.admin} /> <Tab value="settings" label={LL.SETTINGS_OF('NTP')} />
<Tab value="status" label={LL.STATUS_OF('NTP')} /> <Tab value="status" label={LL.STATUS_OF('NTP')} />
</RouterTabs> </RouterTabs>
<Routes> <Routes>

View File

@@ -8,8 +8,8 @@ import type { FC } from 'react';
import type { SecuritySettings } from 'types'; import type { SecuritySettings } from 'types';
import * as SecurityApi from 'api/security'; import * as SecurityApi from 'api/security';
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField, BlockNavigation } from 'components'; import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField, BlockNavigation } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators'; import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';

View File

@@ -232,7 +232,7 @@ const SystemLog: FC = () => {
}; };
return ( return (
<SectionContent title={LL.LOG_OF(LL.SYSTEM(2))} titleGutter id="log-window"> <SectionContent title={LL.LOG_OF(LL.SYSTEM(2))} id="log-window">
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()} {content()}
</SectionContent> </SectionContent>

View File

@@ -46,13 +46,14 @@ function formatNumber(num: number) {
const SystemStatusForm: FC = () => { const SystemStatusForm: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const { me } = useContext(AuthenticatedContext);
const [confirmRestart, setConfirmRestart] = useState<boolean>(false); const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false); const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
const [processing, setProcessing] = useState<boolean>(false); const [processing, setProcessing] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>(); const [restarting, setRestarting] = useState<boolean>();
const [versionDialogOpen, setVersionDialogOpen] = useState<boolean>(false); const [versionDialogOpen, setVersionDialogOpen] = useState<boolean>(false);
const { me } = useContext(AuthenticatedContext);
const { send: restartCommand } = useRequest(SystemApi.restart(), { const { send: restartCommand } = useRequest(SystemApi.restart(), {
immediate: false immediate: false
}); });
@@ -187,7 +188,7 @@ const SystemStatusForm: FC = () => {
<List> <List>
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<BuildIcon /> <BuildIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -199,7 +200,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<TimerIcon /> <TimerIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -208,7 +209,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<DevicesIcon /> <DevicesIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -217,7 +218,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<DeveloperBoardIcon /> <DeveloperBoardIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -240,7 +241,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<MemoryIcon /> <MemoryIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -254,7 +255,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<AppsIcon /> <AppsIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -268,7 +269,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<SdStorageIcon /> <SdStorageIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -282,7 +283,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<SdCardAlertIcon /> <SdCardAlertIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -296,7 +297,7 @@ const SystemStatusForm: FC = () => {
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
<FolderIcon /> <FolderIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>

View File

@@ -31,7 +31,7 @@ const SystemStatusVersionDialog = ({ open, onClose, version, platform }: SystemS
const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md'; const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md'; const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
const uploadURL = window.location.origin + '/system/upload'; const uploadURL = window.location.origin + '/settings/upload';
const connected = latestVersion && latestDevVersion; const connected = latestVersion && latestDevVersion;

View File

@@ -153,11 +153,6 @@ const UploadDownload: FC = () => {
<Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary">
{LL.DOWNLOAD(0)}&nbsp;{LL.SUPPORT_INFORMATION(1)} {LL.DOWNLOAD(0)}&nbsp;{LL.SUPPORT_INFORMATION(1)}
</Typography> </Typography>
<Box color="warning.main">
<Typography mb={1} variant="body2">
{LL.HELP_INFORMATION_4()}
</Typography>
</Box>
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
variant="outlined" variant="outlined"
@@ -216,7 +211,7 @@ const UploadDownload: FC = () => {
)} )}
</> </>
); );
return <SectionContent title={LL.UPLOAD_DOWNLOAD()}>{restarting ? <RestartMonitor /> : content()}</SectionContent>; return <SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>;
}; };
export default UploadDownload; export default UploadDownload;

View File

@@ -155,7 +155,7 @@ const en: Translation = {
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API', CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard', CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
CUSTOMIZATIONS_HELP_6: 'remove from memory', CUSTOMIZATIONS_HELP_6: 'remove from memory',
SELECT_DEVICE: 'Select a device', SELECT_DEVICE: 'select a device',
SET_ALL: 'set all', SET_ALL: 'set all',
OPTIONS: 'Options', OPTIONS: 'Options',
NAME: 'Name', NAME: 'Name',

View File

@@ -1,12 +1,13 @@
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai'; import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg'; import { CgSmartHomeBoiler } from 'react-icons/cg';
import { FaSolarPanel } from 'react-icons/fa'; import { FaSolarPanel } from 'react-icons/fa';
import { GiHeatHaze } from 'react-icons/gi'; import { GiHeatHaze } from 'react-icons/gi';
import { MdThermostatAuto, MdOutlineSensors, MdOutlineExtension, MdOutlineDevices } from 'react-icons/md'; import { MdThermostatAuto, MdOutlineSensors, MdOutlineDevices } from 'react-icons/md';
import { TiFlowSwitch } from 'react-icons/ti'; import { TiFlowSwitch } from 'react-icons/ti';
import { VscVmConnect } from 'react-icons/vsc'; import { VscVmConnect } from 'react-icons/vsc';
import { DeviceType } from './types'; import { DeviceType } from './types';
import type { FC } from 'react'; import type { FC } from 'react';
interface DeviceIconProps { interface DeviceIconProps {
@@ -41,7 +42,7 @@ const DeviceIcon: FC<DeviceIconProps> = ({ type_id }) => {
case DeviceType.EXTENSION: case DeviceType.EXTENSION:
return <MdOutlineDevices />; return <MdOutlineDevices />;
case DeviceType.CUSTOM: case DeviceType.CUSTOM:
return <MdOutlineExtension />; return <PlaylistAddIcon sx={{ color: 'lightblue', fontSize: 24, verticalAlign: 'middle' }} />;
default: default:
return null; return null;
} }

View File

@@ -33,7 +33,7 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
import { useTheme } from '@table-library/react-table-library/theme'; import { useTheme } from '@table-library/react-table-library/theme';
import { useRequest } from 'alova'; import { useRequest } from 'alova';
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react'; import { useState, useEffect, useCallback, useLayoutEffect } from 'react';
import { IconContext } from 'react-icons'; import { IconContext } from 'react-icons';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@@ -50,12 +50,10 @@ import type { Device, DeviceValue } from './types';
import type { FC } from 'react'; import type { FC } from 'react';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import { ButtonRow, SectionContent, MessageBox, useLayoutTitle } from 'components'; import { ButtonRow, SectionContent, MessageBox, useLayoutTitle } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
const Devices: FC = () => { const Devices: FC = () => {
const { me } = useContext(AuthenticatedContext);
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [size, setSize] = useState([0, 0]); const [size, setSize] = useState([0, 0]);
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>(); const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
@@ -422,11 +420,8 @@ const Devices: FC = () => {
}; };
const renderCoreData = () => ( const renderCoreData = () => (
<IconContext.Provider value={{ color: 'lightblue', size: '24', style: { verticalAlign: 'middle' } }}> <IconContext.Provider value={{ color: 'lightblue', size: '18', style: { verticalAlign: 'middle' } }}>
{!coreData.connected && <MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />} {!coreData.connected && <MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />}
{/* {coreData.connected && coreData.devices.length === 0 && (
<MessageBox my={2} level="warning" message={LL.EMS_BUS_SCANNING()} />
)} */}
{coreData.connected && ( {coreData.connected && (
<Table data={{ nodes: coreData.devices }} select={device_select} theme={device_theme} layout={{ custom: true }}> <Table data={{ nodes: coreData.devices }} select={device_select} theme={device_theme} layout={{ custom: true }}>
@@ -589,7 +584,7 @@ const Devices: FC = () => {
<Cell>{renderNameCell(dv)}</Cell> <Cell>{renderNameCell(dv)}</Cell>
<Cell>{formatValue(LL, dv.v, dv.u)}</Cell> <Cell>{formatValue(LL, dv.v, dv.u)}</Cell>
<Cell stiff> <Cell stiff>
{dv.c && me.admin && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && ( {dv.c && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && (
<IconButton size="small" onClick={() => showDeviceValue(dv)}> <IconButton size="small" onClick={() => showDeviceValue(dv)}>
{dv.v === '' && dv.c ? ( {dv.v === '' && dv.c ? (
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} /> <PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
@@ -621,9 +616,7 @@ const Devices: FC = () => {
onSave={deviceValueDialogSave} onSave={deviceValueDialogSave}
selectedItem={selectedDeviceValue} selectedItem={selectedDeviceValue}
writeable={ writeable={
me.admin && selectedDeviceValue.c !== undefined && !hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY)
selectedDeviceValue.c !== undefined &&
!hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY)
} }
validator={deviceValueItemValidation(selectedDeviceValue)} validator={deviceValueItemValidation(selectedDeviceValue)}
progress={submitting} progress={submitting}

View File

@@ -206,7 +206,7 @@ const EMSStatus: FC = () => {
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar sx={{ bgcolor: theme.palette.success.main }}>
<DeviceHubIcon /> <DeviceHubIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
@@ -254,19 +254,20 @@ const EMSStatus: FC = () => {
{LL.REFRESH()} {LL.REFRESH()}
</Button> </Button>
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> {me.admin && (
<ButtonRow> <Box flexWrap="nowrap" whiteSpace="nowrap">
<Button <ButtonRow>
startIcon={<PermScanWifiIcon />} <Button
variant="outlined" startIcon={<PermScanWifiIcon />}
color="primary" variant="outlined"
disabled={!me.admin} color="primary"
onClick={() => setConfirmScan(true)} onClick={() => setConfirmScan(true)}
> >
{LL.SCAN_DEVICES()} {LL.SCAN_DEVICES()}
</Button> </Button>
</ButtonRow> </ButtonRow>
</Box> </Box>
)}
</Box> </Box>
</> </>
); );

View File

@@ -8,7 +8,7 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
import { useTheme } from '@table-library/react-table-library/theme'; import { useTheme } from '@table-library/react-table-library/theme';
import { useRequest } from 'alova'; import { useRequest } from 'alova';
import { useState, useContext, useEffect } from 'react'; import { useState, useEffect, useContext } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@@ -27,12 +27,12 @@ import { useI18nContext } from 'i18n/i18n-react';
const Sensors: FC = () => { const Sensors: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const { me } = useContext(AuthenticatedContext);
const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState<TemperatureSensor>(); const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState<TemperatureSensor>();
const [selectedAnalogSensor, setSelectedAnalogSensor] = useState<AnalogSensor>(); const [selectedAnalogSensor, setSelectedAnalogSensor] = useState<AnalogSensor>();
const [temperatureDialogOpen, setTemperatureDialogOpen] = useState<boolean>(false); const [temperatureDialogOpen, setTemperatureDialogOpen] = useState<boolean>(false);
const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false); const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false);
const [creating, setCreating] = useState<boolean>(false); const [creating, setCreating] = useState<boolean>(false);
const { me } = useContext(AuthenticatedContext);
const { data: sensorData, send: fetchSensorData } = useRequest(() => EMSESP.readSensorData(), { const { data: sensorData, send: fetchSensorData } = useRequest(() => EMSESP.readSensorData(), {
initialData: { initialData: {
@@ -51,8 +51,6 @@ const Sensors: FC = () => {
immediate: false immediate: false
}); });
const isAdmin = me.admin;
const common_theme = useTheme({ const common_theme = useTheme({
BaseRow: ` BaseRow: `
font-size: 14px; font-size: 14px;
@@ -222,10 +220,8 @@ const Sensors: FC = () => {
} }
const updateTemperatureSensor = (ts: TemperatureSensor) => { const updateTemperatureSensor = (ts: TemperatureSensor) => {
if (isAdmin) { setSelectedTemperatureSensor(ts);
setSelectedTemperatureSensor(ts); setTemperatureDialogOpen(true);
setTemperatureDialogOpen(true);
}
}; };
const onTemperatureDialogClose = () => { const onTemperatureDialogClose = () => {
@@ -248,11 +244,9 @@ const Sensors: FC = () => {
}; };
const updateAnalogSensor = (as: AnalogSensor) => { const updateAnalogSensor = (as: AnalogSensor) => {
if (isAdmin) { setCreating(false);
setCreating(false); setSelectedAnalogSensor(as);
setSelectedAnalogSensor(as); setAnalogDialogOpen(true);
setAnalogDialogOpen(true);
}
}; };
const onAnalogDialogClose = () => { const onAnalogDialogClose = () => {
@@ -453,7 +447,7 @@ const Sensors: FC = () => {
{LL.REFRESH()} {LL.REFRESH()}
</Button> </Button>
</Box> </Box>
{sensorData?.analog_enabled === true && ( {sensorData?.analog_enabled === true && me.admin && (
<Button <Button
variant="outlined" variant="outlined"
color="primary" color="primary"

View File

@@ -393,6 +393,7 @@ const system_status = {
uptime: '000+00:15:42.707', uptime: '000+00:15:42.707',
arduino_version: 'ESP32 Arduino v2.0.14' arduino_version: 'ESP32 Arduino v2.0.14'
}; };
let security_settings = { let security_settings = {
jwt_secret: 'naughty!', jwt_secret: 'naughty!',
users: [ users: [
@@ -402,10 +403,18 @@ let security_settings = {
}; };
const verify_authentication = { access_token: '1234' }; const verify_authentication = { access_token: '1234' };
const signin = {
const admin_signin = {
access_token: access_token:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInZlcnNpb24iOiIzLjAuMmIwIn0.MsHSgoJKI1lyYz77EiT5ZN3ECMrb4mPv9FNy3udq0TU' 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInZlcnNpb24iOiIzLjAuMmIwIn0.MsHSgoJKI1lyYz77EiT5ZN3ECMrb4mPv9FNy3udq0TU'
}; };
const guest_signin = {
access_token:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiYWRtaW4iOmZhbHNlfQ.E_lylR_vGIQFZUGNwcl5F6OkHoaELGsC5zqhi0pAiJE'
};
const signin = admin_signin;
// const signin = guest_signin;
const generate_token = { token: '1234' }; const generate_token = { token: '1234' };
// //
@@ -739,7 +748,7 @@ const emsesp_coredata = {
t: 17, t: 17,
tn: 'Custom', tn: 'Custom',
b: '', b: '',
n: 'Custom entities', n: 'Custom Entities',
d: 1, d: 1,
p: 1, p: 1,
v: '', v: '',

View File

@@ -552,7 +552,7 @@ const emsesp_coredata = {
t: 17, t: 17,
tn: 'Custom', tn: 'Custom',
b: '', b: '',
n: 'Custom entities', n: 'Custom Entities',
d: 1, d: 1,
p: 1, p: 1,
v: '', v: '',

View File

@@ -407,7 +407,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
// show any custom entities // show any custom entities
if (webCustomEntityService.count_entities() > 0) { if (webCustomEntityService.count_entities() > 0) {
shell.printfln("Custom entities:"); shell.printfln("Custom Entities:");
JsonDocument custom_doc; // use max size JsonDocument custom_doc; // use max size
JsonObject custom_output = custom_doc.to<JsonObject>(); JsonObject custom_output = custom_doc.to<JsonObject>();
webCustomEntityService.show_values(custom_output); webCustomEntityService.show_values(custom_output);

View File

@@ -53,10 +53,9 @@ MAKE_WORD_TRANSLATION(heatsource_device, "Heatsource", "Heizquelle", "Heatsource
MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı", "Sensori", "Snímače") MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı", "Sensori", "Snímače")
MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "Bilinmeyen", "Sconosciuto", "Neznámy") MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "Bilinmeyen", "Sconosciuto", "Neznámy")
MAKE_WORD_TRANSLATION(custom_device, "Custom", "Nutzerdefiniert", "Aangepast", "", "Niestandardowe", "", "", "Özel", "Personalizzato", "Vlastné") // TODO translate MAKE_WORD_TRANSLATION(custom_device, "Custom", "Nutzerdefiniert", "Aangepast", "", "Niestandardowe", "", "", "Özel", "Personalizzato", "Vlastné") // TODO translate
MAKE_WORD_TRANSLATION(custom_device_name, "Custom entities", "Nutzer deklarierte Entitäten", "Gebruiker gedefineerd", "", "Encje zdefiniowane przez użytkownika", "", "", "Kullanıcı tarafından tanımlanmış varlıklar", "Entità definita da utente", "Používateľom definované entity") // TODO translate MAKE_WORD_TRANSLATION(custom_device_name, "Custom Entities", "Nutzer deklarierte Entitäten", "Gebruiker gedefineerd", "", "Encje zdefiniowane przez użytkownika", "", "", "Kullanıcı tarafından tanımlanmış varlıklar", "Entità definita da utente", "Používateľom definované entity") // TODO translate
MAKE_WORD_TRANSLATION(ventilation_device, "Ventilation", "Lüftung", "Ventilatie", "", "Wentylacja", "", "", "Havalandırma", "Ventilazione", "Vetranie") // TODO translate MAKE_WORD_TRANSLATION(ventilation_device, "Ventilation", "Lüftung", "Ventilatie", "", "Wentylacja", "", "", "Havalandırma", "Ventilazione", "Vetranie") // TODO translate
// commands // commands
MAKE_WORD_TRANSLATION(info_cmd, "lists all values", "Liste aller Werte", "lijst van alle waardes", "", "wyświetl wszystkie wartości", "Viser alle verdier", "", "Tüm değerleri listele", "elenca tutti i valori", "zobraziť všetky hodnoty") // TODO translate MAKE_WORD_TRANSLATION(info_cmd, "lists all values", "Liste aller Werte", "lijst van alle waardes", "", "wyświetl wszystkie wartości", "Viser alle verdier", "", "Tüm değerleri listele", "elenca tutti i valori", "zobraziť všetky hodnoty") // TODO translate
MAKE_WORD_TRANSLATION(commands_cmd, "lists all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy") // TODO translate MAKE_WORD_TRANSLATION(commands_cmd, "lists all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy") // TODO translate