This commit is contained in:
MichaelDvP
2024-07-29 07:38:19 +02:00
19 changed files with 258 additions and 246 deletions

View File

@@ -51,13 +51,13 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.9", "@babel/core": "^7.24.9",
"@eslint/js": "^9.7.0", "@eslint/js": "^9.8.0",
"@preact/compat": "^17.1.2", "@preact/compat": "^17.1.2",
"@preact/preset-vite": "^2.9.0", "@preact/preset-vite": "^2.9.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/babel__core": "^7", "@types/babel__core": "^7",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"eslint": "^9.7.0", "eslint": "^9.8.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"preact": "^10.23.1", "preact": "^10.23.1",
"prettier": "^3.3.3", "prettier": "^3.3.3",

View File

@@ -64,7 +64,7 @@ const CustomEntitiesDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (event: object, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }

View File

@@ -54,7 +54,7 @@ const CustomizationDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (event: object, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }

View File

@@ -200,7 +200,7 @@ const Scheduler: FC = () => {
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100), id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
active: false, active: false,
deleted: false, deleted: false,
flags: 0, flags: ScheduleFlag.SCHEDULE_DAY,
time: '', time: '',
cmd: '', cmd: '',
value: '', value: '',
@@ -219,27 +219,33 @@ const Scheduler: FC = () => {
<Box> <Box>
<Typography <Typography
sx={{ fontSize: 11 }} sx={{ fontSize: 11 }}
color={ color={(si.flags & flag) === flag ? 'primary' : 'grey'}
si.flags >= ScheduleFlag.SCHEDULE_TIMER && si.flags !== flag
? 'gray'
: (si.flags & flag) === flag
? 'primary'
: 'grey'
}
> >
{flag === ScheduleFlag.SCHEDULE_TIMER {dow[Math.log(flag) / Math.log(2)]}
? LL.TIMER(0)
: flag === ScheduleFlag.SCHEDULE_ONCHANGE
? 'On Change'
: flag === ScheduleFlag.SCHEDULE_CONDITION
? 'Condition'
: dow[Math.log(flag) / Math.log(2)]}
</Typography> </Typography>
</Box> </Box>
<Divider orientation="vertical" flexItem /> <Divider orientation="vertical" flexItem />
</> </>
); );
const scheduleType = (si: ScheduleItem) => (
<Box>
<Typography sx={{ fontSize: 11 }} color="primary">
{si.flags === ScheduleFlag.SCHEDULE_IMMEDIATE ? (
<>Immediate</>
) : si.flags === ScheduleFlag.SCHEDULE_TIMER ? (
<>Timer</>
) : si.flags === ScheduleFlag.SCHEDULE_CONDITION ? (
<>Condition</>
) : si.flags === ScheduleFlag.SCHEDULE_ONCHANGE ? (
<>On Change</>
) : (
<></>
)}
</Typography>
</Box>
);
useLayoutTitle(LL.SCHEDULER()); useLayoutTitle(LL.SCHEDULER());
return ( return (
@@ -283,9 +289,9 @@ const Scheduler: FC = () => {
<Cell stiff> <Cell stiff>
<Stack spacing={0.5} direction="row"> <Stack spacing={0.5} direction="row">
<Divider orientation="vertical" flexItem /> <Divider orientation="vertical" flexItem />
{si.flags === 0 ? ( {si.flags > 127 ? (
<></> scheduleType(si)
) : si.flags < ScheduleFlag.SCHEDULE_TIMER ? ( ) : (
<> <>
{dayBox(si, ScheduleFlag.SCHEDULE_MON)} {dayBox(si, ScheduleFlag.SCHEDULE_MON)}
{dayBox(si, ScheduleFlag.SCHEDULE_TUE)} {dayBox(si, ScheduleFlag.SCHEDULE_TUE)}
@@ -295,12 +301,6 @@ const Scheduler: FC = () => {
{dayBox(si, ScheduleFlag.SCHEDULE_SAT)} {dayBox(si, ScheduleFlag.SCHEDULE_SAT)}
{dayBox(si, ScheduleFlag.SCHEDULE_SUN)} {dayBox(si, ScheduleFlag.SCHEDULE_SUN)}
</> </>
) : (
<>
{dayBox(si, ScheduleFlag.SCHEDULE_TIMER)}
{dayBox(si, ScheduleFlag.SCHEDULE_ONCHANGE)}
{dayBox(si, ScheduleFlag.SCHEDULE_CONDITION)}
</>
)} )}
</Stack> </Stack>
</Cell> </Cell>

View File

@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
import { import {
Box, Box,
@@ -53,12 +54,23 @@ const SchedulerDialog = ({
const [editItem, setEditItem] = useState<ScheduleItem>(selectedItem); const [editItem, setEditItem] = useState<ScheduleItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const [scheduleType, setScheduleType] = useState<ScheduleFlag>();
const updateFormValue = updateValue(setEditItem); const updateFormValue = updateValue(setEditItem);
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setFieldErrors(undefined); setFieldErrors(undefined);
setEditItem(selectedItem); setEditItem(selectedItem);
// set the flags based on type when page is loaded...
// 0-127 is day schedule
// 128 is timer
// 129 is on change
// 130 is on condition
// 132 is immediate
setScheduleType(
selectedItem.flags < 128 ? ScheduleFlag.SCHEDULE_DAY : selectedItem.flags
);
} }
}, [open, selectedItem]); }, [open, selectedItem]);
@@ -88,15 +100,15 @@ const SchedulerDialog = ({
onSave(editItem); onSave(editItem);
}; };
const getFlagNumber = (newFlag: string[]) => { const getFlagDOWnumber = (newFlag: string[]) => {
let new_flag = 0; let new_flag = 0;
for (const entry of newFlag) { for (const entry of newFlag) {
new_flag |= Number(entry); new_flag |= Number(entry);
} }
return new_flag; return new_flag & 127;
}; };
const getFlagString = (f: number) => { const getFlagDOWstring = (f: number) => {
const new_flags: string[] = []; const new_flags: string[] = [];
if ((f & 129) === 1) { if ((f & 129) === 1) {
new_flags.push('1'); new_flags.push('1');
@@ -119,46 +131,20 @@ const SchedulerDialog = ({
if ((f & 64) === 64) { if ((f & 64) === 64) {
new_flags.push('64'); new_flags.push('64');
} }
if ((f & 131) === 128) {
new_flags.push('128');
}
if ((f & 131) === 129) {
new_flags.push('129');
}
if ((f & 131) === 130) {
new_flags.push('130');
}
return new_flags; return new_flags;
}; };
const isTimer = editItem.flags === ScheduleFlag.SCHEDULE_TIMER; const showDOW = (si: ScheduleItem, flag: number) => (
const isCondition = editItem.flags === ScheduleFlag.SCHEDULE_CONDITION;
const isOnChange = editItem.flags === ScheduleFlag.SCHEDULE_ONCHANGE;
const showFlag = (si: ScheduleItem, flag: number) => (
<Typography <Typography
variant="button"
sx={{ fontSize: 10 }} sx={{ fontSize: 10 }}
color={ color={(si.flags & flag) === flag ? 'primary' : 'grey'}
(isOnChange && flag !== ScheduleFlag.SCHEDULE_ONCHANGE) ||
(isCondition && flag !== ScheduleFlag.SCHEDULE_CONDITION)
? 'grey'
: (si.flags & flag) === flag
? 'primary'
: 'grey'
}
> >
{flag === ScheduleFlag.SCHEDULE_TIMER {dow[Math.log(flag) / Math.log(2)]}
? LL.TIMER(0)
: flag === ScheduleFlag.SCHEDULE_ONCHANGE
? 'On Change'
: flag === ScheduleFlag.SCHEDULE_CONDITION
? 'Condition'
: dow[Math.log(flag) / Math.log(2)]}
</Typography> </Typography>
); );
const handleClose = (event: object, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }
@@ -171,124 +157,119 @@ const SchedulerDialog = ({
{LL.SCHEDULE(1)} {LL.SCHEDULE(1)}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box display="flex" flexWrap="wrap" mb={1}> <ToggleButtonGroup
<Box> size="small"
<ToggleButtonGroup color="secondary"
size="small" value={scheduleType}
color="secondary" exclusive
value={getFlagString(editItem.flags)} disabled={!creating}
onChange={(_event, flag: string[]) => { onChange={(_event, flag: ScheduleFlag) => {
setEditItem({ ...editItem, flags: getFlagNumber(flag) & 127 }); if (flag !== null) {
}} setFieldErrors(undefined); // clear any validation errors
setScheduleType(flag);
// wipe the time field when changing the schedule type
setEditItem({ ...editItem, time: '' });
// set the flags based on type
// 0-127 is day schedule
// 128 is timer
// 129 is on change
// 130 is on condition
// 132 is immediate
setEditItem(
flag === ScheduleFlag.SCHEDULE_DAY
? { ...editItem, flags: 0 }
: { ...editItem, flags: flag }
);
}
}}
>
<ToggleButton value={ScheduleFlag.SCHEDULE_DAY}>
<Typography
sx={{ fontSize: 10 }}
color={scheduleType === ScheduleFlag.SCHEDULE_DAY ? 'primary' : 'grey'}
> >
<ToggleButton value="2"> {LL.SCHEDULE(0)}
{showFlag(editItem, ScheduleFlag.SCHEDULE_MON)} </Typography>
</ToggleButton> </ToggleButton>
<ToggleButton value="4"> <ToggleButton value={ScheduleFlag.SCHEDULE_TIMER}>
{showFlag(editItem, ScheduleFlag.SCHEDULE_TUE)} <Typography
</ToggleButton> sx={{ fontSize: 10 }}
<ToggleButton value="8"> color={
{showFlag(editItem, ScheduleFlag.SCHEDULE_WED)} scheduleType === ScheduleFlag.SCHEDULE_TIMER ? 'primary' : 'grey'
</ToggleButton> }
<ToggleButton value="16"> >
{showFlag(editItem, ScheduleFlag.SCHEDULE_THU)} {LL.TIMER(0)}
</ToggleButton> </Typography>
<ToggleButton value="32"> </ToggleButton>
{showFlag(editItem, ScheduleFlag.SCHEDULE_FRI)} <ToggleButton value={ScheduleFlag.SCHEDULE_ONCHANGE}>
</ToggleButton> <Typography
<ToggleButton value="64"> sx={{ fontSize: 10 }}
{showFlag(editItem, ScheduleFlag.SCHEDULE_SAT)} color={
</ToggleButton> scheduleType === ScheduleFlag.SCHEDULE_ONCHANGE ? 'primary' : 'grey'
<ToggleButton value="1"> }
{showFlag(editItem, ScheduleFlag.SCHEDULE_SUN)} >
</ToggleButton> {/* TODO translate */}
</ToggleButtonGroup> On Change
</Box> </Typography>
<Box sx={{ '& button, & a, & .MuiCard-root': { ml: 1 } }}> </ToggleButton>
{isTimer ? ( <ToggleButton value={ScheduleFlag.SCHEDULE_CONDITION}>
<Button <Typography
size="large" sx={{ fontSize: 10 }}
sx={{ bgcolor: '#334f65' }} color={
variant="contained" scheduleType === ScheduleFlag.SCHEDULE_CONDITION ? 'primary' : 'grey'
onClick={() => { }
setEditItem({ ...editItem, flags: ScheduleFlag.SCHEDULE_TIMER }); >
}} {/* TODO translate */}
> Condition
{showFlag(editItem, ScheduleFlag.SCHEDULE_TIMER)} </Typography>
</Button> </ToggleButton>
) : ( <ToggleButton value={ScheduleFlag.SCHEDULE_IMMEDIATE}>
<Button <Typography
size="large" sx={{ fontSize: 10 }}
variant="outlined" color={
onClick={() => { scheduleType === ScheduleFlag.SCHEDULE_IMMEDIATE ? 'primary' : 'grey'
setEditItem({ }
...editItem, >
flags: ScheduleFlag.SCHEDULE_TIMER {/* TODO translate */}
}); Immediate
}} </Typography>
> </ToggleButton>
{showFlag(editItem, ScheduleFlag.SCHEDULE_TIMER)} </ToggleButtonGroup>
</Button>
)} {scheduleType === ScheduleFlag.SCHEDULE_DAY && (
{isOnChange ? ( <ToggleButtonGroup
<Button size="small"
size="large" color="secondary"
sx={{ bgcolor: '#334f65' }} value={getFlagDOWstring(editItem.flags)}
variant="contained" onChange={(_event, flag: string[]) => {
onClick={() => { setEditItem({ ...editItem, flags: getFlagDOWnumber(flag) });
setEditItem({ }}
...editItem, >
flags: ScheduleFlag.SCHEDULE_ONCHANGE <ToggleButton value="2">
}); {showDOW(editItem, ScheduleFlag.SCHEDULE_MON)}
}} </ToggleButton>
> <ToggleButton value="4">
{showFlag(editItem, ScheduleFlag.SCHEDULE_ONCHANGE)} {showDOW(editItem, ScheduleFlag.SCHEDULE_TUE)}
</Button> </ToggleButton>
) : ( <ToggleButton value="8">
<Button {showDOW(editItem, ScheduleFlag.SCHEDULE_WED)}
size="large" </ToggleButton>
variant="outlined" <ToggleButton value="16">
onClick={() => { {showDOW(editItem, ScheduleFlag.SCHEDULE_THU)}
setEditItem({ </ToggleButton>
...editItem, <ToggleButton value="32">
flags: ScheduleFlag.SCHEDULE_ONCHANGE {showDOW(editItem, ScheduleFlag.SCHEDULE_FRI)}
}); </ToggleButton>
}} <ToggleButton value="64">
> {showDOW(editItem, ScheduleFlag.SCHEDULE_SAT)}
{showFlag(editItem, ScheduleFlag.SCHEDULE_ONCHANGE)} </ToggleButton>
</Button> <ToggleButton value="1">
)} {showDOW(editItem, ScheduleFlag.SCHEDULE_SUN)}
{isCondition ? ( </ToggleButton>
<Button </ToggleButtonGroup>
size="large" )}
sx={{ bgcolor: '#334f65' }}
variant="contained" {scheduleType !== ScheduleFlag.SCHEDULE_IMMEDIATE && (
onClick={() => {
setEditItem({
...editItem,
flags: ScheduleFlag.SCHEDULE_CONDITION
});
}}
>
{showFlag(editItem, ScheduleFlag.SCHEDULE_CONDITION)}
</Button>
) : (
<Button
size="large"
variant="outlined"
onClick={() => {
setEditItem({
...editItem,
flags: ScheduleFlag.SCHEDULE_CONDITION
});
}}
>
{showFlag(editItem, ScheduleFlag.SCHEDULE_CONDITION)}
</Button>
)}
</Box>
</Box>
{editItem.flags !== 0 && (
<> <>
<Grid container> <Grid container>
<BlockFormControlLabel <BlockFormControlLabel
@@ -303,10 +284,42 @@ const SchedulerDialog = ({
/> />
</Grid> </Grid>
<Grid container> <Grid container>
{isCondition || isOnChange ? ( {scheduleType === ScheduleFlag.SCHEDULE_DAY ||
scheduleType === ScheduleFlag.SCHEDULE_TIMER ? (
<>
<TextField
name="time"
type="time"
label={
scheduleType === ScheduleFlag.SCHEDULE_TIMER
? LL.TIMER(1)
: LL.TIME(1)
}
value={
editItem.time == '' ? (editItem.time = '00:00') : editItem.time
}
margin="normal"
onChange={updateFormValue}
/>
{scheduleType === ScheduleFlag.SCHEDULE_TIMER && (
<Box color="warning.main" ml={2} mt={4}>
<Typography variant="body2">
{LL.SCHEDULER_HELP_2()}
</Typography>
</Box>
)}
</>
) : (
<TextField <TextField
name="time" name="time"
label={isCondition ? 'Condition' : 'On Change Value'} label={
// TODO translate
scheduleType === ScheduleFlag.SCHEDULE_CONDITION
? 'Condition'
: scheduleType === ScheduleFlag.SCHEDULE_ONCHANGE
? 'On Change Value'
: 'Immediate'
}
multiline multiline
fullWidth fullWidth
value={ value={
@@ -315,26 +328,6 @@ const SchedulerDialog = ({
margin="normal" margin="normal"
onChange={updateFormValue} onChange={updateFormValue}
/> />
) : (
<>
<TextField
name="time"
type="time"
label={isTimer ? LL.TIMER(1) : LL.TIME(1)}
value={
editItem.time == '' ? (editItem.time = '00:00') : editItem.time
}
margin="normal"
onChange={updateFormValue}
/>
{isTimer && (
<Box color="warning.main" ml={2} mt={4}>
<Typography variant="body2">
{LL.SCHEDULER_HELP_2()}
</Typography>
</Box>
)}
</>
)} )}
</Grid> </Grid>
</> </>
@@ -368,6 +361,7 @@ const SchedulerDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1}> <Box flexGrow={1}>
@@ -397,12 +391,12 @@ const SchedulerDialog = ({
> >
{creating ? LL.ADD(0) : LL.UPDATE()} {creating ? LL.ADD(0) : LL.UPDATE()}
</Button> </Button>
{editItem.flags === 0 && editItem.cmd !== '' && ( {scheduleType === ScheduleFlag.SCHEDULE_IMMEDIATE && editItem.cmd !== '' && (
<Button <Button
startIcon={<DoneIcon />} startIcon={<PlayArrowIcon />}
variant="outlined" variant="outlined"
onClick={saveandactivate} onClick={saveandactivate}
color="warning" color="success"
> >
{LL.EXECUTE()} {LL.EXECUTE()}
</Button> </Button>

View File

@@ -57,7 +57,7 @@ const SensorsAnalogDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (event: object, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }

View File

@@ -52,7 +52,7 @@ const SensorsTemperatureDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (event: object, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }

View File

@@ -298,12 +298,12 @@ export enum DeviceEntityMask {
export interface ScheduleItem { export interface ScheduleItem {
id: number; // unique index id: number; // unique index
active: boolean; active: boolean;
deleted?: boolean; // optional deleted?: boolean;
flags: number; flags: number;
time: string; time: string;
cmd: string; cmd: string;
value: string; value: string;
name: string; // optional name: string; // is optional
o_id?: number; o_id?: number;
o_active?: boolean; o_active?: boolean;
o_deleted?: boolean; o_deleted?: boolean;
@@ -344,9 +344,12 @@ export enum ScheduleFlag {
SCHEDULE_THU = 16, SCHEDULE_THU = 16,
SCHEDULE_FRI = 32, SCHEDULE_FRI = 32,
SCHEDULE_SAT = 64, SCHEDULE_SAT = 64,
SCHEDULE_TIMER = 128, // types...
SCHEDULE_ONCHANGE = 129, SCHEDULE_DAY = 0, // no bits set
SCHEDULE_CONDITION = 130 SCHEDULE_TIMER = 128, // bit 8
SCHEDULE_ONCHANGE = 129, // bit 1
SCHEDULE_CONDITION = 130, // bit 2
SCHEDULE_IMMEDIATE = 132 // bit 3
} }
export interface EntityItem { export interface EntityItem {

View File

@@ -50,7 +50,7 @@ export function boardProfileSelectItems() {
} }
const ApplicationSettings: FC = () => { const ApplicationSettings: FC = () => {
const { data: hardwareData, error } = useRequest(SystemApi.readHardwareStatus, { const { data: hardwareData } = useRequest(SystemApi.readHardwareStatus, {
force: true force: true
}); });

View File

@@ -1065,14 +1065,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/config-array@npm:^0.17.0": "@eslint/config-array@npm:^0.17.1":
version: 0.17.0 version: 0.17.1
resolution: "@eslint/config-array@npm:0.17.0" resolution: "@eslint/config-array@npm:0.17.1"
dependencies: dependencies:
"@eslint/object-schema": "npm:^2.1.4" "@eslint/object-schema": "npm:^2.1.4"
debug: "npm:^4.3.1" debug: "npm:^4.3.1"
minimatch: "npm:^3.1.2" minimatch: "npm:^3.1.2"
checksum: 10c0/97eb23ef0948dbc5f24884a3b75c537ca37ee2b1f27a864cd0d9189c089bc1a724dc6e1a4d9b7dd304d9f732ca02aa7916243a7715d6f1f17159d8a8c83f0c9e checksum: 10c0/b986a0a96f2b42467578968ce3d4ae3b9284e587f8490f2dcdc44ff1b8d30580c62b221da6e58d07b09e156c3050e2dc38267f9370521d9cafc099c4e30154ef
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1093,10 +1093,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/js@npm:9.7.0, @eslint/js@npm:^9.7.0": "@eslint/js@npm:9.8.0, @eslint/js@npm:^9.8.0":
version: 9.7.0 version: 9.8.0
resolution: "@eslint/js@npm:9.7.0" resolution: "@eslint/js@npm:9.8.0"
checksum: 10c0/73fc10666f6f4aed6f58e407e09f42ceb0d42fa60c52701c64ea9f59a81a6a8ad5caecdfd423d03088481515fe7ec17eb461acb4ef1ad70b649b6eae465b3164 checksum: 10c0/42edaae6b020436410454579509dcb6a8cd5b260e9f18e037fd803ae28d35eb13663d4019f0ab8ba686a19d3c4a43b0e11394c148e23345377ab694da0e83262
languageName: node languageName: node
linkType: hard linkType: hard
@@ -2077,7 +2077,7 @@ __metadata:
"@babel/core": "npm:^7.24.9" "@babel/core": "npm:^7.24.9"
"@emotion/react": "npm:^11.13.0" "@emotion/react": "npm:^11.13.0"
"@emotion/styled": "npm:^11.13.0" "@emotion/styled": "npm:^11.13.0"
"@eslint/js": "npm:^9.7.0" "@eslint/js": "npm:^9.8.0"
"@mui/icons-material": "npm:^5.16.5" "@mui/icons-material": "npm:^5.16.5"
"@mui/material": "npm:^5.16.5" "@mui/material": "npm:^5.16.5"
"@preact/compat": "npm:^17.1.2" "@preact/compat": "npm:^17.1.2"
@@ -2093,7 +2093,7 @@ __metadata:
alova: "npm:^2.21.4" alova: "npm:^2.21.4"
async-validator: "npm:^4.2.5" async-validator: "npm:^4.2.5"
concurrently: "npm:^8.2.2" concurrently: "npm:^8.2.2"
eslint: "npm:^9.7.0" eslint: "npm:^9.8.0"
eslint-config-prettier: "npm:^9.1.0" eslint-config-prettier: "npm:^9.1.0"
history: "npm:^5.3.0" history: "npm:^5.3.0"
jwt-decode: "npm:^4.0.0" jwt-decode: "npm:^4.0.0"
@@ -3617,15 +3617,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eslint@npm:^9.7.0": "eslint@npm:^9.8.0":
version: 9.7.0 version: 9.8.0
resolution: "eslint@npm:9.7.0" resolution: "eslint@npm:9.8.0"
dependencies: dependencies:
"@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/eslint-utils": "npm:^4.2.0"
"@eslint-community/regexpp": "npm:^4.11.0" "@eslint-community/regexpp": "npm:^4.11.0"
"@eslint/config-array": "npm:^0.17.0" "@eslint/config-array": "npm:^0.17.1"
"@eslint/eslintrc": "npm:^3.1.0" "@eslint/eslintrc": "npm:^3.1.0"
"@eslint/js": "npm:9.7.0" "@eslint/js": "npm:9.8.0"
"@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/module-importer": "npm:^1.0.1"
"@humanwhocodes/retry": "npm:^0.3.0" "@humanwhocodes/retry": "npm:^0.3.0"
"@nodelib/fs.walk": "npm:^1.2.8" "@nodelib/fs.walk": "npm:^1.2.8"
@@ -3657,7 +3657,7 @@ __metadata:
text-table: "npm:^0.2.0" text-table: "npm:^0.2.0"
bin: bin:
eslint: bin/eslint.js eslint: bin/eslint.js
checksum: 10c0/e2369a9534404f62f37ee5560e56fb84e0776a9c8f084550170017992772e7034d73571bdf4060e2fe9b836f136d45b07d50407d4b9361de720ee77794259274 checksum: 10c0/a2ee0cce1147565d011fe185733af482f34d5466f5df5f390d0ea2ecf78097883cf568ed6c771d687138609c63cd55cd1e3ff12de7393c03f54fcffcdd0f225d
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -3946,6 +3946,15 @@ let emsesp_schedule = {
cmd: 'boiler/selflowtemp', cmd: 'boiler/selflowtemp',
value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3', value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3',
name: 'heatingcurve' name: 'heatingcurve'
},
{
id: 7,
active: false,
flags: 132,
time: '',
cmd: 'system/message',
value: '\"hello world\"',
name: 'immediate'
} }
] ]
}; };

View File

@@ -148,8 +148,8 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
const char * device_p = device_s; const char * device_p = device_s;
const char * data_p = nullptr; const char * data_p = nullptr;
strlcpy(device_s, d, device_end - d + 1); strlcpy(device_s, d, device_end - d + 1);
data_p = device_end + 1; data_p = device_end + 1;
int8_t id_d = -1; int8_t id_d = -1;
uint8_t device_type = EMSdevice::device_name_2_device_type(device_p); uint8_t device_type = EMSdevice::device_name_2_device_type(device_p);
if (device_type > EMSdevice::DeviceType::BOILER) { if (device_type > EMSdevice::DeviceType::BOILER) {
data_p = parse_command_string(data_p, id_d); data_p = parse_command_string(data_p, id_d);

View File

@@ -1601,7 +1601,7 @@ void EMSESP::start() {
// start the file system // start the file system
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (!LittleFS.begin(true)) { if (!LittleFS.begin(true)) {
Serial.println("LittleFS Mount Failed. Using default settings."); LOG_INFO("LittleFS Mount Failed. Using default settings.");
return; return;
} }
#endif #endif
@@ -1612,9 +1612,7 @@ void EMSESP::start() {
File root = LittleFS.open("/config"); File root = LittleFS.open("/config");
bool factory_settings = !root; bool factory_settings = !root;
if (!root) { if (!root) {
#if defined(EMSESP_DEBUG) LOG_INFO("No config found, assuming factory settings");
Serial.println("No config found, assuming factory settings");
#endif
} }
root.close(); root.close();
#else #else
@@ -1644,8 +1642,6 @@ void EMSESP::start() {
system_.system_restart(); system_.system_restart();
}; };
webSettingsService.begin(); // load EMS-ESP Application settings... webSettingsService.begin(); // load EMS-ESP Application settings...
// do any system upgrades // do any system upgrades

View File

@@ -411,7 +411,8 @@ uint32_t Helpers::hextoint(const char * hex) {
// get current character then increment // get current character then increment
char byte = *hex++; char byte = *hex++;
// transform hex character to the 4bit equivalent number, using the ascii table indexes // transform hex character to the 4bit equivalent number, using the ascii table indexes
if (byte == ' ') byte = *hex++; // skip spaces if (byte == ' ')
byte = *hex++; // skip spaces
if (byte >= '0' && byte <= '9') if (byte >= '0' && byte <= '9')
byte = byte - '0'; byte = byte - '0';
else if (byte >= 'a' && byte <= 'f') else if (byte >= 'a' && byte <= 'f')

View File

@@ -1170,7 +1170,7 @@ bool System::check_upgrade(bool factory_settings) {
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022 // see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5)); missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
if (missing_version) { if (missing_version) {
LOG_WARNING("No version information found (%s)", settingsVersion.c_str()); LOG_WARNING("No version information found");
settingsVersion = "3.5.0"; // this was the last stable version without version info settingsVersion = "3.5.0"; // this was the last stable version without version info
} }
} }
@@ -1200,13 +1200,13 @@ bool System::check_upgrade(bool factory_settings) {
// if we're coming from 3.4.4 or 3.5.0b14 which had no version stored then we need to apply new settings // if we're coming from 3.4.4 or 3.5.0b14 which had no version stored then we need to apply new settings
if (missing_version) { if (missing_version) {
LOG_INFO("Setting MQTT Entity ID format to v3.4 format"); LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.4 format");
EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) { EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) {
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4 mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
}); });
} else if (settings_version.major() == 3 && settings_version.minor() <= 6) { } else if (settings_version.major() == 3 && settings_version.minor() <= 6) {
LOG_INFO("Setting MQTT Entity ID format to v3.6 format"); LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.6 format");
EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) { EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) {
if (mqttSettings.entity_format == 1) { if (mqttSettings.entity_format == 1) {
mqttSettings.entity_format = 3; // use old Entity ID format from v3.6 mqttSettings.entity_format = 3; // use old Entity ID format from v3.6
@@ -1223,7 +1223,7 @@ bool System::check_upgrade(bool factory_settings) {
EMSESP::esp8266React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) { EMSESP::esp8266React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
if (networkSettings.tx_power == 20) { if (networkSettings.tx_power == 20) {
networkSettings.tx_power = WIFI_POWER_19_5dBm; // use 19.5 as we don't have 20 anymore networkSettings.tx_power = WIFI_POWER_19_5dBm; // use 19.5 as we don't have 20 anymore
LOG_INFO("Setting WiFi TX Power to Auto"); LOG_INFO("Upgrade: Setting WiFi TX Power to Auto");
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
} }
return StateUpdateResult::UNCHANGED; return StateUpdateResult::UNCHANGED;
@@ -1237,7 +1237,7 @@ bool System::check_upgrade(bool factory_settings) {
save_version = false; save_version = false;
} }
// if we did a change, set the new version and reboot // if we did a change, set the new version and save it, then reboot
if (save_version) { if (save_version) {
EMSESP::webSettingsService.update([&](WebSettings & settings) { EMSESP::webSettingsService.update([&](WebSettings & settings) {
settings.version = EMSESP_APP_VERSION; settings.version = EMSESP_APP_VERSION;

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.0-dev.27" #define EMSESP_APP_VERSION "3.7.0-dev.28"

View File

@@ -198,7 +198,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
output["onchange"] = scheduleItem.time; output["onchange"] = scheduleItem.time;
} else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER) { } else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER) {
output["timer"] = scheduleItem.time; output["timer"] = scheduleItem.time;
} else if (scheduleItem.flags != 0){ } else if (scheduleItem.flags != 0) {
output["time"] = scheduleItem.time; output["time"] = scheduleItem.time;
} }
output["command"] = scheduleItem.cmd; output["command"] = scheduleItem.cmd;
@@ -489,7 +489,7 @@ void WebSchedulerService::loop() {
} }
for (ScheduleItem & scheduleItem : *scheduleItems_) { for (ScheduleItem & scheduleItem : *scheduleItems_) {
if (scheduleItem.active && scheduleItem.flags == 0) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
command(scheduleItem.name.c_str(), scheduleItem.cmd, compute(scheduleItem.value)); command(scheduleItem.name.c_str(), scheduleItem.cmd, compute(scheduleItem.value));
scheduleItem.active = false; scheduleItem.active = false;
} }

View File

@@ -22,10 +22,18 @@
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json" #define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST #define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
// bit flags for the schedule items. Matches those in interface/src/app/main/SchedulerDialog.tsx
// 0-127 (0->0x7F) is day schedule
// 128/0x80 is timer
// 129/0x81 is on change
// 130/0x82 is on condition
// 132/0x84 is immediate
#define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer #define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer
#define SCHEDULEFLAG_SCHEDULE_ONCHANGE 0x81 // 7th+1st bit for OnChange #define SCHEDULEFLAG_SCHEDULE_ONCHANGE 0x81 // 7th+1st bit for OnChange
#define SCHEDULEFLAG_SCHEDULE_CONDITION 0x82 // 7th+2nd bit for Condition #define SCHEDULEFLAG_SCHEDULE_CONDITION 0x82 // 7th+2nd bit for Condition
#define MAX_STARTUP_RETRIES 3 // retry the start-up commands x times #define SCHEDULEFLAG_SCHEDULE_IMMEDIATE 0x84 // 7th+3rd bit for Condition
#define MAX_STARTUP_RETRIES 3 // retry the start-up commands x times
namespace emsesp { namespace emsesp {

View File

@@ -32,6 +32,7 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit
} }
void WebSettings::read(WebSettings & settings, JsonObject root) { void WebSettings::read(WebSettings & settings, JsonObject root) {
root["version"] = settings.version;
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;