mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-04-29 12:05:12 +00:00
sync with dev
This commit is contained in:
@@ -26,8 +26,8 @@
|
|||||||
"@alova/adapter-xhr": "2.3.1",
|
"@alova/adapter-xhr": "2.3.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^7.3.9",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/material": "^7.3.9",
|
"@mui/material": "^9.0.0",
|
||||||
"@preact/compat": "^18.3.2",
|
"@preact/compat": "^18.3.2",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "^3.5.1",
|
"alova": "^3.5.1",
|
||||||
@@ -37,11 +37,11 @@
|
|||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.29.0",
|
"preact": "^10.29.1",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.5",
|
||||||
"react-icons": "^5.6.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-router": "^7.13.2",
|
"react-router": "^7.14.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"typesafe-i18n": "^5.27.1",
|
"typesafe-i18n": "^5.27.1",
|
||||||
"typescript": "^6.0.2"
|
"typescript": "^6.0.2"
|
||||||
@@ -52,18 +52,18 @@
|
|||||||
"@preact/compat": "^18.3.2",
|
"@preact/compat": "^18.3.2",
|
||||||
"@preact/preset-vite": "^2.10.5",
|
"@preact/preset-vite": "^2.10.5",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.6.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"axe-core": "^4.11.1",
|
"axe-core": "^4.11.3",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint": "^10.1.0",
|
"eslint": "^10.2.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.3",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.46.1",
|
"terser": "^5.46.1",
|
||||||
"typescript-eslint": "^8.58.0",
|
"typescript-eslint": "^8.58.2",
|
||||||
"vite": "^8.0.3",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-imagemin": "^0.6.1"
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||||
|
|||||||
862
interface/pnpm-lock.yaml
generated
862
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ import { toast } from 'react-toastify';
|
|||||||
|
|
||||||
import ForwardIcon from '@mui/icons-material/Forward';
|
import ForwardIcon from '@mui/icons-material/Forward';
|
||||||
import { Box, Button, Paper, Typography } from '@mui/material';
|
import { Box, Button, Paper, Typography } from '@mui/material';
|
||||||
|
import type { Theme } from '@mui/material/styles';
|
||||||
|
|
||||||
import * as AuthenticationApi from 'components/routing/authentication';
|
import * as AuthenticationApi from 'components/routing/authentication';
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
@@ -36,7 +37,7 @@ const SignIn = memo(() => {
|
|||||||
{
|
{
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
).onSuccess((response) => {
|
).onSuccess((response: { data: { access_token: string } }) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
authenticationContext.signIn(response.data.access_token);
|
authenticationContext.signIn(response.data.access_token);
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,6 @@ const SignIn = memo(() => {
|
|||||||
}
|
}
|
||||||
}, [signInRequest, signIn, LL]);
|
}, [signInRequest, signIn, LL]);
|
||||||
|
|
||||||
// Memoize callback to prevent recreation on every render
|
|
||||||
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
|
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
|
||||||
|
|
||||||
// get rid of scrollbar
|
// get rid of scrollbar
|
||||||
@@ -92,13 +92,15 @@ const SignIn = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={(theme: Theme) => ({
|
||||||
height="100vh"
|
display: 'flex',
|
||||||
margin="auto"
|
height: '100vh',
|
||||||
padding={2}
|
margin: 'auto',
|
||||||
justifyContent="center"
|
padding: 2,
|
||||||
flexDirection="column"
|
justifyContent: 'center',
|
||||||
maxWidth={(theme) => theme.breakpoints.values.sm}
|
flexDirection: 'column',
|
||||||
|
maxWidth: theme.breakpoints.values.sm
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Paper
|
<Paper
|
||||||
sx={(theme) => ({
|
sx={(theme) => ({
|
||||||
@@ -111,16 +113,18 @@ const SignIn = memo(() => {
|
|||||||
width: '100%'
|
width: '100%'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Typography mb={1} variant="h4">
|
<Typography sx={{ mb: 1 }} variant="h4">
|
||||||
{PROJECT_NAME}
|
{PROJECT_NAME}
|
||||||
</Typography>
|
</Typography>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
<Box
|
<Box
|
||||||
mt={1}
|
sx={{
|
||||||
display="flex"
|
mt: 1,
|
||||||
flexDirection="column"
|
display: 'flex',
|
||||||
gap={1}
|
flexDirection: 'column',
|
||||||
alignItems="center"
|
gap: 1,
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
|
|||||||
@@ -343,9 +343,9 @@ const CustomEntities = () => {
|
|||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.ENTITIES_HELP_1()}.</Typography>
|
{LL.ENTITIES_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
|
|
||||||
{renderEntity()}
|
{renderEntity()}
|
||||||
|
|
||||||
@@ -361,8 +361,8 @@ const CustomEntities = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box mt={2} display="flex" flexWrap="wrap">
|
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges > 0 && (
|
{numChanges > 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
@@ -384,7 +384,7 @@ const CustomEntities = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import DoneIcon from '@mui/icons-material/Done';
|
|||||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -178,7 +178,7 @@ const CustomEntitiesDialog = ({
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid mt={3}>
|
<Grid sx={{ mt: 3 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -238,7 +238,7 @@ const CustomEntitiesDialog = ({
|
|||||||
)}
|
)}
|
||||||
{editItem.ram === 0 && (
|
{editItem.ram === 0 && (
|
||||||
<>
|
<>
|
||||||
<Grid mt={3}>
|
<Grid sx={{ mt: 3 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -404,7 +404,7 @@ const CustomEntitiesDialog = ({
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -470,10 +470,10 @@ const Customizations = () => {
|
|||||||
|
|
||||||
const renderDeviceList = () => (
|
const renderDeviceList = () => (
|
||||||
<>
|
<>
|
||||||
<Box mb={1} color="warning.main">
|
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
{LL.CUSTOMIZATIONS_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2 }}>
|
||||||
{rename ? (
|
{rename ? (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -570,27 +570,22 @@ const Customizations = () => {
|
|||||||
const renderDeviceData = () => {
|
const renderDeviceData = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box color="warning.main">
|
<Typography sx={{ mt: 1, mb: 1 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2" mt={1} mb={1}>
|
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
||||||
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
|
||||||
|
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
||||||
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
|
||||||
|
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
|
||||||
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
|
{LL.CUSTOMIZATIONS_HELP_4()}
|
||||||
{LL.CUSTOMIZATIONS_HELP_4()}
|
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
||||||
<OptionIcon type="web_exclude" isSet={true} />=
|
|
||||||
{LL.CUSTOMIZATIONS_HELP_5()}
|
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
||||||
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
</Typography>
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
mb={1}
|
|
||||||
mt={0}
|
|
||||||
spacing={2}
|
spacing={2}
|
||||||
direction="row"
|
direction="row"
|
||||||
justifyContent="flex-start"
|
sx={{ mb: 1, mt: 0, justifyContent: 'flex-start', alignItems: 'center' }}
|
||||||
alignItems="center"
|
|
||||||
>
|
>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -779,8 +774,8 @@ const Customizations = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ interface LabelValueProps {
|
|||||||
|
|
||||||
const LabelValue = memo(({ label, value }: LabelValueProps) => (
|
const LabelValue = memo(({ label, value }: LabelValueProps) => (
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
<Typography variant="body2" color="warning.main">
|
<Typography variant="body2" color="warning">
|
||||||
{label}:
|
{label}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">{value}</Typography>
|
<Typography variant="body2">{value}</Typography>
|
||||||
@@ -131,7 +131,7 @@ const CustomizationsDialog = ({
|
|||||||
/>
|
/>
|
||||||
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
|
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
|
||||||
|
|
||||||
<Box mt={1} mb={2}>
|
<Box sx={{ mt: 1, mb: 2 }}>
|
||||||
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
|
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ const CustomizationsDialog = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<Typography variant="body2" color="error" mt={2}>
|
<Typography sx={{ mt: 2 }} variant="body2" color="error">
|
||||||
Error: Check min and max values
|
Error: Check min and max values
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
|
|||||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
|
||||||
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
||||||
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
||||||
import {
|
import {
|
||||||
@@ -263,7 +263,7 @@ const Dashboard = memo(() => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data.connected && data.nodes.length > 0 && !hasFavEntities && (
|
{data.connected && data.nodes.length > 0 && !hasFavEntities && (
|
||||||
<MessageBox mb={2} level="warning">
|
<MessageBox sx={{ mb: 2 }} level="warning">
|
||||||
<Typography>
|
<Typography>
|
||||||
{LL.NO_DATA_1()}
|
{LL.NO_DATA_1()}
|
||||||
<Link to="/customizations" style={{ color: 'white' }}>
|
<Link to="/customizations" style={{ color: 'white' }}>
|
||||||
@@ -280,10 +280,12 @@ const Dashboard = memo(() => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={{
|
||||||
justifyContent="flex-end"
|
display: 'flex',
|
||||||
flexWrap="nowrap"
|
justifyContent: 'flex-end',
|
||||||
whiteSpace="nowrap"
|
flexWrap: 'nowrap',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
size="small"
|
size="small"
|
||||||
@@ -306,7 +308,7 @@ const Dashboard = memo(() => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{data.nodes.length > 0 ? (
|
{data.nodes.length > 0 ? (
|
||||||
<Box mt={1} justifyContent="center" flexDirection="column">
|
<Box sx={{ mt: 1, justifyContent: 'center', flexDirection: 'column' }}>
|
||||||
<IconContext.Provider
|
<IconContext.Provider
|
||||||
value={{
|
value={{
|
||||||
color: 'lightblue',
|
color: 'lightblue',
|
||||||
@@ -373,13 +375,8 @@ const Dashboard = memo(() => {
|
|||||||
</IconContext.Provider>
|
</IconContext.Provider>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box sx={{ display: 'flex' }}>
|
||||||
display="flex"
|
<Typography sx={{ mt: 1 }} color="warning" variant="body1">
|
||||||
// justifyContent="flex-end"
|
|
||||||
// flexWrap="nowrap"
|
|
||||||
// whiteSpace="nowrap"
|
|
||||||
>
|
|
||||||
<Typography mt={1} color="warning.main" variant="body1">
|
|
||||||
no data
|
no data
|
||||||
</Typography>
|
</Typography>
|
||||||
<Tooltip title={LL.DASHBOARD_1()}>
|
<Tooltip title={LL.DASHBOARD_1()}>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import type { IconType } from 'react-icons';
|
||||||
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
|
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } 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';
|
||||||
@@ -15,14 +16,9 @@ import { PiFan, PiGauge } from 'react-icons/pi';
|
|||||||
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
|
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
|
||||||
import { VscVmConnect } from 'react-icons/vsc';
|
import { VscVmConnect } from 'react-icons/vsc';
|
||||||
|
|
||||||
import type { SvgIconProps } from '@mui/material';
|
|
||||||
|
|
||||||
import { DeviceType } from './types';
|
import { DeviceType } from './types';
|
||||||
|
|
||||||
const deviceIconLookup: Record<
|
const deviceIconLookup: Record<DeviceType, IconType | null> = {
|
||||||
DeviceType,
|
|
||||||
React.ComponentType<SvgIconProps> | null
|
|
||||||
> = {
|
|
||||||
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
|
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
|
||||||
[DeviceType.ANALOGSENSOR]: PiGauge,
|
[DeviceType.ANALOGSENSOR]: PiGauge,
|
||||||
[DeviceType.BOILER]: CgSmartHomeBoiler,
|
[DeviceType.BOILER]: CgSmartHomeBoiler,
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ const Devices = memo(() => {
|
|||||||
)
|
)
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<Box justifyContent="center" flexDirection="column">
|
<Box sx={{ justifyContent: 'center', flexDirection: 'column' }}>
|
||||||
<IconContext.Provider
|
<IconContext.Provider
|
||||||
value={{
|
value={{
|
||||||
color: 'lightblue',
|
color: 'lightblue',
|
||||||
@@ -670,12 +670,12 @@ const Devices = memo(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ p: 1 }}>
|
<Box sx={{ p: 1 }}>
|
||||||
<Grid container justifyContent="space-between">
|
<Grid container sx={{ justifyContent: 'space-between' }}>
|
||||||
<Typography noWrap variant="subtitle1" color="warning.main">
|
<Typography noWrap variant="subtitle1" color="warning">
|
||||||
{deviceInfo.n} (
|
{deviceInfo.n} (
|
||||||
{deviceInfo.tn})
|
{deviceInfo.tn})
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid justifyContent="flex-end">
|
<Grid sx={{ justifyContent: 'flex-end' }}>
|
||||||
<ButtonTooltip title={LL.CLOSE()}>
|
<ButtonTooltip title={LL.CLOSE()}>
|
||||||
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
|
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
|
||||||
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />
|
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ const DevicesDialog = ({
|
|||||||
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" mb={2}>
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2">{editItem.id.slice(2)}</Typography>
|
{editItem.id.slice(2)}
|
||||||
</Box>
|
</Typography>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
{editItem.l ? (
|
{editItem.l ? (
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
|
Grid,
|
||||||
Link,
|
Link,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
@@ -42,7 +43,7 @@ interface CustomSupport {
|
|||||||
html: string | null;
|
html: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_IMAGE_URL = 'https://emsesp.org/_media/images/installer.jpeg';
|
const DEFAULT_IMAGE_URL = 'https://emsesp.org/media/images/installer.jpeg';
|
||||||
|
|
||||||
const SUPPORT_BOX_STYLES: SxProps<Theme> = {
|
const SUPPORT_BOX_STYLES: SxProps<Theme> = {
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
@@ -71,7 +72,6 @@ const HelpComponent = () => {
|
|||||||
});
|
});
|
||||||
const [imgError, setImgError] = useState<boolean>(false);
|
const [imgError, setImgError] = useState<boolean>(false);
|
||||||
|
|
||||||
// Memoize the request method to prevent re-creation on every render
|
|
||||||
const getCustomSupportMethod = useMemo(
|
const getCustomSupportMethod = useMemo(
|
||||||
() => callAction({ action: 'getCustomSupport' }),
|
() => callAction({ action: 'getCustomSupport' }),
|
||||||
[]
|
[]
|
||||||
@@ -146,11 +146,9 @@ const HelpComponent = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{customSupport.html && (
|
{customSupport.html && (
|
||||||
<Stack
|
<Stack
|
||||||
padding={1}
|
|
||||||
mb={2}
|
|
||||||
direction="row"
|
direction="row"
|
||||||
divider={<Divider orientation="vertical" flexItem />}
|
divider={<Divider orientation="vertical" flexItem />}
|
||||||
sx={SUPPORT_BOX_STYLES}
|
sx={{ padding: 1, mb: 2, ...SUPPORT_BOX_STYLES }}
|
||||||
>
|
>
|
||||||
<Typography variant="subtitle1">
|
<Typography variant="subtitle1">
|
||||||
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
|
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
|
||||||
@@ -185,9 +183,9 @@ const HelpComponent = () => {
|
|||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box p={2} color="warning.main">
|
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
|
||||||
<Typography mb={1} variant="body1">
|
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
|
||||||
{LL.HELP_INFORMATION_4()}.
|
{LL.HELP_INFORMATION_4()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
@@ -197,11 +195,11 @@ const HelpComponent = () => {
|
|||||||
>
|
>
|
||||||
{LL.SUPPORT_INFORMATION(0)}
|
{LL.SUPPORT_INFORMATION(0)}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Grid>
|
||||||
|
|
||||||
<Divider sx={{ mt: 4 }} />
|
<Divider sx={{ mt: 4 }} />
|
||||||
|
|
||||||
<Typography color="white" variant="subtitle1" align="center" mt={1}>
|
<Typography color="white" variant="subtitle1" align="center" sx={{ mt: 1 }}>
|
||||||
©
|
©
|
||||||
<Link
|
<Link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -186,9 +186,9 @@ const Modules = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}.</Typography>
|
{LL.MODULES_DESCRIPTION()}.
|
||||||
</Box>
|
</Typography>
|
||||||
<Table
|
<Table
|
||||||
data={{ nodes: modules }}
|
data={{ nodes: modules }}
|
||||||
theme={modules_theme}
|
theme={modules_theme}
|
||||||
@@ -236,8 +236,8 @@ const Modules = () => {
|
|||||||
)}
|
)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Box mt={1} display="flex" flexWrap="wrap">
|
<Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const ModulesDialog = ({
|
|||||||
label="Enabled"
|
label="Enabled"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Box mt={2} mb={1}>
|
<Box sx={{ mt: 2, mb: 1 }}>
|
||||||
<TextField
|
<TextField
|
||||||
name="license"
|
name="license"
|
||||||
label="License Key"
|
label="License Key"
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutlined';
|
||||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||||
import StarIcon from '@mui/icons-material/Star';
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
import StarOutlineIcon from '@mui/icons-material/StarOutline';
|
import StarOutlineIcon from '@mui/icons-material/StarOutlined';
|
||||||
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
|
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
|
||||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
||||||
import type { SvgIconProps } from '@mui/material';
|
import type { SvgIconProps } from '@mui/material';
|
||||||
|
|||||||
@@ -358,9 +358,9 @@ const Scheduler = () => {
|
|||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}.</Typography>
|
{LL.SCHEDULER_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
{renderSchedule()}
|
{renderSchedule()}
|
||||||
|
|
||||||
{selectedScheduleItem && (
|
{selectedScheduleItem && (
|
||||||
@@ -375,8 +375,8 @@ const Scheduler = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
@@ -398,7 +398,7 @@ const Scheduler = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import AddIcon from '@mui/icons-material/Add';
|
|||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -338,11 +338,13 @@ const SchedulerDialog = ({
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
/>
|
/>
|
||||||
{isTimerSchedule && (
|
{isTimerSchedule && (
|
||||||
<Box color="warning.main" ml={2} mt={4}>
|
<Typography
|
||||||
<Typography variant="body2">
|
sx={{ ml: 2, mt: 4 }}
|
||||||
{LL.SCHEDULER_HELP_2()}
|
color="warning"
|
||||||
</Typography>
|
variant="body2"
|
||||||
</Box>
|
>
|
||||||
|
{LL.SCHEDULER_HELP_2()}
|
||||||
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -391,7 +393,7 @@ const SchedulerDialog = ({
|
|||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -591,7 +591,14 @@ const Sensors = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sensorData?.analog_enabled === true && me.admin && (
|
{sensorData?.analog_enabled === true && me.admin && (
|
||||||
<Box mt={2} display="flex" flexWrap="wrap" justifyContent="flex-end">
|
<Box
|
||||||
|
sx={{
|
||||||
|
mt: 2,
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -479,7 +479,7 @@ const SensorsAnalogDialog = ({
|
|||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
|
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
|
||||||
<Box mt={1}>
|
<Box sx={{ mt: 1 }}>
|
||||||
{Object.values(fieldErrors).map((errArr, idx) =>
|
{Object.values(fieldErrors).map((errArr, idx) =>
|
||||||
Array.isArray(errArr)
|
Array.isArray(errArr)
|
||||||
? errArr.map((err, j) => (
|
? errArr.map((err, j) => (
|
||||||
@@ -487,7 +487,7 @@ const SensorsAnalogDialog = ({
|
|||||||
key={`${idx}-${j}`}
|
key={`${idx}-${j}`}
|
||||||
color="error"
|
color="error"
|
||||||
variant="caption"
|
variant="caption"
|
||||||
display="block"
|
sx={{ display: 'block' }}
|
||||||
>
|
>
|
||||||
{err.message}
|
{err.message}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -498,7 +498,7 @@ const SensorsAnalogDialog = ({
|
|||||||
)}
|
)}
|
||||||
{editItem.s && (
|
{editItem.s && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography mt={1} color="warning.main" variant="body2">
|
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
|
||||||
<WarningIcon
|
<WarningIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{ mr: 1, verticalAlign: 'middle' }}
|
sx={{ mr: 1, verticalAlign: 'middle' }}
|
||||||
@@ -511,7 +511,7 @@ const SensorsAnalogDialog = ({
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}>
|
<Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
disabled={editItem.s}
|
disabled={editItem.s}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
|
|||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
@@ -111,11 +110,9 @@ const SensorsTemperatureDialog = ({
|
|||||||
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" mb={2}>
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2">
|
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
|
||||||
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
|
</Typography>
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
@@ -142,7 +139,7 @@ const SensorsTemperatureDialog = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
{editItem.s && (
|
{editItem.s && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography mt={1} color="warning.main" variant="body2">
|
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
|
||||||
<WarningIcon
|
<WarningIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{ mr: 1, verticalAlign: 'middle' }}
|
sx={{ mr: 1, verticalAlign: 'middle' }}
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ const UserProfileComponent = () => {
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<Box mt={2} mb={2} display="flex" alignItems="center">
|
<Box sx={{ mt: 2, mb: 2, display: 'flex', alignItems: 'center' }}>
|
||||||
<Typography mr={2} variant="body1" align="center">
|
<Typography
|
||||||
|
sx={{ mr: 2, textAlign: 'center' }}
|
||||||
|
color="warning"
|
||||||
|
variant="body1"
|
||||||
|
>
|
||||||
{LL.LANGUAGE()}:
|
{LL.LANGUAGE()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
|
|||||||
@@ -43,16 +43,6 @@ export interface Settings {
|
|||||||
modbus_port: number;
|
modbus_port: number;
|
||||||
modbus_max_clients: number;
|
modbus_max_clients: number;
|
||||||
modbus_timeout: number;
|
modbus_timeout: number;
|
||||||
email_enabled: boolean;
|
|
||||||
email_ssl?: boolean;
|
|
||||||
email_starttls?: boolean;
|
|
||||||
email_server: string;
|
|
||||||
email_port: number;
|
|
||||||
email_login: string;
|
|
||||||
email_pass: string;
|
|
||||||
email_sender: string;
|
|
||||||
email_recp: string;
|
|
||||||
email_subject: string;
|
|
||||||
developer_mode: boolean;
|
developer_mode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
FormLoader,
|
FormLoader,
|
||||||
MessageBox,
|
MessageBox,
|
||||||
SectionContent,
|
SectionContent,
|
||||||
ValidatedPasswordField,
|
|
||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
@@ -352,156 +351,6 @@ const ApplicationSettings = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
<Typography color="secondary">eMail</Typography>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.email_enabled}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
name="email_enabled"
|
|
||||||
disabled={!hardwareData.psram}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography color={!hardwareData.psram ? 'grey' : 'default'}>
|
|
||||||
Enable eMail notification
|
|
||||||
{!hardwareData.psram && (
|
|
||||||
<Typography variant="caption">
|
|
||||||
({LL.IS_REQUIRED('PSRAM')})
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{data.email_enabled && (
|
|
||||||
<>
|
|
||||||
<Grid
|
|
||||||
container
|
|
||||||
spacing={2}
|
|
||||||
direction="row"
|
|
||||||
justifyContent="flex-start"
|
|
||||||
alignItems="flex-start"
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_server"
|
|
||||||
label="SMTP Server"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_server}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
sx={{ width: '12ch' }}
|
|
||||||
name="email_port"
|
|
||||||
variant="outlined"
|
|
||||||
label="Port"
|
|
||||||
value={numberValue(data.email_port)}
|
|
||||||
type="number"
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid size={4} mt={!data.email_ssl && !data.email_starttls ? 0 : 3}>
|
|
||||||
{!data.email_starttls && (
|
|
||||||
<BlockFormControlLabel
|
|
||||||
sx={{ width: '12ch' }}
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.email_ssl}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
name="email_ssl"
|
|
||||||
disabled={
|
|
||||||
data.email_starttls || data.email_ssl === undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="SSL/TLS"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!data.email_ssl && (
|
|
||||||
<BlockFormControlLabel
|
|
||||||
sx={{ width: '12ch' }}
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.email_starttls}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
name="email_starttls"
|
|
||||||
disabled={
|
|
||||||
data.email_ssl || data.email_starttls === undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="STARTTLS"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid container spacing={2} rowSpacing={0}>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_login"
|
|
||||||
label="Login"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_login}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedPasswordField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_pass"
|
|
||||||
label="Password"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_pass}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid container spacing={2} rowSpacing={0}>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_sender"
|
|
||||||
label="From"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_sender}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_recp"
|
|
||||||
label="To"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_recp}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="email_subject"
|
|
||||||
label="Subject"
|
|
||||||
variant="outlined"
|
|
||||||
value={data.email_subject}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.SENSORS()}
|
{LL.SENSORS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -922,7 +771,7 @@ const ApplicationSettings = () => {
|
|||||||
label={LL.REMOTE_TIMEOUT_EN()}
|
label={LL.REMOTE_TIMEOUT_EN()}
|
||||||
/>
|
/>
|
||||||
{data.remote_timeout_en && (
|
{data.remote_timeout_en && (
|
||||||
<Box mt={2}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
name="remote_timeout"
|
name="remote_timeout"
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, 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 { Box, Button, Grid, Typography } from '@mui/material';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Grid,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
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 { 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 SystemMonitor from 'app/status/SystemMonitor';
|
import SystemMonitor from 'app/status/SystemMonitor';
|
||||||
@@ -19,15 +30,11 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { saveFile } from 'utils';
|
import { saveFile } from 'utils';
|
||||||
|
|
||||||
interface DownloadButton {
|
|
||||||
type: string;
|
|
||||||
label: string | number;
|
|
||||||
isGridButton: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DownloadUpload = () => {
|
const DownloadUpload = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const [confirmBackup, setConfirmBackup] = useState<boolean>(false);
|
||||||
|
|
||||||
const [restarting, setRestarting] = useState<boolean>(false);
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
|
|
||||||
const { send: sendExportData } = useRequest(
|
const { send: sendExportData } = useRequest(
|
||||||
@@ -62,40 +69,44 @@ const DownloadUpload = () => {
|
|||||||
|
|
||||||
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
||||||
|
|
||||||
const downloadButtons: DownloadButton[] = useMemo(
|
const handleCloseBackupDialog = useCallback(() => {
|
||||||
() => [
|
setConfirmBackup(false);
|
||||||
{
|
}, []);
|
||||||
type: 'settings',
|
|
||||||
label: LL.SETTINGS_OF(LL.APPLICATION()),
|
const renderBackupDialog = useMemo(
|
||||||
isGridButton: true
|
() => (
|
||||||
},
|
<Dialog
|
||||||
{
|
sx={dialogStyle}
|
||||||
type: 'customizations',
|
open={confirmBackup}
|
||||||
label: LL.CUSTOMIZATIONS(),
|
onClose={handleCloseBackupDialog}
|
||||||
isGridButton: true
|
>
|
||||||
},
|
<DialogTitle>{LL.DOWNLOAD_SYSTEM_BACKUP()}</DialogTitle>
|
||||||
{
|
<DialogContent dividers>
|
||||||
type: 'entities',
|
<WarningIcon color="warning" sx={{ fontSize: 18 }} />
|
||||||
label: LL.CUSTOM_ENTITIES(0),
|
|
||||||
isGridButton: true
|
{LL.WARNING_SYSTEM_BACKUP()}
|
||||||
},
|
</DialogContent>
|
||||||
{
|
<DialogActions>
|
||||||
type: 'schedule',
|
<Button
|
||||||
label: LL.SCHEDULE(0),
|
startIcon={<CancelIcon />}
|
||||||
isGridButton: true
|
variant="outlined"
|
||||||
},
|
onClick={handleCloseBackupDialog}
|
||||||
{
|
color="secondary"
|
||||||
type: 'systembackup',
|
>
|
||||||
label: LL.DOWNLOAD_SYSTEM_BACKUP(),
|
{LL.CANCEL()}
|
||||||
isGridButton: true
|
</Button>
|
||||||
},
|
<Button
|
||||||
{
|
startIcon={<DownloadIcon />}
|
||||||
type: 'allvalues',
|
variant="outlined"
|
||||||
label: LL.ALLVALUES(),
|
onClick={() => handleDownload('systembackup')()}
|
||||||
isGridButton: false
|
color="primary"
|
||||||
}
|
>
|
||||||
],
|
{LL.DOWNLOAD(0)}
|
||||||
[LL]
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
),
|
||||||
|
[confirmBackup, handleCloseBackupDialog, LL]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDownload = useCallback(
|
const handleDownload = useCallback(
|
||||||
@@ -117,58 +128,57 @@ const DownloadUpload = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gridButtons = downloadButtons.filter((btn) => btn.isGridButton);
|
|
||||||
const standaloneButton = downloadButtons.find((btn) => !btn.isGridButton);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
|
{renderBackupDialog}
|
||||||
|
|
||||||
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.DOWNLOAD(0)}
|
{LL.DOWNLOAD(0)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography mb={1} variant="body1" color="warning">
|
<Grid
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT()}.
|
container
|
||||||
</Typography>
|
spacing={2}
|
||||||
|
sx={{
|
||||||
<Grid container spacing={2}>
|
alignItems: 'center'
|
||||||
{gridButtons.map((button) => (
|
}}
|
||||||
<Grid key={button.type}>
|
>
|
||||||
<Button
|
<Typography variant="body1" color="warning">
|
||||||
startIcon={<DownloadIcon />}
|
{LL.DOWNLOAD_SETTINGS_TEXT()}:
|
||||||
variant="outlined"
|
</Typography>
|
||||||
color="primary"
|
|
||||||
onClick={handleDownload(button.type)}
|
|
||||||
>
|
|
||||||
{button.label}
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Typography mt={2} mb={1} variant="body1" color="warning">
|
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT2()}.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
{standaloneButton && (
|
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleDownload(standaloneButton.type)}
|
onClick={() => setConfirmBackup(true)}
|
||||||
>
|
>
|
||||||
{standaloneButton.label}
|
{LL.DOWNLOAD_SYSTEM_BACKUP()}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</Grid>
|
||||||
|
|
||||||
|
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
|
||||||
|
<Typography variant="body1" color="warning">
|
||||||
|
{LL.DOWNLOAD_SETTINGS_TEXT2()}:
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleDownload('allvalues')}
|
||||||
|
>
|
||||||
|
{LL.ALLVALUES()}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box color="warning.main" sx={{ pb: 2 }}>
|
<Typography sx={{ pb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.UPLOAD_TEXT()}.</Typography>
|
{LL.UPLOAD_TEXT()}:
|
||||||
</Box>
|
</Typography>
|
||||||
|
|
||||||
<SingleUpload text={LL.UPLOAD_DRAG()} doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const MqttSettings = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<>
|
<>
|
||||||
<Box display="flex" gap={2} mb={1}>
|
<Box sx={{ display: 'flex', gap: 2, mb: 1 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -193,9 +193,9 @@ const NTPSettings = () => {
|
|||||||
{timeZoneItems}
|
{timeZoneItems}
|
||||||
</ValidatedTextField>
|
</ValidatedTextField>
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
{!data.enabled && !dirtyFlags.length && (
|
{!data.enabled && !dirtyFlags.length && (
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
onClick={openSetTime}
|
onClick={openSetTime}
|
||||||
@@ -259,9 +259,9 @@ const NTPSettings = () => {
|
|||||||
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
|
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
|
||||||
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}>
|
<Typography color="warning" variant="body2">
|
||||||
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography>
|
{LL.SET_TIME_TEXT()}
|
||||||
</Box>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
label={LL.LOCAL_TIME(0)}
|
label={LL.LOCAL_TIME(0)}
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
|
|||||||
@@ -156,11 +156,13 @@ const Settings = () => {
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
mt={2}
|
sx={{
|
||||||
display="flex"
|
mt: 2,
|
||||||
justifyContent="flex-end"
|
display: 'flex',
|
||||||
flexWrap="nowrap"
|
justifyContent: 'flex-end',
|
||||||
whiteSpace="nowrap"
|
flexWrap: 'nowrap',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
|
|||||||
@@ -54,19 +54,27 @@ const GenerateToken = ({ username, onClose }: GenerateTokenProps) => {
|
|||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
{token ? (
|
{token ? (
|
||||||
<>
|
<>
|
||||||
<MessageBox message={LL.ACCESS_TOKEN_TEXT()} level="info" my={2} />
|
<MessageBox
|
||||||
<Box mt={2} mb={2}>
|
message={LL.ACCESS_TOKEN_TEXT()}
|
||||||
|
level="info"
|
||||||
|
sx={{ mt: 2, mb: 2 }}
|
||||||
|
/>
|
||||||
|
<Box sx={{ mt: 2, mb: 2 }}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Token"
|
label="Token"
|
||||||
multiline
|
multiline
|
||||||
value={token.token}
|
value={token.token}
|
||||||
fullWidth
|
fullWidth
|
||||||
contentEditable={false}
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Box m={4} textAlign="center">
|
<Box sx={{ m: 4, textAlign: 'center' }}>
|
||||||
<LinearProgress />
|
<LinearProgress />
|
||||||
<Typography variant="h6">{LL.GENERATING_TOKEN()}…</Typography>
|
<Typography variant="h6">{LL.GENERATING_TOKEN()}…</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -240,12 +240,16 @@ const ManageUsers = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
{noAdminConfigured() && (
|
{noAdminConfigured() && (
|
||||||
<MessageBox level="warning" message={LL.USER_WARNING()} my={2} />
|
<MessageBox
|
||||||
|
level="warning"
|
||||||
|
message={LL.USER_WARNING()}
|
||||||
|
sx={{ mt: 2, mb: 2 }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
{changed !== 0 && (
|
{changed !== 0 && (
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
<Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<CancelIcon />}
|
startIcon={<CancelIcon />}
|
||||||
@@ -270,7 +274,7 @@ const ManageUsers = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<PersonAddIcon />}
|
startIcon={<PersonAddIcon />}
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ const SystemLog = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container spacing={2} alignItems="center">
|
<Grid container spacing={2} sx={{ alignItems: 'center' }}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
name="level"
|
name="level"
|
||||||
|
|||||||
@@ -118,17 +118,15 @@ const SystemMonitor = () => {
|
|||||||
p: 3
|
p: 3
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" flexDirection="column">
|
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
<img
|
<img
|
||||||
src="/app/icon.png"
|
src="/app/icon.png"
|
||||||
alt="EMS-ESP"
|
alt="EMS-ESP"
|
||||||
style={{ width: '40px', height: '40px', marginBottom: '16px' }}
|
style={{ width: '40px', height: '40px', marginBottom: '16px' }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
color="secondary"
|
sx={{ color: 'secondary', fontWeight: 400, textAlign: 'center' }}
|
||||||
variant="h6"
|
variant="h6"
|
||||||
fontWeight={400}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
>
|
||||||
{statusMessage}
|
{statusMessage}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -148,11 +146,14 @@ const SystemMonitor = () => {
|
|||||||
</MessageBox>
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center">
|
<Typography
|
||||||
|
sx={{ mt: 2, fontWeight: 400, textAlign: 'center' }}
|
||||||
|
variant="h6"
|
||||||
|
>
|
||||||
{LL.PLEASE_WAIT()}…
|
{LL.PLEASE_WAIT()}…
|
||||||
</Typography>
|
</Typography>
|
||||||
{isUploading && (
|
{isUploading && (
|
||||||
<Box width="100%" pl={2} pr={2} py={2}>
|
<Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
|
||||||
<LinearProgressWithLabel value={progressValue} />
|
<LinearProgressWithLabel value={progressValue} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ const InstallDialog = memo(
|
|||||||
fetchDevVersion,
|
fetchDevVersion,
|
||||||
latestVersion,
|
latestVersion,
|
||||||
latestDevVersion,
|
latestDevVersion,
|
||||||
|
upgradeImportantMessageType,
|
||||||
|
downloadOnly,
|
||||||
platform,
|
platform,
|
||||||
LL,
|
LL,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -283,6 +285,8 @@ const InstallDialog = memo(
|
|||||||
fetchDevVersion: boolean;
|
fetchDevVersion: boolean;
|
||||||
latestVersion?: VersionInfo;
|
latestVersion?: VersionInfo;
|
||||||
latestDevVersion?: VersionInfo;
|
latestDevVersion?: VersionInfo;
|
||||||
|
upgradeImportantMessageType: number;
|
||||||
|
downloadOnly: boolean;
|
||||||
platform: string;
|
platform: string;
|
||||||
LL: TranslationFunctions;
|
LL: TranslationFunctions;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -305,12 +309,24 @@ const InstallDialog = memo(
|
|||||||
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
|
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Typography mb={2}>
|
<Typography sx={{ mb: 2 }}>
|
||||||
{LL.INSTALL_VERSION(
|
{LL.INSTALL_VERSION(
|
||||||
LL.INSTALL(),
|
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
|
||||||
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||||
|
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||||
|
<Typography sx={{ mt: 2 }}>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||||
|
style={{ color: 'lightblue' }}
|
||||||
|
>
|
||||||
|
{LL.ONLINE_HELP()}
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
@@ -331,14 +347,16 @@ const InstallDialog = memo(
|
|||||||
{LL.DOWNLOAD(0)}
|
{LL.DOWNLOAD(0)}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{!downloadOnly && (
|
||||||
startIcon={<WarningIcon color="warning" />}
|
<Button
|
||||||
variant="outlined"
|
startIcon={<WarningIcon color="warning" />}
|
||||||
onClick={() => onInstall(binURL)}
|
variant="outlined"
|
||||||
color="primary"
|
onClick={() => onInstall(binURL)}
|
||||||
>
|
color="primary"
|
||||||
{LL.INSTALL()}
|
>
|
||||||
</Button>
|
{LL.INSTALL()}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
@@ -367,7 +385,9 @@ const InstallPartitionDialog = memo(
|
|||||||
{LL.INSTALL()} {LL.STORED_VERSIONS()}
|
{LL.INSTALL()} {LL.STORED_VERSIONS()}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Typography mb={2}>{LL.INSTALL_VERSION(LL.INSTALL(), version)}</Typography>
|
<Typography sx={{ mb: 2 }}>
|
||||||
|
{LL.INSTALL_VERSION(LL.INSTALL(), version)}
|
||||||
|
</Typography>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
@@ -419,6 +439,7 @@ const Version = () => {
|
|||||||
const [stableUpgradeAvailable, setStableUpgradeAvailable] =
|
const [stableUpgradeAvailable, setStableUpgradeAvailable] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [internetLive, setInternetLive] = useState<boolean>(false);
|
const [internetLive, setInternetLive] = useState<boolean>(false);
|
||||||
|
const [downloadOnly, setDownloadOnly] = useState<boolean>(false);
|
||||||
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
|
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
|
||||||
const [firmwareSize, setFirmwareSize] = useState<number>(0);
|
const [firmwareSize, setFirmwareSize] = useState<number>(0);
|
||||||
|
|
||||||
@@ -444,6 +465,9 @@ const Version = () => {
|
|||||||
error
|
error
|
||||||
} = useRequest(SystemApi.readSystemStatus).onSuccess((event) => {
|
} = useRequest(SystemApi.readSystemStatus).onSuccess((event) => {
|
||||||
const systemData = event.data as VersionData;
|
const systemData = event.data as VersionData;
|
||||||
|
if (systemData.arduino_version.startsWith('Tasmota')) {
|
||||||
|
setDownloadOnly(true);
|
||||||
|
}
|
||||||
setUsingDevVersion(systemData.emsesp_version.includes('dev'));
|
setUsingDevVersion(systemData.emsesp_version.includes('dev'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -459,6 +483,26 @@ const Version = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
|
||||||
|
useState<number>(0);
|
||||||
|
|
||||||
|
const { send: checkUpgradeImportantMessages } = useRequest(
|
||||||
|
(version: string) =>
|
||||||
|
callAction({ action: 'upgradeImportantMessages', param: version }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onSuccess((event) => {
|
||||||
|
const upgradeImportantMessageType_n = (
|
||||||
|
event.data as { upgradeImportantMessageType: number }
|
||||||
|
).upgradeImportantMessageType;
|
||||||
|
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
// Memoized values
|
// Memoized values
|
||||||
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
|
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
|
||||||
|
|
||||||
@@ -524,10 +568,16 @@ const Version = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showFirmwareDialog = useCallback((useDevVersion: boolean) => {
|
const showFirmwareDialog = useCallback(
|
||||||
setFetchDevVersion(useDevVersion);
|
(useDevVersion: boolean) => {
|
||||||
setOpenInstallDialog(true);
|
setFetchDevVersion(useDevVersion);
|
||||||
}, []);
|
void checkUpgradeImportantMessages(
|
||||||
|
useDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||||
|
);
|
||||||
|
setOpenInstallDialog(true);
|
||||||
|
},
|
||||||
|
[latestDevVersion, latestVersion, fetchDevVersion]
|
||||||
|
);
|
||||||
|
|
||||||
const closeInstallDialog = useCallback(() => {
|
const closeInstallDialog = useCallback(() => {
|
||||||
setOpenInstallDialog(false);
|
setOpenInstallDialog(false);
|
||||||
@@ -629,8 +679,8 @@ const Version = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box p={2} border="1px solid grey" borderRadius={2}>
|
<Box sx={{ p: 2, border: '1px solid #565656', borderRadius: 2 }}>
|
||||||
<Typography mb={1} variant="h6" color="primary">
|
<Typography sx={{ mb: 1 }} variant="h6" color="primary">
|
||||||
{LL.THIS_VERSION()}
|
{LL.THIS_VERSION()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
@@ -695,7 +745,7 @@ const Version = () => {
|
|||||||
|
|
||||||
{internetLive ? (
|
{internetLive ? (
|
||||||
<>
|
<>
|
||||||
<Typography mt={4} mb={1} variant="h6" color="primary">
|
<Typography sx={{ mt: 4, mb: 1 }} variant="h6" color="primary">
|
||||||
{LL.AVAILABLE_VERSION()}
|
{LL.AVAILABLE_VERSION()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
@@ -717,7 +767,7 @@ const Version = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 8, md: 10 }}>
|
<Grid size={{ xs: 8, md: 10 }}>
|
||||||
{otherPartitions.map((partition) => (
|
{otherPartitions.map((partition) => (
|
||||||
<Typography key={partition.partition} mb={1}>
|
<Typography key={partition.partition} sx={{ mb: 1 }}>
|
||||||
{partition.version}
|
{partition.version}
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -783,7 +833,7 @@ const Version = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Typography mt={2} color="warning">
|
<Typography sx={{ mt: 2 }} color="warning">
|
||||||
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
|
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
|
||||||
{LL.INTERNET_CONNECTION_REQUIRED()}
|
{LL.INTERNET_CONNECTION_REQUIRED()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -807,6 +857,8 @@ const Version = () => {
|
|||||||
fetchDevVersion={fetchDevVersion}
|
fetchDevVersion={fetchDevVersion}
|
||||||
latestVersion={latestVersion}
|
latestVersion={latestVersion}
|
||||||
latestDevVersion={latestDevVersion}
|
latestDevVersion={latestDevVersion}
|
||||||
|
upgradeImportantMessageType={upgradeImportantMessageType}
|
||||||
|
downloadOnly={downloadOnly}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
LL={LL}
|
LL={LL}
|
||||||
onClose={closeInstallDialog}
|
onClose={closeInstallDialog}
|
||||||
@@ -823,7 +875,7 @@ const Version = () => {
|
|||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<SingleUpload text={LL.UPLOAD_DROP_TEXT()} doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -842,6 +894,7 @@ const Version = () => {
|
|||||||
locale,
|
locale,
|
||||||
openInstallDialog,
|
openInstallDialog,
|
||||||
fetchDevVersion,
|
fetchDevVersion,
|
||||||
|
downloadOnly,
|
||||||
me.admin,
|
me.admin,
|
||||||
showButtons,
|
showButtons,
|
||||||
handleVersionInfoClose,
|
handleVersionInfoClose,
|
||||||
|
|||||||
@@ -53,12 +53,16 @@ const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
p={2}
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
borderRadius={1}
|
|
||||||
sx={{ backgroundColor, color: 'white', ...sx }}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 1,
|
||||||
|
backgroundColor,
|
||||||
|
color: 'white',
|
||||||
|
p: 2,
|
||||||
|
...sx
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Icon />
|
<Icon />
|
||||||
{(message || children) && (
|
{(message || children) && (
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
|||||||
() => (
|
() => (
|
||||||
<>
|
<>
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<Box display="flex" alignItems="center" px={2}>
|
<Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
||||||
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
||||||
<Typography variant="h6">{PROJECT_NAME}</Typography>
|
<Typography variant="h6">{PROJECT_NAME}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ const LayoutMenuComponent = () => {
|
|||||||
sx={{ my: 0 }}
|
sx={{ my: 0 }}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
primary: {
|
primary: {
|
||||||
fontWeight: '600',
|
sx: { fontWeight: 600, mb: '2px', color: 'lightblue' }
|
||||||
mb: '2px',
|
|
||||||
color: 'lightblue'
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,8 +32,16 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
|
<Box
|
||||||
<Box py={2}>
|
sx={{
|
||||||
|
m: 2,
|
||||||
|
py: 2,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'column'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
<CircularProgress size={100} />
|
<CircularProgress size={100} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
|
|||||||
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
|
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={{
|
||||||
alignItems="center"
|
display: 'flex',
|
||||||
justifyContent="center"
|
alignItems: 'center',
|
||||||
flexDirection="column"
|
justifyContent: 'center',
|
||||||
padding={2}
|
flexDirection: 'column',
|
||||||
height={height}
|
padding: 2,
|
||||||
|
height
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<CircularProgress sx={circularProgressStyles} size={100} />
|
<CircularProgress sx={circularProgressStyles} size={100} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
|
// drag/drop code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
|
||||||
import {
|
import {
|
||||||
type ChangeEvent,
|
type ChangeEvent,
|
||||||
type DragEvent,
|
type DragEvent,
|
||||||
@@ -6,12 +6,28 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
||||||
import UploadIcon from '@mui/icons-material/Upload';
|
import UploadIcon from '@mui/icons-material/Upload';
|
||||||
import { Box, Button, Typography, styled } from '@mui/material';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Typography,
|
||||||
|
styled
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { callAction } from 'api/app';
|
||||||
|
|
||||||
|
import { dialogStyle } from '@/CustomTheme';
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
|
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
|
||||||
@@ -58,6 +74,29 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
const [dragged, setDragged] = useState(false);
|
const [dragged, setDragged] = useState(false);
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
|
||||||
|
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
|
||||||
|
useState<number>(0);
|
||||||
|
|
||||||
|
const { send: checkUpgradeImportantMessages } = useRequest(
|
||||||
|
(version: string) =>
|
||||||
|
callAction({ action: 'upgradeImportantMessages', param: version }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onSuccess((event) => {
|
||||||
|
const upgradeImportantMessageType_n = (
|
||||||
|
event.data as { upgradeImportantMessageType: number }
|
||||||
|
).upgradeImportantMessageType;
|
||||||
|
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
|
||||||
|
if (upgradeImportantMessageType_n === 0) {
|
||||||
|
onFileSelected(file);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
const checkFileExtension = (file: File) => {
|
const checkFileExtension = (file: File) => {
|
||||||
const validExtensions = ['.json', '.bin', '.md5'];
|
const validExtensions = ['.json', '.bin', '.md5'];
|
||||||
@@ -97,9 +136,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
|
|
||||||
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
|
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (file) {
|
void checkUpgradeImportantMessages(file?.name || '');
|
||||||
onFileSelected(file);
|
setShowUpgradeDialog(true);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBrowseClick = () => {
|
const handleBrowseClick = () => {
|
||||||
@@ -158,6 +196,55 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
{showUpgradeDialog && upgradeImportantMessageType > 0 && (
|
||||||
|
<Dialog
|
||||||
|
sx={dialogStyle}
|
||||||
|
open={showUpgradeDialog}
|
||||||
|
onClose={() => setShowUpgradeDialog(false)}
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
<WarningIcon
|
||||||
|
color="warning"
|
||||||
|
sx={{ fontSize: 18, verticalAlign: 'middle' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{LL.UPGRADE_IMPORTANT_MESSAGES()}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{upgradeImportantMessageType === 1 &&
|
||||||
|
LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||||
|
{upgradeImportantMessageType === 2 &&
|
||||||
|
LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||||
|
<Typography sx={{ mt: 2 }}>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||||
|
style={{ color: 'lightblue' }}
|
||||||
|
>
|
||||||
|
{LL.ONLINE_HELP()}
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setShowUpgradeDialog(false)}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<UploadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => onFileSelected(file)}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.UPLOAD()}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DocumentUploader>
|
</DocumentUploader>
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ import DragNdrop from './DragNdrop';
|
|||||||
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
||||||
|
|
||||||
interface SingleUploadProps {
|
interface SingleUploadProps {
|
||||||
text: string;
|
|
||||||
doRestart: () => void;
|
doRestart: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
const SingleUpload = ({ doRestart }: SingleUploadProps) => {
|
||||||
const [md5, setMd5] = useState<string>();
|
const [md5, setMd5] = useState<string>();
|
||||||
const [file, setFile] = useState<File>();
|
const [file, setFile] = useState<File>();
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -58,7 +57,7 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
|||||||
<>
|
<>
|
||||||
{isUploading ? (
|
{isUploading ? (
|
||||||
<>
|
<>
|
||||||
<Box width="100%" pl={2} pr={2}>
|
<Box sx={{ width: '100%', pl: 2, pr: 2 }}>
|
||||||
<LinearProgressWithLabel
|
<LinearProgressWithLabel
|
||||||
value={
|
value={
|
||||||
progress.total === 0 || progress.loaded === 0
|
progress.total === 0 || progress.loaded === 0
|
||||||
@@ -81,11 +80,11 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<DragNdrop text={text} onFileSelected={setFile} />
|
<DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{md5 && (
|
{md5 && (
|
||||||
<Box mt={2}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const cz: Translation = {
|
|||||||
NO_DATA: 'Žádná data',
|
NO_DATA: 'Žádná data',
|
||||||
USER_PROFILE: 'Uživatelský profil',
|
USER_PROFILE: 'Uživatelský profil',
|
||||||
STORED_VERSIONS: 'Uložené verze',
|
STORED_VERSIONS: 'Uložené verze',
|
||||||
ONLINE_HELP: 'online nápověda'
|
ONLINE_HELP: 'online nápověda',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovat důležité zprávy',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Tato aktualizace vyžaduje obnovení továrního nastavení. Ujistěte se, že jste vytvořili zálohu své konfigurace a nastavení před pokračováním a nahrajte ji po instalaci nové verze.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default cz;
|
export default cz;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const de: Translation = {
|
|||||||
NO_DATA: 'Keine Daten',
|
NO_DATA: 'Keine Daten',
|
||||||
USER_PROFILE: 'Benutzerprofil',
|
USER_PROFILE: 'Benutzerprofil',
|
||||||
STORED_VERSIONS: 'Gespeicherte Versionen',
|
STORED_VERSIONS: 'Gespeicherte Versionen',
|
||||||
ONLINE_HELP: 'Online-Hilfe'
|
ONLINE_HELP: 'Online-Hilfe',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Diese Aktualisierung erfordert eine Werkseinstellung. Stellen Sie sicher, dass Sie eine Sicherung Ihrer Konfiguration und Einstellungen vor dem Fortfahren erstellt haben und diese nach der Installation der neuen Version hochladen.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und -einstellungen erstellen. Alle Passwörter werden im Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const en: Translation = {
|
|||||||
NO_DATA: 'No data',
|
NO_DATA: 'No data',
|
||||||
USER_PROFILE: 'User Profile',
|
USER_PROFILE: 'User Profile',
|
||||||
STORED_VERSIONS: 'Stored Versions',
|
STORED_VERSIONS: 'Stored Versions',
|
||||||
ONLINE_HELP: 'online help'
|
ONLINE_HELP: 'online help',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Important Messages',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'This upgrade requires a factory reset. Make sure you have made a backup of your configuration and settings before continuing, and upload this after the new version is installed.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const fr: Translation = {
|
|||||||
NO_DATA: 'Aucune donnée',
|
NO_DATA: 'Aucune donnée',
|
||||||
USER_PROFILE: 'Profil utilisateur',
|
USER_PROFILE: 'Profil utilisateur',
|
||||||
STORED_VERSIONS: 'Versions stockées',
|
STORED_VERSIONS: 'Versions stockées',
|
||||||
ONLINE_HELP: 'aide en ligne'
|
ONLINE_HELP: 'aide en ligne',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Mettre à jour les messages importants',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Cette mise à jour nécessite une réinitialisation de fabrique. Assurez-vous d\'avoir créé une sauvegarde de vos configurations et paramètres avant de continuer et de la charger après l\'installation de la nouvelle version.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const it: Translation = {
|
|||||||
NO_DATA: 'Nessun dato',
|
NO_DATA: 'Nessun dato',
|
||||||
USER_PROFILE: 'Profilo utente',
|
USER_PROFILE: 'Profilo utente',
|
||||||
STORED_VERSIONS: 'Versioni memorizzate',
|
STORED_VERSIONS: 'Versioni memorizzate',
|
||||||
ONLINE_HELP: 'aiuto online'
|
ONLINE_HELP: 'aiuto online',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aggiorna Messaggi Importanti',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Questa aggiornamento richiede un ripristino di fabbrica. Assicurati di aver creato un backup delle tue configurazioni e impostazioni prima di continuare e di caricarlo dopo l\'installazione della nuova versione.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const nl: Translation = {
|
|||||||
NO_DATA: 'Geen data',
|
NO_DATA: 'Geen data',
|
||||||
USER_PROFILE: 'Gebruikersprofiel',
|
USER_PROFILE: 'Gebruikersprofiel',
|
||||||
STORED_VERSIONS: 'Opgeslagen versies',
|
STORED_VERSIONS: 'Opgeslagen versies',
|
||||||
ONLINE_HELP: 'online help'
|
ONLINE_HELP: 'online help',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Belangrijke Berichten',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Deze upgrade vereist een fabrieksinstelling. Zorg ervoor dat u een back-up van uw configuratie en instellingen hebt gemaakt voordat u doorgaat en upload deze na de installatie van de nieuwe versie.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const no: Translation = {
|
|||||||
NO_DATA: 'Ingen data',
|
NO_DATA: 'Ingen data',
|
||||||
USER_PROFILE: 'Brukerprofil',
|
USER_PROFILE: 'Brukerprofil',
|
||||||
STORED_VERSIONS: 'Lagret versjoner',
|
STORED_VERSIONS: 'Lagret versjoner',
|
||||||
ONLINE_HELP: 'online hjelp'
|
ONLINE_HELP: 'online hjelp',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Oppdater viktige meldinger',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denne oppdateringen krever en fabriksinstilling. Sørg for at du har laget en sikkerhetskopi av din konfigurasjon og innstillinger før du fortsetter, og last denne opp etter at den nye versjonen er installert.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const pl: BaseTranslation = {
|
|||||||
NO_DATA: 'Brak danych',
|
NO_DATA: 'Brak danych',
|
||||||
USER_PROFILE: 'Profil użytkownika',
|
USER_PROFILE: 'Profil użytkownika',
|
||||||
STORED_VERSIONS: 'Zapisane wersje',
|
STORED_VERSIONS: 'Zapisane wersje',
|
||||||
ONLINE_HELP: 'pomoc online'
|
ONLINE_HELP: 'pomoc online',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizuj ważne wiadomości',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Ta aktualizacja wymaga resetu fabrycznego. Upewnij się, że masz utworzoną kopię swoich ustawień i konfiguracji przed kontynuowaniem i przesuń ją po zainstalowaniu nowej wersji.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const sk: Translation = {
|
|||||||
LANGUAGE: 'Jazyk',
|
LANGUAGE: 'Jazyk',
|
||||||
RETRY: 'Opakovať',
|
RETRY: 'Opakovať',
|
||||||
LOADING: 'Načítanie',
|
LOADING: 'Načítanie',
|
||||||
IS_REQUIRED: '{0} je požadovaných',
|
IS_REQUIRED: '{0} je požadovaná',
|
||||||
SIGN_IN: 'Prihlásiť sa',
|
SIGN_IN: 'Prihlásiť sa',
|
||||||
SIGN_OUT: 'Odhlásiť sa',
|
SIGN_OUT: 'Odhlásiť sa',
|
||||||
USERNAME: 'Užívateľské meno',
|
USERNAME: 'Užívateľské meno',
|
||||||
@@ -276,11 +276,11 @@ const sk: Translation = {
|
|||||||
NETWORK_SUBNET: 'Maska podsiete',
|
NETWORK_SUBNET: 'Maska podsiete',
|
||||||
NETWORK_DNS: 'DNS servery',
|
NETWORK_DNS: 'DNS servery',
|
||||||
ADDRESS_OF: '{0} adresa',
|
ADDRESS_OF: '{0} adresa',
|
||||||
ADMINISTRATOR: 'Administrator',
|
ADMINISTRATOR: 'Administrátor',
|
||||||
GUEST: 'Hosť',
|
GUEST: 'Hosť',
|
||||||
NEW: 'Nová',
|
NEW: 'Novú',
|
||||||
NEW_NAME_OF: 'Nový názov {0}',
|
NEW_NAME_OF: 'Nový názov {0}',
|
||||||
ENTITY: 'entita',
|
ENTITY: 'entitu',
|
||||||
MIN: 'min',
|
MIN: 'min',
|
||||||
MAX: 'max',
|
MAX: 'max',
|
||||||
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
|
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
|
||||||
@@ -359,7 +359,12 @@ const sk: Translation = {
|
|||||||
NO_DATA: 'Žiadne dáta',
|
NO_DATA: 'Žiadne dáta',
|
||||||
USER_PROFILE: 'Profil používateľa',
|
USER_PROFILE: 'Profil používateľa',
|
||||||
STORED_VERSIONS: 'Uložené verzie',
|
STORED_VERSIONS: 'Uložené verzie',
|
||||||
ONLINE_HELP: 'online pomoc'
|
ONLINE_HELP: 'online pomoc',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovať dôležité správy',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Táto aktualizácia vyžaduje reštart základných nastavení. Uistite sa, že ste vytvorili zálohu svojich konfigurácií a nastavení pred pokračovaním a nahrajte ju po instalácii novej verzie.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const sv: Translation = {
|
|||||||
NO_DATA: 'Ingen data',
|
NO_DATA: 'Ingen data',
|
||||||
USER_PROFILE: 'Användarprofil',
|
USER_PROFILE: 'Användarprofil',
|
||||||
STORED_VERSIONS: 'Lagrad versioner',
|
STORED_VERSIONS: 'Lagrad versioner',
|
||||||
ONLINE_HELP: 'online hjälp'
|
ONLINE_HELP: 'online hjälp',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Uppdatera viktiga meddelanden',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denna uppdatering kräver en fabriksåterställning. Se till att du har gjort en säkerhetskopia av din konfiguration och inställningar innan du fortsätter och ladda upp denna efter att den nya versionen är installerad.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ const tr: Translation = {
|
|||||||
NO_DATA: 'Hiçbir veri yok',
|
NO_DATA: 'Hiçbir veri yok',
|
||||||
USER_PROFILE: 'Kullanıcı Profili',
|
USER_PROFILE: 'Kullanıcı Profili',
|
||||||
STORED_VERSIONS: 'Kaydedilmiş Sürümler',
|
STORED_VERSIONS: 'Kaydedilmiş Sürümler',
|
||||||
ONLINE_HELP: 'online yardım'
|
ONLINE_HELP: 'online yardım',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Önemli Mesajları Güncelle',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Bu güncelleme továrnı ayarlarını gerektirir. Yapılandırmanızı ve ayarlarınızı önce yedekleyin ve ardından yeni sürüm yüklendikten sonra yükleyin.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"itty-router": "^5.0.23",
|
"itty-router": "^5.0.23",
|
||||||
"prettier": "^3.8.1"
|
"prettier": "^3.8.3"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||||
}
|
}
|
||||||
|
|||||||
32
mock-api/pnpm-lock.yaml
generated
32
mock-api/pnpm-lock.yaml
generated
@@ -13,7 +13,7 @@ importers:
|
|||||||
version: 3.1.3
|
version: 3.1.3
|
||||||
'@trivago/prettier-plugin-sort-imports':
|
'@trivago/prettier-plugin-sort-imports':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(prettier@3.8.1)
|
version: 6.0.2(prettier@3.8.3)
|
||||||
formidable:
|
formidable:
|
||||||
specifier: ^3.5.4
|
specifier: ^3.5.4
|
||||||
version: 3.5.4
|
version: 3.5.4
|
||||||
@@ -21,8 +21,8 @@ importers:
|
|||||||
specifier: ^5.0.23
|
specifier: ^5.0.23
|
||||||
version: 5.0.23
|
version: 5.0.23
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.8.1
|
specifier: ^3.8.3
|
||||||
version: 3.8.1
|
version: 3.8.3
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -112,8 +112,8 @@ packages:
|
|||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
brace-expansion@2.0.3:
|
brace-expansion@2.1.0:
|
||||||
resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==}
|
resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
|
||||||
|
|
||||||
debug@4.4.3:
|
debug@4.4.3:
|
||||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
@@ -145,8 +145,8 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
lodash-es@4.17.23:
|
lodash-es@4.18.1:
|
||||||
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
|
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
|
||||||
|
|
||||||
minimatch@9.0.9:
|
minimatch@9.0.9:
|
||||||
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
||||||
@@ -167,8 +167,8 @@ packages:
|
|||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
prettier@3.8.1:
|
prettier@3.8.3:
|
||||||
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
|
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -246,17 +246,17 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.8.0
|
'@noble/hashes': 1.8.0
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.1)':
|
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 7.29.1
|
'@babel/generator': 7.29.1
|
||||||
'@babel/parser': 7.29.2
|
'@babel/parser': 7.29.2
|
||||||
'@babel/traverse': 7.29.0
|
'@babel/traverse': 7.29.0
|
||||||
'@babel/types': 7.29.0
|
'@babel/types': 7.29.0
|
||||||
javascript-natural-sort: 0.7.1
|
javascript-natural-sort: 0.7.1
|
||||||
lodash-es: 4.17.23
|
lodash-es: 4.18.1
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
parse-imports-exports: 0.2.4
|
parse-imports-exports: 0.2.4
|
||||||
prettier: 3.8.1
|
prettier: 3.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ snapshots:
|
|||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
brace-expansion@2.0.3:
|
brace-expansion@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
||||||
@@ -291,11 +291,11 @@ snapshots:
|
|||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
lodash-es@4.17.23: {}
|
lodash-es@4.18.1: {}
|
||||||
|
|
||||||
minimatch@9.0.9:
|
minimatch@9.0.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.0.3
|
brace-expansion: 2.1.0
|
||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
@@ -311,6 +311,6 @@ snapshots:
|
|||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
prettier@3.8.1: {}
|
prettier@3.8.3: {}
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|||||||
@@ -388,13 +388,34 @@ function custom_support() {
|
|||||||
'',
|
'',
|
||||||
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
|
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
|
||||||
],
|
],
|
||||||
img_url: 'https://emsesp.org/_media/images/designer.png'
|
img_url: 'https://emsesp.org/media/images/designer.png'
|
||||||
// img_url: 'https://picsum.photos/200/300'
|
// img_url: 'https://picsum.photos/200/300'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// called by Action endpoint
|
// called by Action endpoint upgradeImportantMessages
|
||||||
|
function upgradeImportantMessages(version: string) {
|
||||||
|
// 0 is do nothing
|
||||||
|
// 1 means 3.9 and factory reset required
|
||||||
|
// 2 means a major version upgrade
|
||||||
|
let upgradeImportantMessageType_n = 0;
|
||||||
|
|
||||||
|
// see if its a filename with a .bin extension
|
||||||
|
if (version.endsWith('.bin')) {
|
||||||
|
upgradeImportantMessageType_n = 1; // 1 means 3.9 and factory reset required
|
||||||
|
} else if (version.endsWith('.md')) {
|
||||||
|
upgradeImportantMessageType_n = 0;
|
||||||
|
} else {
|
||||||
|
// this is a version string like "3.9.0"
|
||||||
|
upgradeImportantMessageType_n = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('upgradeImportantMessageType: ' + upgradeImportantMessageType_n);
|
||||||
|
return { upgradeImportantMessageType: upgradeImportantMessageType_n };
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by Action endpoint checkUpgrade
|
||||||
function check_upgrade(version: string) {
|
function check_upgrade(version: string) {
|
||||||
let data = {};
|
let data = {};
|
||||||
if (version) {
|
if (version) {
|
||||||
@@ -5170,6 +5191,9 @@ router
|
|||||||
// set partition
|
// set partition
|
||||||
console.log('setting partition to', content.param);
|
console.log('setting partition to', content.param);
|
||||||
return status(200);
|
return status(200);
|
||||||
|
} else if (action === 'upgradeImportantMessages') {
|
||||||
|
// check upgrade important messages
|
||||||
|
return upgradeImportantMessages(content.param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status(404); // cmd not found
|
return status(404); // cmd not found
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ framework = arduino
|
|||||||
board_build.partitions = partitions/esp32_partition_4M.csv
|
board_build.partitions = partitions/esp32_partition_4M.csv
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.app_partition_name = app0
|
board_build.app_partition_name = app0
|
||||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.03.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.7 based on IDF 5.5.3.260313
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
; 16MB Flash variants
|
; 16MB Flash variants
|
||||||
[espressif32_base_16M]
|
[espressif32_base_16M]
|
||||||
@@ -67,7 +67,7 @@ framework = arduino
|
|||||||
board_build.partitions = partitions/esp32_partition_16M.csv
|
board_build.partitions = partitions/esp32_partition_16M.csv
|
||||||
board_upload.flash_size = 16MB
|
board_upload.flash_size = 16MB
|
||||||
board_build.app_partition_name = app0
|
board_build.app_partition_name = app0
|
||||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.03.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.7 based on IDF 5.5.3.260313
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
; 32MB Flash variants
|
; 32MB Flash variants
|
||||||
[espressif32_base_32M]
|
[espressif32_base_32M]
|
||||||
@@ -75,7 +75,7 @@ framework = arduino
|
|||||||
board_build.partitions = partitions/esp32_partition_32M.csv
|
board_build.partitions = partitions/esp32_partition_32M.csv
|
||||||
board_upload.flash_size = 32MB
|
board_upload.flash_size = 32MB
|
||||||
board_build.app_partition_name = app0
|
board_build.app_partition_name = app0
|
||||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.03.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.7 based on IDF 5.5.3.260313
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
build_flags =
|
build_flags =
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UploadFileService::handleEarlyDisconnect() {
|
void UploadFileService::handleEarlyDisconnect() {
|
||||||
emsesp::EMSESP::logger().info("Upload completed");
|
emsesp::EMSESP::logger().info("Upload finished");
|
||||||
emsesp::EMSESP::system_.uart_init(); // re-enable UART
|
emsesp::EMSESP::system_.uart_init(); // re-enable UART
|
||||||
|
|
||||||
_is_firmware = false;
|
_is_firmware = false;
|
||||||
|
|||||||
@@ -38,15 +38,6 @@
|
|||||||
#include "../test/test.h"
|
#include "../test/test.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_TLS_SUPPORT
|
|
||||||
#define ENABLE_SMTP
|
|
||||||
#define USE_ESP_SSLCLIENT
|
|
||||||
#define READYCLIENT_SSL_CLIENT ESP_SSLClient
|
|
||||||
#define READYCLIENT_TYPE_1 // TYPE 1 when using ESP_SSLClient
|
|
||||||
#include <ESP_SSLClient.h>
|
|
||||||
#include <ReadyMail.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
// Languages supported. Note: the order is important
|
// Languages supported. Note: the order is important
|
||||||
@@ -115,110 +106,6 @@ bool System::command_send(const char * value, const int8_t id) {
|
|||||||
return EMSESP::txservice_.send_raw(value); // ignore id
|
return EMSESP::txservice_.send_raw(value); // ignore id
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::command_sendmail(const char * value, const int8_t id) {
|
|
||||||
bool enabled = false;
|
|
||||||
bool ssl, starttls;
|
|
||||||
uint16_t port;
|
|
||||||
String server, login, pass, sender, recp, subject;
|
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
|
||||||
enabled = settings.email_enabled;
|
|
||||||
ssl = settings.email_ssl;
|
|
||||||
starttls = settings.email_starttls;
|
|
||||||
server = settings.email_server;
|
|
||||||
port = settings.email_port;
|
|
||||||
login = settings.email_login;
|
|
||||||
pass = settings.email_pass;
|
|
||||||
sender = settings.email_sender;
|
|
||||||
recp = settings.email_recp;
|
|
||||||
subject = settings.email_subject;
|
|
||||||
});
|
|
||||||
if (!enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("Command sendmail port %d%s called with '%s'", port, ssl ? " (SSL)" : starttls ? " (STARTTLS)" : "", value);
|
|
||||||
// LOG_DEBUG("Command sendmail port %d called with '%s'", port, value);
|
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
#ifndef NO_TLS_SUPPORT
|
|
||||||
WiFiClient * basic_client;
|
|
||||||
ESP_SSLClient * ssl_client;
|
|
||||||
ReadyClient * r_client; // rClient(ssl_client);
|
|
||||||
SMTPClient * smtp; // smtp(rClient);
|
|
||||||
basic_client = new WiFiClient;
|
|
||||||
ssl_client = new ESP_SSLClient;
|
|
||||||
r_client = new ReadyClient(*ssl_client);
|
|
||||||
smtp = new SMTPClient(*r_client);
|
|
||||||
|
|
||||||
ssl_client->setClient(basic_client);
|
|
||||||
ssl_client->setInsecure();
|
|
||||||
ssl_client->setBufferSizes(1024, 1024);
|
|
||||||
r_client->addPort(port, starttls ? readymail_protocol_tls : ssl ? readymail_protocol_ssl : readymail_protocol_plain_text);
|
|
||||||
|
|
||||||
// smtp->connect(server, port, sendmailCallback);
|
|
||||||
smtp->connect(server, port);
|
|
||||||
if (!smtp->isConnected()) {
|
|
||||||
LOG_ERROR("Sendmail connection error");
|
|
||||||
delete smtp;
|
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOG_INFO("authenticate %s:%s", login.c_str(), pass.c_str());
|
|
||||||
smtp->authenticate(login, pass, readymail_auth_password);
|
|
||||||
if (!smtp->isAuthenticated()) {
|
|
||||||
LOG_ERROR("Sendmail authenticate error");
|
|
||||||
delete smtp;
|
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonDocument doc;
|
|
||||||
String body = value;
|
|
||||||
if (body.length()) {
|
|
||||||
auto error = deserializeJson(doc, (const char *)value);
|
|
||||||
if (!error && doc.as<JsonObject>().size() >= 0) {
|
|
||||||
subject = doc["subject"] | subject;
|
|
||||||
recp = doc["to"] | recp;
|
|
||||||
sender = doc["from"] | sender;
|
|
||||||
body = doc["body"] | body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SMTPMessage & msg = smtp->getMessage();
|
|
||||||
msg.headers.add(rfc822_subject, subject);
|
|
||||||
msg.headers.add(rfc822_from, sender);
|
|
||||||
msg.headers.add(rfc822_to, recp);
|
|
||||||
|
|
||||||
// Use addCustom to add custom header e.g. Importance and Priority.
|
|
||||||
// msg.headers.addCustom("Importance", PRIORITY);
|
|
||||||
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
|
|
||||||
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
|
|
||||||
|
|
||||||
msg.text.body(body);
|
|
||||||
|
|
||||||
// bodyText.replace("\r\n", "<br>\r\n");
|
|
||||||
// msg.html.body("<html><body><div style=\"color:#cc0066;\">" + bodyText + "</div></body></html>");
|
|
||||||
// msg.html.transferEncoding("base64");
|
|
||||||
|
|
||||||
// With embedFile function, the html message will send as attachment.
|
|
||||||
// if (EMBED_MESSAGE)
|
|
||||||
// msg.html.embedFile(true, "msg.html", embed_message_type_attachment);
|
|
||||||
|
|
||||||
msg.timestamp = time(nullptr);
|
|
||||||
|
|
||||||
success = smtp->send(msg);
|
|
||||||
|
|
||||||
delete smtp;
|
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
#endif
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return string of languages and count
|
// return string of languages and count
|
||||||
std::string System::languages_string() {
|
std::string System::languages_string() {
|
||||||
std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
|
std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
|
||||||
@@ -309,26 +196,26 @@ bool System::command_publish(const char * value, const int8_t id) {
|
|||||||
// syslog level
|
// syslog level
|
||||||
// commenting this out - don't see the point on having an API service to change the syslog level
|
// commenting this out - don't see the point on having an API service to change the syslog level
|
||||||
/*
|
/*
|
||||||
bool System::command_syslog_level(const char * value, const int8_t id) {
|
bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||||
uint8_t s = 0xff;
|
uint8_t s = 0xff;
|
||||||
if (Helpers::value2enum(value, s, FL_(list_syslog_level))) {
|
if (Helpers::value2enum(value, s, FL_(list_syslog_level))) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
EMSESP::webSettingsService.update(
|
EMSESP::webSettingsService.update(
|
||||||
[&](WebSettings & settings) {
|
[&](WebSettings & settings) {
|
||||||
if (settings.syslog_level != (int8_t)s - 1) {
|
if (settings.syslog_level != (int8_t)s - 1) {
|
||||||
settings.syslog_level = (int8_t)s - 1;
|
settings.syslog_level = (int8_t)s - 1;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
if (changed) {
|
if (changed) {
|
||||||
EMSESP::system_.syslog_init();
|
EMSESP::system_.syslog_init();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// send message - to system log and MQTT
|
// send message - to system log and MQTT
|
||||||
bool System::command_message(const char * value, const int8_t id, JsonObject output) {
|
bool System::command_message(const char * value, const int8_t id, JsonObject output) {
|
||||||
@@ -576,6 +463,7 @@ void System::system_restart(const char * partitionname) {
|
|||||||
|
|
||||||
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
||||||
EMSuart::stop(); // stop UART so there is no interference
|
EMSuart::stop(); // stop UART so there is no interference
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
delay(1000); // wait 1 second
|
delay(1000); // wait 1 second
|
||||||
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
|
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
|
||||||
@@ -677,6 +565,7 @@ void System::store_settings(WebSettings & settings) {
|
|||||||
|
|
||||||
locale_ = settings.locale;
|
locale_ = settings.locale;
|
||||||
developer_mode_ = settings.developer_mode;
|
developer_mode_ = settings.developer_mode;
|
||||||
|
|
||||||
// start services
|
// start services
|
||||||
if (settings.modbus_enabled) {
|
if (settings.modbus_enabled) {
|
||||||
if (EMSESP::modbus_ == nullptr) {
|
if (EMSESP::modbus_ == nullptr) {
|
||||||
@@ -718,11 +607,20 @@ void System::start() {
|
|||||||
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
||||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
temp_sensor_config_t temp_sensor = TSENS_CONFIG_DEFAULT();
|
||||||
|
temp_sensor_get_config(&temp_sensor);
|
||||||
|
temp_sensor.dac_offset = TSENS_DAC_DEFAULT; // DEFAULT: range:-10℃ ~ 80℃, error < 1℃.
|
||||||
|
temp_sensor_set_config(temp_sensor);
|
||||||
|
temp_sensor_start();
|
||||||
|
temp_sensor_read_celsius(&temperature_);
|
||||||
|
#else
|
||||||
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
||||||
temperature_sensor_install(&temp_sensor_config, &temperature_handle_);
|
temperature_sensor_install(&temp_sensor_config, &temperature_handle_);
|
||||||
temperature_sensor_enable(temperature_handle_);
|
temperature_sensor_enable(temperature_handle_);
|
||||||
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||||
@@ -924,14 +822,14 @@ void System::send_info_mqtt() {
|
|||||||
doc["network"] = "ethernet";
|
doc["network"] = "ethernet";
|
||||||
doc["hostname"] = ETH.getHostname();
|
doc["hostname"] = ETH.getHostname();
|
||||||
/*
|
/*
|
||||||
doc["MAC"] = ETH.macAddress();
|
doc["MAC"] = ETH.macAddress();
|
||||||
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||||
doc["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
doc["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
||||||
doc["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
doc["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
||||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
|
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
|
doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
} else if (WiFi.status() == WL_CONNECTED) {
|
} else if (WiFi.status() == WL_CONNECTED) {
|
||||||
doc["network"] = "wifi";
|
doc["network"] = "wifi";
|
||||||
@@ -943,9 +841,16 @@ void System::send_info_mqtt() {
|
|||||||
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||||
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
|
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
|
||||||
|
doc["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
|
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
|
||||||
@@ -1054,8 +959,13 @@ void System::network_init() {
|
|||||||
delay(500);
|
delay(500);
|
||||||
digitalWrite(eth_power_, HIGH);
|
digitalWrite(eth_power_, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
eth_present_ = ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||||
|
#else
|
||||||
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
|
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// check health of system, done every 5 seconds
|
// check health of system, done every 5 seconds
|
||||||
@@ -1066,9 +976,13 @@ void System::system_check() {
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
temp_sensor_read_celsius(&temperature_);
|
||||||
|
#else
|
||||||
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef EMSESP_PINGTEST
|
#ifdef EMSESP_PINGTEST
|
||||||
static uint64_t ping_count = 0;
|
static uint64_t ping_count = 0;
|
||||||
@@ -1120,7 +1034,6 @@ void System::commands_init() {
|
|||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(read), System::command_read, FL_(read_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(read), System::command_read, FL_(read_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_(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);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(sendmail), System::command_sendmail, FL_(sendmail_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_(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_(format), System::command_format, FL_(format_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(txpause), System::command_txpause, FL_(txpause_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(txpause), System::command_txpause, FL_(txpause_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
@@ -1362,9 +1275,16 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
|
||||||
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.localIPv6()).c_str());
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_CONNECT_FAILED:
|
case WL_CONNECT_FAILED:
|
||||||
@@ -1395,9 +1315,15 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
|
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
|
||||||
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.localIPv6()).c_str());
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
shell.println();
|
shell.println();
|
||||||
|
|
||||||
@@ -1419,6 +1345,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shell.println();
|
shell.println();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1722,6 +1649,7 @@ bool System::check_upgrade() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map each config filename to its human-readable section key
|
// map each config filename to its human-readable section key
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
||||||
{NETWORK_SETTINGS_FILE, "Network"},
|
{NETWORK_SETTINGS_FILE, "Network"},
|
||||||
{AP_SETTINGS_FILE, "AP"},
|
{AP_SETTINGS_FILE, "AP"},
|
||||||
@@ -1734,6 +1662,7 @@ static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
|||||||
{EMSESP_CUSTOMENTITY_FILE, "Entities"},
|
{EMSESP_CUSTOMENTITY_FILE, "Entities"},
|
||||||
{EMSESP_MODULES_FILE, "Modules"},
|
{EMSESP_MODULES_FILE, "Modules"},
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// convert a single config file into a section of the output json object
|
// convert a single config file into a section of the output json object
|
||||||
void System::exportSettings(const std::string & type, const char * filename, JsonObject output) {
|
void System::exportSettings(const std::string & type, const char * filename, JsonObject output) {
|
||||||
@@ -1834,29 +1763,38 @@ void System::exportSystemBackup(JsonObject output) {
|
|||||||
|
|
||||||
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
|
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
|
||||||
nvs_iterator_t it = nullptr;
|
nvs_iterator_t it = nullptr;
|
||||||
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
it = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY);
|
||||||
|
if (it == nullptr) {
|
||||||
|
#else
|
||||||
|
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
|
#endif
|
||||||
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
|
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonArray entries = node["nvs"].to<JsonArray>();
|
JsonArray entries = node["nvs"].to<JsonArray>();
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
while (it != nullptr) {
|
||||||
|
nvs_entry_info_t info;
|
||||||
|
nvs_entry_info(it, &info);
|
||||||
|
#else
|
||||||
while (err == ESP_OK) {
|
while (err == ESP_OK) {
|
||||||
nvs_entry_info_t info;
|
nvs_entry_info_t info;
|
||||||
nvs_entry_info(it, &info);
|
nvs_entry_info(it, &info);
|
||||||
|
#endif
|
||||||
JsonObject entry = entries.add<JsonObject>();
|
JsonObject entry = entries.add<JsonObject>();
|
||||||
entry["type"] = info.type; // e.g. NVS_TYPE_U32 or NVS_TYPE_STR etc
|
entry["type"] = info.type;
|
||||||
entry["key"] = info.key;
|
entry["key"] = info.key;
|
||||||
|
|
||||||
LOG_DEBUG("Exporting NVS value: %s = %d", info.key, info.type);
|
LOG_DEBUG("Exporting NVS value: %s = %d", info.key, info.type);
|
||||||
|
|
||||||
// serialize based on the type. We use putString, putChar, putUChar, putDouble, putBool, putULong only
|
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
case NVS_TYPE_I8:
|
case NVS_TYPE_I8:
|
||||||
entry["value"] = EMSESP::nvs_.getChar(info.key);
|
entry["value"] = EMSESP::nvs_.getChar(info.key);
|
||||||
break;
|
break;
|
||||||
case NVS_TYPE_U8:
|
case NVS_TYPE_U8:
|
||||||
// also used for bool
|
|
||||||
entry["value"] = EMSESP::nvs_.getUChar(info.key);
|
entry["value"] = EMSESP::nvs_.getUChar(info.key);
|
||||||
break;
|
break;
|
||||||
case NVS_TYPE_I32:
|
case NVS_TYPE_I32:
|
||||||
@@ -1872,19 +1810,22 @@ void System::exportSystemBackup(JsonObject output) {
|
|||||||
entry["value"] = EMSESP::nvs_.getULong64(info.key);
|
entry["value"] = EMSESP::nvs_.getULong64(info.key);
|
||||||
break;
|
break;
|
||||||
case NVS_TYPE_BLOB:
|
case NVS_TYPE_BLOB:
|
||||||
// used for double (e.g. sensor values, nrgheat, nrgww), and stored as bytes in NVS
|
entry["value"] = EMSESP::nvs_.getDouble(info.key); // bytes used for double values
|
||||||
entry["value"] = EMSESP::nvs_.getDouble(info.key);
|
|
||||||
break;
|
break;
|
||||||
case NVS_TYPE_STR:
|
case NVS_TYPE_STR:
|
||||||
case NVS_TYPE_ANY:
|
case NVS_TYPE_ANY:
|
||||||
default:
|
default:
|
||||||
// any other value we store as a string
|
|
||||||
entry["value"] = EMSESP::nvs_.getString(info.key);
|
entry["value"] = EMSESP::nvs_.getString(info.key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
it = nvs_entry_next(it);
|
||||||
|
}
|
||||||
|
#else
|
||||||
err = nvs_entry_next(&it);
|
err = nvs_entry_next(&it);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (it != nullptr) {
|
if (it != nullptr) {
|
||||||
nvs_release_iterator(it);
|
nvs_release_iterator(it);
|
||||||
@@ -2686,12 +2627,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
|||||||
node["maxWebLogBuffer"] = settings.weblog_buffer;
|
node["maxWebLogBuffer"] = settings.weblog_buffer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#if defined(EMSESP_UNITY)
|
#if defined(EMSESP_UNITY)
|
||||||
node["webLogBuffer"] = 0;
|
node["webLogBuffer"] = 0;
|
||||||
#else
|
#else
|
||||||
node["webLogBuffer"] = EMSESP::webLogService.num_log_messages();
|
node["webLogBuffer"] = EMSESP::webLogService.num_log_messages();
|
||||||
#endif
|
#endif
|
||||||
*/
|
*/
|
||||||
node["modbusEnabled"] = settings.modbus_enabled;
|
node["modbusEnabled"] = settings.modbus_enabled;
|
||||||
node["forceHeatingOff"] = settings.boiler_heatingoff;
|
node["forceHeatingOff"] = settings.boiler_heatingoff;
|
||||||
node["developerMode"] = settings.developer_mode;
|
node["developerMode"] = settings.developer_mode;
|
||||||
@@ -3319,7 +3260,7 @@ void System::set_valid_system_gpios() {
|
|||||||
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
|
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
|
||||||
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||||
// excluded:
|
// excluded:
|
||||||
// GPIO26 - GPIO32 = SPI flash and PSRAM
|
// GPIO26 - GPIO32 = SPI flash and PSRAM
|
||||||
// GPIO45 - GPIO46 = strapping pins
|
// GPIO45 - GPIO46 = strapping pins
|
||||||
@@ -3332,7 +3273,7 @@ void System::set_valid_system_gpios() {
|
|||||||
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
|
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
|
||||||
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||||
// excluded:
|
// excluded:
|
||||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||||
@@ -3351,7 +3292,7 @@ void System::set_valid_system_gpios() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32
|
#elif CONFIG_IDF_TARGET_ESP32
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||||
// excluded:
|
// excluded:
|
||||||
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
|
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
|
||||||
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
|
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
|
||||||
@@ -3382,10 +3323,6 @@ void System::set_valid_system_gpios() {
|
|||||||
} else {
|
} else {
|
||||||
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
|
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
|
||||||
}
|
}
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32c6/api-reference/peripherals/gpio.html
|
|
||||||
// 24-30 used for flash, 12-13 USB, 16-17 uart0
|
|
||||||
valid_system_gpios_ = string_range_to_vector("0-30", "12-13, 16-17, 24-30");
|
|
||||||
#elif defined(EMSESP_STANDALONE)
|
#elif defined(EMSESP_STANDALONE)
|
||||||
valid_system_gpios_ = string_range_to_vector("0-39");
|
valid_system_gpios_ = string_range_to_vector("0-39");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.8.2-dev.C14"
|
#define EMSESP_APP_VERSION "3.8.2-dev.C13"
|
||||||
|
|||||||
@@ -83,20 +83,6 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
|
|||||||
root["modbus_max_clients"] = settings.modbus_max_clients;
|
root["modbus_max_clients"] = settings.modbus_max_clients;
|
||||||
root["modbus_timeout"] = settings.modbus_timeout;
|
root["modbus_timeout"] = settings.modbus_timeout;
|
||||||
root["developer_mode"] = settings.developer_mode;
|
root["developer_mode"] = settings.developer_mode;
|
||||||
#ifndef NO_TLS_SUPPORT
|
|
||||||
root["email_enabled"] = settings.email_enabled;
|
|
||||||
#else
|
|
||||||
root["email_enabled"] = false;
|
|
||||||
#endif
|
|
||||||
root["email_ssl"] = settings.email_ssl;
|
|
||||||
root["email_starttls"] = settings.email_starttls;
|
|
||||||
root["email_server"] = settings.email_server;
|
|
||||||
root["email_port"] = settings.email_port;
|
|
||||||
root["email_login"] = settings.email_login;
|
|
||||||
root["email_pass"] = settings.email_pass;
|
|
||||||
root["email_sender"] = settings.email_sender;
|
|
||||||
root["email_recp"] = settings.email_recp;
|
|
||||||
root["email_subject"] = settings.email_subject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// call on initialization and also when settings are updated/saved via web or console
|
// call on initialization and also when settings are updated/saved via web or console
|
||||||
@@ -257,9 +243,13 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
|
|
||||||
// Modbus settings
|
// Modbus settings
|
||||||
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
||||||
|
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::RESTART);
|
||||||
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
||||||
|
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::RESTART);
|
||||||
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
||||||
|
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::RESTART);
|
||||||
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
||||||
|
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::RESTART);
|
||||||
|
|
||||||
//
|
//
|
||||||
// these may need mqtt restart to rebuild HA discovery topics
|
// these may need mqtt restart to rebuild HA discovery topics
|
||||||
@@ -310,20 +300,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
||||||
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
||||||
|
|
||||||
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
|
|
||||||
settings.email_ssl = root["email_ssl"] | FACTORY_EMAIL_SSL;
|
|
||||||
settings.email_starttls = root["email_starttls"] | FACTORY_EMAIL_STARTTLS;
|
|
||||||
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
|
|
||||||
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
|
|
||||||
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
|
|
||||||
settings.email_pass = root["email_pass"] | FACTORY_EMAIL_PASSWORD;
|
|
||||||
settings.email_sender = root["email_sender"] | FACTORY_EMAIL_FROM;
|
|
||||||
settings.email_recp = root["email_recp"] | FACTORY_EMAIL_TO;
|
|
||||||
settings.email_subject = root["email_subject"] | FACTORY_EMAIL_SUBJECT;
|
|
||||||
|
|
||||||
if (settings.email_ssl && settings.email_starttls) {
|
|
||||||
settings.email_ssl = false;
|
|
||||||
}
|
|
||||||
// if no psram limit weblog buffer to 25 messages
|
// if no psram limit weblog buffer to 25 messages
|
||||||
if (EMSESP::system_.PSram() > 0) {
|
if (EMSESP::system_.PSram() > 0) {
|
||||||
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
||||||
@@ -482,14 +458,23 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
|||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
// check for no PSRAM, could be a E32 or S32?
|
// check for no PSRAM, could be a E32 or S32?
|
||||||
if (!ESP.getPsramSize()) {
|
if (!ESP.getPsramSize()) {
|
||||||
|
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||||
|
if (ETH.begin(1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
|
||||||
|
#else
|
||||||
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
|
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
|
||||||
|
#endif
|
||||||
settings.board_profile = "E32"; // Ethernet without PSRAM
|
settings.board_profile = "E32"; // Ethernet without PSRAM
|
||||||
} else {
|
} else {
|
||||||
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
||||||
|
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||||
|
if (ETH.begin(0, 15, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_OUT)) {
|
||||||
|
#else
|
||||||
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
||||||
|
#endif
|
||||||
|
|
||||||
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||||
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
||||||
} else {
|
} else {
|
||||||
@@ -517,9 +502,9 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
|||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
uint32_t psram_size = ESP.getPsramSize() / 1024; // in KB
|
uint32_t psram_size = ESP.getPsramSize() / 1024; // in KB
|
||||||
if (psram_size > 0) {
|
if (psram_size > 0) {
|
||||||
EMSESP::logger().info("Loaded board profile %s, PSRAM: %lu KB", settings.board_profile.c_str(), psram_size);
|
EMSESP::logger().info("Loaded board profile %s (PSRAM: %lu KB)", settings.board_profile.c_str(), psram_size);
|
||||||
} else {
|
} else {
|
||||||
EMSESP::logger().info("Loaded board profile %s, PSRAM: not available", settings.board_profile.c_str());
|
EMSESP::logger().info("Loaded board profile %s (PSRAM: not available)", settings.board_profile.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,9 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
|
|||||||
} else if (action == "resetMQTT" && is_admin) {
|
} else if (action == "resetMQTT" && is_admin) {
|
||||||
EMSESP::mqtt_.reset_mqtt();
|
EMSESP::mqtt_.reset_mqtt();
|
||||||
ok = true;
|
ok = true;
|
||||||
|
} else if (action == "upgradeImportantMessages") {
|
||||||
|
root["upgradeImportantMessageType"] = upgradeImportantMessages(param);
|
||||||
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)
|
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)
|
||||||
@@ -237,13 +240,75 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// action = upgradeImportantMessages
|
||||||
|
// returns the type of upgrade important message to display in the UI
|
||||||
|
// 0 = no message (if just a minor version upgrade)
|
||||||
|
// 1 = going from <= 3.8 to 3.9 (has new partition layout)
|
||||||
|
// 2 = major version upgrade
|
||||||
|
// version can be like 3.8.2 or a filename like EMS-ESP-3_8_2-dev_13-ESP32-16MB+.bin
|
||||||
|
uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
||||||
|
if (version.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a filename with a .bin or .md extension, try and extract the version from it
|
||||||
|
// e.g. EMS-ESP-3_8_2-dev_13-ESP32-16MB+.bin -> major=3 minor=8 patch=2
|
||||||
|
version::Semver200_version latest_version;
|
||||||
|
if ((version.find(".bin") != std::string::npos) || (version.find(".md") != std::string::npos)) {
|
||||||
|
std::string filename = version;
|
||||||
|
auto pos = filename.find("EMS-ESP-");
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
EMSESP::logger().err("Invalid version string: %s", version.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += 8; // skip past "EMS-ESP-"
|
||||||
|
auto underscore1 = filename.find('_', pos);
|
||||||
|
auto underscore2 = filename.find('_', underscore1 + 1);
|
||||||
|
auto dash = filename.find('-', underscore2 + 1);
|
||||||
|
if (underscore1 == std::string::npos || underscore2 == std::string::npos || dash == std::string::npos) {
|
||||||
|
EMSESP::logger().err("Invalid version string: %s", version.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string major_version = filename.substr(pos, underscore1 - pos);
|
||||||
|
std::string minor_version = filename.substr(underscore1 + 1, underscore2 - underscore1 - 1);
|
||||||
|
std::string patch_version = filename.substr(underscore2 + 1, dash - underscore2 - 1);
|
||||||
|
latest_version = version::Semver200_version(major_version + "." + minor_version + "." + patch_version);
|
||||||
|
} else {
|
||||||
|
// if it's .json file exit
|
||||||
|
if (version.find(".json") != std::string::npos) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// treat it like a version string like "3.9.0"
|
||||||
|
latest_version = version::Semver200_version(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version::Semver200_version current_version(current_version_s); // get current version
|
||||||
|
|
||||||
|
if (latest_version > current_version && current_version.minor() < latest_version.minor()) {
|
||||||
|
return 0; // if it's just a minor version upgrade return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((current_version.major() <= 3 && current_version.minor() <= 8) && (latest_version.major() == 3 && latest_version.minor() == 9)) {
|
||||||
|
return 1; // if moving from below 3.8.x to 3.9.x return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latest_version > current_version && current_version.major() < latest_version.major()) {
|
||||||
|
return 2; // if it's a major version upgrade return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // if it's not a valid version upgrade return 0
|
||||||
|
}
|
||||||
|
|
||||||
// action = checkUpgrade
|
// action = checkUpgrade
|
||||||
// versions holds the latest development version and stable version in one string, comma separated
|
// versions holds the latest development version and stable version in one string, comma separated
|
||||||
bool WebStatusService::checkUpgrade(JsonObject root, std::string & versions) {
|
bool WebStatusService::checkUpgrade(JsonObject root, std::string & version) {
|
||||||
if (!versions.empty()) {
|
if (!version.empty()) {
|
||||||
version::Semver200_version current_version(current_version_s);
|
version::Semver200_version current_version(current_version_s);
|
||||||
version::Semver200_version latest_dev_version(versions.substr(0, versions.find(',')));
|
version::Semver200_version latest_dev_version(version.substr(0, version.find(',')));
|
||||||
version::Semver200_version latest_stable_version(versions.substr(versions.find(',') + 1));
|
version::Semver200_version latest_stable_version(version.substr(version.find(',') + 1));
|
||||||
|
|
||||||
bool dev_upgradeable = latest_dev_version > current_version;
|
bool dev_upgradeable = latest_dev_version > current_version;
|
||||||
bool stable_upgradeable = latest_stable_version > current_version;
|
bool stable_upgradeable = latest_stable_version > current_version;
|
||||||
@@ -342,7 +407,7 @@ bool WebStatusService::getCustomSupport(JsonObject root) {
|
|||||||
|
|
||||||
#if defined(EMSESP_STANDALONE)
|
#if defined(EMSESP_STANDALONE)
|
||||||
// dummy test data for "test api3"
|
// dummy test data for "test api3"
|
||||||
deserializeJson(doc, "{\"type\":\"customSupport\",\"Support\":{\"html\":[\"html code\",\"here\"], \"img_url\": \"https://emsesp.org/_media/images/designer.png\"}");
|
deserializeJson(doc, "{\"type\":\"customSupport\",\"Support\":{\"html\":[\"html code\",\"here\"], \"img_url\": \"https://emsesp.org/media/images/designer.png\"}");
|
||||||
#else
|
#else
|
||||||
// check if we have custom support file uploaded
|
// check if we have custom support file uploaded
|
||||||
File file = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "r");
|
File file = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "r");
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ class WebStatusService {
|
|||||||
SecurityManager * _securityManager;
|
SecurityManager * _securityManager;
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
bool checkUpgrade(JsonObject root, std::string & latest_version);
|
bool checkUpgrade(JsonObject root, std::string & latest_version);
|
||||||
bool exportData(JsonObject root, std::string & type);
|
bool exportData(JsonObject root, std::string & type);
|
||||||
bool getCustomSupport(JsonObject root);
|
bool getCustomSupport(JsonObject root);
|
||||||
bool uploadURL(const char * url);
|
bool uploadURL(const char * url);
|
||||||
bool setSystemStatus(const char * status);
|
bool setSystemStatus(const char * status);
|
||||||
void allvalues(JsonObject output);
|
void allvalues(JsonObject output);
|
||||||
|
uint8_t upgradeImportantMessages(std::string & version);
|
||||||
|
|
||||||
std::string current_version_s = EMSESP_APP_VERSION;
|
std::string current_version_s = EMSESP_APP_VERSION;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,6 @@
|
|||||||
"",
|
"",
|
||||||
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
|
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
|
||||||
],
|
],
|
||||||
"img_url": "https://emsesp.org/_media/images/designer.png"
|
"img_url": "https://emsesp.org/media/images/designer.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
103
test/test_data/emsesp_allvalues.json
Normal file
103
test/test_data/emsesp_allvalues.json
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"Boiler Nefit Trendline HRC30 (DeviceID:0x08, ProductID:123, Version:06.01)": {
|
||||||
|
"force heating off": "off",
|
||||||
|
"heating active": "off",
|
||||||
|
"tapwater active": "off",
|
||||||
|
"selected flow temperature": 5,
|
||||||
|
"heating pump modulation": 0,
|
||||||
|
"current flow temperature": 41.4,
|
||||||
|
"return temperature": 37.7,
|
||||||
|
"system pressure": 1.3,
|
||||||
|
"actual boiler temperature": 44.2,
|
||||||
|
"gas": "off",
|
||||||
|
"gas stage 2": "off",
|
||||||
|
"flame current": 0,
|
||||||
|
"fan": "off",
|
||||||
|
"ignition": "off",
|
||||||
|
"oil preheating": "off",
|
||||||
|
"burner min power": 0,
|
||||||
|
"burner max power": 50,
|
||||||
|
"burner min period": 10,
|
||||||
|
"hysteresis on temperature": -6,
|
||||||
|
"hysteresis off temperature": 6,
|
||||||
|
"heating activated": "on",
|
||||||
|
"heating temperature": 70,
|
||||||
|
"heating pump": "off",
|
||||||
|
"boiler pump max power": 70,
|
||||||
|
"boiler pump min power": 50,
|
||||||
|
"boiler pump mode": "proportional",
|
||||||
|
"pump delay": 2,
|
||||||
|
"burner selected max power": 0,
|
||||||
|
"burner current power": 0,
|
||||||
|
"burner starts": 394602,
|
||||||
|
"total burner operating time": "480 days 4 hours 23 minutes",
|
||||||
|
"burner stage 2 operating time": "0 days 0 hours 0 minutes",
|
||||||
|
"total heat operating time": "395 days 2 hours 14 minutes",
|
||||||
|
"burner starts heating": 46245,
|
||||||
|
"total UBA operating time": "3932 days 23 hours 58 minutes",
|
||||||
|
"last error code": "2E(207) 100.75.2000 65:00 (0 min)",
|
||||||
|
"service code": "0H",
|
||||||
|
"service code number": 203,
|
||||||
|
"maintenance message": "H00",
|
||||||
|
"maintenance scheduled": "manual",
|
||||||
|
"time to next maintenance": 6000,
|
||||||
|
"next maintenance date": "01.01.2012",
|
||||||
|
"dhw turn on/off": "on",
|
||||||
|
"dhw set temperature": 62,
|
||||||
|
"dhw selected temperature": 60,
|
||||||
|
"dhw type": "flow",
|
||||||
|
"dhw comfort": "hot",
|
||||||
|
"dhw flow temperature offset": 40,
|
||||||
|
"dhw max power": 100,
|
||||||
|
"dhw circulation pump available": "off",
|
||||||
|
"dhw charging type": "3-way valve",
|
||||||
|
"dhw hysteresis on temperature": -5,
|
||||||
|
"dhw hysteresis off temperature": 0,
|
||||||
|
"dhw disinfection temperature": 70,
|
||||||
|
"dhw circulation pump mode": "off",
|
||||||
|
"dhw circulation active": "off",
|
||||||
|
"dhw current intern temperature": 33.5,
|
||||||
|
"dhw current tap water flow": 0,
|
||||||
|
"dhw storage intern temperature": 33.5,
|
||||||
|
"dhw activated": "on",
|
||||||
|
"dhw one time charging": "off",
|
||||||
|
"dhw disinfecting": "off",
|
||||||
|
"dhw charging": "off",
|
||||||
|
"dhw recharging": "off",
|
||||||
|
"dhw temperature ok": "on",
|
||||||
|
"dhw active": "off",
|
||||||
|
"dhw 3-way valve active": "on",
|
||||||
|
"dhw set pump power": 0,
|
||||||
|
"dhw starts": 348357,
|
||||||
|
"dhw active time": "85 days 2 hours 9 minutes",
|
||||||
|
"nominal Power": 30,
|
||||||
|
"total energy": 3088.69,
|
||||||
|
"energy heating": 2532.94,
|
||||||
|
"dhw energy": 555.75
|
||||||
|
},
|
||||||
|
"Thermostat RC20 (DeviceID:0x17, ProductID:77, Version:03.03)": {
|
||||||
|
"date/time": "10.12.2023 13:49",
|
||||||
|
"hc1 how hot lounge should be": 19,
|
||||||
|
"hc1 current room temp": 19.5,
|
||||||
|
"hc1 mqtt discovery current room temperature": "roomTemp",
|
||||||
|
"hc1 mode": "auto",
|
||||||
|
"hc1 manual temperature": 21.5,
|
||||||
|
"hc1 temperature when mode is off": 7,
|
||||||
|
"hc1 day temperature T2": 20,
|
||||||
|
"hc1 day temperature T3": 20,
|
||||||
|
"hc1 day temperature T4": 20,
|
||||||
|
"hc1 night temperature T1": 15,
|
||||||
|
"hc1 program switchtime": "00 mo 00:00 T1"
|
||||||
|
},
|
||||||
|
"Controller Module BC10 (DeviceID:0x09, ProductID:190, Version:01.03)": {},
|
||||||
|
"Custom Entities": {
|
||||||
|
"boiler_flowtemp": 5,
|
||||||
|
"nominalpower": 30,
|
||||||
|
"minmodulation": 23,
|
||||||
|
"maxmodulation": 115
|
||||||
|
},
|
||||||
|
"Analog Sensors": {},
|
||||||
|
"Temperature Sensors": {
|
||||||
|
"zolder": 18.3
|
||||||
|
}
|
||||||
|
}
|
||||||
37
test/test_data/emsesp_customizations.json
Normal file
37
test/test_data/emsesp_customizations.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"type": "customizations",
|
||||||
|
"Customizations": {
|
||||||
|
"ts": [],
|
||||||
|
"as": [],
|
||||||
|
"masked_entities": [
|
||||||
|
{
|
||||||
|
"product_id": 77,
|
||||||
|
"device_id": 23,
|
||||||
|
"custom_name": "",
|
||||||
|
"custom_brand": "",
|
||||||
|
"entity_ids": [
|
||||||
|
"08datetime",
|
||||||
|
"08hc1/seltemp|<30",
|
||||||
|
"08hc1/currtemp",
|
||||||
|
"08hc1/mode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product_id": 123,
|
||||||
|
"device_id": 8,
|
||||||
|
"custom_name": "",
|
||||||
|
"custom_brand": "",
|
||||||
|
"entity_ids": [
|
||||||
|
"08heatingactive",
|
||||||
|
"08burngas",
|
||||||
|
"08fanwork",
|
||||||
|
"08ignwork",
|
||||||
|
"08burnmaxpower|>23<121",
|
||||||
|
"08burnminperiod|<120",
|
||||||
|
"08lastcode",
|
||||||
|
"08servicecode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
test/test_data/emsesp_entities.json
Normal file
6
test/test_data/emsesp_entities.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "entities",
|
||||||
|
"Entities": {
|
||||||
|
"entities": []
|
||||||
|
}
|
||||||
|
}
|
||||||
6
test/test_data/emsesp_schedule.json
Normal file
6
test/test_data/emsesp_schedule.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "schedule",
|
||||||
|
"Schedule": {
|
||||||
|
"schedule": []
|
||||||
|
}
|
||||||
|
}
|
||||||
145
test/test_data/emsesp_settings.json
Normal file
145
test/test_data/emsesp_settings.json
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"type": "settings",
|
||||||
|
"System": {
|
||||||
|
"version": "3.8.2"
|
||||||
|
},
|
||||||
|
"Network": {
|
||||||
|
"ssid": "",
|
||||||
|
"bssid": "",
|
||||||
|
"password": "",
|
||||||
|
"hostname": "ems-esp",
|
||||||
|
"static_ip_config": false,
|
||||||
|
"bandwidth20": false,
|
||||||
|
"nosleep": true,
|
||||||
|
"enableMDNS": true,
|
||||||
|
"enableCORS": false,
|
||||||
|
"CORSOrigin": "*",
|
||||||
|
"tx_power": 0
|
||||||
|
},
|
||||||
|
"AP": {
|
||||||
|
"provision_mode": 2,
|
||||||
|
"ssid": "ems-esp",
|
||||||
|
"password": "ems-esp-neo",
|
||||||
|
"channel": 1,
|
||||||
|
"ssid_hidden": false,
|
||||||
|
"max_clients": 4,
|
||||||
|
"local_ip": "192.168.4.1",
|
||||||
|
"gateway_ip": "192.168.4.1",
|
||||||
|
"subnet_mask": "255.255.255.0"
|
||||||
|
},
|
||||||
|
"MQTT": {
|
||||||
|
"enableTLS": false,
|
||||||
|
"rootCA": "",
|
||||||
|
"enabled": true,
|
||||||
|
"host": "192.168.X.X",
|
||||||
|
"port": 1883,
|
||||||
|
"base": "ems-esp",
|
||||||
|
"username": "xxxx",
|
||||||
|
"password": "xxxx",
|
||||||
|
"client_id": "esp32-395c7bcc",
|
||||||
|
"keep_alive": 60,
|
||||||
|
"clean_session": false,
|
||||||
|
"entity_format": 1,
|
||||||
|
"publish_time_boiler": 60,
|
||||||
|
"publish_time_thermostat": 60,
|
||||||
|
"publish_time_solar": 60,
|
||||||
|
"publish_time_mixer": 60,
|
||||||
|
"publish_time_water": 60,
|
||||||
|
"publish_time_other": 60,
|
||||||
|
"publish_time_sensor": 60,
|
||||||
|
"publish_time_heartbeat": 10,
|
||||||
|
"mqtt_qos": 0,
|
||||||
|
"mqtt_retain": false,
|
||||||
|
"ha_enabled": true,
|
||||||
|
"nested_format": 1,
|
||||||
|
"discovery_prefix": "homeassistant",
|
||||||
|
"discovery_type": 0,
|
||||||
|
"ha_number_mode": 1,
|
||||||
|
"publish_single": false,
|
||||||
|
"publish_single2cmd": false,
|
||||||
|
"send_response": false
|
||||||
|
},
|
||||||
|
"NTP": {
|
||||||
|
"enabled": true,
|
||||||
|
"server": "time.google.com",
|
||||||
|
"tz_label": "Europe/Amsterdam",
|
||||||
|
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
|
},
|
||||||
|
"Security": {
|
||||||
|
"jwt_secret": "ems-esp-neo",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin",
|
||||||
|
"admin": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "guest",
|
||||||
|
"password": "guest",
|
||||||
|
"admin": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Settings": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"board_profile": "E32V2",
|
||||||
|
"platform": "ESP32",
|
||||||
|
"locale": "en",
|
||||||
|
"tx_mode": 1,
|
||||||
|
"ems_bus_id": 11,
|
||||||
|
"syslog_enabled": false,
|
||||||
|
"syslog_level": 3,
|
||||||
|
"trace_raw": false,
|
||||||
|
"syslog_mark_interval": 0,
|
||||||
|
"syslog_host": "",
|
||||||
|
"syslog_port": 514,
|
||||||
|
"boiler_heatingoff": false,
|
||||||
|
"remote_timeout": 24,
|
||||||
|
"remote_timeout_en": false,
|
||||||
|
"shower_timer": true,
|
||||||
|
"shower_alert": false,
|
||||||
|
"shower_alert_coldshot": 10,
|
||||||
|
"shower_alert_trigger": 7,
|
||||||
|
"shower_min_duration": 180,
|
||||||
|
"rx_gpio": 4,
|
||||||
|
"tx_gpio": 5,
|
||||||
|
"dallas_gpio": 14,
|
||||||
|
"dallas_parasite": false,
|
||||||
|
"led_gpio": 2,
|
||||||
|
"hide_led": true,
|
||||||
|
"led_type": 0,
|
||||||
|
"low_clock": false,
|
||||||
|
"telnet_enabled": true,
|
||||||
|
"notoken_api": false,
|
||||||
|
"readonly_mode": false,
|
||||||
|
"analog_enabled": true,
|
||||||
|
"pbutton_gpio": 34,
|
||||||
|
"solar_maxflow": 30,
|
||||||
|
"fahrenheit": false,
|
||||||
|
"bool_format": 1,
|
||||||
|
"bool_dashboard": 1,
|
||||||
|
"enum_format": 1,
|
||||||
|
"weblog_level": 6,
|
||||||
|
"weblog_buffer": 500,
|
||||||
|
"weblog_compact": true,
|
||||||
|
"phy_type": 1,
|
||||||
|
"eth_power": 15,
|
||||||
|
"eth_phy_addr": 0,
|
||||||
|
"eth_clock_mode": 1,
|
||||||
|
"modbus_enabled": false,
|
||||||
|
"modbus_port": 502,
|
||||||
|
"modbus_max_clients": 10,
|
||||||
|
"modbus_timeout": 300,
|
||||||
|
"developer_mode": true,
|
||||||
|
"email_enabled": false,
|
||||||
|
"email_ssl": false,
|
||||||
|
"email_starttls": true,
|
||||||
|
"email_server": "smtp.example.net",
|
||||||
|
"email_port": 587,
|
||||||
|
"email_login": "",
|
||||||
|
"email_pass": "",
|
||||||
|
"email_sender": "ems-esp@example.net",
|
||||||
|
"email_recp": "",
|
||||||
|
"email_subject": "ems-esp notification"
|
||||||
|
}
|
||||||
|
}
|
||||||
249
test/test_data/emsesp_systembackup.json
Normal file
249
test/test_data/emsesp_systembackup.json
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
{
|
||||||
|
"type": "systembackup",
|
||||||
|
"version": "3.8.2",
|
||||||
|
"date": "2026-04-15T08:06:45",
|
||||||
|
"systembackup": [
|
||||||
|
{
|
||||||
|
"type": "settings",
|
||||||
|
"Network": {
|
||||||
|
"ssid": "",
|
||||||
|
"bssid": "",
|
||||||
|
"password": "",
|
||||||
|
"hostname": "ems-esp",
|
||||||
|
"static_ip_config": false,
|
||||||
|
"bandwidth20": false,
|
||||||
|
"nosleep": true,
|
||||||
|
"enableMDNS": true,
|
||||||
|
"enableCORS": false,
|
||||||
|
"CORSOrigin": "*",
|
||||||
|
"tx_power": 0
|
||||||
|
},
|
||||||
|
"AP": {
|
||||||
|
"provision_mode": 2,
|
||||||
|
"ssid": "ems-esp",
|
||||||
|
"password": "ems-esp-neo",
|
||||||
|
"channel": 1,
|
||||||
|
"ssid_hidden": false,
|
||||||
|
"max_clients": 4,
|
||||||
|
"local_ip": "192.168.X.X",
|
||||||
|
"gateway_ip": "192.168.X.X",
|
||||||
|
"subnet_mask": "255.255.255.0"
|
||||||
|
},
|
||||||
|
"MQTT": {
|
||||||
|
"enableTLS": false,
|
||||||
|
"rootCA": "",
|
||||||
|
"enabled": true,
|
||||||
|
"host": "192.168.X.X",
|
||||||
|
"port": 1883,
|
||||||
|
"base": "ems-esp",
|
||||||
|
"username": "xxxx",
|
||||||
|
"password": "xxxx",
|
||||||
|
"client_id": "esp32-395c7bcc",
|
||||||
|
"keep_alive": 60,
|
||||||
|
"clean_session": false,
|
||||||
|
"entity_format": 1,
|
||||||
|
"publish_time_boiler": 60,
|
||||||
|
"publish_time_thermostat": 60,
|
||||||
|
"publish_time_solar": 60,
|
||||||
|
"publish_time_mixer": 60,
|
||||||
|
"publish_time_water": 60,
|
||||||
|
"publish_time_other": 60,
|
||||||
|
"publish_time_sensor": 60,
|
||||||
|
"publish_time_heartbeat": 10,
|
||||||
|
"mqtt_qos": 0,
|
||||||
|
"mqtt_retain": false,
|
||||||
|
"ha_enabled": true,
|
||||||
|
"nested_format": 1,
|
||||||
|
"discovery_prefix": "homeassistant",
|
||||||
|
"discovery_type": 0,
|
||||||
|
"ha_number_mode": 1,
|
||||||
|
"publish_single": false,
|
||||||
|
"publish_single2cmd": false,
|
||||||
|
"send_response": false
|
||||||
|
},
|
||||||
|
"NTP": {
|
||||||
|
"enabled": true,
|
||||||
|
"server": "time.google.com",
|
||||||
|
"tz_label": "Europe/Amsterdam",
|
||||||
|
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
|
},
|
||||||
|
"Security": {
|
||||||
|
"jwt_secret": "ems-esp-neo",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin",
|
||||||
|
"admin": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "guest",
|
||||||
|
"password": "guest",
|
||||||
|
"admin": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Settings": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"board_profile": "E32V2",
|
||||||
|
"platform": "ESP32",
|
||||||
|
"locale": "en",
|
||||||
|
"tx_mode": 1,
|
||||||
|
"ems_bus_id": 11,
|
||||||
|
"syslog_enabled": false,
|
||||||
|
"syslog_level": 3,
|
||||||
|
"trace_raw": false,
|
||||||
|
"syslog_mark_interval": 0,
|
||||||
|
"syslog_host": "",
|
||||||
|
"syslog_port": 514,
|
||||||
|
"boiler_heatingoff": false,
|
||||||
|
"remote_timeout": 24,
|
||||||
|
"remote_timeout_en": false,
|
||||||
|
"shower_timer": true,
|
||||||
|
"shower_alert": false,
|
||||||
|
"shower_alert_coldshot": 10,
|
||||||
|
"shower_alert_trigger": 7,
|
||||||
|
"shower_min_duration": 180,
|
||||||
|
"rx_gpio": 4,
|
||||||
|
"tx_gpio": 5,
|
||||||
|
"dallas_gpio": 14,
|
||||||
|
"dallas_parasite": false,
|
||||||
|
"led_gpio": 2,
|
||||||
|
"hide_led": true,
|
||||||
|
"led_type": 0,
|
||||||
|
"low_clock": false,
|
||||||
|
"telnet_enabled": true,
|
||||||
|
"notoken_api": false,
|
||||||
|
"readonly_mode": false,
|
||||||
|
"analog_enabled": true,
|
||||||
|
"pbutton_gpio": 34,
|
||||||
|
"solar_maxflow": 30,
|
||||||
|
"fahrenheit": false,
|
||||||
|
"bool_format": 1,
|
||||||
|
"bool_dashboard": 1,
|
||||||
|
"enum_format": 1,
|
||||||
|
"weblog_level": 6,
|
||||||
|
"weblog_buffer": 75,
|
||||||
|
"weblog_compact": true,
|
||||||
|
"phy_type": 1,
|
||||||
|
"eth_power": 15,
|
||||||
|
"eth_phy_addr": 0,
|
||||||
|
"eth_clock_mode": 1,
|
||||||
|
"modbus_enabled": false,
|
||||||
|
"modbus_port": 502,
|
||||||
|
"modbus_max_clients": 10,
|
||||||
|
"modbus_timeout": 300,
|
||||||
|
"developer_mode": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "schedule",
|
||||||
|
"Schedule": {
|
||||||
|
"schedule": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "customizations",
|
||||||
|
"Customizations": {
|
||||||
|
"ts": [],
|
||||||
|
"as": [],
|
||||||
|
"masked_entities": [
|
||||||
|
{
|
||||||
|
"product_id": 77,
|
||||||
|
"device_id": 23,
|
||||||
|
"custom_name": "",
|
||||||
|
"custom_brand": "",
|
||||||
|
"entity_ids": [
|
||||||
|
"08datetime",
|
||||||
|
"08hc1/seltemp|<30",
|
||||||
|
"08hc1/currtemp",
|
||||||
|
"08hc1/mode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product_id": 123,
|
||||||
|
"device_id": 8,
|
||||||
|
"custom_name": "",
|
||||||
|
"custom_brand": "",
|
||||||
|
"entity_ids": [
|
||||||
|
"08heatingactive",
|
||||||
|
"08burngas",
|
||||||
|
"08fanwork",
|
||||||
|
"08ignwork",
|
||||||
|
"08burnmaxpower|>23<121",
|
||||||
|
"08burnminperiod|<120",
|
||||||
|
"08lastcode",
|
||||||
|
"08servicecode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "entities",
|
||||||
|
"Entities": {
|
||||||
|
"entities": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "modules",
|
||||||
|
"Modules": {
|
||||||
|
"modules": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "nvs",
|
||||||
|
"nvs": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"key": "nompower",
|
||||||
|
"value": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 33,
|
||||||
|
"key": "boot",
|
||||||
|
"value": "3.8.1-dev.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 4,
|
||||||
|
"key": "d_boot",
|
||||||
|
"value": 1767525325
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 33,
|
||||||
|
"key": "app1",
|
||||||
|
"value": "3.8.2-dev.13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 33,
|
||||||
|
"key": "app0",
|
||||||
|
"value": "3.8.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 4,
|
||||||
|
"key": "d_app0",
|
||||||
|
"value": 1774988066
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"key": "fresh_firmware",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 4,
|
||||||
|
"key": "d_app1",
|
||||||
|
"value": 1776194060
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 66,
|
||||||
|
"key": "nrgheat",
|
||||||
|
"value": 485871.5481
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 66,
|
||||||
|
"key": "nrgww",
|
||||||
|
"value": 101649.2176
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user