This commit is contained in:
MichaelDvP
2023-03-06 16:15:28 +01:00
32 changed files with 1471 additions and 933 deletions

4
.gitignore vendored
View File

@@ -1,8 +1,8 @@
# vscode
.vscode/c_cpp_properties.json
.vscode/extensions.json
# .vscode/extensions.json
.vscode/launch.json
.vscode/settings.json
# .vscode/settings.json
# c++ compiling
.clang_complete

13
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.nodePath": "interface/.yarn/sdks",
"prettier.prettierPath": "interface/.yarn/sdks/prettier/index.js",
"typescript.tsdk": "interface/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

2
.vscode/tasks.json vendored
View File

@@ -10,7 +10,7 @@
"linux": {
"options": {
"env": {
// Workaroung for sdl2 `-m32` crash
// Workaround for sdl2 `-m32` crash
// https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1775067/comments/7
"DBUS_FATAL_WARNINGS": "0"
}

View File

@@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "8.34.0-sdk",
"version": "8.35.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, maximum-scale=1, minimum-scale=1" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="/css/roboto.css" />
<link rel="manifest" href="/app/manifest.json" />
<title>EMS-ESP</title>

View File

@@ -27,10 +27,11 @@
"@remix-run/router": "^1.3.3",
"@table-library/react-table-library": "4.0.29",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.14.5",
"@types/node": "^18.14.6",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/react-router-dom": "^5.3.3",
"@yarnpkg/pnpify": "^4.0.0-rc.40",
"async-validator": "^4.2.5",
"axios": "^1.3.4",
"history": "^5.3.0",
@@ -38,8 +39,8 @@
"lodash-es": "^4.17.21",
"mime-types": "^2.1.35",
"notistack": "^2.0.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "latest",
"react-dom": "latest",
"react-dropzone": "^14.2.3",
"react-icons": "^4.8.0",
"react-router-dom": "^6.8.2",
@@ -49,6 +50,7 @@
},
"devDependencies": {
"@types/mime-types": "^2",
"@types/styled-components": "^5",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@vitejs/plugin-react-swc": "^3.2.0",

View File

@@ -311,10 +311,10 @@ const de: Translation = {
LEAVE: 'Verlassen',
SCHEDULER: 'Planer',
SCHEDULER_HELP_1: 'Fügen Sie eigene, geplante Befehle zur Automatisierung hinzu. Vergeben Sie einen Entitätsnamen um die Aktivierung über API/Mqtt zu steuern',
SCHEDULER_HELP_2: 'Use 00:00 to trigger on boot', // TODO translate
SCHEDULE: 'Zeitplan',
TIME: 'Zeit',
TIMER: 'Timer',
WEEKLY: 'Wöchentlich',
SCHEDULE_SAVED: 'Plan gespeichert',
SCHEDULE_TIMER_1: 'beim Start',
SCHEDULE_TIMER_2: 'jede Minute',

View File

@@ -310,11 +310,11 @@ const en: Translation = {
STAY: 'Stay',
LEAVE: 'Leave',
SCHEDULER: 'Scheduler',
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt',
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.',
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up',
SCHEDULE: 'Schedule',
TIME: 'Time',
TIMER: 'Timer',
WEEKLY: 'Weekly',
SCHEDULE_SAVED: 'Schedule updated',
SCHEDULE_TIMER_1: 'on startup',
SCHEDULE_TIMER_2: 'every minute',

View File

@@ -310,11 +310,11 @@ const fr: Translation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -310,11 +310,11 @@ const nl: Translation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -310,11 +310,11 @@ const no: Translation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -310,11 +310,11 @@ const pl: BaseTranslation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULE: 'Schedule', // TODO translate SCHEDULE: 'Schedule', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -310,11 +310,11 @@ const sv: Translation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -310,11 +310,11 @@ const tr: Translation = {
STAY: 'Stay', // TODO translate
LEAVE: 'Leave', // TODO translate
SCHEDULER: 'Scheduler', // TODO translate
SCHEDULER_HELP_1: 'Add custom scheduled commands to automate EMS-ESP. Add entity name to control activation by api/mqtt', // TODO translate
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', // TODO translate
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
SCHEDULE: 'Schedule', // TODO translate
TIME: 'Time', // TODO translate
TIMER: 'Timer', // TODO translate
WEEKLY: 'Weekly', // TODO translate
SCHEDULE_SAVED: 'Schedule updated', // TODO translate
SCHEDULE_TIMER_1: 'on startup', // TODO translate
SCHEDULE_TIMER_2: 'every minute', // TODO translate

View File

@@ -5,18 +5,17 @@ import {
Button,
Typography,
Box,
Stack,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
ToggleButton,
MenuItem,
ToggleButtonGroup,
Checkbox,
Grid,
TextField,
Radio,
RadioGroup,
FormControlLabel
Divider
} from '@mui/material';
import { useTheme } from '@table-library/react-table-library/theme';
@@ -25,11 +24,11 @@ import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-li
import { useSnackbar } from 'notistack';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
import CheckIcon from '@mui/icons-material/Check';
import WarningIcon from '@mui/icons-material/Warning';
import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done';
import AddIcon from '@mui/icons-material/Add';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import {
ValidatedTextField,
@@ -52,6 +51,18 @@ import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from './api';
function makeid() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter = 0;
while (counter < 4) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
const SettingsScheduler: FC = () => {
const { LL, locale } = useI18nContext();
@@ -68,8 +79,8 @@ const SettingsScheduler: FC = () => {
time: '12:00',
cmd: '',
value: '',
description: '',
o_id: ''
name: '',
o_name: ''
};
const [schedule, setSchedule] = useState<ScheduleItem[]>([emptySchedule]);
const [scheduleItem, setScheduleItem] = useState<ScheduleItem>();
@@ -97,7 +108,7 @@ const SettingsScheduler: FC = () => {
const schedule_theme = useTheme({
Table: `
--data-table-library_grid-template-columns: 152px 36px 324px 72px 240px repeat(1, minmax(100px, 1fr));
--data-table-library_grid-template-columns: 36px 324px 50px 192px repeat(1, minmax(100px, 1fr)) 160px;
`,
BaseRow: `
font-size: 14px;
@@ -106,12 +117,12 @@ const SettingsScheduler: FC = () => {
}
`,
BaseCell: `
&:nth-of-type(1) {
padding: 8px;
}
&:nth-of-type(2) {
text-align: center;
}
&:nth-of-type(1) {
text-align: center;
}
`,
HeaderRow: `
text-transform: uppercase;
@@ -171,7 +182,7 @@ const SettingsScheduler: FC = () => {
o_time: si.time,
o_cmd: si.cmd,
o_value: si.value,
o_description: si.description
o_name: si.name
}))
);
};
@@ -216,7 +227,7 @@ const SettingsScheduler: FC = () => {
function hasScheduleChanged(si: ScheduleItem) {
return (
si.id !== si.o_id ||
(si?.description || '') !== (si?.o_description || '') ||
(si?.name || '') !== (si?.o_name || '') ||
si.active !== si.o_active ||
si.deleted !== si.o_deleted ||
si.flags !== si.o_flags ||
@@ -247,7 +258,7 @@ const SettingsScheduler: FC = () => {
time: condensed_si.time,
cmd: condensed_si.cmd,
value: condensed_si.value,
description: condensed_si.description
name: condensed_si.name
};
})
});
@@ -263,43 +274,54 @@ const SettingsScheduler: FC = () => {
}
};
function showFlag(si: ScheduleItem, flag: number) {
let text = '';
function getFlagName(flag: number) {
if ((flag & ScheduleFlag.SCHEDULE_MON) === ScheduleFlag.SCHEDULE_MON) {
text = dow[1];
return dow[1];
}
if ((flag & ScheduleFlag.SCHEDULE_TUE) === ScheduleFlag.SCHEDULE_TUE) {
text = dow[2];
return dow[2];
}
if ((flag & ScheduleFlag.SCHEDULE_WED) === ScheduleFlag.SCHEDULE_WED) {
text = dow[3];
return dow[3];
}
if ((flag & ScheduleFlag.SCHEDULE_THU) === ScheduleFlag.SCHEDULE_THU) {
text = dow[4];
return dow[4];
}
if ((flag & ScheduleFlag.SCHEDULE_FRI) === ScheduleFlag.SCHEDULE_FRI) {
text = dow[5];
return dow[5];
}
if ((flag & ScheduleFlag.SCHEDULE_SAT) === ScheduleFlag.SCHEDULE_SAT) {
text = dow[6];
return dow[6];
}
if ((flag & ScheduleFlag.SCHEDULE_SUN) === ScheduleFlag.SCHEDULE_SUN) {
text = dow[0];
return dow[0];
}
if ((flag & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER) {
text = LL.TIMER();
return LL.TIMER();
}
return (
<Typography variant="button" sx={{ fontSize: 10 }} color={(si.flags & flag) === flag ? 'primary' : 'grey'}>
{text}
</Typography>
);
return '';
}
const dayBox = (si: ScheduleItem, flag: number) => (
<>
<Box>
<Typography sx={{ fontSize: 11 }} color={(si.flags & flag) === flag ? 'primary' : 'grey'}>
{getFlagName(flag)}
</Typography>
</Box>
<Divider orientation="vertical" flexItem />
</>
);
const showFlag = (si: ScheduleItem, flag: number) => (
<Typography variant="button" sx={{ fontSize: 10 }} color={(si.flags & flag) === flag ? 'primary' : 'grey'}>
{getFlagName(flag)}
</Typography>
);
const editScheduleItem = (si: ScheduleItem) => {
if (si.description === undefined) {
si.description = '';
if (si.name === undefined) {
si.name = '';
}
setCreating(false);
setScheduleItem(si);
@@ -308,19 +330,21 @@ const SettingsScheduler: FC = () => {
const addScheduleItem = () => {
setCreating(true);
setScheduleItem({
id: '',
id: makeid(),
active: false,
deleted: false,
flags: 0,
time: '12:00',
cmd: '',
value: '',
description: ''
name: ''
});
};
const updateScheduleItem = () => {
setSchedule([...schedule.filter((si) => creating || si.o_id !== scheduleItem.o_id), scheduleItem]);
if (scheduleItem) {
setSchedule([...schedule.filter((si) => creating || si.o_id !== scheduleItem.o_id), scheduleItem]);
}
setScheduleItem(undefined);
};
@@ -339,56 +363,37 @@ const SettingsScheduler: FC = () => {
<>
<Header>
<HeaderRow>
<HeaderCell stiff>{LL.NAME()}</HeaderCell>
<HeaderCell stiff>
<CheckIcon sx={{ fontSize: 16, verticalAlign: 'middle' }} />
</HeaderCell>
<HeaderCell />
<HeaderCell stiff>{LL.SCHEDULE()}</HeaderCell>
<HeaderCell stiff>{LL.TIME()}</HeaderCell>
<HeaderCell stiff>{LL.COMMAND()}</HeaderCell>
<HeaderCell>{LL.VALUE(0)}</HeaderCell>
<HeaderCell stiff>{LL.VALUE(0)}</HeaderCell>
<HeaderCell stiff>{LL.NAME(0)}</HeaderCell>
</HeaderRow>
</Header>
<Body>
{tableList.map((si: ScheduleItem) => (
<Row key={si.id} item={si} onClick={() => editScheduleItem(si)}>
<Cell>{si.id}</Cell>
<Cell stiff>
<Checkbox
size="small"
checked={si.active}
onChange={() => {
si.active = !si.active;
setFlags(['']); // forces refresh
}}
/>
{si.active && <CheckCircleIcon sx={{ color: '#79D200', fontSize: 16, verticalAlign: 'middle' }} />}
</Cell>
<Cell stiff>
<ToggleButtonGroup
size="small"
color="secondary"
value={getFlagString(si.flags)}
onChange={(event, flag) => {
si.flags = getFlagNumber(flag);
if (si.flags & ScheduleFlag.SCHEDULE_TIMER) {
si.flags = ScheduleFlag.SCHEDULE_TIMER;
}
setFlags(['']); // forces refresh
}}
>
<ToggleButton value="2">{showFlag(si, ScheduleFlag.SCHEDULE_MON)}</ToggleButton>
<ToggleButton value="4">{showFlag(si, ScheduleFlag.SCHEDULE_TUE)}</ToggleButton>
<ToggleButton value="8">{showFlag(si, ScheduleFlag.SCHEDULE_WED)}</ToggleButton>
<ToggleButton value="16">{showFlag(si, ScheduleFlag.SCHEDULE_THU)}</ToggleButton>
<ToggleButton value="32">{showFlag(si, ScheduleFlag.SCHEDULE_FRI)}</ToggleButton>
<ToggleButton value="64">{showFlag(si, ScheduleFlag.SCHEDULE_SAT)}</ToggleButton>
<ToggleButton value="1">{showFlag(si, ScheduleFlag.SCHEDULE_SUN)}</ToggleButton>
<ToggleButton value="128">{showFlag(si, ScheduleFlag.SCHEDULE_TIMER)}</ToggleButton>
</ToggleButtonGroup>
<Stack spacing={1} direction="row">
<Divider orientation="vertical" flexItem />
{dayBox(si, ScheduleFlag.SCHEDULE_MON)}
{dayBox(si, ScheduleFlag.SCHEDULE_TUE)}
{dayBox(si, ScheduleFlag.SCHEDULE_WED)}
{dayBox(si, ScheduleFlag.SCHEDULE_THU)}
{dayBox(si, ScheduleFlag.SCHEDULE_FRI)}
{dayBox(si, ScheduleFlag.SCHEDULE_SAT)}
{dayBox(si, ScheduleFlag.SCHEDULE_SUN)}
{dayBox(si, ScheduleFlag.SCHEDULE_TIMER)}
</Stack>
</Cell>
<Cell>{si.time}</Cell>
<Cell>{si.cmd}</Cell>
<Cell>{si.value}</Cell>
<Cell>{si.name}</Cell>
</Row>
))}
</Body>
@@ -408,7 +413,7 @@ const SettingsScheduler: FC = () => {
if (scheduleItem) {
try {
setFieldErrors(undefined);
await validate(schedulerItemValidation(schedule, scheduleItem.o_id), scheduleItem);
await validate(schedulerItemValidation(schedule, scheduleItem), scheduleItem);
updateScheduleItem();
} catch (errors: any) {
setFieldErrors(errors);
@@ -418,89 +423,94 @@ const SettingsScheduler: FC = () => {
const closeDialog = () => {
setScheduleItem(undefined);
setFieldErrors();
setFieldErrors(undefined);
};
const renderEditSchedule = () => {
if (scheduleItem) {
const isTimer = scheduleItem.flags === ScheduleFlag.SCHEDULE_TIMER;
return (
<Dialog open={!!scheduleItem} onClose={() => closeDialog()}>
<DialogTitle>
{creating ? LL.ADD(0) + ' ' + LL.NEW() + ' ' + LL.SCHEDULE() : LL.EDIT() + " '" + scheduleItem.id + "'"}
{creating ? LL.ADD(0) + ' ' + LL.NEW() : LL.EDIT()}&nbsp;{LL.SCHEDULE()}
</DialogTitle>
<DialogContent dividers>
<ValidatedTextField
fieldErrors={fieldErrors}
name="id"
label={LL.NAME()}
value={scheduleItem.id}
fullWidth
margin="normal"
sx={{ width: '60ch' }}
onChange={updateValue(setScheduleItem)}
/>
{creating ? (
<RadioGroup
row
name="schedule-type"
onChange={(event) => {
if ((event.target as HTMLInputElement).value === 't') {
scheduleItem.flags = ScheduleFlag.SCHEDULE_TIMER;
scheduleItem.time = '01:00';
} else {
scheduleItem.flags = 0;
}
updateValue(setScheduleItem);
setFlags(['']); // refresh screen
}}
>
<FormControlLabel value="w" control={<Radio />} label={LL.WEEKLY()} />
<FormControlLabel value="t" control={<Radio />} label={LL.TIMER()} />
</RadioGroup>
) : (
<Typography variant="h6" color="primary" sx={{ pb: 1 }}>
{LL.TYPE()}:&nbsp;{scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER ? LL.TIMER() : LL.WEEKLY()}
</Typography>
)}
<TextField
name="description"
label={LL.ENTITY_NAME()}
value={scheduleItem.description}
fullWidth
margin="normal"
sx={{ width: '60ch' }}
onChange={updateValue(setScheduleItem)}
/>
<BlockFormControlLabel
control={<Checkbox checked={scheduleItem.active} onChange={updateValue(setScheduleItem)} name="active" />}
label={LL.ACTIVE()}
/>
<Box display="flex" flexWrap="wrap" mb={1}>
<Box flexGrow={1}>
<ToggleButtonGroup
size="small"
color="secondary"
value={getFlagString(scheduleItem.flags)}
onChange={(event, flag) => {
scheduleItem.flags = getFlagNumber(flag) & 127;
setFlags(['']); // forces refresh
}}
>
<ToggleButton value="2">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_MON)}</ToggleButton>
<ToggleButton value="4">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_TUE)}</ToggleButton>
<ToggleButton value="8">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_WED)}</ToggleButton>
<ToggleButton value="16">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_THU)}</ToggleButton>
<ToggleButton value="32">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_FRI)}</ToggleButton>
<ToggleButton value="64">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_SAT)}</ToggleButton>
<ToggleButton value="1">{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_SUN)}</ToggleButton>
</ToggleButtonGroup>
</Box>
<Box flexWrap="nowrap" whiteSpace="nowrap">
{isTimer ? (
<Button
size="large"
sx={{ bgcolor: '#334f65' }}
variant="contained"
onClick={() => {
scheduleItem.flags = 0;
setFlags(['']); // forces refresh
}}
>
{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_TIMER)}
</Button>
) : (
<Button
size="large"
variant="outlined"
onClick={() => {
scheduleItem.flags = ScheduleFlag.SCHEDULE_TIMER;
setFlags(['']); // forces refresh
}}
>
{showFlag(scheduleItem, ScheduleFlag.SCHEDULE_TIMER)}
</Button>
)}
</Box>
</Box>
<Grid container>
<BlockFormControlLabel
control={
<Checkbox checked={scheduleItem.active} onChange={updateValue(setScheduleItem)} name="active" />
}
label={LL.ACTIVE()}
/>
{scheduleItem.active && (
<Grid item sx={{ mt: 1 }}>
<CheckCircleIcon sx={{ color: '#79D200', fontSize: 16, verticalAlign: 'middle' }} />
</Grid>
)}
</Grid>
{(scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER ? (
<TextField
name="time"
label={LL.TIMER()}
value={scheduleItem.time}
fullWidth
variant="outlined"
onChange={updateValue(setScheduleItem)}
margin="normal"
select
>
<MenuItem value={'00:00'}>{LL.SCHEDULE_TIMER_1()}</MenuItem>
<MenuItem value={'00:01'}>{LL.SCHEDULE_TIMER_2()}</MenuItem>
<MenuItem value={'01:00'}>{LL.SCHEDULE_TIMER_3()}</MenuItem>
</TextField>
) : (
<Grid container>
<TextField
name="time"
type="time"
label={LL.TIME()}
label={isTimer ? LL.TIMER() : LL.TIME()}
value={scheduleItem.time}
margin="normal"
onChange={updateValue(setScheduleItem)}
/>
)}
{isTimer && (
<Box color="warning.main" ml={2} mt={4}>
<Typography variant="body2">{LL.SCHEDULER_HELP_2()}</Typography>
</Box>
)}
</Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
name="cmd"
@@ -519,6 +529,15 @@ const SettingsScheduler: FC = () => {
value={scheduleItem.value}
onChange={updateValue(setScheduleItem)}
/>
<ValidatedTextField
fieldErrors={fieldErrors}
name="name"
label={LL.NAME(0)}
value={scheduleItem.name}
fullWidth
margin="normal"
onChange={updateValue(setScheduleItem)}
/>
</DialogContent>
<DialogActions>
{!creating && (

View File

@@ -305,14 +305,14 @@ export enum DeviceEntityMask {
}
export interface ScheduleItem {
id: string; // unique index which is name
id: string; // unique index
active: boolean;
deleted?: boolean; // optional
flags: number;
time: string;
cmd: string;
value: string;
description?: string; // optional
name?: string; // optional
o_id?: string;
o_active?: boolean;
o_deleted?: boolean;
@@ -320,7 +320,7 @@ export interface ScheduleItem {
o_time?: string;
o_cmd?: string;
o_value?: string;
o_description?: string;
o_name?: string;
}
export interface Schedule {
@@ -328,12 +328,12 @@ export interface Schedule {
}
export enum ScheduleFlag {
SCHEDULE_SUN = 1,
SCHEDULE_MON = 2,
SCHEDULE_TUE = 4,
SCHEDULE_WED = 8,
SCHEDULE_THU = 16,
SCHEDULE_FRI = 32,
SCHEDULE_SAT = 64,
SCHEDULE_SUN = 1,
SCHEDULE_TIMER = 128
}

View File

@@ -85,16 +85,15 @@ export const createSettingsValidator = (settings: Settings) =>
})
});
export const schedulerItemValidation = (schedule: ScheduleItem[], o_id: string) =>
export const schedulerItemValidation = (schedule: ScheduleItem[], scheduleItem: ScheduleItem) =>
new Schema({
id: [
{ required: true, message: 'Name is required' },
name: [
{
type: 'string',
pattern: /^[a-zA-Z0-9_\\.]{1,15}$/,
message: "Must be 1-15 characters: alpha numeric, '_' or '.'"
pattern: /^[a-zA-Z0-9_\\.]{0,15}$/,
message: "Must be <15 characters: alpha numeric, '_' or '.'"
},
...[uniqueIDValidator(schedule, o_id)]
...[uniqueNameValidator(schedule, scheduleItem.o_name)]
],
cmd: [
{ required: true, message: 'Command is required' },
@@ -102,9 +101,9 @@ export const schedulerItemValidation = (schedule: ScheduleItem[], o_id: string)
]
});
export const uniqueIDValidator = (schedule: ScheduleItem[], o_id: string) => ({
validator(rule: InternalRuleItem, id: string, callback: (error?: string) => void) {
if (id && o_id !== id && schedule.find((si) => si.id === id)) {
export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({
validator(rule: InternalRuleItem, name: string, callback: (error?: string) => void) {
if (name && o_name && o_name !== name && schedule.find((si) => si.name === name)) {
callback('Name already in use');
} else {
callback();

View File

@@ -15,6 +15,15 @@ __metadata:
languageName: node
linkType: hard
"@arcanis/slice-ansi@npm:^1.1.1":
version: 1.1.1
resolution: "@arcanis/slice-ansi@npm:1.1.1"
dependencies:
grapheme-splitter: ^1.0.4
checksum: 14ed60cb45750d386c64229ac7bab20e10eedc193503fa4decff764162d329d6d3363ed2cd3debec833186ee54affe4f824f6e8eff531295117fd1ebda200270
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.18.6":
version: 7.18.6
resolution: "@babel/code-frame@npm:7.18.6"
@@ -929,6 +938,13 @@ __metadata:
languageName: node
linkType: hard
"@sindresorhus/is@npm:^4.0.0":
version: 4.6.0
resolution: "@sindresorhus/is@npm:4.6.0"
checksum: 83839f13da2c29d55c97abc3bc2c55b250d33a0447554997a85c539e058e57b8da092da396e252b11ec24a0279a0bed1f537fa26302209327060643e327f81d2
languageName: node
linkType: hard
"@svgr/babel-plugin-add-jsx-attribute@npm:^6.5.1":
version: 6.5.1
resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:6.5.1"
@@ -1165,6 +1181,15 @@ __metadata:
languageName: node
linkType: hard
"@szmarczak/http-timer@npm:^4.0.5":
version: 4.0.6
resolution: "@szmarczak/http-timer@npm:4.0.6"
dependencies:
defer-to-connect: ^2.0.0
checksum: c29df3bcec6fc3bdec2b17981d89d9c9fc9bd7d0c9bcfe92821dc533f4440bc890ccde79971838b4ceed1921d456973c4180d7175ee1d0023ad0562240a58d95
languageName: node
linkType: hard
"@table-library/react-table-library@npm:4.0.29":
version: 4.0.29
resolution: "@table-library/react-table-library@npm:4.0.29"
@@ -1187,6 +1212,25 @@ __metadata:
languageName: node
linkType: hard
"@types/cacheable-request@npm:^6.0.1":
version: 6.0.3
resolution: "@types/cacheable-request@npm:6.0.3"
dependencies:
"@types/http-cache-semantics": "*"
"@types/keyv": ^3.1.4
"@types/node": "*"
"@types/responselike": ^1.0.0
checksum: d9b26403fe65ce6b0cb3720b7030104c352bcb37e4fac2a7089a25a97de59c355fa08940658751f2f347a8512aa9d18fdb66ab3ade835975b2f454f2d5befbd9
languageName: node
linkType: hard
"@types/emscripten@npm:^1.39.6":
version: 1.39.6
resolution: "@types/emscripten@npm:1.39.6"
checksum: 437f2f9cdfd9057255662508fa9a415fe704ba484c6198f3549c5b05feebcdcd612b1ec7b10026d2566935d05d3c36f9366087cb42bc90bd25772a88fcfc9343
languageName: node
linkType: hard
"@types/estree@npm:^1.0.0":
version: 1.0.0
resolution: "@types/estree@npm:1.0.0"
@@ -1201,6 +1245,23 @@ __metadata:
languageName: node
linkType: hard
"@types/hoist-non-react-statics@npm:*":
version: 3.3.1
resolution: "@types/hoist-non-react-statics@npm:3.3.1"
dependencies:
"@types/react": "*"
hoist-non-react-statics: ^3.3.0
checksum: 2c0778570d9a01d05afabc781b32163f28409bb98f7245c38d5eaf082416fdb73034003f5825eb5e21313044e8d2d9e1f3fe2831e345d3d1b1d20bcd12270719
languageName: node
linkType: hard
"@types/http-cache-semantics@npm:*":
version: 4.0.1
resolution: "@types/http-cache-semantics@npm:4.0.1"
checksum: 1048aacf627829f0d5f00184e16548205cd9f964bf0841c29b36bc504509230c40bc57c39778703a1c965a6f5b416ae2cbf4c1d4589c889d2838dd9dbfccf6e9
languageName: node
linkType: hard
"@types/json-schema@npm:^7.0.9":
version: 7.0.11
resolution: "@types/json-schema@npm:7.0.11"
@@ -1215,6 +1276,15 @@ __metadata:
languageName: node
linkType: hard
"@types/keyv@npm:^3.1.4":
version: 3.1.4
resolution: "@types/keyv@npm:3.1.4"
dependencies:
"@types/node": "*"
checksum: e009a2bfb50e90ca9b7c6e8f648f8464067271fd99116f881073fa6fa76dc8d0133181dd65e6614d5fb1220d671d67b0124aef7d97dc02d7e342ab143a47779d
languageName: node
linkType: hard
"@types/lodash-es@npm:^4.17.6":
version: 4.17.6
resolution: "@types/lodash-es@npm:4.17.6"
@@ -1238,10 +1308,10 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^18.14.5":
version: 18.14.5
resolution: "@types/node@npm:18.14.5"
checksum: 415fb0edc132baa9580f1b7a381a3f10b662f5d7a7d11641917fa0961788ccede3272badc414aadc47306e9fc35c5f6c59159ac470b46d3f3a15fb0446224c8c
"@types/node@npm:*, @types/node@npm:^18.11.11, @types/node@npm:^18.14.6":
version: 18.14.6
resolution: "@types/node@npm:18.14.6"
checksum: 2f88f482cabadc6dbddd627a1674239e68c3c9beab56eb4ae2309fb96fd17fc3a509d99b0309bafe13b58529574f49ecf3a583f2ebe2896dd32fe4be436dc96e
languageName: node
linkType: hard
@@ -1318,6 +1388,15 @@ __metadata:
languageName: node
linkType: hard
"@types/responselike@npm:^1.0.0":
version: 1.0.0
resolution: "@types/responselike@npm:1.0.0"
dependencies:
"@types/node": "*"
checksum: e99fc7cc6265407987b30deda54c1c24bb1478803faf6037557a774b2f034c5b097ffd65847daa87e82a61a250d919f35c3588654b0fdaa816906650f596d1b0
languageName: node
linkType: hard
"@types/scheduler@npm:*":
version: 0.16.2
resolution: "@types/scheduler@npm:0.16.2"
@@ -1325,13 +1404,31 @@ __metadata:
languageName: node
linkType: hard
"@types/semver@npm:^7.3.12":
"@types/semver@npm:^7.1.0, @types/semver@npm:^7.3.12":
version: 7.3.13
resolution: "@types/semver@npm:7.3.13"
checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0
languageName: node
linkType: hard
"@types/styled-components@npm:^5":
version: 5.1.26
resolution: "@types/styled-components@npm:5.1.26"
dependencies:
"@types/hoist-non-react-statics": "*"
"@types/react": "*"
csstype: ^3.0.2
checksum: 84f53b3101739b20d1731554fb7735bc2f3f5d050a8b392e9845403c8c8bbd729737d033978649f9195a97b557875b010d46e35a4538564a2d0dbcce661dbf76
languageName: node
linkType: hard
"@types/treeify@npm:^1.0.0":
version: 1.0.0
resolution: "@types/treeify@npm:1.0.0"
checksum: 1b2397030d13beee7f82b878ca80feeddb0d550a6b00d8be30082a370c0ac5985ecf7b9378cf93ea278ff00c3e900b416ae8d9379f2c7e8caecdece1dfc77380
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^5.54.0":
version: 5.54.0
resolution: "@typescript-eslint/eslint-plugin@npm:5.54.0"
@@ -1475,6 +1572,125 @@ __metadata:
languageName: node
linkType: hard
"@yarnpkg/core@npm:^4.0.0-rc.40":
version: 4.0.0-rc.40
resolution: "@yarnpkg/core@npm:4.0.0-rc.40"
dependencies:
"@arcanis/slice-ansi": ^1.1.1
"@types/semver": ^7.1.0
"@types/treeify": ^1.0.0
"@yarnpkg/fslib": ^3.0.0-rc.40
"@yarnpkg/libzip": ^3.0.0-rc.40
"@yarnpkg/parsers": ^3.0.0-rc.40
"@yarnpkg/shell": ^4.0.0-rc.40
camelcase: ^5.3.1
chalk: ^3.0.0
ci-info: ^3.2.0
clipanion: ^3.2.0-rc.10
cross-spawn: 7.0.3
diff: ^5.1.0
globby: ^11.0.1
got: ^11.7.0
lodash: ^4.17.15
micromatch: ^4.0.2
p-limit: ^2.2.0
semver: ^7.1.2
strip-ansi: ^6.0.0
tar: ^6.0.5
tinylogic: ^2.0.0
treeify: ^1.1.0
tslib: ^2.4.0
tunnel: ^0.0.6
checksum: 38a141f5e26355fd75e23c2a3afbe6bb79693ad914d263a75a2d212185ad2f987c740aea2e9f61fe360fcee666e9c56b961ffe57444e367acb7cc2ed77327127
languageName: node
linkType: hard
"@yarnpkg/fslib@npm:^3.0.0-rc.40":
version: 3.0.0-rc.40
resolution: "@yarnpkg/fslib@npm:3.0.0-rc.40"
dependencies:
tslib: ^2.4.0
checksum: 8f36ade258a21ee9bec8621367dce68dab5d4e318863bd6b7585595f3697268f76fee5dd855c357ced31213d8ddbf2a2ab7a931d91699b295d60aa02433cc442
languageName: node
linkType: hard
"@yarnpkg/libzip@npm:^3.0.0-rc.40":
version: 3.0.0-rc.40
resolution: "@yarnpkg/libzip@npm:3.0.0-rc.40"
dependencies:
"@types/emscripten": ^1.39.6
"@yarnpkg/fslib": ^3.0.0-rc.40
tslib: ^2.4.0
peerDependencies:
"@yarnpkg/fslib": ^3.0.0-rc.40
checksum: eca50e5f56709a98111a77bede2e25da573668ad9bc98be01326c0ef1b7e47aac4de19896cc8f2a59f4f4175dfc6e9e307ee18c25facc32353d1209ab43b1c2e
languageName: node
linkType: hard
"@yarnpkg/nm@npm:^4.0.0-rc.40":
version: 4.0.0-rc.40
resolution: "@yarnpkg/nm@npm:4.0.0-rc.40"
dependencies:
"@yarnpkg/core": ^4.0.0-rc.40
"@yarnpkg/fslib": ^3.0.0-rc.40
"@yarnpkg/pnp": ^4.0.0-rc.40
checksum: d575264238be9dbd7c5dc435227e830f2d4a1a61c82292a3d6e1dd1f966d552e1acc02aa0f7a2fa60ffa551a3b0862fde320428d6fea20dd110fec4dd1394c7d
languageName: node
linkType: hard
"@yarnpkg/parsers@npm:^3.0.0-rc.40":
version: 3.0.0-rc.40
resolution: "@yarnpkg/parsers@npm:3.0.0-rc.40"
dependencies:
js-yaml: ^3.10.0
tslib: ^2.4.0
checksum: 64df0d8dad4cd43af5fcff758b0cfc47e7dc56e00eed87e3bd6ce8a9ea265c41fe758c1e01607e630bae46c9f8324ca853bced58be3d1c93492e125757ab7f30
languageName: node
linkType: hard
"@yarnpkg/pnp@npm:^4.0.0-rc.40":
version: 4.0.0-rc.40
resolution: "@yarnpkg/pnp@npm:4.0.0-rc.40"
dependencies:
"@types/node": ^18.11.11
"@yarnpkg/fslib": ^3.0.0-rc.40
checksum: ae680b0131027025cf22d35d41c9a72783aab0b4765fd73558834226a62154276da884a93bf7877d29dc5a696642c4d3999944dbb3d0743b4e3c6a4c71dc9855
languageName: node
linkType: hard
"@yarnpkg/pnpify@npm:^4.0.0-rc.40":
version: 4.0.0-rc.40
resolution: "@yarnpkg/pnpify@npm:4.0.0-rc.40"
dependencies:
"@yarnpkg/core": ^4.0.0-rc.40
"@yarnpkg/fslib": ^3.0.0-rc.40
"@yarnpkg/nm": ^4.0.0-rc.40
clipanion: ^3.2.0-rc.10
tslib: ^2.4.0
bin:
pnpify: ./lib/cli.js
checksum: 51ef0c5db61faedb0b35dd9d19e2ce539d3dcf4e2d17080c0c3f3bcc4570c4ea644c98b989872da275b9b68b04afb9541676433d1a2838774878b659f896bc72
languageName: node
linkType: hard
"@yarnpkg/shell@npm:^4.0.0-rc.40":
version: 4.0.0-rc.40
resolution: "@yarnpkg/shell@npm:4.0.0-rc.40"
dependencies:
"@yarnpkg/fslib": ^3.0.0-rc.40
"@yarnpkg/parsers": ^3.0.0-rc.40
chalk: ^3.0.0
clipanion: ^3.2.0-rc.10
cross-spawn: 7.0.3
fast-glob: ^3.2.2
micromatch: ^4.0.2
tslib: ^2.4.0
bin:
shell: ./lib/cli.js
checksum: 0e136522ce5f157b38bdbb9633b9860cdfa06846cb6eafa5b04a64b790381cf4d946037669d4e975d4c4c9b5264f7f89e206dac5c82f01673ca5e479235d3c63
languageName: node
linkType: hard
"EMS-ESP@workspace:.":
version: 0.0.0-use.local
resolution: "EMS-ESP@workspace:."
@@ -1488,13 +1704,15 @@ __metadata:
"@table-library/react-table-library": 4.0.29
"@types/lodash-es": ^4.17.6
"@types/mime-types": ^2
"@types/node": ^18.14.5
"@types/node": ^18.14.6
"@types/react": ^18.0.28
"@types/react-dom": ^18.0.11
"@types/react-router-dom": ^5.3.3
"@types/styled-components": ^5
"@typescript-eslint/eslint-plugin": ^5.54.0
"@typescript-eslint/parser": ^5.54.0
"@vitejs/plugin-react-swc": ^3.2.0
"@yarnpkg/pnpify": ^4.0.0-rc.40
async-validator: ^4.2.5
axios: ^1.3.4
eslint: ^8.35.0
@@ -1512,8 +1730,8 @@ __metadata:
notistack: ^2.0.8
npm-run-all: ^4.1.5
prettier: ^2.8.4
react: ^18.2.0
react-dom: ^18.2.0
react: latest
react-dom: latest
react-dropzone: ^14.2.3
react-icons: ^4.8.0
react-router-dom: ^6.8.2
@@ -1646,6 +1864,15 @@ __metadata:
languageName: node
linkType: hard
"argparse@npm:^1.0.7":
version: 1.0.10
resolution: "argparse@npm:1.0.10"
dependencies:
sprintf-js: ~1.0.2
checksum: 7ca6e45583a28de7258e39e13d81e925cfa25d7d4aacbf806a382d3c02fcb13403a07fb8aeef949f10a7cfe4a62da0e2e807b348a5980554cc28ee573ef95945
languageName: node
linkType: hard
"argparse@npm:^2.0.1":
version: 2.0.1
resolution: "argparse@npm:2.0.1"
@@ -1874,6 +2101,28 @@ __metadata:
languageName: node
linkType: hard
"cacheable-lookup@npm:^5.0.3":
version: 5.0.4
resolution: "cacheable-lookup@npm:5.0.4"
checksum: 763e02cf9196bc9afccacd8c418d942fc2677f22261969a4c2c2e760fa44a2351a81557bd908291c3921fe9beb10b976ba8fa50c5ca837c5a0dd945f16468f2d
languageName: node
linkType: hard
"cacheable-request@npm:^7.0.2":
version: 7.0.2
resolution: "cacheable-request@npm:7.0.2"
dependencies:
clone-response: ^1.0.2
get-stream: ^5.1.0
http-cache-semantics: ^4.0.0
keyv: ^4.0.0
lowercase-keys: ^2.0.0
normalize-url: ^6.0.1
responselike: ^2.0.0
checksum: 6152813982945a5c9989cb457a6c499f12edcc7ade323d2fbfd759abc860bdbd1306e08096916bb413c3c47e812f8e4c0a0cc1e112c8ce94381a960f115bc77f
languageName: node
linkType: hard
"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2":
version: 1.0.2
resolution: "call-bind@npm:1.0.2"
@@ -1891,6 +2140,13 @@ __metadata:
languageName: node
linkType: hard
"camelcase@npm:^5.3.1":
version: 5.3.1
resolution: "camelcase@npm:5.3.1"
checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b
languageName: node
linkType: hard
"camelcase@npm:^6.2.0":
version: 6.3.0
resolution: "camelcase@npm:6.3.0"
@@ -1916,6 +2172,16 @@ __metadata:
languageName: node
linkType: hard
"chalk@npm:^3.0.0":
version: 3.0.0
resolution: "chalk@npm:3.0.0"
dependencies:
ansi-styles: ^4.1.0
supports-color: ^7.1.0
checksum: 8e3ddf3981c4da405ddbd7d9c8d91944ddf6e33d6837756979f7840a29272a69a5189ecae0ff84006750d6d1e92368d413335eab4db5476db6e6703a1d1e0505
languageName: node
linkType: hard
"chalk@npm:^4.0.0":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
@@ -1952,6 +2218,13 @@ __metadata:
languageName: node
linkType: hard
"ci-info@npm:^3.2.0":
version: 3.8.0
resolution: "ci-info@npm:3.8.0"
checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
@@ -1959,6 +2232,17 @@ __metadata:
languageName: node
linkType: hard
"clipanion@npm:^3.2.0-rc.10":
version: 3.2.0
resolution: "clipanion@npm:3.2.0"
dependencies:
typanion: ^3.8.0
peerDependencies:
typanion: "*"
checksum: e28e6f0d48aecff86097823c604aa486082d76d2a5d3abc71069a0d9f3338af769fd7c6634b2f444c5b1aac0743b503325cc0b30552c094c01ebc602631b273d
languageName: node
linkType: hard
"cliui@npm:^8.0.1":
version: 8.0.1
resolution: "cliui@npm:8.0.1"
@@ -1970,6 +2254,15 @@ __metadata:
languageName: node
linkType: hard
"clone-response@npm:^1.0.2":
version: 1.0.3
resolution: "clone-response@npm:1.0.3"
dependencies:
mimic-response: ^1.0.0
checksum: 4e671cac39b11c60aa8ba0a450657194a5d6504df51bca3fac5b3bd0145c4f8e8464898f87c8406b83232e3bc5cca555f51c1f9c8ac023969ebfbf7f6bdabb2e
languageName: node
linkType: hard
"clsx@npm:1.1.1":
version: 1.1.1
resolution: "clsx@npm:1.1.1"
@@ -2068,6 +2361,17 @@ __metadata:
languageName: node
linkType: hard
"cross-spawn@npm:7.0.3, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
dependencies:
path-key: ^3.1.0
shebang-command: ^2.0.0
which: ^2.0.1
checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52
languageName: node
linkType: hard
"cross-spawn@npm:^6.0.5":
version: 6.0.5
resolution: "cross-spawn@npm:6.0.5"
@@ -2081,17 +2385,6 @@ __metadata:
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
dependencies:
path-key: ^3.1.0
shebang-command: ^2.0.0
which: ^2.0.1
checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52
languageName: node
linkType: hard
"csstype@npm:^3.0.2, csstype@npm:^3.1.1":
version: 3.1.1
resolution: "csstype@npm:3.1.1"
@@ -2127,6 +2420,15 @@ __metadata:
languageName: node
linkType: hard
"decompress-response@npm:^6.0.0":
version: 6.0.0
resolution: "decompress-response@npm:6.0.0"
dependencies:
mimic-response: ^3.1.0
checksum: d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812
languageName: node
linkType: hard
"deep-equal@npm:^2.0.5":
version: 2.2.0
resolution: "deep-equal@npm:2.2.0"
@@ -2159,6 +2461,13 @@ __metadata:
languageName: node
linkType: hard
"defer-to-connect@npm:^2.0.0":
version: 2.0.1
resolution: "defer-to-connect@npm:2.0.1"
checksum: 8a9b50d2f25446c0bfefb55a48e90afd58f85b21bcf78e9207cd7b804354f6409032a1705c2491686e202e64fc05f147aa5aa45f9aa82627563f045937f5791b
languageName: node
linkType: hard
"define-lazy-prop@npm:^2.0.0":
version: 2.0.0
resolution: "define-lazy-prop@npm:2.0.0"
@@ -2197,6 +2506,13 @@ __metadata:
languageName: node
linkType: hard
"diff@npm:^5.1.0":
version: 5.1.0
resolution: "diff@npm:5.1.0"
checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90
languageName: node
linkType: hard
"dir-glob@npm:^3.0.1":
version: 3.0.1
resolution: "dir-glob@npm:3.0.1"
@@ -2264,6 +2580,15 @@ __metadata:
languageName: node
linkType: hard
"end-of-stream@npm:^1.1.0":
version: 1.4.4
resolution: "end-of-stream@npm:1.4.4"
dependencies:
once: ^1.4.0
checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b
languageName: node
linkType: hard
"enhanced-resolve@npm:^5.10.0":
version: 5.12.0
resolution: "enhanced-resolve@npm:5.12.0"
@@ -2739,6 +3064,16 @@ __metadata:
languageName: node
linkType: hard
"esprima@npm:^4.0.0":
version: 4.0.1
resolution: "esprima@npm:4.0.1"
bin:
esparse: ./bin/esparse.js
esvalidate: ./bin/esvalidate.js
checksum: b45bc805a613dbea2835278c306b91aff6173c8d034223fa81498c77dcbce3b2931bf6006db816f62eacd9fd4ea975dfd85a5b7f3c6402cfd050d4ca3c13a628
languageName: node
linkType: hard
"esquery@npm:^1.4.2":
version: 1.4.2
resolution: "esquery@npm:1.4.2"
@@ -2792,7 +3127,7 @@ __metadata:
languageName: node
linkType: hard
"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.9":
"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.9":
version: 3.2.12
resolution: "fast-glob@npm:3.2.12"
dependencies:
@@ -3021,6 +3356,15 @@ __metadata:
languageName: node
linkType: hard
"get-stream@npm:^5.1.0":
version: 5.2.0
resolution: "get-stream@npm:5.2.0"
dependencies:
pump: ^3.0.0
checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12
languageName: node
linkType: hard
"get-symbol-description@npm:^1.0.0":
version: 1.0.0
resolution: "get-symbol-description@npm:1.0.0"
@@ -3115,7 +3459,7 @@ __metadata:
languageName: node
linkType: hard
"globby@npm:^11.1.0":
"globby@npm:^11.0.1, globby@npm:^11.1.0":
version: 11.1.0
resolution: "globby@npm:11.1.0"
dependencies:
@@ -3158,6 +3502,25 @@ __metadata:
languageName: node
linkType: hard
"got@npm:^11.7.0":
version: 11.8.6
resolution: "got@npm:11.8.6"
dependencies:
"@sindresorhus/is": ^4.0.0
"@szmarczak/http-timer": ^4.0.5
"@types/cacheable-request": ^6.0.1
"@types/responselike": ^1.0.0
cacheable-lookup: ^5.0.3
cacheable-request: ^7.0.2
decompress-response: ^6.0.0
http2-wrapper: ^1.0.0-beta.5.2
lowercase-keys: ^2.0.0
p-cancelable: ^2.0.0
responselike: ^2.0.0
checksum: bbc783578a8d5030c8164ef7f57ce41b5ad7db2ed13371e1944bef157eeca5a7475530e07c0aaa71610d7085474d0d96222c9f4268d41db333a17e39b463f45d
languageName: node
linkType: hard
"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6":
version: 4.2.10
resolution: "graceful-fs@npm:4.2.10"
@@ -3266,7 +3629,7 @@ __metadata:
languageName: node
linkType: hard
"http-cache-semantics@npm:^4.1.0":
"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236
@@ -3284,6 +3647,16 @@ __metadata:
languageName: node
linkType: hard
"http2-wrapper@npm:^1.0.0-beta.5.2":
version: 1.0.3
resolution: "http2-wrapper@npm:1.0.3"
dependencies:
quick-lru: ^5.1.1
resolve-alpn: ^1.0.0
checksum: 74160b862ec699e3f859739101ff592d52ce1cb207b7950295bf7962e4aa1597ef709b4292c673bece9c9b300efad0559fc86c71b1409c7a1e02b7229456003e
languageName: node
linkType: hard
"https-proxy-agent@npm:^5.0.0":
version: 5.0.1
resolution: "https-proxy-agent@npm:5.0.1"
@@ -3669,6 +4042,18 @@ __metadata:
languageName: node
linkType: hard
"js-yaml@npm:^3.10.0":
version: 3.14.1
resolution: "js-yaml@npm:3.14.1"
dependencies:
argparse: ^1.0.7
esprima: ^4.0.0
bin:
js-yaml: bin/js-yaml.js
checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c
languageName: node
linkType: hard
"js-yaml@npm:^4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
@@ -3689,6 +4074,13 @@ __metadata:
languageName: node
linkType: hard
"json-buffer@npm:3.0.1":
version: 3.0.1
resolution: "json-buffer@npm:3.0.1"
checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581
languageName: node
linkType: hard
"json-parse-better-errors@npm:^1.0.1":
version: 1.0.2
resolution: "json-parse-better-errors@npm:1.0.2"
@@ -3754,6 +4146,15 @@ __metadata:
languageName: node
linkType: hard
"keyv@npm:^4.0.0":
version: 4.5.2
resolution: "keyv@npm:4.5.2"
dependencies:
json-buffer: 3.0.1
checksum: 13ad58303acd2261c0d4831b4658451603fd159e61daea2121fcb15feb623e75ee328cded0572da9ca76b7b3ceaf8e614f1806c6b3af5db73c9c35a345259651
languageName: node
linkType: hard
"language-subtag-registry@npm:~0.3.2":
version: 0.3.22
resolution: "language-subtag-registry@npm:0.3.22"
@@ -3822,6 +4223,13 @@ __metadata:
languageName: node
linkType: hard
"lodash@npm:^4.17.15":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
languageName: node
linkType: hard
"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
@@ -3833,6 +4241,13 @@ __metadata:
languageName: node
linkType: hard
"lowercase-keys@npm:^2.0.0":
version: 2.0.0
resolution: "lowercase-keys@npm:2.0.0"
checksum: 24d7ebd56ccdf15ff529ca9e08863f3c54b0b9d1edb97a3ae1af34940ae666c01a1e6d200707bce730a8ef76cb57cc10e65f245ecaaf7e6bc8639f2fb460ac23
languageName: node
linkType: hard
"lru-cache@npm:^5.1.1":
version: 5.1.1
resolution: "lru-cache@npm:5.1.1"
@@ -3903,7 +4318,7 @@ __metadata:
languageName: node
linkType: hard
"micromatch@npm:^4.0.4":
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4":
version: 4.0.5
resolution: "micromatch@npm:4.0.5"
dependencies:
@@ -3929,6 +4344,20 @@ __metadata:
languageName: node
linkType: hard
"mimic-response@npm:^1.0.0":
version: 1.0.1
resolution: "mimic-response@npm:1.0.1"
checksum: 034c78753b0e622bc03c983663b1cdf66d03861050e0c8606563d149bc2b02d63f62ce4d32be4ab50d0553ae0ffe647fc34d1f5281184c6e1e8cf4d85e8d9823
languageName: node
linkType: hard
"mimic-response@npm:^3.1.0":
version: 3.1.0
resolution: "mimic-response@npm:3.1.0"
checksum: 25739fee32c17f433626bf19f016df9036b75b3d84a3046c7d156e72ec963dd29d7fc8a302f55a3d6c5a4ff24259676b15d915aad6480815a969ff2ec0836867
languageName: node
linkType: hard
"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
@@ -4179,6 +4608,13 @@ __metadata:
languageName: node
linkType: hard
"normalize-url@npm:^6.0.1":
version: 6.1.0
resolution: "normalize-url@npm:6.1.0"
checksum: 4a4944631173e7d521d6b80e4c85ccaeceb2870f315584fa30121f505a6dfd86439c5e3fdd8cd9e0e291290c41d0c3599f0cb12ab356722ed242584c30348e50
languageName: node
linkType: hard
"notistack@npm:^2.0.8":
version: 2.0.8
resolution: "notistack@npm:2.0.8"
@@ -4319,7 +4755,7 @@ __metadata:
languageName: node
linkType: hard
"once@npm:^1.3.0":
"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
@@ -4353,6 +4789,22 @@ __metadata:
languageName: node
linkType: hard
"p-cancelable@npm:^2.0.0":
version: 2.1.1
resolution: "p-cancelable@npm:2.1.1"
checksum: 3dba12b4fb4a1e3e34524535c7858fc82381bbbd0f247cc32dedc4018592a3950ce66b106d0880b4ec4c2d8d6576f98ca885dc1d7d0f274d1370be20e9523ddf
languageName: node
linkType: hard
"p-limit@npm:^2.2.0":
version: 2.3.0
resolution: "p-limit@npm:2.3.0"
dependencies:
p-try: ^2.0.0
checksum: 84ff17f1a38126c3314e91ecfe56aecbf36430940e2873dadaa773ffe072dc23b7af8e46d4b6485d302a11673fe94c6b67ca2cfbb60c989848b02100d0594ac1
languageName: node
linkType: hard
"p-limit@npm:^3.0.2":
version: 3.1.0
resolution: "p-limit@npm:3.1.0"
@@ -4380,6 +4832,13 @@ __metadata:
languageName: node
linkType: hard
"p-try@npm:^2.0.0":
version: 2.2.0
resolution: "p-try@npm:2.2.0"
checksum: f8a8e9a7693659383f06aec604ad5ead237c7a261c18048a6e1b5b85a5f8a067e469aa24f5bc009b991ea3b058a87f5065ef4176793a200d4917349881216cae
languageName: node
linkType: hard
"parent-module@npm:^1.0.0":
version: 1.0.1
resolution: "parent-module@npm:1.0.1"
@@ -4561,6 +5020,16 @@ __metadata:
languageName: node
linkType: hard
"pump@npm:^3.0.0":
version: 3.0.0
resolution: "pump@npm:3.0.0"
dependencies:
end-of-stream: ^1.1.0
once: ^1.3.1
checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9
languageName: node
linkType: hard
"punycode@npm:^2.1.0":
version: 2.3.0
resolution: "punycode@npm:2.3.0"
@@ -4575,7 +5044,14 @@ __metadata:
languageName: node
linkType: hard
"react-dom@npm:^18.2.0":
"quick-lru@npm:^5.1.1":
version: 5.1.1
resolution: "quick-lru@npm:5.1.1"
checksum: a516faa25574be7947969883e6068dbe4aa19e8ef8e8e0fd96cddd6d36485e9106d85c0041a27153286b0770b381328f4072aa40d3b18a19f5f7d2b78b94b5ed
languageName: node
linkType: hard
"react-dom@npm:latest":
version: 18.2.0
resolution: "react-dom@npm:18.2.0"
dependencies:
@@ -4685,7 +5161,7 @@ __metadata:
languageName: node
linkType: hard
"react@npm:^18.2.0":
"react@npm:latest":
version: 18.2.0
resolution: "react@npm:18.2.0"
dependencies:
@@ -4757,6 +5233,13 @@ __metadata:
languageName: node
linkType: hard
"resolve-alpn@npm:^1.0.0":
version: 1.2.1
resolution: "resolve-alpn@npm:1.2.1"
checksum: f558071fcb2c60b04054c99aebd572a2af97ef64128d59bef7ab73bd50d896a222a056de40ffc545b633d99b304c259ea9d0c06830d5c867c34f0bfa60b8eae0
languageName: node
linkType: hard
"resolve-from@npm:^4.0.0":
version: 4.0.0
resolution: "resolve-from@npm:4.0.0"
@@ -4816,6 +5299,15 @@ __metadata:
languageName: node
linkType: hard
"responselike@npm:^2.0.0":
version: 2.0.1
resolution: "responselike@npm:2.0.1"
dependencies:
lowercase-keys: ^2.0.0
checksum: b122535466e9c97b55e69c7f18e2be0ce3823c5d47ee8de0d9c0b114aa55741c6db8bfbfce3766a94d1272e61bfb1ebf0a15e9310ac5629fbb7446a861b4fd3a
languageName: node
linkType: hard
"retry@npm:^0.12.0":
version: 0.12.0
resolution: "retry@npm:0.12.0"
@@ -4935,7 +5427,7 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.3.7":
"semver@npm:^7.1.2, semver@npm:^7.3.5, semver@npm:^7.3.7":
version: 7.3.8
resolution: "semver@npm:7.3.8"
dependencies:
@@ -5132,6 +5624,13 @@ __metadata:
languageName: node
linkType: hard
"sprintf-js@npm:~1.0.2":
version: 1.0.3
resolution: "sprintf-js@npm:1.0.3"
checksum: 19d79aec211f09b99ec3099b5b2ae2f6e9cdefe50bc91ac4c69144b6d3928a640bb6ae5b3def70c2e85a2c3d9f5ec2719921e3a59d3ca3ef4b2fd1a4656a0df3
languageName: node
linkType: hard
"ssri@npm:^9.0.0":
version: 9.0.1
resolution: "ssri@npm:9.0.1"
@@ -5298,7 +5797,7 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.2":
"tar@npm:^6.0.5, tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.1.13
resolution: "tar@npm:6.1.13"
dependencies:
@@ -5329,6 +5828,13 @@ __metadata:
languageName: node
linkType: hard
"tinylogic@npm:^2.0.0":
version: 2.0.0
resolution: "tinylogic@npm:2.0.0"
checksum: b966cbb41241a048095fb9e685d5e2020475fdea2c65b4ae51e5dee48964860a4505d987503c004b8a76e96b64c7da2f49954dd36c691d559c315d878ce7da29
languageName: node
linkType: hard
"to-fast-properties@npm:^2.0.0":
version: 2.0.0
resolution: "to-fast-properties@npm:2.0.0"
@@ -5356,6 +5862,13 @@ __metadata:
languageName: node
linkType: hard
"treeify@npm:^1.1.0":
version: 1.1.0
resolution: "treeify@npm:1.1.0"
checksum: aa00dded220c1dd052573bd6fc2c52862f09870851a284f0d3650d72bf913ba9b4f6b824f4f1ab81899bae29375f4266b07fe47cbf82343a1efa13cc09ce87af
languageName: node
linkType: hard
"tsconfck@npm:^2.0.1":
version: 2.0.3
resolution: "tsconfck@npm:2.0.3"
@@ -5407,6 +5920,20 @@ __metadata:
languageName: node
linkType: hard
"tunnel@npm:^0.0.6":
version: 0.0.6
resolution: "tunnel@npm:0.0.6"
checksum: c362948df9ad34b649b5585e54ce2838fa583aa3037091aaed66793c65b423a264e5229f0d7e9a95513a795ac2bd4cb72cda7e89a74313f182c1e9ae0b0994fa
languageName: node
linkType: hard
"typanion@npm:^3.8.0":
version: 3.12.1
resolution: "typanion@npm:3.12.1"
checksum: a2e26fa216f8a1dbd2ffbaacb75b1e2dc042a0356e9702fba05a968cad95d9f661b24e37f6c6d8c3adad2c8582c99fca4826ff26a2d07cd2ae617ea87e6187eb
languageName: node
linkType: hard
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"

File diff suppressed because it is too large Load Diff

View File

@@ -20,190 +20,200 @@ template <typename T, typename Enable = void>
struct Comparer;
template <typename T>
struct Comparer<T, typename enable_if<IsString<T>::value>::type>
: ComparerBase {
T rhs; // TODO: store adapted string?
struct Comparer<T, typename enable_if<IsString<T>::value>::type> : ComparerBase {
T rhs;
explicit Comparer(T value) : rhs(value) {}
explicit Comparer(T value)
: rhs(value) {
}
CompareResult visitString(const char* lhs, size_t n) {
int i = stringCompare(adaptString(rhs), adaptString(lhs, n));
if (i < 0)
return COMPARE_RESULT_GREATER;
else if (i > 0)
return COMPARE_RESULT_LESS;
else
return COMPARE_RESULT_EQUAL;
}
CompareResult visitString(const char * lhs, size_t n) {
int i = stringCompare(adaptString(rhs), adaptString(lhs, n));
if (i < 0)
return COMPARE_RESULT_GREATER;
else if (i > 0)
return COMPARE_RESULT_LESS;
else
return COMPARE_RESULT_EQUAL;
}
CompareResult visitNull() {
if (adaptString(rhs).isNull())
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
CompareResult visitNull() {
if (adaptString(rhs).isNull())
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
};
template <typename T>
struct Comparer<T, typename enable_if<is_integral<T>::value ||
is_floating_point<T>::value>::type>
: ComparerBase {
T rhs;
struct Comparer<T, typename enable_if<is_integral<T>::value || is_floating_point<T>::value>::type> : ComparerBase {
T rhs;
explicit Comparer(T value) : rhs(value) {}
explicit Comparer(T value)
: rhs(value) {
}
CompareResult visitFloat(JsonFloat lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitFloat(JsonFloat lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitSignedInteger(JsonInteger lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitSignedInteger(JsonInteger lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitUnsignedInteger(JsonUInt lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitUnsignedInteger(JsonUInt lhs) {
return arithmeticCompare(lhs, rhs);
}
CompareResult visitBoolean(bool lhs) {
return visitUnsignedInteger(static_cast<JsonUInt>(lhs));
}
CompareResult visitBoolean(bool lhs) {
return visitUnsignedInteger(static_cast<JsonUInt>(lhs));
}
};
struct NullComparer : ComparerBase {
CompareResult visitNull() {
return COMPARE_RESULT_EQUAL;
}
CompareResult visitNull() {
return COMPARE_RESULT_EQUAL;
}
};
#if ARDUINOJSON_HAS_NULLPTR
template <>
struct Comparer<decltype(nullptr), void> : NullComparer {
explicit Comparer(decltype(nullptr)) : NullComparer() {}
explicit Comparer(decltype(nullptr))
: NullComparer() {
}
};
#endif
struct ArrayComparer : ComparerBase {
const CollectionData* _rhs;
const CollectionData * _rhs;
explicit ArrayComparer(const CollectionData& rhs) : _rhs(&rhs) {}
explicit ArrayComparer(const CollectionData & rhs)
: _rhs(&rhs) {
}
CompareResult visitArray(const CollectionData& lhs) {
if (JsonArrayConst(&lhs) == JsonArrayConst(_rhs))
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
CompareResult visitArray(const CollectionData & lhs) {
if (JsonArrayConst(&lhs) == JsonArrayConst(_rhs))
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
};
struct ObjectComparer : ComparerBase {
const CollectionData* _rhs;
const CollectionData * _rhs;
explicit ObjectComparer(const CollectionData& rhs) : _rhs(&rhs) {}
explicit ObjectComparer(const CollectionData & rhs)
: _rhs(&rhs) {
}
CompareResult visitObject(const CollectionData& lhs) {
if (JsonObjectConst(&lhs) == JsonObjectConst(_rhs))
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
CompareResult visitObject(const CollectionData & lhs) {
if (JsonObjectConst(&lhs) == JsonObjectConst(_rhs))
return COMPARE_RESULT_EQUAL;
else
return COMPARE_RESULT_DIFFER;
}
};
struct RawComparer : ComparerBase {
const char* _rhsData;
size_t _rhsSize;
const char * _rhsData;
size_t _rhsSize;
explicit RawComparer(const char* rhsData, size_t rhsSize)
: _rhsData(rhsData), _rhsSize(rhsSize) {}
explicit RawComparer(const char * rhsData, size_t rhsSize)
: _rhsData(rhsData)
, _rhsSize(rhsSize) {
}
CompareResult visitRawJson(const char* lhsData, size_t lhsSize) {
size_t size = _rhsSize < lhsSize ? _rhsSize : lhsSize;
int n = memcmp(lhsData, _rhsData, size);
if (n < 0)
return COMPARE_RESULT_LESS;
else if (n > 0)
return COMPARE_RESULT_GREATER;
else
return COMPARE_RESULT_EQUAL;
}
CompareResult visitRawJson(const char * lhsData, size_t lhsSize) {
size_t size = _rhsSize < lhsSize ? _rhsSize : lhsSize;
int n = memcmp(lhsData, _rhsData, size);
if (n < 0)
return COMPARE_RESULT_LESS;
else if (n > 0)
return COMPARE_RESULT_GREATER;
else
return COMPARE_RESULT_EQUAL;
}
};
struct VariantComparer : ComparerBase {
const VariantData* rhs;
const VariantData * rhs;
explicit VariantComparer(const VariantData* value) : rhs(value) {}
CompareResult visitArray(const CollectionData& lhs) {
ArrayComparer comparer(lhs);
return accept(comparer);
}
CompareResult visitObject(const CollectionData& lhs) {
ObjectComparer comparer(lhs);
return accept(comparer);
}
CompareResult visitFloat(JsonFloat lhs) {
Comparer<JsonFloat> comparer(lhs);
return accept(comparer);
}
CompareResult visitString(const char* lhs, size_t) {
Comparer<const char*> comparer(lhs);
return accept(comparer);
}
CompareResult visitRawJson(const char* lhsData, size_t lhsSize) {
RawComparer comparer(lhsData, lhsSize);
return accept(comparer);
}
CompareResult visitSignedInteger(JsonInteger lhs) {
Comparer<JsonInteger> comparer(lhs);
return accept(comparer);
}
CompareResult visitUnsignedInteger(JsonUInt lhs) {
Comparer<JsonUInt> comparer(lhs);
return accept(comparer);
}
CompareResult visitBoolean(bool lhs) {
Comparer<bool> comparer(lhs);
return accept(comparer);
}
CompareResult visitNull() {
NullComparer comparer;
return accept(comparer);
}
private:
template <typename TComparer>
CompareResult accept(TComparer& comparer) {
CompareResult reversedResult = variantAccept(rhs, comparer);
switch (reversedResult) {
case COMPARE_RESULT_GREATER:
return COMPARE_RESULT_LESS;
case COMPARE_RESULT_LESS:
return COMPARE_RESULT_GREATER;
default:
return reversedResult;
explicit VariantComparer(const VariantData * value)
: rhs(value) {
}
CompareResult visitArray(const CollectionData & lhs) {
ArrayComparer comparer(lhs);
return accept(comparer);
}
CompareResult visitObject(const CollectionData & lhs) {
ObjectComparer comparer(lhs);
return accept(comparer);
}
CompareResult visitFloat(JsonFloat lhs) {
Comparer<JsonFloat> comparer(lhs);
return accept(comparer);
}
CompareResult visitString(const char * lhs, size_t) {
Comparer<const char *> comparer(lhs);
return accept(comparer);
}
CompareResult visitRawJson(const char * lhsData, size_t lhsSize) {
RawComparer comparer(lhsData, lhsSize);
return accept(comparer);
}
CompareResult visitSignedInteger(JsonInteger lhs) {
Comparer<JsonInteger> comparer(lhs);
return accept(comparer);
}
CompareResult visitUnsignedInteger(JsonUInt lhs) {
Comparer<JsonUInt> comparer(lhs);
return accept(comparer);
}
CompareResult visitBoolean(bool lhs) {
Comparer<bool> comparer(lhs);
return accept(comparer);
}
CompareResult visitNull() {
NullComparer comparer;
return accept(comparer);
}
private:
template <typename TComparer>
CompareResult accept(TComparer & comparer) {
CompareResult reversedResult = variantAccept(rhs, comparer);
switch (reversedResult) {
case COMPARE_RESULT_GREATER:
return COMPARE_RESULT_LESS;
case COMPARE_RESULT_LESS:
return COMPARE_RESULT_GREATER;
default:
return reversedResult;
}
}
}
};
template <typename T>
struct Comparer<
T, typename enable_if<is_convertible<T, JsonVariantConst>::value>::type>
: VariantComparer {
explicit Comparer(const T& value)
: VariantComparer(VariantAttorney::getData(value)) {}
struct Comparer<T, typename enable_if<is_convertible<T, JsonVariantConst>::value>::type> : VariantComparer {
explicit Comparer(const T & value)
: VariantComparer(VariantAttorney::getData(value)) {
}
};
template <typename T>
CompareResult compare(JsonVariantConst lhs, const T& rhs) {
Comparer<T> comparer(rhs);
return variantAccept(VariantAttorney::getData(lhs), comparer);
CompareResult compare(JsonVariantConst lhs, const T & rhs) {
Comparer<T> comparer(rhs);
return variantAccept(VariantAttorney::getData(lhs), comparer);
}
} // namespace ARDUINOJSON_NAMESPACE
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -417,7 +417,6 @@ void Shell::loop_delay() {
function_copy(*this);
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}
@@ -448,7 +447,6 @@ void Shell::loop_blocking() {
stop();
}
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}
@@ -561,7 +559,6 @@ void Shell::process_command() {
}
}
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}

View File

@@ -349,7 +349,7 @@ void SyslogService::loop() {
}
bool SyslogService::can_transmit() {
// TODO this should be checked for Eth
// TODO this should be checked also for Eth
if (!host_.empty() && (uint32_t)ip_ == 0) {
WiFi.hostByName(host_.c_str(), ip_);
}

View File

@@ -603,40 +603,40 @@ const emsesp_devicedata_4 = {
let emsesp_schedule = {
schedule: [
{
id: 'hc1_on',
id: '1',
active: true,
flags: 31,
flags: 6,
time: '07:30',
cmd: 'hc1/mode',
value: 'day',
description: 'Turn on central heating in morning'
name: 'day_mode'
},
{
id: 'hc1_off',
id: '2',
active: true,
flags: 31,
time: '23:00',
cmd: 'hc1/mode',
value: 'night',
description: 'Turn off central heating for the night'
name: 'night_mode'
},
{
id: 'set_roomtemp',
id: '3',
active: true,
flags: 128,
time: '00:01',
flags: 10,
time: '00:00',
cmd: 'thermostat/hc2/seltemp',
value: '20',
description: 'Force thermostat temperature to 20 degrees every minute'
name: 'temp_20'
},
{
id: 'restart',
id: '4',
active: false,
flags: 1,
time: '04:00',
cmd: 'system/restart',
value: '',
description: 'auto restart EMS-EPS at 4am every Sunday'
name: 'auto_restart'
}
]
};

View File

@@ -1,12 +1,15 @@
; example custom platformio.ini file for EMS-ESP
[common]
; custom build flags
; options are: EMSESP_DEBUG, EMSESP_UART_DEBUG, EMSESP_DEBUG_SENSOR, EMSESP_WIFI_TWEAK, EMSESP_DEFAULT_BOARD_PROFILE, EMSESP_TEST
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" ; hard code the default board name
; my_build_flags = -DEMSESP_DEBUG_SENSOR ; additional debug for the dallas sensors
; my_build_flags = -DEMSESP_DEBUG -DCORE_DEBUG_LEVEL=5 ; verbose debugging 5=verbose, 4=debug, 3=info
; my_build_flags = -DEMSESP_DEBUG ; enables DEBUG to the log. Will generate a lot of traffic on Console and Syslog
; custom build flags:
; -DEMSESP_WIFI_TWEAK ; experimental WiFi tweaks for stability
; -DEMSESP_UART_DEBUG ; debugging UART
; -DEMSESP_DEBUG ; enables DEBUG to the log. Will generate a lot of extra traffic on Console and Syslog
; -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" ; hard code the default board name
; -DEMSESP_DEBUG_SENSOR ; additional debug for the dallas sensors
; -DCORE_DEBUG_LEVEL=5 ; verbose core level debugging 5=verbose, 4=debug, 3=info
; -DEMSESP_TEST ; enable the tests. EN language only
; my_build_flags = -DEMSESP_DEBUG
[env:esp32_4M]
; if using OTA enter your details below
@@ -21,8 +24,6 @@ upload_port = /dev/ttyUSB*
; override arduino espressif core
; platform = espressif32 ; take latest
; platform = espressif32@6.0.1
; platform = espressif32@5.3.0
; platform = espressif32@5.2.0
extra_scripts =

View File

@@ -149,7 +149,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
if (data.is<const char *>()) {
const char * d = data.as<const char *>();
if (strlen(d)) {
char * device_end = strchr(d, '/');
char * device_end = (char *)strchr(d, '/');
if (device_end != nullptr) {
char device_s[15] = {'\0'};
const char * device_p = device_s;

View File

@@ -1050,8 +1050,7 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
for (auto & dv : devicevalues_) {
// TODO check if pointer reference is safe. strcpy better?
if (dv.tag == tag && dv.short_name == FL_(haclimate[0])) {
if (dv.tag == tag && (strcmp(dv.short_name, FL_(haclimate[0])) == 0)) {
if (dv.min != min || dv.max != max) {
dv.min = min;
dv.max = max;

View File

@@ -50,6 +50,7 @@ MAKE_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer",
MAKE_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu")
// commands
// TODO translate
MAKE_TRANSLATION(info_cmd, "lists all values", "Liste aller Werte", "", "", "", "", "", "Tüm değerleri listele")
MAKE_TRANSLATION(commands_cmd, "lists all commands", "Liste aller Kommandos", "", "", "", "", "", "Tüm komutları listele")
MAKE_TRANSLATION(entities_cmd, "lists all entities", "Liste aller Entitäten", "", "", "", "", "", "Tüm varlıkları listele")

View File

@@ -1071,7 +1071,7 @@ bool System::check_upgrade(bool factory_settings) {
// if we're coming from 3.4.4 or 3.5.0b14 then we need to apply new settings
if (missing_version) {
LOG_DEBUG("Setting MQTT ID Entity to v3.4 format");
LOG_DEBUG("Setting MQTT Entity ID format to v3.4 format");
EMSESP::esp8266React.getMqttSettingsService()->update(
[&](MqttSettings & mqttSettings) {
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.0-dev.2"
#define EMSESP_APP_VERSION "3.6.0-dev.3"

View File

@@ -34,27 +34,30 @@ void WebSchedulerService::begin() {
}
// this creates the scheduler file, saving it to the FS
// and also calls when the Scheduler web page is refreshed
void WebScheduler::read(WebScheduler & webScheduler, JsonObject & root) {
JsonArray schedule = root.createNestedArray("schedule");
uint8_t counter = 0;
char s[3];
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
JsonObject si = schedule.createNestedObject();
si["id"] = scheduleItem.id; // name, is unqiue
si["active"] = scheduleItem.active;
si["flags"] = scheduleItem.flags;
si["time"] = scheduleItem.time;
si["cmd"] = scheduleItem.cmd;
si["value"] = scheduleItem.value;
si["description"] = scheduleItem.description;
JsonObject si = schedule.createNestedObject();
si["id"] = Helpers::smallitoa(s, ++counter); // id is only used to render the table and must be unique
si["active"] = scheduleItem.active;
si["flags"] = scheduleItem.flags;
si["time"] = scheduleItem.time;
si["cmd"] = scheduleItem.cmd;
si["value"] = scheduleItem.value;
si["name"] = scheduleItem.name;
}
}
// call on initialization and also when the page is saved via web UI
// call on initialization and also when the Scheduile web page is updated
// this loads the data into the internal class
StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webScheduler) {
#ifdef EMSESP_STANDALONE
// invoke some fake data for testing
const char * json = "{[{\"id\":\"test1\",\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1/mode\",\"value\": \"day\",\"description\": \"turn "
"on central heating\"}]}";
const char * json =
"{[{\"id\":\"01\",\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1/mode\",\"value\": \"day\",\"name\": \"turn on central heating\"}]}";
StaticJsonDocument<500> doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
@@ -65,7 +68,7 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche
#endif
for (ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
Command::erase_command(EMSdevice::DeviceType::SCHEDULER, scheduleItem.description.c_str());
Command::erase_command(EMSdevice::DeviceType::SCHEDULER, scheduleItem.name.c_str());
}
webScheduler.scheduleItems.clear();
@@ -73,26 +76,25 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche
for (const JsonObject schedule : root["schedule"].as<JsonArray>()) {
// create each schedule item, overwriting any previous settings
// ignore the id (as this is only used in the web for table rendering)
auto si = ScheduleItem();
si.id = schedule["id"].as<std::string>();
si.active = schedule["active"];
si.flags = schedule["flags"];
si.time = schedule["time"].as<std::string>();
si.cmd = schedule["cmd"].as<std::string>();
si.value = schedule["value"].as<std::string>();
si.description = schedule["description"].as<std::string>();
auto si = ScheduleItem();
si.active = schedule["active"];
si.flags = schedule["flags"];
si.time = schedule["time"].as<std::string>();
si.cmd = schedule["cmd"].as<std::string>();
si.value = schedule["value"].as<std::string>();
si.name = schedule["name"].as<std::string>();
// calculated elapsed minutes
si.elapsed_min = Helpers::string2minutes(si.time);
si.retry_cnt = 0xFF; // no starup retries
si.retry_cnt = 0xFF; // no startup retries
webScheduler.scheduleItems.push_back(si); // add to list
if (!webScheduler.scheduleItems.back().description.empty()) {
if (!webScheduler.scheduleItems.back().name.empty()) {
Command::add(
EMSdevice::DeviceType::SCHEDULER,
webScheduler.scheduleItems.back().description.c_str(),
webScheduler.scheduleItems.back().name.c_str(),
[webScheduler](const char * value, const int8_t id) {
return EMSESP::webSchedulerService.command_setvalue(value, webScheduler.scheduleItems.back().description);
return EMSESP::webSchedulerService.command_setvalue(value, webScheduler.scheduleItems.back().name);
},
FL_(schedule_cmd),
CommandFlag::ADMIN_ONLY);
@@ -111,7 +113,7 @@ bool WebSchedulerService::command_setvalue(const char * value, const std::string
}
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
for (ScheduleItem & scheduleItem : *scheduleItems) {
if (scheduleItem.description == name) {
if (scheduleItem.name == name) {
if (scheduleItem.active == v) {
return true;
}
@@ -148,20 +150,20 @@ void WebSchedulerService::publish(const bool force) {
}
if (Mqtt::publish_single() && force) {
for (const ScheduleItem & scheduleItem : *scheduleItems) {
publish_single(scheduleItem.description.c_str(), scheduleItem.active);
publish_single(scheduleItem.name.c_str(), scheduleItem.active);
}
}
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
for (const ScheduleItem & scheduleItem : *scheduleItems) {
if (!scheduleItem.description.empty() && !doc.containsKey(scheduleItem.description)) {
if (!scheduleItem.name.empty() && !doc.containsKey(scheduleItem.name)) {
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
doc[scheduleItem.description] = scheduleItem.active;
doc[scheduleItem.name] = scheduleItem.active;
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
doc[scheduleItem.description] = scheduleItem.active ? 1 : 0;
doc[scheduleItem.name] = scheduleItem.active ? 1 : 0;
} else {
char result[12];
doc[scheduleItem.description] = Helpers::render_boolean(result, scheduleItem.active);
doc[scheduleItem.name] = Helpers::render_boolean(result, scheduleItem.active);
}
// create HA config
@@ -173,21 +175,21 @@ void WebSchedulerService::publish(const bool force) {
char val_obj[50];
char val_cond[65];
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", scheduleItem.description.c_str());
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", scheduleItem.name.c_str());
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
char uniq_s[70];
snprintf(uniq_s, sizeof(uniq_s), "scheduler_%s", scheduleItem.description.c_str());
snprintf(uniq_s, sizeof(uniq_s), "scheduler_%s", scheduleItem.name.c_str());
config["obj_id"] = uniq_s;
config["uniq_id"] = uniq_s; // same as object_id
config["name"] = scheduleItem.description.c_str();
config["name"] = scheduleItem.name.c_str();
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.description.c_str());
snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::basename().c_str(), scheduleItem.description.c_str());
snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.name.c_str());
snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::basename().c_str(), scheduleItem.name.c_str());
config["cmd_t"] = command_topic;
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
config["pl_on"] = true;
@@ -220,7 +222,7 @@ bool WebSchedulerService::has_commands() {
return false;
}
for (const ScheduleItem & scheduleItem : *scheduleItems) {
if (!scheduleItem.description.empty()) {
if (!scheduleItem.name.empty()) {
return true;
}
}
@@ -267,7 +269,7 @@ bool WebSchedulerService::command(const char * cmd, const char * data) {
}
// process any scheduled jobs
// checks on the minute
// checks on the minute and at startup
void WebSchedulerService::loop() {
// initialize static value on startup
static int8_t last_tm_min = -1; // invalid value also used for startup commands

View File

@@ -23,20 +23,20 @@
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
#define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer
#define MAX_STARTUP_RETRIES 3 // retry the statup commands x times
#define MAX_STARTUP_RETRIES 3 // retry the start-up commands x times
namespace emsesp {
class ScheduleItem {
public:
std::string id; // unqiue id, is the name
std::string id; // unqiue id
boolean active;
uint8_t flags;
uint16_t elapsed_min; // total mins from 00:00
std::string time; // HH:MM
std::string cmd;
std::string value;
std::string description;
std::string name;
uint8_t retry_cnt;
};