mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-15 12:26:33 +03:00
@@ -35,10 +35,10 @@
|
|||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.29.2",
|
"preact": "^10.29.2",
|
||||||
"react": "^19.2.6",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^19.2.6",
|
"react-dom": "^19.2.7",
|
||||||
"react-icons": "^5.6.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-router": "^7.16.0",
|
"react-router": "^7.17.0",
|
||||||
"react-toastify": "^11.1.0",
|
"react-toastify": "^11.1.0",
|
||||||
"typesafe-i18n": "^5.27.1",
|
"typesafe-i18n": "^5.27.1",
|
||||||
"typescript": "^6.0.3"
|
"typescript": "^6.0.3"
|
||||||
@@ -47,18 +47,18 @@
|
|||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/preset-vite": "^2.10.5",
|
"@preact/preset-vite": "^2.10.5",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.2",
|
||||||
"@types/react": "^19.2.15",
|
"@types/react": "^19.2.17",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"concurrently": "^10.0.0",
|
"concurrently": "^10.0.3",
|
||||||
"eslint": "^10.4.0",
|
"eslint": "^10.4.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.48.0",
|
"terser": "^5.48.0",
|
||||||
"typescript-eslint": "^8.60.0",
|
"typescript-eslint": "^8.60.1",
|
||||||
"vite": "^8.0.14",
|
"vite": "^8.0.16",
|
||||||
"vite-plugin-imagemin": "^0.6.1"
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.34.1+sha512.b58fbde6dca66a929538021581f648b4570b6ca19b18e7cbd7f2c07a7b24454155388dacdf08f2af3678e88a6d1fe04f9d609df24bf51735a060ea041b374ab7"
|
"packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916"
|
||||||
}
|
}
|
||||||
|
|||||||
662
interface/pnpm-lock.yaml
generated
662
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
11
interface/pnpm-workspace.yaml
Normal file
11
interface/pnpm-workspace.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
allowBuilds:
|
||||||
|
cwebp-bin: false
|
||||||
|
esbuild: false
|
||||||
|
gifsicle: false
|
||||||
|
jpegtran-bin: false
|
||||||
|
mozjpeg: false
|
||||||
|
optipng-bin: false
|
||||||
|
pngquant-bin: false
|
||||||
|
minimumReleaseAgeExclude:
|
||||||
|
- '@types/node@25.9.2'
|
||||||
|
- '@types/react@19.2.17'
|
||||||
@@ -7,6 +7,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
|||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
|
import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
|
||||||
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
||||||
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
||||||
import {
|
import {
|
||||||
@@ -75,24 +76,25 @@ const Dashboard = memo(() => {
|
|||||||
{
|
{
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
.onSuccess(() => {
|
||||||
|
toast.success(LL.WRITE_CMD_SENT());
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
||||||
if (!selectedDashboardItem) {
|
if (!selectedDashboardItem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// skip if we're executing an immediate schedule as this is handled in DevicesDialog::doAction()
|
||||||
|
if (devicevalue.v !== undefined) {
|
||||||
const id = selectedDashboardItem.parentNode.id; // this is the parent ID
|
const id = selectedDashboardItem.parentNode.id; // this is the parent ID
|
||||||
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
|
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v });
|
||||||
.then(() => {
|
}
|
||||||
toast.success(LL.WRITE_CMD_SENT());
|
|
||||||
})
|
|
||||||
.catch((error: Error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setDeviceValueDialogOpen(false);
|
setDeviceValueDialogOpen(false);
|
||||||
setSelectedDashboardItem(undefined);
|
setSelectedDashboardItem(undefined);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const dashboard_theme = useTheme({
|
const dashboard_theme = useTheme({
|
||||||
@@ -210,7 +212,12 @@ const Dashboard = memo(() => {
|
|||||||
(parseInt(id.slice(0, 2), 16) & mask) === mask;
|
(parseInt(id.slice(0, 2), 16) & mask) === mask;
|
||||||
|
|
||||||
const editDashboardValue = (di: DashboardItem) => {
|
const editDashboardValue = (di: DashboardItem) => {
|
||||||
if (me.admin && di.dv?.c) {
|
// don't execute on parent nodes
|
||||||
|
if (!me.admin || di.id <= 99) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (di.dv?.c) {
|
||||||
setSelectedDashboardItem(di);
|
setSelectedDashboardItem(di);
|
||||||
setDeviceValueDialogOpen(true);
|
setDeviceValueDialogOpen(true);
|
||||||
}
|
}
|
||||||
@@ -321,7 +328,19 @@ const Dashboard = memo(() => {
|
|||||||
<Cell>
|
<Cell>
|
||||||
{me.admin &&
|
{me.admin &&
|
||||||
di.dv?.c &&
|
di.dv?.c &&
|
||||||
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && (
|
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) &&
|
||||||
|
(di.dv.v === '' || di.dv.v === undefined ? (
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
aria-label={LL.RUN_COMMAND()}
|
||||||
|
onClick={() => editDashboardValue(di)}
|
||||||
|
>
|
||||||
|
<PlayArrowIcon
|
||||||
|
color="primary"
|
||||||
|
sx={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
) : (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
aria-label={
|
aria-label={
|
||||||
@@ -334,7 +353,7 @@ const Dashboard = memo(() => {
|
|||||||
sx={{ fontSize: 16 }}
|
sx={{ fontSize: 16 }}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
))}
|
||||||
</Cell>
|
</Cell>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
@@ -18,7 +19,9 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { callAction } from '@/api/app';
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
import type Schema from 'async-validator';
|
import type Schema from 'async-validator';
|
||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import { ValidatedTextField } from 'components';
|
import { ValidatedTextField } from 'components';
|
||||||
@@ -61,13 +64,29 @@ const DevicesDialog = ({
|
|||||||
}
|
}
|
||||||
}, [open, selectedItem]);
|
}, [open, selectedItem]);
|
||||||
|
|
||||||
const save = async () => {
|
const { send: executeSchedule } = useRequest(
|
||||||
|
(id: string) => callAction({ action: 'executeSchedule', param: id }),
|
||||||
|
{ immediate: false }
|
||||||
|
)
|
||||||
|
.onSuccess(() => {
|
||||||
|
toast.success(LL.EXECUTE_SCHEDULE_SENT());
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const doAction = async () => {
|
||||||
try {
|
try {
|
||||||
setFieldErrors(undefined);
|
setFieldErrors(undefined);
|
||||||
|
if (editItem.v === undefined && editItem.c !== undefined) {
|
||||||
|
await executeSchedule(editItem.c);
|
||||||
|
} else {
|
||||||
await validate(validator, editItem);
|
await validate(validator, editItem);
|
||||||
onSave(editItem);
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors((error as ValidationError).fieldErrors);
|
||||||
|
} finally {
|
||||||
|
onSave(editItem);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,9 +119,14 @@ const DevicesDialog = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCommand = selectedItem.v === '' && selectedItem.c;
|
const isCommand =
|
||||||
|
(selectedItem.v === '' || selectedItem.v === undefined) &&
|
||||||
|
Boolean(selectedItem.c);
|
||||||
|
const isSchedulerImmediate = selectedItem.v === undefined;
|
||||||
const dialogTitle = isCommand
|
const dialogTitle = isCommand
|
||||||
? LL.RUN_COMMAND()
|
? isSchedulerImmediate
|
||||||
|
? LL.EXECUTE() + ' ' + LL.SCHEDULE(0)
|
||||||
|
: LL.RUN_COMMAND()
|
||||||
: writeable
|
: writeable
|
||||||
? LL.CHANGE_VALUE()
|
? LL.CHANGE_VALUE()
|
||||||
: LL.VALUE(0);
|
: LL.VALUE(0);
|
||||||
@@ -118,6 +142,7 @@ const DevicesDialog = ({
|
|||||||
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
{editItem.id.slice(2)}
|
{editItem.id.slice(2)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{!isSchedulerImmediate && (
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
{editItem.l ? (
|
{editItem.l ? (
|
||||||
@@ -179,6 +204,7 @@ const DevicesDialog = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
@@ -202,7 +228,7 @@ const DevicesDialog = ({
|
|||||||
<Button
|
<Button
|
||||||
startIcon={<WarningIcon color="warning" />}
|
startIcon={<WarningIcon color="warning" />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={save}
|
onClick={doAction}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{buttonLabel}
|
{buttonLabel}
|
||||||
|
|||||||
@@ -134,7 +134,11 @@ const SchedulerDialog = ({
|
|||||||
const { send: executeSchedule } = useRequest(
|
const { send: executeSchedule } = useRequest(
|
||||||
(id: string) => callAction({ action: 'executeSchedule', param: id }),
|
(id: string) => callAction({ action: 'executeSchedule', param: id }),
|
||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
).onError((error) => {
|
)
|
||||||
|
.onSuccess(() => {
|
||||||
|
toast.success(LL.EXECUTE_SCHEDULE_SENT());
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
toast.error(String(error.error?.message || 'An error occurred'));
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
system_name: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
tx_mode: number;
|
tx_mode: number;
|
||||||
ems_bus_id: number;
|
ems_bus_id: number;
|
||||||
|
|||||||
@@ -189,7 +189,19 @@ const ApplicationSettings = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pb: 1 }} variant="h6" color="primary">
|
<Typography variant="h6" color="primary">
|
||||||
|
{LL.SYSTEM(0)}
|
||||||
|
{LL.CUSTOMIZATIONS()}
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
name="system_name"
|
||||||
|
label={LL.SYSTEM_NAME()}
|
||||||
|
value={data.system_name}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.SERVICES()}
|
{LL.SERVICES()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color="secondary">API</Typography>
|
<Typography color="secondary">API</Typography>
|
||||||
|
|||||||
@@ -145,9 +145,21 @@ const SystemMonitor = () => {
|
|||||||
{LL.PLEASE_WAIT()}…
|
{LL.PLEASE_WAIT()}…
|
||||||
</Typography>
|
</Typography>
|
||||||
{isUploading && (
|
{isUploading && (
|
||||||
|
<>
|
||||||
<Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
|
<Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
|
||||||
<LinearProgressWithLabel value={progressValue} />
|
<LinearProgressWithLabel value={progressValue} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2, mt: 2 }}
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
||||||
|
|
||||||
|
import { readSettings } from 'api/app';
|
||||||
|
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
import { PROJECT_NAME } from 'env';
|
import { PROJECT_NAME } from 'env';
|
||||||
|
|
||||||
import { DRAWER_WIDTH } from './Layout';
|
import { DRAWER_WIDTH } from './Layout';
|
||||||
@@ -24,12 +27,28 @@ interface LayoutDrawerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
||||||
|
const { data: settings } = useRequest(readSettings);
|
||||||
|
const system_name = settings?.system_name;
|
||||||
|
|
||||||
const drawer = (
|
const drawer = (
|
||||||
<>
|
<>
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
||||||
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
||||||
<Typography variant="h6">{PROJECT_NAME}</Typography>
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>{PROJECT_NAME}</Typography>
|
||||||
|
{system_name && (
|
||||||
|
<Typography color="secondary" variant="body2">
|
||||||
|
{system_name}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Divider absolute />
|
<Divider absolute />
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const cz: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Tato aktualizace vyžaduje obnovení továrního nastavení. Ujistěte se, že nejprve stáhnete systémovou zálohu před pokračováním a poté nahrajte tento soubor po instalaci nové verze.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Tato aktualizace vyžaduje obnovení továrního nastavení. Ujistěte se, že nejprve stáhnete systémovou zálohu před pokračováním a poté nahrajte tento soubor po instalaci nové verze.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?',
|
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán'
|
TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán',
|
||||||
|
SYSTEM_NAME: 'Název systému',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Plán byl úspěšně proveden'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default cz;
|
export default cz;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const de: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?',
|
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet'
|
TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet',
|
||||||
|
SYSTEM_NAME: 'Systemname',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Zeitplan erfolgreich ausgeführt'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const en: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'This upgrade requires a factory reset. Make sure you first download a System Backup before continuing, and then upload this file after the new version is installed.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'This upgrade requires a factory reset. Make sure you first download a System Backup before continuing, and then upload this file after the new version is installed.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
|
||||||
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?',
|
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully'
|
TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully',
|
||||||
|
SYSTEM_NAME: 'System Name',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Schedule executed successfully'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const fr: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Cette mise à jour nécessite une réinitialisation de fabrique. Assurez-vous de télécharger une sauvegarde système avant de continuer, et de la charger après l\'installation de la nouvelle version.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Cette mise à jour nécessite une réinitialisation de fabrique. Assurez-vous de télécharger une sauvegarde système avant de continuer, et de la charger après l\'installation de la nouvelle version.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?',
|
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès'
|
TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès',
|
||||||
|
SYSTEM_NAME: 'Nom du système',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Planlegger exécuté avec succès'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const it: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Questa aggiornamento richiede un ripristino di fabbrica. Assicurati di prima scaricare un backup del sistema prima di continuare, e poi caricare questo file dopo l\'installazione della nuova versione.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Questa aggiornamento richiede un ripristino di fabbrica. Assicurati di prima scaricare un backup del sistema prima di continuare, e poi caricare questo file dopo l\'installazione della nuova versione.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?',
|
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo'
|
TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo',
|
||||||
|
SYSTEM_NAME: 'Nome del sistema',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Programma eseguito con successo'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const nl: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Deze upgrade vereist een fabrieksinstelling. Zorg ervoor dat u eerst een Systeem Backup download voordat u doorgaat, en upload deze file na de installatie van de nieuwe versie.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Deze upgrade vereist een fabrieksinstelling. Zorg ervoor dat u eerst een Systeem Backup download voordat u doorgaat, en upload deze file na de installatie van de nieuwe versie.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?',
|
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol'
|
TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol',
|
||||||
|
SYSTEM_NAME: 'Systeemnaam',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Planlegger uitgevoerd succesvol'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const no: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Denne oppdateringen krever en fabriksinstilling. Sørg for at du først lastet ned en System Backup før du fortsetter, og last denne filen etter at den nye versjonen er installert.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denne oppdateringen krever en fabriksinstilling. Sørg for at du først lastet ned en System Backup før du fortsetter, og last denne filen etter at den nye versjonen er installert.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?',
|
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt'
|
TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt',
|
||||||
|
SYSTEM_NAME: 'Systemnavn',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Planlegger utført suksessfullt'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const pl: BaseTranslation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Ta aktualizacja wymaga resetu fabrycznego. Upewnij się, że najpierw pobierzesz kopię zapasową systemu przed kontynuowaniem, a następnie przesuń tę plik po zainstalowaniu nowej wersji.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Ta aktualizacja wymaga resetu fabrycznego. Upewnij się, że najpierw pobierzesz kopię zapasową systemu przed kontynuowaniem, a następnie przesuń tę plik po zainstalowaniu nowej wersji.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
|
||||||
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?',
|
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie'
|
TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie',
|
||||||
|
SYSTEM_NAME: 'Nazwa systemu',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Harmonogram wykonany pomyślnie'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const sk: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Táto aktualizácia vyžaduje reštart základných nastavení. Uistite sa, že najprv stiahnete systémovú zálohu pred pokračovaním, a potom nahrajte tento súbor po instalácii novej verzie.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Táto aktualizácia vyžaduje reštart základných nastavení. Uistite sa, že najprv stiahnete systémovú zálohu pred pokračovaním, a potom nahrajte tento súbor po instalácii novej verzie.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?',
|
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný'
|
TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný',
|
||||||
|
SYSTEM_NAME: 'Názov systému',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Plán bol úspešne vykonaný'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const sv: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Denna uppdatering kräver en fabriksåterställning. Se till att du först laddar ned en System Backup innan du fortsätter, och ladda upp denna fil efter att den nya versionen är installerad.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denna uppdatering kräver en fabriksåterställning. Se till att du först laddar ned en System Backup innan du fortsätter, och ladda upp denna fil efter att den nya versionen är installerad.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?',
|
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades'
|
TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades',
|
||||||
|
SYSTEM_NAME: 'Systemnamn',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Schema utfört'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -363,7 +363,9 @@ const tr: Translation = {
|
|||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Bu güncelleme továrnı ayarlarını gerektirir. Yapılandırmanızı ve ayarlarınızı önce yedekleyin ve ardından yeni sürüm yüklendikten sonra yükleyin.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Bu güncelleme továrnı ayarlarını gerektirir. Yapılandırmanızı ve ayarlarınızı önce yedekleyin ve ardından yeni sürüm yüklendikten sonra yükleyin.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?',
|
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?',
|
||||||
TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi'
|
TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi',
|
||||||
|
SYSTEM_NAME: 'Sistem Adı',
|
||||||
|
EXECUTE_SCHEDULE_SENT: 'Zamanlama başarıyla uygulandı'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -15,5 +15,5 @@
|
|||||||
"itty-router": "^5.0.23",
|
"itty-router": "^5.0.23",
|
||||||
"prettier": "^3.8.3"
|
"prettier": "^3.8.3"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.34.1+sha512.b58fbde6dca66a929538021581f648b4570b6ca19b18e7cbd7f2c07a7b24454155388dacdf08f2af3678e88a6d1fe04f9d609df24bf51735a060ea041b374ab7"
|
"packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const headers = {
|
|||||||
|
|
||||||
// EMS-ESP Application Settings
|
// EMS-ESP Application Settings
|
||||||
let settings = {
|
let settings = {
|
||||||
|
system_name: 'standalone',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
tx_mode: 1,
|
tx_mode: 1,
|
||||||
ems_bus_id: 11,
|
ems_bus_id: 11,
|
||||||
@@ -229,6 +230,21 @@ let countWifiScanPoll = 0; // wifi network scan
|
|||||||
let countHardwarePoll = 0; // for during an upload
|
let countHardwarePoll = 0; // for during an upload
|
||||||
|
|
||||||
// DeviceTypes
|
// DeviceTypes
|
||||||
|
const enum ScheduleFlag {
|
||||||
|
SCHEDULE_SUN = 1,
|
||||||
|
SCHEDULE_MON = 2,
|
||||||
|
SCHEDULE_TUE = 4,
|
||||||
|
SCHEDULE_WED = 8,
|
||||||
|
SCHEDULE_THU = 16,
|
||||||
|
SCHEDULE_FRI = 32,
|
||||||
|
SCHEDULE_SAT = 64,
|
||||||
|
SCHEDULE_DAY = 0,
|
||||||
|
SCHEDULE_TIMER = 128,
|
||||||
|
SCHEDULE_ONCHANGE = 129,
|
||||||
|
SCHEDULE_CONDITION = 130,
|
||||||
|
SCHEDULE_IMMEDIATE = 132
|
||||||
|
}
|
||||||
|
|
||||||
const enum DeviceType {
|
const enum DeviceType {
|
||||||
SYSTEM = 0,
|
SYSTEM = 0,
|
||||||
TEMPERATURESENSOR,
|
TEMPERATURESENSOR,
|
||||||
@@ -4164,7 +4180,7 @@ let emsesp_schedule = {
|
|||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
active: false,
|
active: false,
|
||||||
flags: 1,
|
flags: ScheduleFlag.SCHEDULE_TIMER,
|
||||||
time: '04:00',
|
time: '04:00',
|
||||||
cmd: 'system/restart',
|
cmd: 'system/restart',
|
||||||
value: '',
|
value: '',
|
||||||
@@ -4173,7 +4189,7 @@ let emsesp_schedule = {
|
|||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
active: false,
|
active: false,
|
||||||
flags: 130,
|
flags: ScheduleFlag.SCHEDULE_CONDITION,
|
||||||
time: 'system/network info/rssi < -70',
|
time: 'system/network info/rssi < -70',
|
||||||
cmd: 'system/restart',
|
cmd: 'system/restart',
|
||||||
value: '',
|
value: '',
|
||||||
@@ -4182,7 +4198,7 @@ let emsesp_schedule = {
|
|||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
active: false,
|
active: false,
|
||||||
flags: 129,
|
flags: ScheduleFlag.SCHEDULE_ONCHANGE,
|
||||||
time: 'boiler/outdoortemp',
|
time: 'boiler/outdoortemp',
|
||||||
cmd: 'boiler/selflowtemp',
|
cmd: 'boiler/selflowtemp',
|
||||||
value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3',
|
value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3',
|
||||||
@@ -4191,11 +4207,11 @@ let emsesp_schedule = {
|
|||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
active: false,
|
active: false,
|
||||||
flags: 132,
|
flags: ScheduleFlag.SCHEDULE_IMMEDIATE,
|
||||||
time: '',
|
time: '',
|
||||||
cmd: 'system/message',
|
cmd: 'system/message',
|
||||||
value: '"hello world"',
|
value: '"hello world"',
|
||||||
name: '' // empty
|
name: 'send_message'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -4756,9 +4772,13 @@ router
|
|||||||
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
|
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
|
||||||
dv: {
|
dv: {
|
||||||
id: '00' + item.name,
|
id: '00' + item.name,
|
||||||
v: item.active ? 'on' : 'off',
|
|
||||||
c: item.name,
|
c: item.name,
|
||||||
|
...(item.flags === ScheduleFlag.SCHEDULE_IMMEDIATE
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
v: item.active ? 'on' : 'off',
|
||||||
l: ['off', 'on']
|
l: ['off', 'on']
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
dashboard_object = {
|
dashboard_object = {
|
||||||
|
|||||||
@@ -1341,3 +1341,4 @@ serialises
|
|||||||
SPIRAM
|
SPIRAM
|
||||||
optimisations
|
optimisations
|
||||||
IILE
|
IILE
|
||||||
|
Sumr
|
||||||
76
scripts/generate_test_api.py
Normal file
76
scripts/generate_test_api.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Regenerate test/test_api/test_api.h from the native-test-create build, then run the API tests.
|
||||||
|
|
||||||
|
Workflow:
|
||||||
|
1. Run `pio run -e native-test-create -t exec` and capture its output.
|
||||||
|
2. Extract everything between the START/END "CUT HERE" markers.
|
||||||
|
3. Write that block to test/test_api/test_api.h.
|
||||||
|
4. Run `pio run -e native-test -t exec`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
START_MARKER = "// ---------- START - CUT HERE ----------"
|
||||||
|
END_MARKER = "// ---------- END - CUT HERE ----------"
|
||||||
|
|
||||||
|
# project root is the parent of this script's "scripts" directory
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
OUTPUT_HEADER = PROJECT_ROOT / "test" / "test_api" / "test_api.h"
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd, capture):
|
||||||
|
"""Run a command in the project root. Streams to the console; optionally captures stdout."""
|
||||||
|
print(f"\n>>> {' '.join(cmd)}\n", flush=True)
|
||||||
|
if capture:
|
||||||
|
# capture stdout while still echoing it so the user sees progress
|
||||||
|
result = subprocess.run(cmd, cwd=PROJECT_ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
print(result.stdout, end="", flush=True)
|
||||||
|
return result.returncode, result.stdout
|
||||||
|
result = subprocess.run(cmd, cwd=PROJECT_ROOT)
|
||||||
|
return result.returncode, None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_between_markers(output):
|
||||||
|
"""Return the text strictly between the START and END markers (markers excluded)."""
|
||||||
|
lines = output.splitlines()
|
||||||
|
start = end = None
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if START_MARKER in line and start is None:
|
||||||
|
start = i
|
||||||
|
elif END_MARKER in line and start is not None:
|
||||||
|
end = i
|
||||||
|
break
|
||||||
|
if start is None or end is None:
|
||||||
|
return None
|
||||||
|
return "\n".join(lines[start + 1 : end]) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 1. build + exec the generator
|
||||||
|
code, output = run(["pio", "run", "-e", "native-test-create", "-t", "exec"], capture=True)
|
||||||
|
if code != 0:
|
||||||
|
print(f"\nERROR: 'native-test-create' exec failed with exit code {code}", file=sys.stderr)
|
||||||
|
return code
|
||||||
|
|
||||||
|
# 2. extract the header content
|
||||||
|
content = extract_between_markers(output)
|
||||||
|
if content is None:
|
||||||
|
print(f"\nERROR: could not find content between markers:\n {START_MARKER}\n {END_MARKER}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 3. write it to test_api.h
|
||||||
|
OUTPUT_HEADER.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
OUTPUT_HEADER.write_text(content, encoding="utf-8")
|
||||||
|
print(f"\nWrote {len(content.splitlines())} lines to {OUTPUT_HEADER}", flush=True)
|
||||||
|
|
||||||
|
# 4. build + exec the actual tests
|
||||||
|
code, _ = run(["pio", "run", "-e", "native-test", "-t", "exec"], capture=False)
|
||||||
|
if code != 0:
|
||||||
|
print(f"\nERROR: 'native-test' exec failed with exit code {code}", file=sys.stderr)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
cd interface
|
cd interface
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
corepack use pnpm@latest-10
|
corepack use pnpm@latest
|
||||||
pnpm update --latest
|
pnpm update --latest
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm format
|
pnpm format
|
||||||
@@ -13,7 +13,7 @@ pnpm lint
|
|||||||
|
|
||||||
cd ../mock-api
|
cd ../mock-api
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
corepack use pnpm@latest-10
|
corepack use pnpm@latest
|
||||||
pnpm update --latest
|
pnpm update --latest
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm format
|
pnpm format
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ std::vector<uint8_t> AnalogSensor::exclude_types_;
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
unsigned long AnalogSensor::edge[] = {0, 0, 0};
|
volatile unsigned long AnalogSensor::edge[] = {0, 0, 0};
|
||||||
unsigned long AnalogSensor::edgecnt[] = {0, 0, 0};
|
volatile unsigned long AnalogSensor::edgecnt[] = {0, 0, 0};
|
||||||
|
|
||||||
void IRAM_ATTR AnalogSensor::freqIrq0() {
|
void IRAM_ATTR AnalogSensor::freqIrq0() {
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
portENTER_CRITICAL_ISR(&mux);
|
||||||
if (micros() - edge[0] > 10) { // limit to 100kHz
|
if (micros() - edge[0] > 10) { // limit to 100kHz
|
||||||
edgecnt[0]++;
|
edgecnt[0] = edgecnt[0] + 1;
|
||||||
edge[0] = micros();
|
edge[0] = micros();
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
portEXIT_CRITICAL_ISR(&mux);
|
||||||
@@ -40,7 +40,7 @@ void IRAM_ATTR AnalogSensor::freqIrq0() {
|
|||||||
void IRAM_ATTR AnalogSensor::freqIrq1() {
|
void IRAM_ATTR AnalogSensor::freqIrq1() {
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
portENTER_CRITICAL_ISR(&mux);
|
||||||
if (micros() - edge[1] > 10) { // limit to 100kHz
|
if (micros() - edge[1] > 10) { // limit to 100kHz
|
||||||
edgecnt[1]++;
|
edgecnt[1] = edgecnt[1] + 1;
|
||||||
edge[1] = micros();
|
edge[1] = micros();
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
portEXIT_CRITICAL_ISR(&mux);
|
||||||
@@ -48,7 +48,7 @@ void IRAM_ATTR AnalogSensor::freqIrq1() {
|
|||||||
void IRAM_ATTR AnalogSensor::freqIrq2() {
|
void IRAM_ATTR AnalogSensor::freqIrq2() {
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
portENTER_CRITICAL_ISR(&mux);
|
||||||
if (micros() - edge[2] > 10) { // limit to 100kHz
|
if (micros() - edge[2] > 10) { // limit to 100kHz
|
||||||
edgecnt[2]++;
|
edgecnt[2] = edgecnt[2] + 1;
|
||||||
edge[2] = micros();
|
edge[2] = micros();
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
portEXIT_CRITICAL_ISR(&mux);
|
||||||
@@ -272,7 +272,9 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
sensor.set_value(0);
|
sensor.set_value(0);
|
||||||
publish_sensor(sensor);
|
publish_sensor(sensor);
|
||||||
attachInterrupt(sensor.gpio(), index == 0 ? freqIrq0 : index == 1 ? freqIrq1 : freqIrq2, FALLING);
|
attachInterrupt(sensor.gpio(), index == 0 ? freqIrq0 : index == 1 ? freqIrq1 : freqIrq2, FALLING);
|
||||||
lastedge[index] = edge[index] = micros();
|
unsigned long now = micros();
|
||||||
|
edge[index] = now;
|
||||||
|
lastedge[index] = now;
|
||||||
edgecnt[index] = 0;
|
edgecnt[index] = 0;
|
||||||
} else if (sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2) {
|
} else if (sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2) {
|
||||||
auto index = sensor.type() - AnalogType::CNT_0;
|
auto index = sensor.type() - AnalogType::CNT_0;
|
||||||
|
|||||||
@@ -210,8 +210,8 @@ class AnalogSensor {
|
|||||||
static void IRAM_ATTR freqIrq0();
|
static void IRAM_ATTR freqIrq0();
|
||||||
static void IRAM_ATTR freqIrq1();
|
static void IRAM_ATTR freqIrq1();
|
||||||
static void IRAM_ATTR freqIrq2();
|
static void IRAM_ATTR freqIrq2();
|
||||||
static unsigned long edge[3];
|
static volatile unsigned long edge[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||||
static unsigned long edgecnt[3];
|
static volatile unsigned long edgecnt[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||||
unsigned long lastedge[3] = {0, 0, 0};
|
unsigned long lastedge[3] = {0, 0, 0};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
// GENERAL SETTINGS
|
// GENERAL SETTINGS
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSTEM_NAME
|
||||||
|
#define EMSESP_DEFAULT_SYSTEM_NAME ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_LOCALE
|
#ifndef EMSESP_DEFAULT_LOCALE
|
||||||
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -347,8 +347,9 @@ std::string EMSdevice::to_string() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns string of EMS device version and productID
|
// returns string of EMS device version and productID
|
||||||
|
// this is used in the MQTT Discovery config
|
||||||
std::string EMSdevice::to_string_version() {
|
std::string EMSdevice::to_string_version() {
|
||||||
return "DeviceID:" + Helpers::hextoa(device_id_) + " ProductID:" + Helpers::itoa(product_id_) + " Version:" + version_;
|
return "DeviceID " + Helpers::hextoa(device_id_) + ", ProductID " + Helpers::itoa(product_id_) + ", Version " + version_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns out brand + device name
|
// returns out brand + device name
|
||||||
|
|||||||
@@ -545,8 +545,8 @@ void Mqtt::ha_status() {
|
|||||||
JsonObject dev = doc["dev"].to<JsonObject>();
|
JsonObject dev = doc["dev"].to<JsonObject>();
|
||||||
dev["name"] = Mqtt::basename();
|
dev["name"] = Mqtt::basename();
|
||||||
dev["sw"] = "v" + std::string(EMSESP_APP_VERSION);
|
dev["sw"] = "v" + std::string(EMSESP_APP_VERSION);
|
||||||
dev["mf"] = "EMS-ESP";
|
dev["mf"] = "EMS-ESP"; // manufacturer is EMS-ESP always
|
||||||
dev["mdl"] = "EMS-ESP";
|
dev["mdl"] = EMSESP::system_.system_name().empty() ? "EMS-ESP" : EMSESP::system_.system_name(); // use users custom system name if set
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
dev["cu"] = std::string("http://") + EMSESP::system_.get_ip_or_hostname().c_str();
|
dev["cu"] = std::string("http://") + EMSESP::system_.get_ip_or_hostname().c_str();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -684,6 +684,7 @@ void System::store_settings(WebSettings & settings) {
|
|||||||
enum_format_ = settings.enum_format;
|
enum_format_ = settings.enum_format;
|
||||||
readonly_mode_ = settings.readonly_mode;
|
readonly_mode_ = settings.readonly_mode;
|
||||||
locale_ = settings.locale;
|
locale_ = settings.locale;
|
||||||
|
system_name_ = settings.system_name;
|
||||||
developer_mode_ = settings.developer_mode;
|
developer_mode_ = settings.developer_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,8 +837,9 @@ void System::send_info_mqtt() {
|
|||||||
}
|
}
|
||||||
_connection = connection;
|
_connection = connection;
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
// doc["event"] = "connected";
|
|
||||||
doc["version"] = EMSESP_APP_VERSION;
|
doc["version"] = EMSESP_APP_VERSION;
|
||||||
|
doc["systemName"] = system_name_.isEmpty() ? "EMS-ESP" : system_name_;
|
||||||
|
|
||||||
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
|
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
|
||||||
// https://github.com/emsesp/EMS-ESP32/issues/751
|
// https://github.com/emsesp/EMS-ESP32/issues/751
|
||||||
@@ -852,16 +854,6 @@ void System::send_info_mqtt() {
|
|||||||
if (EMSESP::network_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
doc["network"] = "ethernet";
|
doc["network"] = "ethernet";
|
||||||
doc["hostname"] = ETH.getHostname();
|
doc["hostname"] = ETH.getHostname();
|
||||||
/*
|
|
||||||
doc["MAC"] = ETH.macAddress();
|
|
||||||
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
|
||||||
doc["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
|
||||||
doc["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
|
||||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
|
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
} else if (EMSESP::network_.wifi_connected()) {
|
} else if (EMSESP::network_.wifi_connected()) {
|
||||||
doc["network"] = "wifi";
|
doc["network"] = "wifi";
|
||||||
doc["hostname"] = WiFi.getHostname();
|
doc["hostname"] = WiFi.getHostname();
|
||||||
@@ -3149,6 +3141,18 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
int last_pct = -1;
|
int last_pct = -1;
|
||||||
|
|
||||||
while (total_read < (size_t)firmware_size) {
|
while (total_read < (size_t)firmware_size) {
|
||||||
|
// a cancel is signalled by the WebUI dropping the status below UPLOADING (back to NORMAL)
|
||||||
|
// via the systemStatus action, which runs on the AsyncTCP task while we're blocked here
|
||||||
|
if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||||
|
LOG_WARNING("Firmware upload cancelled at %u of %d bytes", (unsigned)total_read, firmware_size);
|
||||||
|
Update.abort(); // release the OTA partition handle so a later attempt can start cleanly
|
||||||
|
ssl_client.stop(); // drop the connection
|
||||||
|
saved_url.clear(); // prevent it from downloading again
|
||||||
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
|
||||||
|
Shell::loop_all(); // flush log buffers so the cancel message shows in the console
|
||||||
|
return true; // not an error - don't trigger the failure/reset path in emsesp.cpp
|
||||||
|
}
|
||||||
|
|
||||||
// wait for some data or for the connection to drop
|
// wait for some data or for the connection to drop
|
||||||
uint32_t wait_start = millis();
|
uint32_t wait_start = millis();
|
||||||
while (!stream->available()) {
|
while (!stream->available()) {
|
||||||
@@ -3158,10 +3162,18 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
if (millis() - wait_start > READ_TIMEOUT_MS) {
|
if (millis() - wait_start > READ_TIMEOUT_MS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// also bail out promptly if a cancel arrives mid-stall
|
||||||
|
if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream->available()) {
|
if (!stream->available()) {
|
||||||
|
// if the inner wait broke because of a cancel, loop back so the top-of-loop handler runs
|
||||||
|
if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
LOG_ERROR("Firmware upload failed - read stalled at %u of %d bytes", (unsigned)total_read, firmware_size);
|
LOG_ERROR("Firmware upload failed - read stalled at %u of %d bytes", (unsigned)total_read, firmware_size);
|
||||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -265,6 +265,10 @@ class System {
|
|||||||
return std::string(locale_.c_str());
|
return std::string(locale_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string system_name() {
|
||||||
|
return std::string(system_name_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void healthcheck(uint8_t healthcheck) {
|
void healthcheck(uint8_t healthcheck) {
|
||||||
healthcheck_ = healthcheck;
|
healthcheck_ = healthcheck;
|
||||||
}
|
}
|
||||||
@@ -358,7 +362,7 @@ class System {
|
|||||||
static uint32_t heap_mem_;
|
static uint32_t heap_mem_;
|
||||||
static uint32_t min_free_mem_;
|
static uint32_t min_free_mem_;
|
||||||
|
|
||||||
uint8_t systemStatus_; // uses SYSTEM_STATUS enum
|
volatile uint8_t systemStatus_; // uses SYSTEM_STATUS enum - written from the AsyncTCP task (e.g. cancel) and read from the main loop during OTA
|
||||||
|
|
||||||
void set_partition_install_date();
|
void set_partition_install_date();
|
||||||
|
|
||||||
@@ -410,6 +414,7 @@ class System {
|
|||||||
|
|
||||||
// EMS-ESP settings
|
// EMS-ESP settings
|
||||||
std::string hostname_;
|
std::string hostname_;
|
||||||
|
String system_name_;
|
||||||
String locale_;
|
String locale_;
|
||||||
bool low_clock_;
|
bool low_clock_;
|
||||||
String board_profile_;
|
String board_profile_;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.10"
|
#define EMSESP_APP_VERSION "3.9.0-dev.11"
|
||||||
|
|||||||
@@ -478,8 +478,8 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// show scheduler, with name, on/off, unless it's of type SCHEDULE_IMMEDIATE
|
// show scheduler items
|
||||||
if (EMSESP::webSchedulerService.count_entities(true)) {
|
if (EMSESP::webSchedulerService.count_entities()) {
|
||||||
JsonObject obj = nodes.add<JsonObject>();
|
JsonObject obj = nodes.add<JsonObject>();
|
||||||
obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id
|
||||||
obj["t"] = EMSdevice::DeviceType::SCHEDULER; // device type number
|
obj["t"] = EMSdevice::DeviceType::SCHEDULER; // device type number
|
||||||
@@ -488,19 +488,20 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
|||||||
|
|
||||||
EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) {
|
EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) {
|
||||||
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||||
// only add if we have a name and it's not of type SCHEDULE_IMMEDIATE - we don't need a u (UOM) for this
|
|
||||||
if (scheduleItem.name[0] != '\0' && scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
|
||||||
JsonObject node = nodes.add<JsonObject>();
|
JsonObject node = nodes.add<JsonObject>();
|
||||||
node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++;
|
node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++;
|
||||||
|
|
||||||
JsonObject dv = node["dv"].to<JsonObject>();
|
JsonObject dv = node["dv"].to<JsonObject>();
|
||||||
dv["id"] = std::string("00") + scheduleItem.name;
|
dv["id"] = std::string("00") + scheduleItem.name;
|
||||||
dv["c"] = scheduleItem.name;
|
dv["c"] = scheduleItem.name;
|
||||||
|
|
||||||
|
// for immediate schedules, we don't show the active/inactive state or on/off options
|
||||||
|
if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||||
char s[12];
|
char s[12];
|
||||||
dv["v"] = Helpers::render_boolean(s, scheduleItem.active, true);
|
dv["v"] = Helpers::render_boolean(s, scheduleItem.active, true);
|
||||||
JsonArray l = dv["l"].to<JsonArray>();
|
JsonArray l = dv["l"].to<JsonArray>();
|
||||||
l.add(Helpers::render_boolean(s, false, true));
|
l.add(Helpers::render_boolean(s, false, true)); // False option
|
||||||
l.add(Helpers::render_boolean(s, true, true));
|
l.add(Helpers::render_boolean(s, true, true)); // True option
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -334,15 +334,9 @@ void WebSchedulerService::publish(const bool force) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// count number of entries, default: only named items
|
// count number of scheduler entries
|
||||||
uint8_t WebSchedulerService::count_entities(bool cmd_only) {
|
uint8_t WebSchedulerService::count_entities() {
|
||||||
uint8_t count = 0;
|
return static_cast<uint8_t>(scheduleItems_ ? scheduleItems_->size() : 0);
|
||||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
|
||||||
if (scheduleItem.name[0] != '\0' || !cmd_only) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute scheduled command
|
// execute scheduled command
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
|||||||
void ha_reset() {
|
void ha_reset() {
|
||||||
ha_configdone_ = false;
|
ha_configdone_ = false;
|
||||||
}
|
}
|
||||||
uint8_t count_entities(bool cmd_only = false);
|
uint8_t count_entities();
|
||||||
bool onChange(const char * cmd);
|
bool onChange(const char * cmd);
|
||||||
|
|
||||||
bool executeSchedule(const char * name);
|
bool executeSchedule(const char * name);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
|
|||||||
root["version"] = settings.version;
|
root["version"] = settings.version;
|
||||||
root["board_profile"] = settings.board_profile;
|
root["board_profile"] = settings.board_profile;
|
||||||
root["platform"] = EMSESP_PLATFORM;
|
root["platform"] = EMSESP_PLATFORM;
|
||||||
|
root["system_name"] = settings.system_name;
|
||||||
root["locale"] = settings.locale;
|
root["locale"] = settings.locale;
|
||||||
root["tx_mode"] = settings.tx_mode;
|
root["tx_mode"] = settings.tx_mode;
|
||||||
root["ems_bus_id"] = settings.ems_bus_id;
|
root["ems_bus_id"] = settings.ems_bus_id;
|
||||||
@@ -284,6 +285,8 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
//
|
//
|
||||||
// without checks or necessary restarts...
|
// without checks or necessary restarts...
|
||||||
//
|
//
|
||||||
|
settings.system_name = root["system_name"] | EMSESP_DEFAULT_SYSTEM_NAME;
|
||||||
|
|
||||||
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
||||||
EMSESP::trace_raw(settings.trace_raw);
|
EMSESP::trace_raw(settings.trace_raw);
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ namespace emsesp {
|
|||||||
class WebSettings {
|
class WebSettings {
|
||||||
public:
|
public:
|
||||||
String version = EMSESP_APP_VERSION;
|
String version = EMSESP_APP_VERSION;
|
||||||
|
String system_name;
|
||||||
String locale;
|
String locale;
|
||||||
uint8_t tx_mode;
|
uint8_t tx_mode;
|
||||||
uint8_t ems_bus_id;
|
uint8_t ems_bus_id;
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ static TestStream stream;
|
|||||||
// load the tests
|
// load the tests
|
||||||
// this is generated from this file when compiled with -DEMSESP_UNITY_CREATE
|
// this is generated from this file when compiled with -DEMSESP_UNITY_CREATE
|
||||||
// copy the output to the test_api.h file
|
// copy the output to the test_api.h file
|
||||||
|
#ifndef EMSESP_UNITY_CREATE
|
||||||
#include "test_api.h"
|
#include "test_api.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Unity's setup call - is called before each test - empty for now
|
// Unity's setup call - is called before each test - empty for now
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user