mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-01-26 08:39:09 +03:00
Merge pull request #2066 from proddy/dev
custom support page, action endpoint
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -68,3 +68,6 @@ venv/
|
||||
|
||||
# cspell
|
||||
words-found-verbose.txt
|
||||
|
||||
# sonarlint
|
||||
compile_commands.json
|
||||
|
||||
4
.sonarlint/connectedMode.json
Normal file
4
.sonarlint/connectedMode.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sonarCloudOrganization": "emsesp",
|
||||
"projectKey": "emsesp_EMS-ESP32"
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -96,5 +96,6 @@
|
||||
"sonarlint.connectedMode.project": {
|
||||
"connectionId": "emsesp",
|
||||
"projectKey": "emsesp_EMS-ESP32"
|
||||
}
|
||||
},
|
||||
"sonarlint.pathToCompileCommands": "${workspaceFolder}/compile_commands.json"
|
||||
}
|
||||
@@ -40,8 +40,9 @@ For more details go to [www.emsesp.org](https://www.emsesp.org/).
|
||||
- autodetect and download firmware upgrades via the WebUI
|
||||
- command 'show log' that lists out the current weblog buffer, showing last messages.
|
||||
- default web log buffer to 25 lines for ESP32s with no PSRAM
|
||||
- Try and determine correct board profile if none is set
|
||||
- try and determine correct board profile if none is set during boot
|
||||
- auto Scroll in WebLog UI - reduced delay so incoming logs are faster
|
||||
- uploading custom support info for Guest users [#2054]<https://github.com/emsesp/EMS-ESP32/issues/2054>
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -72,6 +73,7 @@ For more details go to [www.emsesp.org](https://www.emsesp.org/).
|
||||
- WebLog UI matches color schema of the terminal console correctly
|
||||
- Updated Web libraries, ArduinoJson
|
||||
- Help page doesn't show detailed tech info if the user is not 'admin' role [#2054](https://github.com/emsesp/EMS-ESP32/issues/2054)
|
||||
- removed system command `allvalues` and moved to an action called `export`
|
||||
- Show ems-esp internal devices in device list of system/info
|
||||
- Scheduler and mqtt run async on systems with psram
|
||||
- Show IPv6 address type (local/global/ula) in log
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@mui/icons-material": "^6.1.1",
|
||||
"@mui/material": "^6.1.1",
|
||||
"@table-library/react-table-library": "4.1.7",
|
||||
"alova": "3.0.16",
|
||||
"alova": "3.0.17",
|
||||
"async-validator": "^4.2.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
@@ -47,8 +47,8 @@
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/formidable": "^3",
|
||||
"@types/node": "^22.7.3",
|
||||
"@types/react": "^18.3.9",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.10",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"concurrently": "^9.0.1",
|
||||
@@ -57,8 +57,8 @@
|
||||
"formidable": "^3.5.1",
|
||||
"prettier": "^3.3.3",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"terser": "^5.34.0",
|
||||
"typescript-eslint": "8.7.0",
|
||||
"terser": "^5.34.1",
|
||||
"typescript-eslint": "8.8.0",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^5.0.1"
|
||||
|
||||
@@ -2,6 +2,7 @@ import { alovaInstance } from 'api/endpoints';
|
||||
|
||||
import type {
|
||||
APIcall,
|
||||
Action,
|
||||
Activity,
|
||||
CoreData,
|
||||
DeviceData,
|
||||
@@ -52,9 +53,9 @@ export const readActivity = () => alovaInstance.Get<Activity>('/rest/activity');
|
||||
// API
|
||||
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
|
||||
|
||||
// DownloadUpload
|
||||
export const exportData = (type: string) =>
|
||||
alovaInstance.Get('/rest/exportData', { params: { type } });
|
||||
// Generic action
|
||||
export const callAction = (action: Action) =>
|
||||
alovaInstance.Post('/rest/action', action);
|
||||
|
||||
// SettingsCustomization
|
||||
export const readDeviceEntities = (id: number) =>
|
||||
@@ -118,7 +119,7 @@ export const writeModules = (data: {
|
||||
license: string;
|
||||
}) => alovaInstance.Post('/rest/modules', data);
|
||||
|
||||
// SettingsEntities
|
||||
// CustomEntities
|
||||
export const readCustomEntities = () =>
|
||||
alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
|
||||
transform(data) {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const updateLogSettings = (data: LogSettings) =>
|
||||
alovaInstance.Post('/rest/logSettings', data);
|
||||
export const fetchLogES = () => alovaInstance.Get('/es/log');
|
||||
|
||||
// Get versions from github
|
||||
// Get versions from GitHub
|
||||
export const getStableVersion = () =>
|
||||
alovaInstanceGH.Get('latest', {
|
||||
transform(response: { data: { name: string } }) {
|
||||
@@ -34,9 +34,3 @@ export const uploadFile = (file: File) => {
|
||||
timeout: 60000 // override timeout for uploading firmware - 1 minute
|
||||
});
|
||||
};
|
||||
|
||||
export const uploadURL = (data: { url: string }) =>
|
||||
alovaInstance.Post('/rest/uploadURL', data);
|
||||
|
||||
export const checkUpgrade = (data: { version: string }) =>
|
||||
alovaInstance.Post('/rest/checkUpgrade', data);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import CommentIcon from '@mui/icons-material/CommentTwoTone';
|
||||
@@ -16,18 +16,17 @@ import {
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Stack,
|
||||
Typography,
|
||||
styled
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
|
||||
import { useRequest } from 'alova/client';
|
||||
import { SectionContent, useLayoutTitle } from 'components';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { saveFile } from 'utils/file';
|
||||
|
||||
import { API } from '../../api/app';
|
||||
import { API, callAction } from '../../api/app';
|
||||
import type { APIcall } from './types';
|
||||
|
||||
const Help = () => {
|
||||
@@ -36,131 +35,150 @@ const Help = () => {
|
||||
|
||||
const { me } = useContext(AuthenticatedContext);
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
}).onSuccess((event) => {
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(event.data, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
const [customSupportIMG, setCustomSupportIMG] = useState<string | null>(null);
|
||||
const [customSupportHTML, setCustomSupportHTML] = useState<string | null>(null);
|
||||
|
||||
anchor.download =
|
||||
'emsesp_' + event.args[0].device + '_' + event.args[0].entity + '.txt';
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(anchor.href);
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
useRequest(() => callAction({ action: 'customSupport' })).onSuccess((event) => {
|
||||
if (event && event.data && Object.keys(event.data).length !== 0) {
|
||||
const data = event.data.Support;
|
||||
if (data.img_url) {
|
||||
setCustomSupportIMG(data.img_url);
|
||||
}
|
||||
if (data.html) {
|
||||
setCustomSupportHTML(data.html.join('<br/>'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const callAPI = async (device: string, cmd: string) => {
|
||||
await sendAPI({ device, cmd, id: 0 }).catch((error: Error) => {
|
||||
const { send: sendExportAllValues } = useRequest(
|
||||
() => callAction({ action: 'export', param: 'allvalues' }),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
)
|
||||
.onSuccess((event) => {
|
||||
saveFile(event.data, 'allvalues', '.txt');
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
})
|
||||
.onError((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
immediate: false
|
||||
})
|
||||
.onSuccess((event) => {
|
||||
saveFile(event.data, 'system_info', '.json');
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
})
|
||||
.onError((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionContent>
|
||||
{me.admin ? (
|
||||
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||
<ListItem>
|
||||
<ListItemButton component="a" href="https://emsesp.org">
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<MenuBookIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_1()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<SectionContent>
|
||||
<Stack
|
||||
padding={1}
|
||||
mb={2}
|
||||
direction="row"
|
||||
divider={<Divider orientation="vertical" flexItem />}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
border: '2px solid grey',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
{customSupportHTML ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: customSupportHTML }} />
|
||||
) : (
|
||||
LL.HELP_INFORMATION_5()
|
||||
)}
|
||||
</Typography>
|
||||
<Box
|
||||
component="img"
|
||||
referrerPolicy="no-referrer"
|
||||
sx={{
|
||||
maxHeight: { xs: 100, md: 250 }
|
||||
}}
|
||||
src={customSupportIMG || 'https://emsesp.org/_media/images/installer.jpeg'}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<ListItem>
|
||||
<ListItemButton component="a" href="https://discord.gg/3J3GgnzpyT">
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<CommentIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_2()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
{me.admin && (
|
||||
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||
<ListItem>
|
||||
<ListItemButton component="a" href="https://emsesp.org">
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<MenuBookIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_1()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<ListItemButton
|
||||
component="a"
|
||||
href="https://github.com/emsesp/EMS-ESP32/issues/new/choose"
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<GitHubIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_3()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
) : (
|
||||
<Stack
|
||||
spacing={1}
|
||||
padding={1}
|
||||
direction="row"
|
||||
divider={<Divider orientation="vertical" flexItem />}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
border: '2px solid grey',
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography border="red" variant="subtitle1">
|
||||
{LL.HELP_INFORMATION_5()}
|
||||
</Typography>
|
||||
<Box
|
||||
padding={1}
|
||||
component="img"
|
||||
sx={{
|
||||
maxHeight: { xs: 100, md: 250 }
|
||||
}}
|
||||
src="https://emsesp.org/_media/images/installer.jpeg"
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
<ListItem>
|
||||
<ListItemButton component="a" href="https://discord.gg/3J3GgnzpyT">
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<CommentIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_2()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<Box p={2} color="warning.main">
|
||||
<Typography mb={1} variant="body2">
|
||||
{LL.HELP_INFORMATION_4()}
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => callAPI('system', 'info')}
|
||||
>
|
||||
{LL.DOWNLOAD(1)} {LL.SUPPORT_INFORMATION(0)}
|
||||
</Button>
|
||||
</Box>
|
||||
<ListItem>
|
||||
<ListItemButton
|
||||
component="a"
|
||||
href="https://github.com/emsesp/EMS-ESP32/issues/new/choose"
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||
<GitHubIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={LL.HELP_INFORMATION_3()} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
<Box p={2} color="warning.main">
|
||||
<Typography mb={1} variant="body2">
|
||||
{LL.HELP_INFORMATION_4()}
|
||||
</Typography>
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => callAPI('system', 'allvalues')}
|
||||
onClick={() => sendAPI({ device: 'system', cmd: 'info', id: 0 })}
|
||||
>
|
||||
{LL.DOWNLOAD(1)} {LL.ALLVALUES()}
|
||||
{LL.DOWNLOAD(1)} {LL.SUPPORT_INFORMATION(0)}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ mt: 4 }} />
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => sendExportAllValues()}
|
||||
>
|
||||
{LL.DOWNLOAD(1)} {LL.ALLVALUES()}
|
||||
</Button>
|
||||
|
||||
<Typography color="white" variant="subtitle1" align="center" mt={1}>
|
||||
©
|
||||
<Link target="_blank" href="https://emsesp.org" color="primary">
|
||||
{'emsesp.org'}
|
||||
</Link>
|
||||
</Typography>
|
||||
</SectionContent>
|
||||
</>
|
||||
<Divider sx={{ mt: 4 }} />
|
||||
|
||||
<Typography color="white" variant="subtitle1" align="center" mt={1}>
|
||||
©
|
||||
<Link target="_blank" href="https://emsesp.org" color="primary">
|
||||
{'emsesp.org'}
|
||||
</Link>
|
||||
</Typography>
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -265,6 +265,12 @@ export interface APIcall {
|
||||
cmd: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface Action {
|
||||
action: string;
|
||||
param?: string; // optional
|
||||
}
|
||||
|
||||
export interface WriteAnalogSensor {
|
||||
id: number;
|
||||
gpio: number;
|
||||
|
||||
@@ -18,13 +18,8 @@ import {
|
||||
import Grid from '@mui/material/Grid2';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { API, exportData } from 'api/app';
|
||||
import {
|
||||
checkUpgrade,
|
||||
getDevVersion,
|
||||
getStableVersion,
|
||||
uploadURL
|
||||
} from 'api/system';
|
||||
import { API, callAction } from 'api/app';
|
||||
import { getDevVersion, getStableVersion } from 'api/system';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova/client';
|
||||
@@ -37,6 +32,7 @@ import {
|
||||
useLayoutTitle
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { saveFile } from 'utils/file';
|
||||
|
||||
const DownloadUpload = () => {
|
||||
const { LL } = useI18nContext();
|
||||
@@ -46,11 +42,23 @@ const DownloadUpload = () => {
|
||||
const [useDev, setUseDev] = useState<boolean>(false);
|
||||
const [upgradeAvailable, setUpgradeAvailable] = useState<boolean>(false);
|
||||
|
||||
const { send: sendExportData } = useRequest((type: string) => exportData(type), {
|
||||
immediate: false
|
||||
})
|
||||
const { send: sendCheckUpgrade } = useRequest(
|
||||
(version: string) => callAction({ action: 'checkUpgrade', param: version }),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
).onSuccess((event) => {
|
||||
setUpgradeAvailable((event.data as { upgradeable: boolean }).upgradeable);
|
||||
});
|
||||
|
||||
const { send: sendExportData } = useRequest(
|
||||
(type: string) => callAction({ action: 'export', param: type }),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
)
|
||||
.onSuccess((event) => {
|
||||
saveFile(event.data, event.args[0]);
|
||||
saveFile(event.data, event.args[0], '.json');
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
})
|
||||
.onError((error) => {
|
||||
@@ -61,14 +69,10 @@ const DownloadUpload = () => {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const {
|
||||
data: data,
|
||||
send: loadData,
|
||||
error
|
||||
} = useRequest(SystemApi.readSystemStatus);
|
||||
const { data, send: loadData, error } = useRequest(SystemApi.readSystemStatus);
|
||||
|
||||
const { send: sendUploadURL } = useRequest(
|
||||
(data: { url: string }) => uploadURL(data),
|
||||
(url: string) => callAction({ action: 'uploadURL', param: url }),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
@@ -83,12 +87,6 @@ const DownloadUpload = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const { send: sendCheckUpgrade } = useRequest(checkUpgrade, {
|
||||
immediate: false
|
||||
}).onSuccess((event) => {
|
||||
setUpgradeAvailable(event.data.upgradeable);
|
||||
});
|
||||
|
||||
// called immediately to get the latest version, on page load
|
||||
const { data: latestVersion } = useRequest(getStableVersion, {
|
||||
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/v3.6.5/EMS-ESP-3_6_5-ESP32-16MB+.bin
|
||||
@@ -102,7 +100,7 @@ const DownloadUpload = () => {
|
||||
// immediate: false,
|
||||
// initialData: '3.7.0-dev.32'
|
||||
}).onSuccess((event) => {
|
||||
void sendCheckUpgrade({ version: event.data });
|
||||
void sendCheckUpgrade(event.data);
|
||||
});
|
||||
|
||||
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
||||
@@ -136,24 +134,12 @@ const DownloadUpload = () => {
|
||||
};
|
||||
|
||||
const installFirmwareURL = async (url: string) => {
|
||||
await sendUploadURL({ url: url }).catch((error: Error) => {
|
||||
await sendUploadURL(url).catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
setRestarting(true);
|
||||
};
|
||||
|
||||
const saveFile = (json: unknown, filename: string) => {
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
anchor.download = 'emsesp_' + filename + '.json';
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(anchor.href);
|
||||
};
|
||||
|
||||
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
||||
|
||||
const internet_live =
|
||||
|
||||
@@ -45,7 +45,7 @@ const RestartMonitor = () => {
|
||||
return (
|
||||
<Dialog fullWidth={true} sx={dialogStyle} open={true}>
|
||||
<DialogContent dividers>
|
||||
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
|
||||
<Box m={0} py={0} display="flex" alignItems="center" flexDirection="column">
|
||||
<Typography
|
||||
color="secondary"
|
||||
variant="h6"
|
||||
@@ -69,7 +69,7 @@ const RestartMonitor = () => {
|
||||
<MessageBox my={2} level="error" message={errorMessage} />
|
||||
) : (
|
||||
<Box py={2}>
|
||||
<CircularProgress size={48} />
|
||||
<CircularProgress size={32} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -41,6 +41,7 @@ const LayoutAppBar = ({ title, onToggleDrawer }: LayoutAppBarProps) => {
|
||||
|
||||
{show_back && (
|
||||
<IconButton
|
||||
sx={{ mr: 1 }}
|
||||
color="inherit"
|
||||
edge="start"
|
||||
onClick={() => navigate(pathnames[0])}
|
||||
|
||||
@@ -12,6 +12,7 @@ import './dragNdrop.css';
|
||||
|
||||
const DragNdrop = ({ onFileSelected }) => {
|
||||
const [file, setFile] = useState<File>();
|
||||
const [dragged, setDragged] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
@@ -45,6 +46,7 @@ const DragNdrop = ({ onFileSelected }) => {
|
||||
const handleRemoveFile = (event) => {
|
||||
event.stopPropagation();
|
||||
setFile(undefined);
|
||||
setDragged(false);
|
||||
};
|
||||
|
||||
const handleUploadClick = (event) => {
|
||||
@@ -56,11 +58,17 @@ const DragNdrop = ({ onFileSelected }) => {
|
||||
inputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleDragOver = (event) => {
|
||||
event.preventDefault(); // prevent file from being opened
|
||||
setDragged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`document-uploader ${file ? 'upload-box active' : 'upload-box'}`}
|
||||
className={`document-uploader ${file || dragged ? 'active' : ''}`}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={() => setDragged(false)}
|
||||
onClick={handleBrowseClick}
|
||||
>
|
||||
<div className="upload-info">
|
||||
|
||||
11
interface/src/utils/file.ts
Normal file
11
interface/src/utils/file.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const saveFile = (json: unknown, filename: string, extension: string) => {
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
anchor.download = 'emsesp_' + filename + extension;
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(anchor.href);
|
||||
};
|
||||
@@ -1446,12 +1446,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^22.7.3":
|
||||
version: 22.7.3
|
||||
resolution: "@types/node@npm:22.7.3"
|
||||
"@types/node@npm:^22.7.4":
|
||||
version: 22.7.4
|
||||
resolution: "@types/node@npm:22.7.4"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.19.2"
|
||||
checksum: 10c0/0e579813528b0370454337a952f43b792cd12731e10fdca0fdb627158e980c1219bba99e9048c134b6a19325d817016059afe016ccd372326c838a1b85a51574
|
||||
checksum: 10c0/c22bf54515c78ff3170142c1e718b90e2a0003419dc2d55f79c9c9362edd590a6ab1450deb09ff6e1b32d1b4698da407930b16285e8be3a009ea6cd2695cac01
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1518,13 +1518,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:^18.3.9":
|
||||
version: 18.3.9
|
||||
resolution: "@types/react@npm:18.3.9"
|
||||
"@types/react@npm:^18.3.10":
|
||||
version: 18.3.10
|
||||
resolution: "@types/react@npm:18.3.10"
|
||||
dependencies:
|
||||
"@types/prop-types": "npm:*"
|
||||
csstype: "npm:^3.0.2"
|
||||
checksum: 10c0/a92b8e061d0c833e096254782c56a802316593f4a907fb834b557cabe848a0829b9eb6056404ea239eb4d5ec5ac7b7724309761516c0a7a277916fa04dd4f805
|
||||
checksum: 10c0/f5be1de1b0331c1fdb33d577f4cf7f1b949d4bded5347b2351a537f03c51dade5be115e21b161dcf1b37061954d320f6a0bdf8d7b70e24eda51071fdd614383d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1546,15 +1546,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.7.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.8.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.10.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.7.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.7.0"
|
||||
"@typescript-eslint/utils": "npm:8.7.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.7.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.8.0"
|
||||
"@typescript-eslint/utils": "npm:8.8.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
||||
graphemer: "npm:^1.4.0"
|
||||
ignore: "npm:^5.3.1"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
@@ -1565,66 +1565,66 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/f04d6fa6a30e32d51feba0f08789f75ca77b6b67cfe494bdbd9aafa241871edc918fa8b344dc9d13dd59ae055d42c3920f0e542534f929afbfdca653dae598fa
|
||||
checksum: 10c0/98ac37587eda02a713710f0a62ca979833482024968f1d1735881718abe102a6b49707db4f1dac0d7c731d1cbf8111d829c5125348d4829ab6fad7a7b3b344e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.7.0"
|
||||
"@typescript-eslint/parser@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.7.0"
|
||||
"@typescript-eslint/types": "npm:8.7.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.7.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.7.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
||||
"@typescript-eslint/types": "npm:8.8.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/1d5020ff1f5d3eb726bc6034d23f0a71e8fe7a713756479a0a0b639215326f71c0b44e2c25cc290b4e7c144bd3c958f1405199711c41601f0ea9174068714a64
|
||||
checksum: 10c0/cf72a644b89c62cd55b09fa1d22b51a2c726714aac344a797f0c2ad80bfbabcb7567000fadd4ea8188aa1d923675bebdca06acc1d28ac1b8360bf28a36b46f3a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.7.0"
|
||||
"@typescript-eslint/scope-manager@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.7.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.7.0"
|
||||
checksum: 10c0/8b731a0d0bd3e8f6a322b3b25006f56879b5d2aad86625070fa438b803cf938cb8d5c597758bfa0d65d6e142b204dc6f363fa239bc44280a74e25aa427408eda
|
||||
"@typescript-eslint/types": "npm:8.8.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
||||
checksum: 10c0/29ddf589ff0e465dbbf3eb87b79a29face4ec5a6cb617bbaafbac6ae8340d376b5b405bca762ee1c7a40cbdf7912a32734f9119f6864df048c7a0b2de21bdd3d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.7.0"
|
||||
"@typescript-eslint/type-utils@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": "npm:8.7.0"
|
||||
"@typescript-eslint/utils": "npm:8.7.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
||||
"@typescript-eslint/utils": "npm:8.8.0"
|
||||
debug: "npm:^4.3.4"
|
||||
ts-api-utils: "npm:^1.3.0"
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/2bd9fb93a50ff1c060af41528e39c775ae93b09dd71450defdb42a13c68990dd388460ae4e81fb2f4a49c38dc12152c515d43e845eca6198c44b14aab66733bc
|
||||
checksum: 10c0/d6ee11f4686fb54daea1f436f73b96eb31a95f6e535abc0534abf5794e7597669a92d12300969c8afee0fc1912dbc1591664f7e37f0da5935016cc981b2921a8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/types@npm:8.7.0"
|
||||
checksum: 10c0/f7529eaea4ecc0f5e2d94ea656db8f930f6d1c1e65a3ffcb2f6bec87361173de2ea981405c2c483a35a927b3bdafb606319a1d0395a6feb1284448c8ba74c31e
|
||||
"@typescript-eslint/types@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/types@npm:8.8.0"
|
||||
checksum: 10c0/cd168fafcaf77641b023c4405ea3a8c30fbad1737abb5aec9fce67fe2ae20224b624b5a2e3e84900ba81dc7dd33343add3653763703a225326cc81356b182d09
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.7.0"
|
||||
"@typescript-eslint/typescript-estree@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.7.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.7.0"
|
||||
"@typescript-eslint/types": "npm:8.8.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
||||
debug: "npm:^4.3.4"
|
||||
fast-glob: "npm:^3.3.2"
|
||||
is-glob: "npm:^4.0.3"
|
||||
@@ -1634,31 +1634,31 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/d714605b6920a9631ab1511b569c1c158b1681c09005ab240125c442a63e906048064151a61ce5eb5f8fe75cea861ce5ae1d87be9d7296b012e4ab6d88755e8b
|
||||
checksum: 10c0/9b9e849f6b2d4e250840ef8e05f55a97d6598adaf48c1e6df83084b94c30feca6a3e7916ee1c235178188d0db6364a877cbf8fe218c36d5f8d5acb50767f3273
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.7.0"
|
||||
"@typescript-eslint/utils@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.8.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.7.0"
|
||||
"@typescript-eslint/types": "npm:8.7.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.7.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
||||
"@typescript-eslint/types": "npm:8.8.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
checksum: 10c0/7355b754ce2fc118773ed27a3e02b7dfae270eec73c2d896738835ecf842e8309544dfd22c5105aba6cae2787bfdd84129bbc42f4b514f57909dc7f6890b8eba
|
||||
checksum: 10c0/fcf2dfd4a2d9491aa096a29c2c1fdd891ca3c13933d20cfea44e51b3d10a397e7ed9a9cd71ac9a29e8c4706264ae00c25a29394e2a6bda3291be298062901f2c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.7.0"
|
||||
"@typescript-eslint/visitor-keys@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.7.0"
|
||||
"@typescript-eslint/types": "npm:8.8.0"
|
||||
eslint-visitor-keys: "npm:^3.4.3"
|
||||
checksum: 10c0/1240da13c15f9f875644b933b0ad73713ef12f1db5715236824c1ec359e6ef082ce52dd9b2186d40e28be6a816a208c226e6e9af96e5baeb24b4399fe786ae7c
|
||||
checksum: 10c0/580ce74c9b09b9e6a6f3f0ac2d2f0c6a6b983a78ce3b2544822ee08107c57142858d674897f61ff32a9a5e8fca00c916545c159bb75d134f4380884642542d38
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1678,11 +1678,11 @@ __metadata:
|
||||
"@table-library/react-table-library": "npm:4.1.7"
|
||||
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
|
||||
"@types/formidable": "npm:^3"
|
||||
"@types/node": "npm:^22.7.3"
|
||||
"@types/react": "npm:^18.3.9"
|
||||
"@types/node": "npm:^22.7.4"
|
||||
"@types/react": "npm:^18.3.10"
|
||||
"@types/react-dom": "npm:^18.3.0"
|
||||
"@types/react-router-dom": "npm:^5.3.3"
|
||||
alova: "npm:3.0.16"
|
||||
alova: "npm:3.0.17"
|
||||
async-validator: "npm:^4.2.5"
|
||||
concurrently: "npm:^9.0.1"
|
||||
eslint: "npm:^9.11.1"
|
||||
@@ -1698,10 +1698,10 @@ __metadata:
|
||||
react-router-dom: "npm:^6.26.2"
|
||||
react-toastify: "npm:^10.0.5"
|
||||
rollup-plugin-visualizer: "npm:^5.12.0"
|
||||
terser: "npm:^5.34.0"
|
||||
terser: "npm:^5.34.1"
|
||||
typesafe-i18n: "npm:^5.26.2"
|
||||
typescript: "npm:^5.6.2"
|
||||
typescript-eslint: "npm:8.7.0"
|
||||
typescript-eslint: "npm:8.8.0"
|
||||
vite: "npm:^5.4.8"
|
||||
vite-plugin-imagemin: "npm:^0.6.1"
|
||||
vite-tsconfig-paths: "npm:^5.0.1"
|
||||
@@ -1764,13 +1764,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"alova@npm:3.0.16":
|
||||
version: 3.0.16
|
||||
resolution: "alova@npm:3.0.16"
|
||||
"alova@npm:3.0.17":
|
||||
version: 3.0.17
|
||||
resolution: "alova@npm:3.0.17"
|
||||
dependencies:
|
||||
"@alova/shared": "npm:^1.0.5"
|
||||
rate-limiter-flexible: "npm:^5.0.3"
|
||||
checksum: 10c0/66cb597f4f00feda04b7619dd852fde92bc920cc97b018be70791240c8e8c64677a998a02a684f3aace5997322236a677264f25afe6bcaf4ec856ae42be859a8
|
||||
checksum: 10c0/e8a2ae885a3ff44dafec230d9388dc22b6445bb0cf8511fc9855b5a98ad9961941b0d33a7da874df23db4af0dba75872a470e3edebbdcc5ead8aecbc7fcc3d6b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6654,9 +6654,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"terser@npm:^5.34.0":
|
||||
version: 5.34.0
|
||||
resolution: "terser@npm:5.34.0"
|
||||
"terser@npm:^5.34.1":
|
||||
version: 5.34.1
|
||||
resolution: "terser@npm:5.34.1"
|
||||
dependencies:
|
||||
"@jridgewell/source-map": "npm:^0.3.3"
|
||||
acorn: "npm:^8.8.2"
|
||||
@@ -6664,7 +6664,7 @@ __metadata:
|
||||
source-map-support: "npm:~0.5.20"
|
||||
bin:
|
||||
terser: bin/terser
|
||||
checksum: 10c0/74e8ef4e565e5600415cd9377a90eed419b8076465d453c0c76aef4053c45371512d2de76c34d01e004cdd49ea5a749d77eeb343f7e665b2d172158ca08ba23e
|
||||
checksum: 10c0/51c7d704c5c4ae88bf937124112c9972aed4e1fd29d805cc2d86e0f54cd631ecd4e69db5bb3c1e3b450c741c86e2313328bea0fde925329e8a31a07a7941723c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6803,17 +6803,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.7.0":
|
||||
version: 8.7.0
|
||||
resolution: "typescript-eslint@npm:8.7.0"
|
||||
"typescript-eslint@npm:8.8.0":
|
||||
version: 8.8.0
|
||||
resolution: "typescript-eslint@npm:8.8.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.7.0"
|
||||
"@typescript-eslint/parser": "npm:8.7.0"
|
||||
"@typescript-eslint/utils": "npm:8.7.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.8.0"
|
||||
"@typescript-eslint/parser": "npm:8.8.0"
|
||||
"@typescript-eslint/utils": "npm:8.8.0"
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/c0c3f909227c664f193d11a912851d6144a7cfcc0ac5e57f695c3e50679ef02bb491cc330ad9787e00170ce3be3a3b8c80bb81d5e20a40c1b3ee713ec3b0955a
|
||||
checksum: 10c0/545f0ce051282921aff56288baf288cffe6f7bafee5149f1b87af2c67f81f8c2088924a2e0fc0f0dcd12692b6a97eca10149a619c8c85d4aaef2fe763938da8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -24,11 +24,6 @@ UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager *
|
||||
[this](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
handleUpload(request, filename, index, data, len, final);
|
||||
});
|
||||
|
||||
// upload from a URL
|
||||
server->on(UPLOAD_URL_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { uploadURL(request, json); },
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void UploadFileService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
@@ -91,7 +86,6 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
Update.setMD5(_md5.data());
|
||||
_md5.front() = '\0';
|
||||
}
|
||||
// emsesp::EMSESP::system_.upload_status(true); // force just in case, this is stop UART, MQTT and other services
|
||||
request->onDisconnect([this] { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
||||
} else {
|
||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||
@@ -171,15 +165,3 @@ void UploadFileService::handleEarlyDisconnect() {
|
||||
_is_firmware = false;
|
||||
Update.abort();
|
||||
}
|
||||
|
||||
// upload firmware from a URL, like GitHub Release assets, Cloudflare R2 or Amazon S3
|
||||
void UploadFileService::uploadURL(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
// this will keep a copy of the URL, but won't initiate the download yet
|
||||
emsesp::EMSESP::system_.uploadFirmwareURL(json["url"].as<const char *>());
|
||||
|
||||
// end the connection
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <array>
|
||||
|
||||
#define UPLOAD_FILE_PATH "/rest/uploadFile"
|
||||
#define UPLOAD_URL_PATH "/rest/uploadURL"
|
||||
|
||||
#define TEMP_FILENAME_PATH "/tmp_upload" // for uploaded json files
|
||||
|
||||
@@ -28,7 +27,6 @@ class UploadFileService {
|
||||
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
||||
void uploadComplete(AsyncWebServerRequest * request);
|
||||
void handleError(AsyncWebServerRequest * request, int code);
|
||||
void uploadURL(AsyncWebServerRequest * request, JsonVariant json);
|
||||
|
||||
void handleEarlyDisconnect();
|
||||
};
|
||||
|
||||
@@ -117,6 +117,61 @@ function updateMask(entity: any, de: any, dd: any) {
|
||||
}
|
||||
}
|
||||
|
||||
// called by Action endpoint
|
||||
function export_data(type: string) {
|
||||
console.log('exporting ' + type + '...');
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
return emsesp_info;
|
||||
case 'customizations':
|
||||
return emsesp_deviceentities_2; // fixed for one device
|
||||
case 'entities':
|
||||
return emsesp_customentities;
|
||||
case 'schedule':
|
||||
return emsesp_schedule;
|
||||
case 'modules':
|
||||
return emsesp_modules;
|
||||
case 'allvalues':
|
||||
return emsesp_allvalues;
|
||||
default:
|
||||
return status(404);
|
||||
}
|
||||
}
|
||||
|
||||
// called by Action endpoint
|
||||
function custom_support() {
|
||||
// return {};
|
||||
return {
|
||||
type: 'custom_support',
|
||||
Support: {
|
||||
html: [
|
||||
'This product is installed and managed by:',
|
||||
'',
|
||||
'<b>Bosch Installer Example</b>',
|
||||
'',
|
||||
'Nefit Road 12',
|
||||
'1234 AB Amsterdam',
|
||||
'Phone: +31 123 456 789',
|
||||
'email: support@boschinstaller.nl',
|
||||
'',
|
||||
"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://picsum.photos/200/300'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// called by Action endpoint
|
||||
function check_upgrade(version: string) {
|
||||
console.log('check upgrade from version', version);
|
||||
const data = {
|
||||
upgradeable: true
|
||||
// upgradeable: false
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
// START DATA
|
||||
|
||||
// LOG
|
||||
@@ -402,7 +457,6 @@ const EMSESP_DEVICEDATA_ENDPOINT2 = REST_ENDPOINT_ROOT + 'deviceData/:id?';
|
||||
const EMSESP_DEVICEENTITIES_ENDPOINT1 = REST_ENDPOINT_ROOT + 'deviceEntities';
|
||||
const EMSESP_DEVICEENTITIES_ENDPOINT2 = REST_ENDPOINT_ROOT + 'deviceEntities/:id?';
|
||||
|
||||
const EMSESP_CHECK_UPGRADE_ENDPOINT = REST_ENDPOINT_ROOT + 'checkUpgrade';
|
||||
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile';
|
||||
const EMSESP_WRITE_DEVICEVALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue';
|
||||
const EMSESP_WRITE_DEVICENAME_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceName';
|
||||
@@ -416,7 +470,8 @@ const EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT =
|
||||
const EMSESP_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule';
|
||||
const EMSESP_CUSTOMENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customEntities';
|
||||
const EMSESP_MODULES_ENDPOINT = REST_ENDPOINT_ROOT + 'modules';
|
||||
const EMSESP_EXPORT_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'exportData';
|
||||
|
||||
const EMSESP_ACTION_ENDPOINT = REST_ENDPOINT_ROOT + 'action';
|
||||
|
||||
// these are used in the API calls only
|
||||
const EMSESP_SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info';
|
||||
@@ -4081,16 +4136,10 @@ router
|
||||
router
|
||||
.get(ACTIVITY_ENDPOINT, () => activity)
|
||||
.get(SYSTEM_STATUS_ENDPOINT, () => {
|
||||
if (countHardwarePoll === 0) {
|
||||
console.log('Resetting hardware count...');
|
||||
}
|
||||
|
||||
if (countHardwarePoll >= 2) {
|
||||
countHardwarePoll = 0;
|
||||
system_status.status = 'ready';
|
||||
}
|
||||
|
||||
console.log('Hardware count ' + countHardwarePoll + ' of 2');
|
||||
countHardwarePoll++;
|
||||
|
||||
return system_status;
|
||||
@@ -4402,17 +4451,6 @@ router
|
||||
return status(200);
|
||||
})
|
||||
|
||||
// check upgrade
|
||||
.post(EMSESP_CHECK_UPGRADE_ENDPOINT, async (request: any) => {
|
||||
const content = await request.json();
|
||||
console.log('check upgrade from ', content.version);
|
||||
const data = {
|
||||
upgradeable: true
|
||||
// upgradeable: false
|
||||
};
|
||||
return data;
|
||||
})
|
||||
|
||||
// Settings - board profile
|
||||
.get(EMSESP_BOARDPROFILE_ENDPOINT, (request) => {
|
||||
const board_profile = request.query.boardProfile;
|
||||
@@ -4549,30 +4587,28 @@ router
|
||||
return data;
|
||||
})
|
||||
|
||||
// Download Settings
|
||||
.get(EMSESP_EXPORT_DATA_ENDPOINT, (request) => {
|
||||
const type = request.query.type;
|
||||
console.log('exporting ' + type + ' data');
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
return emsesp_info;
|
||||
case 'customizations':
|
||||
return emsesp_deviceentities_2; // fixed for one device
|
||||
case 'entities':
|
||||
return emsesp_customentities;
|
||||
case 'schedule':
|
||||
return emsesp_schedule;
|
||||
case 'modules':
|
||||
return emsesp_modules;
|
||||
default:
|
||||
return status(404);
|
||||
// generic action for all /rest/... endpoints
|
||||
// takes an action and param in JSON
|
||||
.post(EMSESP_ACTION_ENDPOINT, async (request: any) => {
|
||||
const content = await request.json();
|
||||
if (content.hasOwnProperty('action')) {
|
||||
const action = content.action;
|
||||
if (action === 'export') {
|
||||
// export data
|
||||
return export_data(content.param);
|
||||
} else if (action === 'customSupport') {
|
||||
// send custom support
|
||||
return custom_support();
|
||||
} else if (action === 'checkUpgrade') {
|
||||
// check upgrade
|
||||
return check_upgrade(content.param);
|
||||
} else if (action === 'uploadURL') {
|
||||
// upload URL
|
||||
console.log('upload File from URL', content.param);
|
||||
return status(200);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// upload URL
|
||||
.post('/rest/uploadURL', () => {
|
||||
console.log('upload File from URL');
|
||||
return status(200);
|
||||
return status(404); // cmd not found
|
||||
})
|
||||
|
||||
// API which are usually POST for security
|
||||
@@ -4594,9 +4630,6 @@ router
|
||||
if (cmd === 'info') {
|
||||
return emsesp_info;
|
||||
}
|
||||
if (cmd === 'allvalues') {
|
||||
return emsesp_allvalues;
|
||||
}
|
||||
if (cmd === 'format') {
|
||||
console.log('formatting...');
|
||||
return status(200);
|
||||
|
||||
@@ -39,7 +39,7 @@ unbuild_flags =
|
||||
${common.core_unbuild_flags}
|
||||
|
||||
[espressif32_base]
|
||||
platform = espressif32@6.8.1
|
||||
platform = espressif32@6.9.0
|
||||
framework = arduino
|
||||
board_build.filesystem = littlefs
|
||||
build_flags =
|
||||
@@ -78,7 +78,7 @@ check_flags =
|
||||
clangtidy: --checks=-*,clang-analyzer-*,performance-*
|
||||
lib_ldf_mode = chain+
|
||||
lib_deps =
|
||||
https://github.com/emsesp/EMS-ESP-Modules.git@1.0.1
|
||||
https://github.com/emsesp/EMS-ESP-Modules.git@1.0.2
|
||||
|
||||
;
|
||||
; builds for GitHub Actions CI
|
||||
|
||||
@@ -1345,4 +1345,7 @@ zulufttemp
|
||||
zyxwvutsrqponmlkjihgfedcba
|
||||
Omea
|
||||
Bolv
|
||||
hardwarestatus
|
||||
hardwarestatus
|
||||
hpcurrpower
|
||||
hppowerlimit
|
||||
CUSTOMSUPPORT
|
||||
@@ -102,9 +102,6 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// if (!System::is_valid_gpio(sensor.gpio)) {
|
||||
// continue;
|
||||
// }
|
||||
sensors_.emplace_back(sensor.gpio, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type);
|
||||
sensors_.back().ha_registered = false; // this will trigger recreate of the HA config
|
||||
if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
|
||||
@@ -610,7 +607,7 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
for (auto const & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
|
||||
@@ -420,7 +420,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
} else {
|
||||
if (single_command) {
|
||||
// log as DEBUG (TRACE) regardless if compiled with EMSESP_DEBUG
|
||||
logger_.debug(("%sCalled command %s"), ro.c_str(), info_s);
|
||||
logger_.debug("%sCalled command %s", ro.c_str(), info_s);
|
||||
} else {
|
||||
if (id > 0) {
|
||||
LOG_INFO(("%sCalled command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id);
|
||||
@@ -499,7 +499,7 @@ void Command::erase_command(const uint8_t device_type, const char * cmd, uint8_t
|
||||
return;
|
||||
}
|
||||
auto it = cmdfunctions_.begin();
|
||||
for (auto & cf : cmdfunctions_) {
|
||||
for (auto const & cf : cmdfunctions_) {
|
||||
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type) && ((flag & 0x3F) == (cf.flags_ & 0x3F))) {
|
||||
cmdfunctions_.erase(it);
|
||||
return;
|
||||
@@ -561,16 +561,13 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose) {
|
||||
sorted_cmds.push_back(F_(info));
|
||||
sorted_cmds.push_back(F_(commands));
|
||||
sorted_cmds.push_back(F_(values));
|
||||
}
|
||||
|
||||
sorted_cmds.sort(); // sort them
|
||||
|
||||
// if not in verbose mode, just print them on a single line and exit
|
||||
if (!verbose) {
|
||||
sorted_cmds.emplace_front(F_(info));
|
||||
sorted_cmds.emplace_front(F_(commands));
|
||||
sorted_cmds.emplace_front(F_(values));
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
shell.print(cl);
|
||||
shell.print(" ");
|
||||
@@ -582,14 +579,6 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
// verbose mode
|
||||
shell.printfln("\n%s%s %s:%s", COLOR_BOLD_ON, COLOR_YELLOW, EMSdevice::device_type_2_device_name(device_type), COLOR_RESET);
|
||||
|
||||
// we hard code 'info' and 'commands' commands so print them first
|
||||
shell.printf(" info \t\t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" commands \t\t\t%slist all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" values \t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
// find and print the description
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
@@ -682,6 +671,13 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
||||
// calls show with verbose mode set
|
||||
void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.printfln("Showing all available commands (%s*%s=authentication not required):", COLOR_BRIGHT_GREEN, COLOR_RESET);
|
||||
shell.println("Each device has these additional default commands:");
|
||||
shell.printf(" info \t\t\t\t%slist all values with description%s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" commands \t\t\t%slist all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" values \t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
|
||||
// show system ones first
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
|
||||
@@ -76,54 +76,47 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
//
|
||||
// Show commands
|
||||
//
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {F_(show)}, [=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
to_app(shell).system_.show_system(shell);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
string_vector{F_(show), F_(system)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) { to_app(shell).system_.show_system(shell); });
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
{F_(show)},
|
||||
{F_(show_commands)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
to_app(shell).system_.show_system(shell);
|
||||
return;
|
||||
}
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(show), F_(users)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) { to_app(shell).system_.show_users(shell); });
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
string_vector{F_(show), F_(devices)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) { to_app(shell).show_devices(shell); });
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{F_(show), F_(log)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
to_app(shell).webLogService.show(shell);
|
||||
});
|
||||
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{F_(show), F_(ems)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
to_app(shell).show_ems(shell);
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{F_(show), F_(values)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
to_app(shell).show_device_values(shell);
|
||||
to_app(shell).show_sensor_values(shell);
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
string_vector{F_(show), F_(mqtt)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) { Mqtt::show_mqtt(shell); });
|
||||
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
string_vector{F_(show), F_(commands)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) { Command::show_all(shell); });
|
||||
auto command = arguments.front();
|
||||
if (command == F_(commands)) {
|
||||
Command::show_all(shell);
|
||||
} else if (command == F_(system)) {
|
||||
to_app(shell).system_.show_system(shell);
|
||||
} else if (command == F_(users) && (shell.has_flags(CommandFlags::ADMIN))) {
|
||||
to_app(shell).system_.show_users(shell); // admin only
|
||||
} else if (command == F_(devices)) {
|
||||
to_app(shell).show_devices(shell);
|
||||
} else if (command == F_(log)) {
|
||||
to_app(shell).webLogService.show(shell);
|
||||
} else if (command == F_(ems)) {
|
||||
to_app(shell).show_ems(shell);
|
||||
} else if (command == F_(values)) {
|
||||
to_app(shell).show_device_values(shell);
|
||||
to_app(shell).show_sensor_values(shell);
|
||||
} else if (command == F_(mqtt)) {
|
||||
Mqtt::show_mqtt(shell);
|
||||
} else {
|
||||
shell.printfln("Unknown show command");
|
||||
}
|
||||
},
|
||||
[](Shell & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) -> std::vector<std::string> {
|
||||
return std::vector<std::string>{"system", "users", "devices", "log", "ems", "values", "mqtt", "commands"};
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
// System commands
|
||||
//
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
// create commands test
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
|
||||
@@ -297,7 +297,7 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
|
||||
}
|
||||
|
||||
// returns string of a human friendly description of the EMS device
|
||||
const std::string EMSdevice::to_string() {
|
||||
std::string EMSdevice::to_string() {
|
||||
// for devices that haven't been lookup yet, don't show all details
|
||||
if (product_id_ == 0) {
|
||||
return std::string(name()) + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
|
||||
@@ -313,7 +313,7 @@ const std::string EMSdevice::to_string() {
|
||||
|
||||
// returns out brand + device name
|
||||
// translated
|
||||
const std::string EMSdevice::to_string_short() {
|
||||
std::string EMSdevice::to_string_short() {
|
||||
if (brand_ == Brand::NO_BRAND) {
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + name();
|
||||
}
|
||||
@@ -553,11 +553,11 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||
std::string custom_fullname = std::string(""); // custom fullname
|
||||
auto short_name = name[0]; // entity name
|
||||
bool has_cmd = (f != nullptr); // is it a command?
|
||||
bool ignore = false; // ignore this entity?
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||
auto custom_fullname = std::string(""); // custom fullname
|
||||
auto short_name = name[0]; // entity name
|
||||
bool has_cmd = (f != nullptr); // is it a command?
|
||||
bool ignore = false; // ignore this entity?
|
||||
|
||||
// get fullname, getting translation if it exists
|
||||
const char * const * fullname;
|
||||
@@ -1025,7 +1025,7 @@ void EMSdevice::generate_values_web(JsonObject output) {
|
||||
// this is used only for WebCustomizationService::device_entities()
|
||||
void EMSdevice::generate_values_web_customization(JsonArray output) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
// also show commands and entities that have an empty full name
|
||||
// also show commands and entities that have an empty fullname
|
||||
JsonObject obj = output.add<JsonObject>();
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ class EMSdevice {
|
||||
, flags_(flags)
|
||||
, brand_(brand) {
|
||||
strlcpy(version_, version, sizeof(version_));
|
||||
custom_name_ = ""; // init custom name to blank
|
||||
}
|
||||
|
||||
// static functions, used outside the class like in console.cpp, command.cpp, emsesp.cpp, mqtt.cpp
|
||||
@@ -113,7 +112,7 @@ class EMSdevice {
|
||||
}
|
||||
|
||||
// set custom device name
|
||||
inline void custom_name(std::string & custom_name) {
|
||||
inline void custom_name(std::string const & custom_name) {
|
||||
custom_name_ = custom_name;
|
||||
}
|
||||
std::string name(); // returns either default or custom name if defined
|
||||
@@ -205,17 +204,16 @@ class EMSdevice {
|
||||
int get_modbus_value(uint8_t tag, const std::string & shortname, std::vector<uint16_t> & result);
|
||||
int modbus_value_to_json(uint8_t tag, const std::string & shortname, const std::vector<uint8_t> & modbus_data, JsonObject jsonValue);
|
||||
|
||||
const char * brand_to_char();
|
||||
const std::string to_string();
|
||||
const std::string to_string_short();
|
||||
const char * brand_to_char();
|
||||
std::string to_string();
|
||||
std::string to_string_short();
|
||||
|
||||
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING, IGNORED };
|
||||
|
||||
void show_telegram_handlers(uuid::console::Shell & shell) const;
|
||||
char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers);
|
||||
void show_mqtt_handlers(uuid::console::Shell & shell) const;
|
||||
// void list_device_entries(JsonObject output) const;
|
||||
void add_handlers_ignored(const uint16_t handler);
|
||||
void add_handlers_ignored(const uint16_t handler);
|
||||
|
||||
void set_climate_minmax(int8_t tag, int16_t min, uint32_t max);
|
||||
void setCustomizationEntity(const std::string & entity_id);
|
||||
@@ -456,15 +454,13 @@ class EMSdevice {
|
||||
uint8_t count_entities();
|
||||
bool has_entities() const;
|
||||
|
||||
/*
|
||||
void reserve_device_values(uint8_t elements) {
|
||||
devicevalues_.reserve(elements);
|
||||
}
|
||||
// void reserve_device_values(uint8_t elements) {
|
||||
// devicevalues_.reserve(elements);
|
||||
// }
|
||||
|
||||
void reserve_telegram_functions(uint8_t elements) {
|
||||
telegram_functions_.reserve(elements);
|
||||
}
|
||||
*/
|
||||
// void reserve_telegram_functions(uint8_t elements) {
|
||||
// telegram_functions_.reserve(elements);
|
||||
// }
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
struct TelegramFunctionDump {
|
||||
@@ -487,10 +483,10 @@ class EMSdevice {
|
||||
uint8_t device_id_ = 0;
|
||||
uint8_t product_id_ = 0;
|
||||
char version_[6];
|
||||
const char * default_name_; // the fixed name the EMS model taken from the device library
|
||||
std::string custom_name_; // custom name
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
const char * default_name_; // the fixed name the EMS model taken from the device library
|
||||
std::string custom_name_ = ""; // custom name
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
|
||||
@@ -208,7 +208,7 @@ void EMSESP::uart_init() {
|
||||
uint8_t tx_mode = 0;
|
||||
uint8_t rx_gpio = 0;
|
||||
uint8_t tx_gpio = 0;
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
tx_mode = settings.tx_mode;
|
||||
rx_gpio = settings.rx_gpio;
|
||||
tx_gpio = settings.tx_gpio;
|
||||
@@ -279,7 +279,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
|
||||
if (bus_status() != BUS_STATUS_OFFLINE) {
|
||||
shell.printfln("EMS Bus info:");
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(" Tx mode: %d", settings.tx_mode); });
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) { shell.printfln(" Tx mode: %d", settings.tx_mode); });
|
||||
shell.printfln(" Bus protocol: %s", EMSbus::is_ht3() ? "HT3" : "Buderus");
|
||||
shell.printfln(" #recognized EMS devices: %d", EMSESP::emsdevices.size());
|
||||
shell.printfln(" #telegrams received: %d", rxservice_.telegram_count());
|
||||
@@ -1317,7 +1317,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
emsdevices.push_back(EMSFactory::add(device_type, device_id, product_id, version, default_name, flags, brand));
|
||||
|
||||
// see if we have a custom device name in our Customizations list, and if so set it
|
||||
webCustomizationService.read([&](WebCustomization & settings) {
|
||||
webCustomizationService.read([&](WebCustomization const & settings) {
|
||||
for (EntityCustomization e : settings.entityCustomizations) {
|
||||
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
||||
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
||||
|
||||
@@ -798,10 +798,6 @@ uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
if (tmp > 60) {
|
||||
return 0;
|
||||
}
|
||||
// Serial.print("*");
|
||||
// Serial.print(tmp);
|
||||
// Serial.println("*");
|
||||
|
||||
res += tmp;
|
||||
}
|
||||
// Or we got an extra colon
|
||||
|
||||
@@ -154,6 +154,7 @@ MAKE_WORD_CUSTOM(deviceid_mandatory, "<deviceID>")
|
||||
MAKE_WORD_CUSTOM(device_type_optional, "[device]")
|
||||
MAKE_WORD_CUSTOM(invalid_log_level, "Invalid log level")
|
||||
MAKE_WORD_CUSTOM(log_level_optional, "[level]")
|
||||
MAKE_WORD_CUSTOM(show_commands, "[system | users | devices | log | ems | values | mqtt | commands")
|
||||
MAKE_WORD_CUSTOM(name_mandatory, "<name>")
|
||||
MAKE_WORD_CUSTOM(name_optional, "[name]")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt1, "Enter new password: ")
|
||||
|
||||
@@ -63,7 +63,7 @@ MAKE_WORD_TRANSLATION(info_cmd, "list all values (verbose)", "Liste aller Werte"
|
||||
MAKE_WORD_TRANSLATION(commands_cmd, "list all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "", "wyświetl wszsytkie encje", "Viser alle enheter", "", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "", "wyślij telegram", "send et telegram", "", "Bir telegram gönder", "invia un telegramma", "poslať telegram") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(setiovalue_cmd, "set io value", "Setze Werte E/A", "instellen standaardwaarde", "", "ustaw wartość", "sett en io verdi", "", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(setiovalue_cmd, "set I/O value", "Setze Werte E/A", "instellen standaardwaarde", "", "ustaw wartość", "sett en io verdi", "", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Protokollebene", "aanpassen log niveau", "", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "zmeniť úroveň protokolu") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Aktualisiere alle EMS-Werte", "Verversen alle EMS waardes", "", "odśwież wszystkie wartości EMS", "oppfrisk alle EMS verdier", "", "Bütün EMS değerlerini yenile", "aggiornare tutti i valori EMS", "obnoviť všetky hodnoty EMS") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "opnieuw opstarten", "", "uruchom ponownie EMS-ESP", "restart EMS-ESP", "redémarrer EMS-ESP", "EMS-ESPyi yeniden başlat", "riavvia EMS-ESP", "reštart EMS-ESP") // TODO translate
|
||||
@@ -75,7 +75,6 @@ MAKE_WORD_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplane
|
||||
MAKE_WORD_TRANSLATION(entity_cmd, "set custom value on ems", "Sende eigene Entitäten zu EMS", "verstuur custom waarde naar EMS", "", "wyślij własną wartość na EMS", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati su EMS", "nastaviť vlastnú hodnotu na ems") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoek om antwoord", "", "uzyskaj odpowiedź", "", "", "gelen cevap", "", "získať odpoveď") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(coldshot_cmd, "send a cold shot of water", "Zugabe einer Menge kalten Wassers", "", "", "uruchom tryśnięcie zimnej wody", "", "", "soğuk su gönder", "", "pošlite studenú dávku vody") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(allvalues_cmd, "output all values in system", "Alle Werte im System ausgeben", "", "", "wyświetl wszystkie wartości", "", "", "", "", "vypísať všetky hodnoty") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(message_cmd, "send a message", "Eine Nachricht senden", "", "", "", "", "", "", "", "poslať správu") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(values_cmd, "list all values", "Liste alle Werte auf", "", "", "", "", "", "", "", "vypísať všetky hodnoty") // TODO translate
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
|
||||
#include <modbus_test.h>
|
||||
|
||||
#include <../test/test_modbus/modbus_test.h>
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
@@ -1115,11 +1115,12 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
|
||||
// special case to handle booleans
|
||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||
// has no unit of measure or icon
|
||||
// has no unit of measure or icon, and must be true/false (not on/off or 1/0)
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
add_ha_bool(doc);
|
||||
Helpers::render_boolean(sample_val, false);
|
||||
strlcpy(sample_val, "false", sizeof(sample_val)); // default is "false"
|
||||
}
|
||||
|
||||
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
|
||||
|
||||
// add the dev json object to the end, not for commands
|
||||
|
||||
@@ -124,10 +124,7 @@ void Roomctrl::send(uint8_t addr) {
|
||||
}
|
||||
temperature(addr, 0x10, hc); // send to master-thermostat
|
||||
}
|
||||
} else if (type_[hc] == RC200 || type_[hc] == RC100) {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
temperature(addr, 0x10, hc);
|
||||
} else if (type_[hc] == FB10) {
|
||||
} else if (type_[hc] == RC200 || type_[hc] == RC100 || type_[hc] == FB10) {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336)
|
||||
} else { // type==RC20 or SENSOR
|
||||
|
||||
@@ -25,7 +25,7 @@ uuid::log::Logger Shower::logger_{F_(shower), uuid::log::Facility::CONSOLE};
|
||||
static bool force_coldshot = false;
|
||||
|
||||
void Shower::start() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
shower_timer_ = settings.shower_timer;
|
||||
shower_alert_ = settings.shower_alert;
|
||||
shower_alert_trigger_ = settings.shower_alert_trigger * 60; // convert from minutes to seconds
|
||||
@@ -61,7 +61,6 @@ void Shower::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// uint32_t time_now = uuid::get_uptime(); // in ms
|
||||
auto time_now = uuid::get_uptime_sec(); // in sec
|
||||
|
||||
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
||||
|
||||
@@ -111,44 +111,6 @@ bool System::command_response(const char * value, const int8_t id, JsonObject ou
|
||||
return true;
|
||||
}
|
||||
|
||||
// output all the devices and the values
|
||||
// not system info
|
||||
bool System::command_allvalues(const char * value, const int8_t id, JsonObject output) {
|
||||
JsonDocument doc;
|
||||
JsonObject device_output;
|
||||
// default to values
|
||||
if (value == nullptr || strlen(value) == 0) {
|
||||
value = F_(values);
|
||||
}
|
||||
|
||||
// System Entities
|
||||
// device_output = output["System"].to<JsonObject>();
|
||||
// get_value_info(device_output, value);
|
||||
|
||||
// EMS-Device Entities
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
std::string title = emsdevice->device_type_2_device_name_translated() + std::string(" ") + emsdevice->to_string();
|
||||
device_output = output[title].to<JsonObject>();
|
||||
emsdevice->get_value_info(device_output, value, DeviceValueTAG::TAG_NONE);
|
||||
}
|
||||
|
||||
// Custom Entities
|
||||
device_output = output["Custom Entities"].to<JsonObject>();
|
||||
EMSESP::webCustomEntityService.get_value_info(device_output, value);
|
||||
|
||||
// Scheduler
|
||||
device_output = output["Scheduler"].to<JsonObject>();
|
||||
EMSESP::webSchedulerService.get_value_info(device_output, value);
|
||||
|
||||
// Sensors
|
||||
device_output = output["Analog Sensors"].to<JsonObject>();
|
||||
EMSESP::analogsensor_.get_value_info(device_output, value);
|
||||
device_output = output["Temperature Sensors"].to<JsonObject>();
|
||||
EMSESP::temperaturesensor_.get_value_info(device_output, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch device values
|
||||
bool System::command_fetch(const char * value, const int8_t id) {
|
||||
std::string value_s;
|
||||
@@ -156,13 +118,13 @@ bool System::command_fetch(const char * value, const int8_t id) {
|
||||
if (value_s == "all") {
|
||||
LOG_INFO("Requesting data from EMS devices");
|
||||
EMSESP::fetch_device_values();
|
||||
} else if (value_s == (F_(boiler))) {
|
||||
} else if (value_s == F_(boiler)) {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::BOILER);
|
||||
} else if (value_s == (F_(thermostat))) {
|
||||
} else if (value_s == F_(thermostat)) {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::THERMOSTAT);
|
||||
} else if (value_s == (F_(solar))) {
|
||||
} else if (value_s == F_(solar)) {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::SOLAR);
|
||||
} else if (value_s == (F_(mixer))) {
|
||||
} else if (value_s == F_(mixer)) {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::MIXER);
|
||||
}
|
||||
} else {
|
||||
@@ -338,6 +300,8 @@ void System::system_restart(const char * partitionname) {
|
||||
delay(1000); // wait 1 second
|
||||
ESP.restart();
|
||||
#else
|
||||
restart_requested(false);
|
||||
restart_pending(false);
|
||||
if (partitionname != nullptr) {
|
||||
LOG_INFO("Restarting EMS-ESP from %s partition", partitionname);
|
||||
} else {
|
||||
@@ -375,9 +339,6 @@ void System::syslog_init() {
|
||||
syslog_.destination(syslog_host_.c_str(), syslog_port_);
|
||||
syslog_.hostname(hostname().c_str());
|
||||
|
||||
// removed in 3.6.0
|
||||
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, FL_(changeloglevel_cmd), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
} else if (syslog_.started()) {
|
||||
// in case service is still running, this flushes the queue
|
||||
// https://github.com/emsesp/EMS-ESP/issues/496
|
||||
@@ -874,7 +835,6 @@ void System::commands_init() {
|
||||
|
||||
// these commands will return data in JSON format
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("response"), System::command_response, FL_(commands_response));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("allvalues"), System::command_allvalues, FL_(allvalues_cmd));
|
||||
|
||||
// MQTT subscribe "ems-esp/system/#"
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback
|
||||
@@ -1031,8 +991,11 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(" App used/free: %lu KB / %lu KB", appUsed(), appFree());
|
||||
uint32_t FSused = LittleFS.usedBytes() / 1024;
|
||||
shell.printfln(" FS used/free: %lu KB / %lu KB", FSused, FStotal() - FSused);
|
||||
shell.printfln(" Flash size: %lu KB", ESP.getFlashChipSize() / 1024);
|
||||
if (PSram()) {
|
||||
shell.printfln(" PSRAM size/free: %lu KB / %lu KB", PSram(), ESP.getFreePsram() / 1024);
|
||||
} else {
|
||||
shell.printfln(" PSRAM: not available");
|
||||
}
|
||||
|
||||
shell.println();
|
||||
@@ -1087,7 +1050,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(" WiFi Network: Disconnected");
|
||||
break;
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
// case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(" WiFi MAC address: %s", WiFi.macAddress().c_str());
|
||||
shell.printfln(" WiFi Network: not connected");
|
||||
@@ -1121,7 +1084,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
} else {
|
||||
shell.printfln(" Syslog: %s", syslog_.started() ? "started" : "stopped");
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(host_fmt), !syslog_host_.isEmpty() ? syslog_host_.c_str() : (F_(unset)));
|
||||
shell.printfln(F_(host_fmt), !syslog_host_.isEmpty() ? syslog_host_.c_str() : F_(unset));
|
||||
shell.printfln(" IP: %s", uuid::printable_to_string(syslog_.ip()).c_str());
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(port_fmt), syslog_port_);
|
||||
@@ -1139,7 +1102,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
|
||||
// see if there is a restore of an older settings file that needs to be applied
|
||||
bool System::check_restore() {
|
||||
bool reboot_required = false;
|
||||
bool reboot_required = false; // true if we need to reboot
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File new_file = LittleFS.open(TEMP_FILENAME_PATH);
|
||||
@@ -1167,11 +1130,20 @@ bool System::check_restore() {
|
||||
} else if (settings_type == "entities") {
|
||||
// it's a entity file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", input);
|
||||
} else if (settings_type == "customSupport") {
|
||||
// it's a custom support file - save it to /config
|
||||
new_file.close();
|
||||
if (LittleFS.rename(TEMP_FILENAME_PATH, EMSESP_CUSTOMSUPPORT_FILE)) {
|
||||
LOG_DEBUG("Custom support information found");
|
||||
return false; // no need to reboot
|
||||
} else {
|
||||
LOG_ERROR("Failed to save custom support file");
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Unrecognized file uploaded");
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Unrecognized file uploaded, not json. Will be removed.");
|
||||
LOG_ERROR("Unrecognized file uploaded, not json.");
|
||||
}
|
||||
|
||||
// close (just in case) and remove the temp file
|
||||
@@ -1192,7 +1164,7 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
if (!factory_settings) {
|
||||
// fetch current version from settings file
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { settingsVersion = settings.version.c_str(); });
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) { settingsVersion = settings.version.c_str(); });
|
||||
|
||||
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
|
||||
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
|
||||
@@ -1363,14 +1335,11 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
if (!strcmp(cmd, F_(entities))) {
|
||||
for (JsonPair p : root) {
|
||||
if (p.value().is<JsonObject>()) {
|
||||
// String prefix = p.key().c_str();
|
||||
for (JsonPair p1 : p.value().as<JsonObject>()) {
|
||||
JsonObject entity = output[std::string(p.key().c_str()) + "." + p1.key().c_str()].to<JsonObject>();
|
||||
get_value_json(entity, p.key().c_str(), p1.key().c_str(), p1.value());
|
||||
}
|
||||
} // else { // we don't have pairs in json root object
|
||||
// get_value_json(entity, "", p.key().c_str(), p.value());
|
||||
// }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1523,7 +1492,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
}
|
||||
});
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
|
||||
EMSESP::esp8266React.getAPSettingsService()->read([&](const APSettings & settings) {
|
||||
const char * pM[] = {"always", "disconnected", "never"};
|
||||
node["APProvisionMode"] = pM[settings.provisionMode];
|
||||
node["APSecurity"] = settings.password.length() ? "wpa2" : "open";
|
||||
@@ -1535,11 +1504,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node = output["ntp"].to<JsonObject>();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["NTPStatus"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected";
|
||||
EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
|
||||
EMSESP::esp8266React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
|
||||
node["enabled"] = settings.enabled;
|
||||
node["server"] = settings.server;
|
||||
node["tzLabel"] = settings.tzLabel;
|
||||
// node["tz format"] = settings.tzFormat;
|
||||
});
|
||||
#endif
|
||||
|
||||
@@ -1552,7 +1520,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["MQTTPublishFails"] = Mqtt::publish_fails();
|
||||
node["MQTTConnects"] = Mqtt::connect_count();
|
||||
}
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](const MqttSettings & settings) {
|
||||
node["enabled"] = settings.enabled;
|
||||
node["clientID"] = settings.clientId;
|
||||
node["keepAlive"] = settings.keepAlive;
|
||||
@@ -1631,7 +1599,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["busStatus"] = "unknown";
|
||||
break;
|
||||
}
|
||||
// if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) {
|
||||
node["busProtocol"] = EMSbus::is_ht3() ? "HT3" : "Buderus";
|
||||
node["busTelegramsReceived"] = EMSESP::rxservice_.telegram_count();
|
||||
node["busReads"] = EMSESP::txservice_.telegram_read_count();
|
||||
@@ -1641,11 +1608,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["busWritesFailed"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
node["busRxLineQuality"] = EMSESP::rxservice_.quality();
|
||||
node["busTxLineQuality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2;
|
||||
// }
|
||||
|
||||
// Settings
|
||||
node = output["settings"].to<JsonObject>();
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
EMSESP::webSettingsService.read([&](const WebSettings & settings) {
|
||||
node["boardProfile"] = settings.board_profile;
|
||||
node["locale"] = settings.locale;
|
||||
node["txMode"] = settings.tx_mode;
|
||||
@@ -1732,7 +1698,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
obj["name"] = F_(temperaturesensor);
|
||||
obj["entities"] = EMSESP::temperaturesensor_.count_entities();
|
||||
}
|
||||
// if (EMSESP::analog_enabled()) {
|
||||
if (EMSESP::analogsensor_.count_entities()) {
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = F_(analogsensor);
|
||||
@@ -1895,7 +1860,7 @@ std::string System::reset_reason(uint8_t cpu) const {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return ("Unknown");
|
||||
return "Unknown";
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ using uuid::console::Shell;
|
||||
|
||||
#define EMSESP_FS_CONFIG_DIRECTORY "/config"
|
||||
|
||||
#define EMSESP_CUSTOMSUPPORT_FILE "/config/customSupport.json"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 };
|
||||
@@ -58,12 +60,10 @@ class System {
|
||||
static bool command_fetch(const char * value, const int8_t id);
|
||||
static bool command_restart(const char * value, const int8_t id);
|
||||
static bool command_format(const char * value, const int8_t id);
|
||||
// static bool command_syslog_level(const char * value, const int8_t id);
|
||||
static bool command_watch(const char * value, const int8_t id);
|
||||
static bool command_message(const char * value, const int8_t id);
|
||||
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
||||
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
||||
static bool command_allvalues(const char * value, const int8_t id, JsonObject output);
|
||||
|
||||
static bool get_value_info(JsonObject root, const char * cmd);
|
||||
static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val);
|
||||
@@ -162,10 +162,12 @@ class System {
|
||||
readonly_mode_ = readonly_mode;
|
||||
}
|
||||
|
||||
// Boolean Format API/MQTT
|
||||
uint8_t bool_format() {
|
||||
return bool_format_;
|
||||
}
|
||||
|
||||
// Boolean Format Web
|
||||
uint8_t bool_dashboard() {
|
||||
return bool_dashboard_;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ void TemperatureSensor::start() {
|
||||
// load settings
|
||||
void TemperatureSensor::reload() {
|
||||
// load the service settings
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
parasite_ = settings.dallas_parasite;
|
||||
});
|
||||
@@ -274,7 +274,7 @@ int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
case 11:
|
||||
raw_value &= ~0x1;
|
||||
break;
|
||||
case 12:
|
||||
default: // 12
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -302,7 +302,7 @@ bool TemperatureSensor::update(const std::string & id, const std::string & name,
|
||||
sensor.set_offset(offset);
|
||||
|
||||
// store the new name and offset in our configuration
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
EMSESP::webCustomizationService.update([&id, &name, &offset, &sensor](WebCustomization & settings) {
|
||||
// look it up to see if it exists
|
||||
bool found = false;
|
||||
for (auto & SensorCustomization : settings.sensorCustomizations) {
|
||||
@@ -315,10 +315,10 @@ bool TemperatureSensor::update(const std::string & id, const std::string & name,
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
SensorCustomization newSensor = SensorCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
auto newSensor = SensorCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for sensor ID %s", id.c_str());
|
||||
}
|
||||
@@ -401,9 +401,9 @@ void TemperatureSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::enabled() && Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", (F_(temperaturesensor)), sensor.name().c_str());
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(temperaturesensor), sensor.name().c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(temperaturesensor)), "_data", sensor.name().c_str());
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(temperaturesensor), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
@@ -513,7 +513,7 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
@@ -570,8 +570,8 @@ std::string TemperatureSensor::Sensor::name() const {
|
||||
// look up in customization service for a specific sensor
|
||||
// and set the name and offset from that entry if it exists
|
||||
bool TemperatureSensor::Sensor::apply_customization() {
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
auto sensors = settings.sensorCustomizations;
|
||||
EMSESP::webCustomizationService.read([&](const WebCustomization & settings) {
|
||||
auto const & sensors = settings.sensorCustomizations;
|
||||
if (!sensors.empty()) {
|
||||
for (const auto & sensor : sensors) {
|
||||
if (id_ == sensor.id) {
|
||||
|
||||
@@ -90,23 +90,23 @@ class TemperatureSensor {
|
||||
return sensors_;
|
||||
}
|
||||
|
||||
uint32_t reads() {
|
||||
uint32_t reads() const {
|
||||
return sensorreads_;
|
||||
}
|
||||
|
||||
uint32_t fails() {
|
||||
uint32_t fails() const {
|
||||
return sensorfails_;
|
||||
}
|
||||
|
||||
bool sensor_enabled() {
|
||||
bool sensor_enabled() const {
|
||||
return (dallas_gpio_ != 0);
|
||||
}
|
||||
|
||||
bool have_sensors() {
|
||||
bool have_sensors() const {
|
||||
return (!sensors_.empty());
|
||||
}
|
||||
|
||||
size_t count_entities() {
|
||||
size_t count_entities() const {
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
|
||||
@@ -327,7 +327,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// shell.invoke_command("show devices");
|
||||
// shell.invoke_command("show values");
|
||||
// shell.invoke_command("call system allvalues");
|
||||
// shell.invoke_command("call system publish");
|
||||
// shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
@@ -792,7 +791,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call temperaturesensor");
|
||||
shell.invoke_command("show values");
|
||||
shell.invoke_command("call system allvalues");
|
||||
shell.invoke_command("call temperaturesensor info");
|
||||
shell.invoke_command("call temperaturesensor values");
|
||||
|
||||
@@ -842,7 +840,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call analogsensor");
|
||||
shell.invoke_command("show values");
|
||||
shell.invoke_command("call system allvalues");
|
||||
shell.invoke_command("call analogsensor info");
|
||||
shell.invoke_command("call analogsensor values");
|
||||
|
||||
@@ -992,11 +989,26 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// EMSESP::webAPIService.webAPIService(&request);
|
||||
|
||||
request.method(HTTP_POST);
|
||||
char data_api[] = "{\"device\":\"system\", \"cmd\":\"restart\",\"id\":-1}";
|
||||
deserializeJson(doc, data_api);
|
||||
json = doc.as<JsonVariant>();
|
||||
|
||||
char data1[] = "{\"device\":\"system\", \"cmd\":\"restart\",\"id\":-1}";
|
||||
deserializeJson(doc, data1);
|
||||
request.url("/api");
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, doc.as<JsonVariant>());
|
||||
|
||||
char data2[] = "{\"action\":\"customSupport\", \"param\":\"hello\"}";
|
||||
deserializeJson(doc, data2);
|
||||
request.url("/rest/action");
|
||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||
|
||||
char data3[] = "{\"action\":\"export\", \"param\":\"schedule\"}";
|
||||
deserializeJson(doc, data3);
|
||||
request.url("/rest/action");
|
||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||
|
||||
char data4[] = "{\"action\":\"export\", \"param\":\"allvalues\"}";
|
||||
deserializeJson(doc, data4);
|
||||
request.url("/rest/action");
|
||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||
|
||||
// request.url("/api/thermostat");
|
||||
// EMSESP::webAPIService.webAPIService(&request);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.7.0-dev.41"
|
||||
#define EMSESP_APP_VERSION "3.7.0-dev.42"
|
||||
|
||||
@@ -58,7 +58,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||
void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
|
||||
// check if the user has admin privileges (token is included and authorized)
|
||||
bool is_admin = false;
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
is_admin = settings.notoken_api || AuthenticationPredicates::IS_ADMIN(authentication);
|
||||
});
|
||||
@@ -94,7 +94,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
|
||||
emsesp::EMSESP::system_.refreshHeapMem();
|
||||
|
||||
// output json buffer
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
auto response = new AsyncJsonResponse(false);
|
||||
|
||||
// add more mem if needed - won't be needed in ArduinoJson 7
|
||||
// while (!response->getSize()) {
|
||||
|
||||
@@ -47,7 +47,7 @@ void WebCustomEntityService::begin() {
|
||||
void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) {
|
||||
JsonArray entity = root["entities"].to<JsonArray>();
|
||||
uint8_t counter = 0;
|
||||
for (const CustomEntityItem & entityItem : webEntity.customEntityItems) {
|
||||
for (CustomEntityItem & entityItem : webEntity.customEntityItems) {
|
||||
JsonObject ei = entity.add<JsonObject>();
|
||||
ei["id"] = counter++; // id is only used to render the table and must be unique
|
||||
ei["ram"] = entityItem.ram;
|
||||
@@ -213,7 +213,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
|
||||
// output of a single value
|
||||
// if add_uom is true it will add the UOM string to the value
|
||||
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem entity, const bool useVal, const bool web, const bool add_uom) {
|
||||
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem & entity, const bool useVal, const bool web, const bool add_uom) {
|
||||
char payload[12];
|
||||
std::string name = useVal ? "value" : entity.name;
|
||||
switch (entity.value_type) {
|
||||
@@ -262,7 +262,7 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem en
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
// case DeviceValueType::STRING:
|
||||
default:
|
||||
// if no type treat it as a string
|
||||
if (entity.data.length() > 0) {
|
||||
@@ -275,7 +275,7 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem en
|
||||
// display all custom entities
|
||||
// adding each one, with UOM to a json object string
|
||||
void WebCustomEntityService::show_values(JsonObject output) {
|
||||
for (const CustomEntityItem & entity : *customEntityItems_) {
|
||||
for (CustomEntityItem & entity : *customEntityItems_) {
|
||||
render_value(output, entity, false, false, true); // with add_uom
|
||||
}
|
||||
}
|
||||
@@ -285,14 +285,14 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
// if no custom entries, return empty json
|
||||
// even if we're looking for a specific entity
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/1297
|
||||
if (customEntityItems_->size() == 0) {
|
||||
if (customEntityItems_->empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if it's info or values...
|
||||
if (!strlen(cmd) || !strcmp(cmd, F_(values)) || !strcmp(cmd, F_(info))) {
|
||||
// list all names
|
||||
for (const CustomEntityItem & entity : *customEntityItems_) {
|
||||
for (CustomEntityItem & entity : *customEntityItems_) {
|
||||
render_value(output, entity);
|
||||
}
|
||||
return true;
|
||||
@@ -300,7 +300,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
|
||||
// list all entities
|
||||
if (!strcmp(cmd, F_(entities))) {
|
||||
for (const auto & entity : *customEntityItems_) {
|
||||
for (auto & entity : *customEntityItems_) {
|
||||
auto nest = output[entity.name].to<JsonObject>();
|
||||
get_value_json(nest, entity);
|
||||
}
|
||||
@@ -309,7 +309,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
|
||||
// specific value info
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
for (const auto & entity : *customEntityItems_) {
|
||||
for (auto & entity : *customEntityItems_) {
|
||||
if (Helpers::toLower(entity.name) == cmd) {
|
||||
get_value_json(output, entity);
|
||||
return Command::set_attribute(output, cmd, attribute_s);
|
||||
@@ -319,7 +319,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
}
|
||||
|
||||
// build the json for specific entity
|
||||
void WebCustomEntityService::get_value_json(JsonObject output, const CustomEntityItem & entity) {
|
||||
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem & entity) {
|
||||
output["name"] = entity.name;
|
||||
output["fullname"] = entity.name;
|
||||
output["storage"] = entity.ram ? "ram" : "ems";
|
||||
@@ -344,7 +344,7 @@ void WebCustomEntityService::get_value_json(JsonObject output, const CustomEntit
|
||||
}
|
||||
|
||||
// publish single value
|
||||
void WebCustomEntityService::publish_single(const CustomEntityItem & entity) {
|
||||
void WebCustomEntityService::publish_single(CustomEntityItem & entity) {
|
||||
if (!Mqtt::enabled() || !Mqtt::publish_single()) {
|
||||
return;
|
||||
}
|
||||
@@ -372,11 +372,11 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (customEntityItems_->size() == 0) {
|
||||
if (customEntityItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
if (Mqtt::publish_single() && force) {
|
||||
for (const CustomEntityItem & entityItem : *customEntityItems_) {
|
||||
for (CustomEntityItem & entityItem : *customEntityItems_) {
|
||||
publish_single(entityItem);
|
||||
}
|
||||
}
|
||||
@@ -385,7 +385,7 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
bool ha_created = ha_registered_;
|
||||
|
||||
for (const CustomEntityItem & entityItem : *customEntityItems_) {
|
||||
for (CustomEntityItem & entityItem : *customEntityItems_) {
|
||||
render_value(output, entityItem);
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && !ha_registered_) {
|
||||
@@ -454,16 +454,16 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
|
||||
// count only entities with valid value or command to show in dashboard
|
||||
uint8_t WebCustomEntityService::count_entities() {
|
||||
if (customEntityItems_->size() == 0) {
|
||||
if (customEntityItems_->empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
uint8_t count = 0;
|
||||
for (const CustomEntityItem & entity : *customEntityItems_) {
|
||||
|
||||
for (CustomEntityItem & entity : *customEntityItems_) {
|
||||
render_value(output, entity);
|
||||
// TODO check JsonVariant
|
||||
if (output[entity.name].is<JsonVariantConst>() || entity.writeable) {
|
||||
count++;
|
||||
}
|
||||
@@ -558,7 +558,7 @@ void WebCustomEntityService::generate_value_web(JsonObject output) {
|
||||
void WebCustomEntityService::fetch() {
|
||||
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3, 4};
|
||||
|
||||
for (auto & entity : *customEntityItems_) {
|
||||
for (auto const & entity : *customEntityItems_) {
|
||||
if (entity.device_id > 0 && entity.type_id > 0) { // this excludes also RAM type
|
||||
bool needFetch = true;
|
||||
uint8_t fetchblock = entity.type_id > 0x0FF ? 25 : 27;
|
||||
@@ -580,7 +580,6 @@ void WebCustomEntityService::fetch() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// EMSESP::logger().debug("fetch custom entities");
|
||||
}
|
||||
|
||||
// called on process telegram, read from telegram
|
||||
|
||||
@@ -55,14 +55,14 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
|
||||
WebCustomEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void publish_single(const CustomEntityItem & entity);
|
||||
void publish_single(CustomEntityItem & entity);
|
||||
void publish(const bool force = false);
|
||||
bool command_setvalue(const char * value, const int8_t id, const char * name);
|
||||
bool get_value_info(JsonObject output, const char * cmd);
|
||||
void get_value_json(JsonObject output, const CustomEntityItem & entity);
|
||||
void get_value_json(JsonObject output, CustomEntityItem & entity);
|
||||
bool get_value(std::shared_ptr<const Telegram> telegram);
|
||||
void fetch();
|
||||
void render_value(JsonObject output, CustomEntityItem entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
|
||||
void render_value(JsonObject output, CustomEntityItem & entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
|
||||
void show_values(JsonObject output);
|
||||
void generate_value_web(JsonObject output);
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id,
|
||||
|
||||
// process json output for info/commands and value_info
|
||||
bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
if (scheduleItems_->size() == 0) {
|
||||
if (scheduleItems_->empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ void WebSchedulerService::publish(const bool force) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scheduleItems_->size() == 0) {
|
||||
if (scheduleItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ void WebSchedulerService::loop() {
|
||||
static uint32_t last_uptime_sec = 0;
|
||||
|
||||
// get list of scheduler events and exit if it's empty
|
||||
if (scheduleItems_->size() == 0) {
|
||||
if (scheduleItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -584,7 +584,7 @@ void WebSchedulerService::test() {
|
||||
test_value = "(custom/seltemp)";
|
||||
command("test5", test_cmd.c_str(), compute(test_value).c_str());
|
||||
|
||||
// this will fail unless test("boiler") is loaded
|
||||
// note: this will fail unless test("boiler") is loaded before hand
|
||||
test_value = "(boiler/outdoortemp)";
|
||||
command("test6", test_cmd.c_str(), compute(test_value).c_str());
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
// GET
|
||||
server->on(EMSESP_SYSTEM_STATUS_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { systemStatus(request); });
|
||||
server->on(EMSESP_EXPORT_DATA_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { exportData(request); });
|
||||
|
||||
// POST
|
||||
server->on(EMSESP_CHECK_UPGRADE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { checkUpgrade(request, json); });
|
||||
// POST - generic action handler
|
||||
server->on(EMSESP_ACTION_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { action(request, json); });
|
||||
}
|
||||
|
||||
// /rest/systemStatus
|
||||
@@ -146,13 +146,55 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// returns trues if there is an upgrade available
|
||||
void WebStatusService::checkUpgrade(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
// generic action handler - as a POST
|
||||
void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
auto * response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// get action and any optional param
|
||||
std::string action = json["action"];
|
||||
std::string param = json["param"]; // is optional
|
||||
|
||||
// check if we're authenticated for admin tasks, some actions are only for admins
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
bool is_admin = AuthenticationPredicates::IS_ADMIN(authentication);
|
||||
|
||||
bool ok = true;
|
||||
if (action == "checkUpgrade") {
|
||||
ok = checkUpgrade(root, param);
|
||||
} else if (action == "export") {
|
||||
ok = exportData(root, param);
|
||||
} else if (action == "customSupport") {
|
||||
ok = customSupport(root);
|
||||
} else if (action == "uploadURL" && is_admin) {
|
||||
ok = uploadURL(param.c_str());
|
||||
}
|
||||
|
||||
#if defined(EMSESP_UNITY)
|
||||
// store the result so we can test with Unity later
|
||||
storeResponse(output);
|
||||
#endif
|
||||
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)
|
||||
Serial.printf("%sweb output: %s[%s]", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str());
|
||||
Serial.printf(" %s(%d)%s ", ok ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_RED, ok ? 200 : 400, COLOR_YELLOW);
|
||||
serializeJson(root, Serial);
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
// send response
|
||||
if (!ok) {
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// action = checkUpgrade
|
||||
// returns true if there is an upgrade available
|
||||
bool WebStatusService::checkUpgrade(JsonObject root, std::string & latest_version) {
|
||||
version::Semver200_version settings_version(EMSESP_APP_VERSION);
|
||||
const std::string latest_version = json["version"] | EMSESP_APP_VERSION;
|
||||
version::Semver200_version this_version(latest_version);
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
@@ -161,16 +203,40 @@ void WebStatusService::checkUpgrade(AsyncWebServerRequest * request, JsonVariant
|
||||
|
||||
root["upgradeable"] = (this_version > settings_version);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns data for a specific feature/settings as a json object
|
||||
void WebStatusService::exportData(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot();
|
||||
// action = allvalues
|
||||
// output all the devices and the values
|
||||
void WebStatusService::allvalues(JsonObject output) {
|
||||
JsonObject device_output;
|
||||
auto value = F_(values);
|
||||
|
||||
String type = request->getParam("type")->value();
|
||||
// EMS-Device Entities
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
std::string title = emsdevice->device_type_2_device_name_translated() + std::string(" ") + emsdevice->to_string();
|
||||
device_output = output[title].to<JsonObject>();
|
||||
emsdevice->get_value_info(device_output, value, DeviceValueTAG::TAG_NONE);
|
||||
}
|
||||
|
||||
// Custom Entities
|
||||
device_output = output["Custom Entities"].to<JsonObject>();
|
||||
EMSESP::webCustomEntityService.get_value_info(device_output, value);
|
||||
|
||||
// Scheduler
|
||||
device_output = output["Scheduler"].to<JsonObject>();
|
||||
EMSESP::webSchedulerService.get_value_info(device_output, value);
|
||||
|
||||
// Sensors
|
||||
device_output = output["Analog Sensors"].to<JsonObject>();
|
||||
EMSESP::analogsensor_.get_value_info(device_output, value);
|
||||
device_output = output["Temperature Sensors"].to<JsonObject>();
|
||||
EMSESP::temperaturesensor_.get_value_info(device_output, value);
|
||||
}
|
||||
|
||||
// action = export
|
||||
// returns data for a specific feature/settings as a json object
|
||||
bool WebStatusService::exportData(JsonObject root, std::string & type) {
|
||||
root["type"] = type;
|
||||
|
||||
if (type == "settings") {
|
||||
@@ -188,13 +254,44 @@ void WebStatusService::exportData(AsyncWebServerRequest * request) {
|
||||
System::extractSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", root);
|
||||
} else if (type == "entities") {
|
||||
System::extractSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", root);
|
||||
} else if (type == "allvalues") {
|
||||
root.clear(); // don't need the "type" key
|
||||
allvalues(root);
|
||||
} else {
|
||||
request->send(400);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// action = customSupport
|
||||
// reads any upload customSupport.json file and sends to to Help page to be shown as Guest
|
||||
bool WebStatusService::customSupport(JsonObject root) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// check if we have custom support file uploaded
|
||||
File file = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "r");
|
||||
if (!file) {
|
||||
// there is no custom file, return empty object
|
||||
return true;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
// read the contents of the file into the root output json object
|
||||
DeserializationError error = deserializeJson(root, file);
|
||||
if (error) {
|
||||
emsesp::EMSESP::logger().err("Failed to read custom support file");
|
||||
return false;
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// action = uploadURL
|
||||
// uploads a firmware file from a URL
|
||||
bool WebStatusService::uploadURL(const char * url) {
|
||||
// this will keep a copy of the URL, but won't initiate the download yet
|
||||
emsesp::EMSESP::system_.uploadFirmwareURL(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#define WebStatusService_h
|
||||
|
||||
#define EMSESP_SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"
|
||||
#define EMSESP_CHECK_UPGRADE_PATH "/rest/checkUpgrade"
|
||||
#define EMSESP_EXPORT_DATA_SERVICE_PATH "/rest/exportData"
|
||||
#define EMSESP_ACTION_SERVICE_PATH "/rest/action"
|
||||
|
||||
#include <semver200.h> // for version checking
|
||||
|
||||
@@ -13,10 +12,23 @@ class WebStatusService {
|
||||
public:
|
||||
WebStatusService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
// make action function public so we can test in the debug and standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
protected:
|
||||
#endif
|
||||
void systemStatus(AsyncWebServerRequest * request);
|
||||
void checkUpgrade(AsyncWebServerRequest * request, JsonVariant json);
|
||||
void exportData(AsyncWebServerRequest * request);
|
||||
void action(AsyncWebServerRequest * request, JsonVariant json);
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
|
||||
// actions
|
||||
bool checkUpgrade(JsonObject root, std::string & latest_version);
|
||||
bool exportData(JsonObject root, std::string & type);
|
||||
bool customSupport(JsonObject root);
|
||||
bool uploadURL(const char * url);
|
||||
|
||||
void allvalues(JsonObject output);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
Reference in New Issue
Block a user