mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Merge pull request #1245 from proddy/dev
Enhance version check to differentiate between ESP32 and ESP32-S3 bin versions #1240
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
- Native support for Home Assistant, Domoticz and openHAB via [MQTT Discovery](https://www.home-assistant.io/docs/mqtt/discovery/)
|
- Native support for Home Assistant, Domoticz and openHAB via [MQTT Discovery](https://www.home-assistant.io/docs/mqtt/discovery/)
|
||||||
- Can run standalone as an independent WiFi Access Point or join an existing WiFi network
|
- Can run standalone as an independent WiFi Access Point or join an existing WiFi network
|
||||||
- Easy first-time configuration via a web Captive Portal
|
- Easy first-time configuration via a web Captive Portal
|
||||||
- Support for more than [110 EMS devices](https://emsesp.github.io/docs/#/Supported-EMS-Devices) (boilers, thermostats, solar modules, mixer modules, heat pumps, gateways, switches, heat sources)
|
- Support for more than [110+ EMS devices](https://emsesp.github.io/docs/All-Devices/) (boilers, thermostats, solar modules, mixer modules, heat pumps, gateways, switches, heat sources)
|
||||||
|
|
||||||
## **Documentation**
|
## **Documentation**
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"adapter": "react",
|
"adapter": "react",
|
||||||
"baseLocale": "pl",
|
"baseLocale": "pl",
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@5.25.1/schema/typesafe-i18n.json"
|
"$schema": "https://unpkg.com/typesafe-i18n@5.26.0/schema/typesafe-i18n.json"
|
||||||
}
|
}
|
||||||
@@ -22,13 +22,14 @@
|
|||||||
"@alova/adapter-xhr": "^1.0.1",
|
"@alova/adapter-xhr": "^1.0.1",
|
||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.14.1",
|
"@mui/icons-material": "^5.14.3",
|
||||||
"@mui/material": "^5.14.2",
|
"@mui/material": "^5.14.3",
|
||||||
"@preact/compat": "^17.1.2",
|
"@preact/compat": "^17.1.2",
|
||||||
|
"@prefresh/vite": "^2.4.1",
|
||||||
"@table-library/react-table-library": "4.1.4",
|
"@table-library/react-table-library": "4.1.4",
|
||||||
"@types/lodash-es": "^4.17.8",
|
"@types/lodash-es": "^4.17.8",
|
||||||
"@types/node": "^20.4.5",
|
"@types/node": "^20.4.8",
|
||||||
"@types/react": "^18.2.17",
|
"@types/react": "^18.2.18",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"alova": "^2.9.3",
|
"alova": "^2.9.3",
|
||||||
@@ -45,18 +46,19 @@
|
|||||||
"react-router-dom": "^6.14.2",
|
"react-router-dom": "^6.14.2",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"sockette": "^2.0.6",
|
"sockette": "^2.0.6",
|
||||||
"typesafe-i18n": "^5.25.1",
|
"typesafe-i18n": "^5.26.0",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.22.10",
|
||||||
"@preact/preset-vite": "^2.5.0",
|
"@preact/preset-vite": "^2.5.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
"@types/babel__core": "^7",
|
||||||
"@typescript-eslint/parser": "^6.2.0",
|
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||||
"cspell": "^6.31.2",
|
"@typescript-eslint/parser": "^6.3.0",
|
||||||
"eslint": "^8.46.0",
|
"eslint": "^8.46.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-typescript": "^17.1.0",
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||||
"eslint-config-prettier": "^8.9.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.5",
|
"eslint-import-resolver-typescript": "^3.5.5",
|
||||||
"eslint-plugin-autofix": "^1.1.0",
|
"eslint-plugin-autofix": "^1.1.0",
|
||||||
"eslint-plugin-import": "^2.28.0",
|
"eslint-plugin-import": "^2.28.0",
|
||||||
@@ -66,10 +68,10 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.1",
|
||||||
"rollup-plugin-visualizer": "^5.9.2",
|
"rollup-plugin-visualizer": "^5.9.2",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.19.2",
|
||||||
"vite": "^4.4.7",
|
"vite": "^4.4.9",
|
||||||
"vite-plugin-svgr": "^3.2.0",
|
"vite-plugin-svgr": "^3.2.0",
|
||||||
"vite-tsconfig-paths": "^4.2.0"
|
"vite-tsconfig-paths": "^4.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import ForwardIcon from '@mui/icons-material/Forward';
|
import ForwardIcon from '@mui/icons-material/Forward';
|
||||||
import { Box, Fab, Paper, Typography, Button } from '@mui/material';
|
import { Box, Paper, Typography, MenuItem, TextField, Button } from '@mui/material';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@@ -7,12 +7,12 @@ import { FeaturesContext } from './contexts/features';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
|
|
||||||
import type { Locales } from 'i18n/i18n-types';
|
import type { Locales } from 'i18n/i18n-types';
|
||||||
import type { FC } from 'react';
|
import type { ChangeEventHandler, FC } from 'react';
|
||||||
import type { SignInRequest } from 'types';
|
import type { SignInRequest } from 'types';
|
||||||
import * as AuthenticationApi from 'api/authentication';
|
import * as AuthenticationApi from 'api/authentication';
|
||||||
import { PROJECT_NAME } from 'api/env';
|
import { PROJECT_NAME } from 'api/env';
|
||||||
|
|
||||||
import { ValidatedTextField } from 'components';
|
import { ValidatedPasswordField, ValidatedTextField } from 'components';
|
||||||
import { AuthenticationContext } from 'contexts/authentication';
|
import { AuthenticationContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { ReactComponent as DEflag } from 'i18n/DE.svg';
|
import { ReactComponent as DEflag } from 'i18n/DE.svg';
|
||||||
@@ -82,7 +82,8 @@ const SignIn: FC = () => {
|
|||||||
|
|
||||||
const submitOnEnter = onEnterCallback(signIn);
|
const submitOnEnter = onEnterCallback(signIn);
|
||||||
|
|
||||||
const selectLocale = async (loc: Locales) => {
|
const onLocaleSelected: ChangeEventHandler<HTMLInputElement> = async ({ target }) => {
|
||||||
|
const loc = target.value as Locales;
|
||||||
localStorage.setItem('lang', loc);
|
localStorage.setItem('lang', loc);
|
||||||
await loadLocaleAsync(loc);
|
await loadLocaleAsync(loc);
|
||||||
setLocale(loc);
|
setLocale(loc);
|
||||||
@@ -111,82 +112,79 @@ const SignIn: FC = () => {
|
|||||||
>
|
>
|
||||||
<Typography variant="h4">{PROJECT_NAME}</Typography>
|
<Typography variant="h4">{PROJECT_NAME}</Typography>
|
||||||
<Typography variant="subtitle2">{features.version}</Typography>
|
<Typography variant="subtitle2">{features.version}</Typography>
|
||||||
<Box
|
|
||||||
mt={2}
|
|
||||||
mb={2}
|
|
||||||
sx={{
|
|
||||||
'& button, & a, & .MuiCard-root': {
|
|
||||||
mt: 1,
|
|
||||||
mx: 1
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button size="small" variant={locale === 'de' ? 'contained' : 'outlined'} onClick={() => selectLocale('de')}>
|
|
||||||
<DEflag style={{ width: 24 }} />
|
|
||||||
DE
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'en' ? 'contained' : 'outlined'} onClick={() => selectLocale('en')}>
|
|
||||||
<GBflag style={{ width: 24 }} />
|
|
||||||
EN
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'fr' ? 'contained' : 'outlined'} onClick={() => selectLocale('fr')}>
|
|
||||||
<FRflag style={{ width: 24 }} />
|
|
||||||
FR
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'it' ? 'contained' : 'outlined'} onClick={() => selectLocale('it')}>
|
|
||||||
<ITflag style={{ width: 24 }} />
|
|
||||||
IT
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'nl' ? 'contained' : 'outlined'} onClick={() => selectLocale('nl')}>
|
|
||||||
<NLflag style={{ width: 24 }} />
|
|
||||||
NL
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'no' ? 'contained' : 'outlined'} onClick={() => selectLocale('no')}>
|
|
||||||
<NOflag style={{ width: 24 }} />
|
|
||||||
NO
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'pl' ? 'contained' : 'outlined'} onClick={() => selectLocale('pl')}>
|
|
||||||
<PLflag style={{ width: 24 }} />
|
|
||||||
PL
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'sv' ? 'contained' : 'outlined'} onClick={() => selectLocale('sv')}>
|
|
||||||
<SVflag style={{ width: 24 }} />
|
|
||||||
SV
|
|
||||||
</Button>
|
|
||||||
<Button size="small" variant={locale === 'tr' ? 'contained' : 'outlined'} onClick={() => selectLocale('tr')}>
|
|
||||||
<TRflag style={{ width: 24 }} />
|
|
||||||
TR
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
<TextField name="locale" variant="outlined" value={locale} onChange={onLocaleSelected} size="small" select>
|
||||||
|
<MenuItem key="de" value="de">
|
||||||
|
<DEflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
DE
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="en" value="en">
|
||||||
|
<GBflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
EN
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="fr" value="fr">
|
||||||
|
<FRflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
FR
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="it" value="it">
|
||||||
|
<ITflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
IT
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="nl" value="nl">
|
||||||
|
<NLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="no" value="no">
|
||||||
|
<NOflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NO
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="pl" value="pl">
|
||||||
|
<PLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
PL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="sv" value="sv">
|
||||||
|
<SVflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
SV
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="tr" value="tr">
|
||||||
|
<TRflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
TR
|
||||||
|
</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
|
||||||
|
<Box display="flex" flexDirection="column" alignItems="center">
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
|
sx={{
|
||||||
|
width: 240
|
||||||
|
}}
|
||||||
name="username"
|
name="username"
|
||||||
label={LL.USERNAME(0)}
|
label={LL.USERNAME(0)}
|
||||||
value={signInRequest.username}
|
value={signInRequest.username}
|
||||||
onChange={updateLoginRequestValue}
|
onChange={updateLoginRequestValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
|
||||||
/>
|
/>
|
||||||
<ValidatedTextField
|
<ValidatedPasswordField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
type="password"
|
sx={{
|
||||||
|
width: 240
|
||||||
|
}}
|
||||||
name="password"
|
name="password"
|
||||||
label={LL.PASSWORD()}
|
label={LL.PASSWORD()}
|
||||||
value={signInRequest.password}
|
value={signInRequest.password}
|
||||||
onChange={updateLoginRequestValue}
|
onChange={updateLoginRequestValue}
|
||||||
onKeyDown={submitOnEnter}
|
onKeyDown={submitOnEnter}
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
|
||||||
/>
|
/>
|
||||||
<Fab variant="extended" color="primary" sx={{ mt: 2 }} onClick={validateAndSignIn} disabled={processing}>
|
</Box>
|
||||||
|
|
||||||
|
<Button variant="contained" color="primary" sx={{ mt: 2 }} onClick={validateAndSignIn} disabled={processing}>
|
||||||
<ForwardIcon sx={{ mr: 1 }} />
|
<ForwardIcon sx={{ mr: 1 }} />
|
||||||
{LL.SIGN_IN()}
|
{LL.SIGN_IN()}
|
||||||
</Fab>
|
</Button>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const alovaInstance = createAlova({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const alovaInstanceGH = createAlova({
|
export const alovaInstanceGH = createAlova({
|
||||||
baseURL: 'https://api.github.com/repos/emsesp/EMS-ESP32',
|
baseURL: 'https://api.github.com/repos/emsesp/EMS-ESP32/releases',
|
||||||
statesHook: ReactHook,
|
statesHook: ReactHook,
|
||||||
requestAdapter: xhrRequestAdapter()
|
requestAdapter: xhrRequestAdapter()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
||||||
import type { OTASettings, SystemStatus, LogSettings, Version } from 'types';
|
import type { OTASettings, SystemStatus, LogSettings } from 'types';
|
||||||
|
|
||||||
// SystemStatus - also used to ping in Restart monitor for pinging
|
// SystemStatus - also used to ping in Restart monitor for pinging
|
||||||
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
||||||
@@ -20,24 +20,15 @@ export const fetchLog = () => alovaInstance.Post('/rest/fetchLog');
|
|||||||
|
|
||||||
// Get versions from github
|
// Get versions from github
|
||||||
export const getStableVersion = () =>
|
export const getStableVersion = () =>
|
||||||
alovaInstanceGH.Get<Version>('releases/latest', {
|
alovaInstanceGH.Get('latest', {
|
||||||
transformData(response: any) {
|
transformData(response: any) {
|
||||||
return {
|
return response.data.name.substring(1);
|
||||||
version: response.data.name,
|
|
||||||
url: response.data.assets[1].browser_download_url,
|
|
||||||
changelog: response.data.assets[0].browser_download_url
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getDevVersion = () =>
|
export const getDevVersion = () =>
|
||||||
alovaInstanceGH.Get<Version>('releases/tags/latest', {
|
alovaInstanceGH.Get('tags/latest', {
|
||||||
transformData(response: any) {
|
transformData(response: any) {
|
||||||
return {
|
return response.data.name.split(/\s+/).splice(-1)[0].substring(1);
|
||||||
version: response.data.name.split(/\s+/).splice(-1),
|
|
||||||
url: response.data.assets[1].browser_download_url,
|
|
||||||
changelog: response.data.assets[0].browser_download_url
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,26 +23,22 @@ import {
|
|||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
ListItemText,
|
ListItemText
|
||||||
Link,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import { FeaturesContext } from '../../contexts/features';
|
||||||
import RestartMonitor from './RestartMonitor';
|
import RestartMonitor from './RestartMonitor';
|
||||||
|
import SystemStatusVersionDialog from './SystemStatusVersionDialog';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import { ButtonRow, FormLoader, SectionContent, MessageBox } from 'components';
|
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
export const VERSIONCHECK_ENDPOINT = 'https://api.github.com/repos/emsesp/EMS-ESP32/releases/latest';
|
|
||||||
export const VERSIONCHECK_DEV_ENDPOINT = 'https://api.github.com/repos/emsesp/EMS-ESP32/releases/tags/latest';
|
|
||||||
export const uploadURL = window.location.origin + '/system/upload';
|
|
||||||
|
|
||||||
function formatNumber(num: number) {
|
function formatNumber(num: number) {
|
||||||
return new Intl.NumberFormat().format(num);
|
return new Intl.NumberFormat().format(num);
|
||||||
}
|
}
|
||||||
@@ -54,8 +50,10 @@ const SystemStatusForm: FC = () => {
|
|||||||
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
||||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
const [showingVersion, setShowingVersion] = useState<boolean>(false);
|
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
const [versionDialogOpen, setVersionDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { features } = useContext(FeaturesContext);
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||||
immediate: false
|
immediate: false
|
||||||
@@ -69,10 +67,6 @@ const SystemStatusForm: FC = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// fetch versions from GH on load
|
|
||||||
const { data: latestVersion } = useRequest(SystemApi.getStableVersion);
|
|
||||||
const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion);
|
|
||||||
|
|
||||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||||
|
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
@@ -158,60 +152,6 @@ const SystemStatusForm: FC = () => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderVersionDialog = () => (
|
|
||||||
<Dialog open={showingVersion} onClose={() => setShowingVersion(false)}>
|
|
||||||
<DialogTitle>{LL.VERSION_CHECK(1)}</DialogTitle>
|
|
||||||
<DialogContent dividers>
|
|
||||||
<MessageBox my={0} level="info" message={LL.VERSION_ON() + ' v' + data?.emsesp_version} />
|
|
||||||
{latestVersion && (
|
|
||||||
<Box mt={2} mb={2}>
|
|
||||||
{LL.THE_LATEST()} <u>{LL.OFFICIAL()}</u> {LL.RELEASE_IS()} <b>{latestVersion.version}</b>
|
|
||||||
(
|
|
||||||
<Link target="_blank" href={latestVersion.changelog} color="primary">
|
|
||||||
{LL.RELEASE_NOTES()}
|
|
||||||
</Link>
|
|
||||||
) (
|
|
||||||
<Link target="_blank" href={latestVersion.url} color="primary">
|
|
||||||
{LL.DOWNLOAD(1)}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{latestDevVersion && (
|
|
||||||
<Box mt={2} mb={2}>
|
|
||||||
{LL.THE_LATEST()} <u>{LL.DEVELOPMENT()}</u> {LL.RELEASE_IS()}
|
|
||||||
<b>{latestDevVersion.version}</b>
|
|
||||||
(
|
|
||||||
<Link target="_blank" href={latestDevVersion.changelog} color="primary">
|
|
||||||
{LL.RELEASE_NOTES()}
|
|
||||||
</Link>
|
|
||||||
) (
|
|
||||||
<Link target="_blank" href={latestDevVersion.url} color="primary">
|
|
||||||
{LL.DOWNLOAD(1)}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>
|
|
||||||
<Typography variant="body2">
|
|
||||||
{LL.USE()}
|
|
||||||
<Link href={uploadURL} color="primary">
|
|
||||||
{LL.UPLOAD()}
|
|
||||||
</Link>
|
|
||||||
{LL.SYSTEM_APPLY_FIRMWARE()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button variant="outlined" onClick={() => setShowingVersion(false)} color="secondary">
|
|
||||||
{LL.CLOSE()}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderFactoryResetDialog = () => (
|
const renderFactoryResetDialog = () => (
|
||||||
<Dialog open={confirmFactoryReset} onClose={() => setConfirmFactoryReset(false)}>
|
<Dialog open={confirmFactoryReset} onClose={() => setConfirmFactoryReset(false)}>
|
||||||
<DialogTitle>{LL.FACTORY_RESET()}</DialogTitle>
|
<DialogTitle>{LL.FACTORY_RESET()}</DialogTitle>
|
||||||
@@ -253,12 +193,10 @@ const SystemStatusForm: FC = () => {
|
|||||||
<BuildIcon />
|
<BuildIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary={LL.EMS_ESP_VER()} secondary={'v' + data.emsesp_version} />
|
<ListItemText primary={LL.EMS_ESP_VER()} secondary={data.emsesp_version} />
|
||||||
{latestVersion && (
|
<Button color="primary" onClick={() => setVersionDialogOpen(true)}>
|
||||||
<Button color="primary" onClick={() => setShowingVersion(true)}>
|
|
||||||
{LL.VERSION_CHECK(0)}
|
{LL.VERSION_CHECK(0)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@@ -386,7 +324,6 @@ const SystemStatusForm: FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{renderVersionDialog()}
|
|
||||||
{renderRestartDialog()}
|
{renderRestartDialog()}
|
||||||
{renderFactoryResetDialog()}
|
{renderFactoryResetDialog()}
|
||||||
</>
|
</>
|
||||||
@@ -396,6 +333,14 @@ const SystemStatusForm: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<SectionContent title={LL.STATUS_OF(LL.SYSTEM(1))} titleGutter>
|
<SectionContent title={LL.STATUS_OF(LL.SYSTEM(1))} titleGutter>
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
{restarting ? <RestartMonitor /> : content()}
|
||||||
|
{data && (
|
||||||
|
<SystemStatusVersionDialog
|
||||||
|
open={versionDialogOpen}
|
||||||
|
onClose={() => setVersionDialogOpen(false)}
|
||||||
|
version={data.emsesp_version}
|
||||||
|
platform={features.platform}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
108
interface/src/framework/system/SystemStatusVersionDialog.tsx
Normal file
108
interface/src/framework/system/SystemStatusVersionDialog.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Link, Typography } from '@mui/material';
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
|
||||||
|
import MessageBox from 'components/MessageBox';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
type SystemStatusVersionDialogProps = {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
version: string;
|
||||||
|
platform: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SystemStatusVersionDialog = ({ open, onClose, version, platform }: SystemStatusVersionDialogProps) => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
const { send: getLatestVersion, data: latestVersion } = useRequest(SystemApi.getStableVersion, {
|
||||||
|
immediate: false,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
const { send: getLatestDevVersion, data: latestDevVersion } = useRequest(SystemApi.getDevVersion, {
|
||||||
|
immediate: false,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
||||||
|
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
||||||
|
|
||||||
|
const uploadURL = window.location.origin + '/system/upload';
|
||||||
|
|
||||||
|
const connected = latestVersion && latestDevVersion;
|
||||||
|
|
||||||
|
const getVersions = useCallback(async () => {
|
||||||
|
await getLatestVersion();
|
||||||
|
await getLatestDevVersion();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
void getVersions();
|
||||||
|
}
|
||||||
|
}, [getVersions, open]);
|
||||||
|
|
||||||
|
const getBinURL = (v: string) => 'EMS-ESP-' + v.replaceAll('.', '_') + '-' + platform.replaceAll('-', '_') + '.bin';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose}>
|
||||||
|
<DialogTitle>{LL.VERSION_CHECK(1)}</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<MessageBox my={0} level="info" message={LL.VERSION_ON() + ' ' + version + ' (' + platform + ')'} />
|
||||||
|
{latestVersion && (
|
||||||
|
<Box mt={2} mb={2}>
|
||||||
|
{LL.THE_LATEST()} <b>{LL.OFFICIAL()}</b> {LL.RELEASE_IS()} <b>{latestVersion}</b>
|
||||||
|
(
|
||||||
|
<Link target="_blank" href={STABLE_URL + 'v' + latestVersion + '/CHANGELOG.md'} color="primary">
|
||||||
|
{LL.RELEASE_NOTES()}
|
||||||
|
</Link>
|
||||||
|
) (
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion)}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.DOWNLOAD(1)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{latestDevVersion && (
|
||||||
|
<Box mt={2} mb={2}>
|
||||||
|
{LL.THE_LATEST()} <b>{LL.DEVELOPMENT()}</b> {LL.RELEASE_IS()}
|
||||||
|
<b>{latestDevVersion}</b>
|
||||||
|
(
|
||||||
|
<Link target="_blank" href={DEV_URL + 'CHANGELOG_LATEST.md'} color="primary">
|
||||||
|
{LL.RELEASE_NOTES()}
|
||||||
|
</Link>
|
||||||
|
) (
|
||||||
|
<Link target="_blank" href={DEV_URL + getBinURL(latestDevVersion)} color="primary">
|
||||||
|
{LL.DOWNLOAD(1)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{connected && (
|
||||||
|
<Box color="warning.main" mt={2}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{LL.USE()}
|
||||||
|
<Link href={uploadURL} color="primary">
|
||||||
|
{LL.UPLOAD()}
|
||||||
|
</Link>
|
||||||
|
{LL.SYSTEM_APPLY_FIRMWARE()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!connected && <MessageBox my={2} level="warning" message="No internet connection" />}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button variant="outlined" onClick={onClose} color="secondary">
|
||||||
|
{LL.CLOSE()}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemStatusVersionDialog;
|
||||||
@@ -180,7 +180,7 @@ const en: Translation = {
|
|||||||
LOG_OF: '{0} Log',
|
LOG_OF: '{0} Log',
|
||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||||
VERSION_ON: 'You are currently on',
|
VERSION_ON: 'You are currently on version',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'to apply the new firmware',
|
SYSTEM_APPLY_FIRMWARE: 'to apply the new firmware',
|
||||||
CLOSE: 'Close',
|
CLOSE: 'Close',
|
||||||
USE: 'Use',
|
USE: 'Use',
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ const nl: Translation = {
|
|||||||
LOG_OF: '{0} Log',
|
LOG_OF: '{0} Log',
|
||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||||
VERSION_ON: 'U bevindt zich momenteel op',
|
VERSION_ON: 'U bevindt zich momenteel op versie',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'om de nieuwe firmware te activeren',
|
SYSTEM_APPLY_FIRMWARE: 'om de nieuwe firmware te activeren',
|
||||||
CLOSE: 'Sluiten',
|
CLOSE: 'Sluiten',
|
||||||
USE: 'Gebruik',
|
USE: 'Gebruik',
|
||||||
|
|||||||
@@ -47,9 +47,3 @@ export interface LogSettings {
|
|||||||
max_messages: number;
|
max_messages: number;
|
||||||
compact: false;
|
compact: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Version {
|
|
||||||
version: string;
|
|
||||||
url: string;
|
|
||||||
changelog: string;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import svgrPlugin from 'vite-plugin-svgr';
|
|||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
import ProgmemGenerator from './progmem-generator';
|
import ProgmemGenerator from './progmem-generator';
|
||||||
import preact from '@preact/preset-vite';
|
import preact from '@preact/preset-vite';
|
||||||
|
// import prefresh from '@prefresh/vite';
|
||||||
|
|
||||||
export default defineConfig(({ command, mode }) => {
|
export default defineConfig(({ command, mode }) => {
|
||||||
if (mode === 'hosted') {
|
if (mode === 'hosted') {
|
||||||
@@ -17,8 +18,9 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
plugins: [
|
plugins: [
|
||||||
preact(),
|
preact(),
|
||||||
viteTsconfigPaths(),
|
viteTsconfigPaths(),
|
||||||
svgrPlugin(),
|
svgrPlugin()
|
||||||
ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 })
|
// prefresh()
|
||||||
|
// ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 })
|
||||||
],
|
],
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
@@ -67,3 +69,6 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
function prefresh(): PluginOption {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
}
|
||||||
|
|||||||
1769
interface/yarn.lock
1769
interface/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -324,6 +324,7 @@ security_settings = {
|
|||||||
};
|
};
|
||||||
const features = {
|
const features = {
|
||||||
version: 'v3.6.0-demo',
|
version: 'v3.6.0-demo',
|
||||||
|
// platform: 'ESP32'
|
||||||
platform: 'ESP32-S3'
|
platform: 'ESP32-S3'
|
||||||
};
|
};
|
||||||
const verify_authentication = { access_token: '1234' };
|
const verify_authentication = { access_token: '1234' };
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ unbuild_flags =
|
|||||||
${common.core_unbuild_flags}
|
${common.core_unbuild_flags}
|
||||||
|
|
||||||
[espressi32_base]
|
[espressi32_base]
|
||||||
platform = espressif32
|
platform = espressif32@6.3.2
|
||||||
framework = arduino
|
framework = arduino
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
build_unflags = ${common.unbuild_flags}
|
build_unflags = ${common.unbuild_flags}
|
||||||
@@ -64,8 +64,7 @@ check_flags =
|
|||||||
; build for GitHub Actions CI
|
; build for GitHub Actions CI
|
||||||
; the Web interface is built seperately
|
; the Web interface is built seperately
|
||||||
[env:ci]
|
[env:ci]
|
||||||
platform = espressif32
|
extends = espressi32_base
|
||||||
framework = arduino
|
|
||||||
extra_scripts = scripts/rename_fw.py
|
extra_scripts = scripts/rename_fw.py
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
@@ -75,8 +74,7 @@ build_unflags = ${common.unbuild_flags}
|
|||||||
|
|
||||||
; for github Actions actually standard platform is buggy, use dev-platform
|
; for github Actions actually standard platform is buggy, use dev-platform
|
||||||
[env:ci_s3]
|
[env:ci_s3]
|
||||||
platform = https://github.com/platformio/platform-espressif32.git
|
extends = espressi32_base
|
||||||
framework = arduino
|
|
||||||
extra_scripts = scripts/rename_fw.py
|
extra_scripts = scripts/rename_fw.py
|
||||||
board = lolin_s3
|
board = lolin_s3
|
||||||
board_build.f_cpu = 240000000L
|
board_build.f_cpu = 240000000L
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.6.0-dev.14"
|
#define EMSESP_APP_VERSION "3.6.0-dev.16"
|
||||||
|
|||||||
Reference in New Issue
Block a user