Merge branch 'dev' into dev2

This commit is contained in:
MichaelDvP
2024-01-21 09:35:16 +01:00
76 changed files with 963 additions and 958 deletions

View File

@@ -18,6 +18,9 @@
- Show network hostname in Web UI under Network Status - Show network hostname in Web UI under Network Status
- Improved HA Discovery so each section (EMS device, Scheduler, Analog, Temperature, Custom, Shower) have their own section - Improved HA Discovery so each section (EMS device, Scheduler, Analog, Temperature, Custom, Shower) have their own section
- boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536) - boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536)
- mixer MM100 telegram 0x2CC [#1554](https://github.com/emsesp/EMS-ESP32/issues/1554)
- boiler hpSetDiffPressure [#1563](https://github.com/emsesp/EMS-ESP32/issues/1563)
- custom variables [#1423](https://github.com/emsesp/EMS-ESP32/issues/1423)
## Fixed ## Fixed
@@ -27,9 +30,10 @@
- changed HA name and grouping to be consistent [#1528](https://github.com/emsesp/EMS-ESP32/issues/1528) - changed HA name and grouping to be consistent [#1528](https://github.com/emsesp/EMS-ESP32/issues/1528)
- MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528) - MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528)
- dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495) - dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495)
- added writeable icon to Web's Custom Entity page for each entity shown in the table
## Changed ## Changed
- use flag for BC400 compatible thermostats, manage different mode settings - use flag for BC400 compatible thermostats, manage different mode settings
- HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459) - HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459)
- Upgraded ArduinoJson to 7.0.0 #1538 - upgraded ArduinoJson to 7.0.0 #1538 and then 7.0.2

View File

@@ -20,20 +20,20 @@
"lint": "eslint . --cache --fix" "lint": "eslint . --cache --fix"
}, },
"dependencies": { "dependencies": {
"@alova/adapter-xhr": "^1.0.2", "@alova/adapter-xhr": "^1.0.3",
"@babel/core": "^7.23.7", "@babel/core": "^7.23.7",
"@emotion/react": "^11.11.3", "@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.3", "@mui/icons-material": "^5.15.5",
"@mui/material": "^5.15.3", "@mui/material": "^5.15.5",
"@table-library/react-table-library": "4.1.7", "@table-library/react-table-library": "4.1.7",
"@types/imagemin": "^8.0.5", "@types/imagemin": "^8.0.5",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.6", "@types/node": "^20.11.5",
"@types/react": "^18.2.47", "@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"alova": "^2.16.2", "alova": "^2.17.0",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"history": "^5.3.0", "history": "^5.3.0",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
@@ -42,9 +42,9 @@
"react": "latest", "react": "latest",
"react-dom": "latest", "react-dom": "latest",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^4.12.0", "react-icons": "^5.0.1",
"react-router-dom": "^6.21.1", "react-router-dom": "^6.21.3",
"react-toastify": "^9.1.3", "react-toastify": "^10.0.3",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.26.2", "typesafe-i18n": "^5.26.2",
"typescript": "^5.3.3" "typescript": "^5.3.3"
@@ -52,8 +52,8 @@
"devDependencies": { "devDependencies": {
"@preact/compat": "^17.1.2", "@preact/compat": "^17.1.2",
"@preact/preset-vite": "^2.8.1", "@preact/preset-vite": "^2.8.1",
"@typescript-eslint/eslint-plugin": "^6.18.0", "@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.18.0", "@typescript-eslint/parser": "^6.19.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
@@ -67,12 +67,12 @@
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"preact": "^10.19.3", "preact": "^10.19.3",
"prettier": "^3.1.1", "prettier": "^3.2.4",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"terser": "^5.26.0", "terser": "^5.27.0",
"vite": "^5.0.11", "vite": "^5.0.12",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^4.2.3" "vite-tsconfig-paths": "^4.3.1"
}, },
"packageManager": "yarn@4.0.2" "packageManager": "yarn@4.0.2"
} }

View File

@@ -50,8 +50,10 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
const progressText = () => { const progressText = () => {
if (uploading) { if (uploading) {
if (progress.total) { if (progress.total && progress.loaded) {
return LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%'; return progress.loaded <= progress.total
? LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%'
: LL.UPLOADING() + ': ' + Math.round((progress.total * 100) / progress.loaded) + '%';
} }
} }
return LL.UPLOAD_DROP_TEXT(); return LL.UPLOAD_DROP_TEXT();
@@ -83,7 +85,13 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
<Box width="100%" p={2}> <Box width="100%" p={2}>
<LinearProgress <LinearProgress
variant="determinate" variant="determinate"
value={progress.total === 0 ? 0 : Math.round((progress.loaded * 100) / progress.total)} value={
progress.total === 0 || progress.loaded === 0
? 0
: progress.loaded <= progress.total
? Math.round((progress.loaded * 100) / progress.total)
: Math.round((progress.total * 100) / progress.loaded)
}
/> />
</Box> </Box>
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}> <Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>

View File

@@ -37,7 +37,8 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
if (open) { if (open) {
void generateToken(); void generateToken();
} }
}, [open, generateToken]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
return ( return (
<Dialog sx={dialogStyle} onClose={onClose} open={!!username} fullWidth maxWidth="sm"> <Dialog sx={dialogStyle} onClose={onClose} open={!!username} fullWidth maxWidth="sm">

View File

@@ -2,8 +2,8 @@ import { Tab } from '@mui/material';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import SettingsApplication from './SettingsApplication'; import SettingsApplication from './SettingsApplication';
import SettingsCustomEntities from './SettingsCustomEntities';
import SettingsCustomization from './SettingsCustomization'; import SettingsCustomization from './SettingsCustomization';
import SettingsEntities from './SettingsEntities';
import SettingsScheduler from './SettingsScheduler'; import SettingsScheduler from './SettingsScheduler';
import type { FC } from 'react'; import type { FC } from 'react';
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components'; import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
@@ -27,7 +27,7 @@ const Settings: FC = () => {
<Route path="application" element={<SettingsApplication />} /> <Route path="application" element={<SettingsApplication />} />
<Route path="customization" element={<SettingsCustomization />} /> <Route path="customization" element={<SettingsCustomization />} />
<Route path="scheduler" element={<SettingsScheduler />} /> <Route path="scheduler" element={<SettingsScheduler />} />
<Route path="customentities" element={<SettingsEntities />} /> <Route path="customentities" element={<SettingsCustomEntities />} />
<Route path="*" element={<Navigate replace to="/settings/application" />} /> <Route path="*" element={<Navigate replace to="/settings/application" />} />
</Routes> </Routes>
</> </>

View File

@@ -1,5 +1,7 @@
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 RefreshIcon from '@mui/icons-material/Refresh';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { Button, Typography, Box } from '@mui/material'; import { Button, Typography, Box } from '@mui/material';
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
@@ -11,7 +13,7 @@ import { useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import SettingsEntitiesDialog from './SettingsEntitiesDialog'; import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog';
import * as EMSESP from './api'; import * as EMSESP from './api';
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import { entityItemValidation } from './validators'; import { entityItemValidation } from './validators';
@@ -21,7 +23,7 @@ import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'componen
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
const SettingsEntities: FC = () => { const SettingsCustomEntities: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0); const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0); const blocker = useBlocker(numChanges !== 0);
@@ -43,6 +45,7 @@ const SettingsEntities: FC = () => {
function hasEntityChanged(ei: EntityItem) { function hasEntityChanged(ei: EntityItem) {
return ( return (
ei.id !== ei.o_id || ei.id !== ei.o_id ||
ei.ram !== ei.o_ram ||
(ei?.name || '') !== (ei?.o_name || '') || (ei?.name || '') !== (ei?.o_name || '') ||
ei.device_id !== ei.o_device_id || ei.device_id !== ei.o_device_id ||
ei.type_id !== ei.o_type_id || ei.type_id !== ei.o_type_id ||
@@ -51,7 +54,8 @@ const SettingsEntities: FC = () => {
ei.factor !== ei.o_factor || ei.factor !== ei.o_factor ||
ei.value_type !== ei.o_value_type || ei.value_type !== ei.o_value_type ||
ei.writeable !== ei.o_writeable || ei.writeable !== ei.o_writeable ||
ei.deleted !== ei.o_deleted ei.deleted !== ei.o_deleted ||
(ei.value || '') !== (ei.o_value || '')
); );
} }
@@ -118,6 +122,7 @@ const SettingsEntities: FC = () => {
.filter((ei) => !ei.deleted) .filter((ei) => !ei.deleted)
.map((condensed_ei) => ({ .map((condensed_ei) => ({
id: condensed_ei.id, id: condensed_ei.id,
ram: condensed_ei.ram,
name: condensed_ei.name, name: condensed_ei.name,
device_id: condensed_ei.device_id, device_id: condensed_ei.device_id,
type_id: condensed_ei.type_id, type_id: condensed_ei.type_id,
@@ -125,7 +130,8 @@ const SettingsEntities: FC = () => {
factor: condensed_ei.factor, factor: condensed_ei.factor,
uom: condensed_ei.uom, uom: condensed_ei.uom,
writeable: condensed_ei.writeable, writeable: condensed_ei.writeable,
value_type: condensed_ei.value_type value_type: condensed_ei.value_type,
value: condensed_ei.value
})) }))
}) })
.then(() => { .then(() => {
@@ -173,14 +179,16 @@ const SettingsEntities: FC = () => {
setSelectedEntityItem({ setSelectedEntityItem({
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100), id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
name: '', name: '',
device_id: '', ram: 0,
type_id: '', device_id: '0',
type_id: '0',
offset: 0, offset: 0,
factor: 1, factor: 1,
uom: 0, uom: 0,
value_type: 0, value_type: 0,
writeable: false, writeable: false,
deleted: false deleted: false,
value: ''
}); });
setDialogOpen(true); setDialogOpen(true);
}; };
@@ -212,18 +220,21 @@ const SettingsEntities: FC = () => {
<HeaderCell stiff>{LL.ID_OF(LL.DEVICE())}</HeaderCell> <HeaderCell stiff>{LL.ID_OF(LL.DEVICE())}</HeaderCell>
<HeaderCell stiff>{LL.ID_OF(LL.TYPE(1))}</HeaderCell> <HeaderCell stiff>{LL.ID_OF(LL.TYPE(1))}</HeaderCell>
<HeaderCell stiff>{LL.OFFSET()}</HeaderCell> <HeaderCell stiff>{LL.OFFSET()}</HeaderCell>
<HeaderCell stiff>{LL.VALUE(1) + ' ' + LL.TYPE(1)}</HeaderCell> <HeaderCell stiff>{LL.TYPE(1)}</HeaderCell>
<HeaderCell stiff>{LL.VALUE(1)}</HeaderCell> <HeaderCell stiff>{LL.VALUE(1)}</HeaderCell>
</HeaderRow> </HeaderRow>
</Header> </Header>
<Body> <Body>
{tableList.map((ei: EntityItem) => ( {tableList.map((ei: EntityItem) => (
<Row key={ei.name} item={ei} onClick={() => editEntityItem(ei)}> <Row key={ei.name} item={ei} onClick={() => editEntityItem(ei)}>
<Cell>{ei.name}</Cell> <Cell>
<Cell>{showHex(ei.device_id as number, 2)}</Cell> {ei.name}&nbsp;
<Cell>{showHex(ei.type_id as number, 3)}</Cell> {ei.writeable && <EditOutlinedIcon color="primary" sx={{ fontSize: 12 }} />}
<Cell>{ei.offset}</Cell> </Cell>
<Cell>{DeviceValueTypeNames[ei.value_type]}</Cell> <Cell>{ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)}</Cell>
<Cell>{ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)}</Cell>
<Cell>{ei.ram === 1 ? '' : ei.offset}</Cell>
<Cell>{ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]}</Cell>
<Cell>{formatValue(ei.value, ei.uom)}</Cell> <Cell>{formatValue(ei.value, ei.uom)}</Cell>
</Row> </Row>
))} ))}
@@ -244,7 +255,7 @@ const SettingsEntities: FC = () => {
{renderEntity()} {renderEntity()}
{selectedEntityItem && ( {selectedEntityItem && (
<SettingsEntitiesDialog <SettingsCustomEntitiesDialog
open={dialogOpen} open={dialogOpen}
creating={creating} creating={creating}
onClose={onDialogClose} onClose={onDialogClose}
@@ -274,7 +285,10 @@ const SettingsEntities: FC = () => {
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box flexWrap="nowrap" whiteSpace="nowrap">
<ButtonRow> <ButtonRow>
<Button startIcon={<AddIcon />} variant="outlined" color="secondary" onClick={addEntityItem}> <Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={fetchEntities}>
{LL.REFRESH()}
</Button>
<Button startIcon={<AddIcon />} variant="outlined" color="primary" onClick={addEntityItem}>
{LL.ADD(0)} {LL.ADD(0)}
</Button> </Button>
</ButtonRow> </ButtonRow>
@@ -284,4 +298,4 @@ const SettingsEntities: FC = () => {
); );
}; };
export default SettingsEntities; export default SettingsCustomEntities;

View File

@@ -30,7 +30,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { validate } from 'validators'; import { validate } from 'validators';
type SettingsEntitiesDialogProps = { type SettingsCustomEntitiesDialogProps = {
open: boolean; open: boolean;
creating: boolean; creating: boolean;
onClose: () => void; onClose: () => void;
@@ -39,14 +39,14 @@ type SettingsEntitiesDialogProps = {
validator: Schema; validator: Schema;
}; };
const SettingsEntitiesDialog = ({ const SettingsCustomEntitiesDialog = ({
open, open,
creating, creating,
onClose, onClose,
onSave, onSave,
selectedItem, selectedItem,
validator validator
}: SettingsEntitiesDialogProps) => { }: SettingsCustomEntitiesDialogProps) => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<EntityItem>(selectedItem); const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -100,7 +100,7 @@ const SettingsEntitiesDialog = ({
<Box flexWrap="nowrap" whiteSpace="nowrap" /> <Box flexWrap="nowrap" whiteSpace="nowrap" />
</Box> </Box>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={8}> <Grid item xs={4}>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors}
name="name" name="name"
@@ -111,6 +111,36 @@ const SettingsEntitiesDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
</Grid> </Grid>
<Grid item xs={4}>
<TextField
name="ram"
label={LL.VALUE(1) + ' ' + LL.TYPE(1)}
value={editItem.ram}
variant="outlined"
onChange={updateFormValue}
margin="normal"
fullWidth
select
>
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
</TextField>
</Grid>
{editItem.ram === 1 && (
<Grid item xs={4}>
<TextField
name="value"
label={LL.STARTVALUE()}
value={editItem.value}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
/>
</Grid>
)}
{editItem.ram === 0 && (
<>
<Grid item xs={4} mt={3}> <Grid item xs={4} mt={3}>
<BlockFormControlLabel <BlockFormControlLabel
control={<Checkbox checked={editItem.writeable} onChange={updateFormValue} name="writeable" />} control={<Checkbox checked={editItem.writeable} onChange={updateFormValue} name="writeable" />}
@@ -212,7 +242,7 @@ const SettingsEntitiesDialog = ({
</Grid> </Grid>
</> </>
)} )}
{editItem.value_type === DeviceValueType.STRING && ( {editItem.value_type === DeviceValueType.STRING && editItem.device_id !== '0' && (
<Grid item xs={4}> <Grid item xs={4}>
<TextField <TextField
name="factor" name="factor"
@@ -227,6 +257,8 @@ const SettingsEntitiesDialog = ({
/> />
</Grid> </Grid>
)} )}
</>
)}
</Grid> </Grid>
</DialogContent> </DialogContent>
@@ -249,4 +281,4 @@ const SettingsEntitiesDialog = ({
); );
}; };
export default SettingsEntitiesDialog; export default SettingsCustomEntitiesDialog;

View File

@@ -88,7 +88,7 @@ export const writeSchedule = (data: any) => alovaInstance.Post('/rest/schedule',
// SettingsEntities // SettingsEntities
export const readCustomEntities = () => export const readCustomEntities = () =>
alovaInstance.Get<EntityItem[]>('/rest/customentities', { alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
name: 'entities', name: 'entities',
transformData(data: any) { transformData(data: any) {
return data.entities.map((ei: EntityItem) => ({ return data.entities.map((ei: EntityItem) => ({
@@ -106,4 +106,4 @@ export const readCustomEntities = () =>
})); }));
} }
}); });
export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customentities', data); export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customEntities', data);

View File

@@ -177,7 +177,8 @@ export enum DeviceValueUOM {
L, L,
KMIN, KMIN,
K, K,
VOLTS VOLTS,
MBAR
} }
export const DeviceValueUOM_s = [ export const DeviceValueUOM_s = [
@@ -204,7 +205,8 @@ export const DeviceValueUOM_s = [
'l', 'l',
'K*min', 'K*min',
'K', 'K',
'V' 'V',
'mbar'
]; ];
export enum AnalogType { export enum AnalogType {
@@ -323,6 +325,7 @@ export enum ScheduleFlag {
export interface EntityItem { export interface EntityItem {
id: number; // unique number id: number; // unique number
ram: number;
name: string; name: string;
device_id: number | string; device_id: number | string;
type_id: number | string; type_id: number | string;
@@ -334,6 +337,7 @@ export interface EntityItem {
writeable: boolean; writeable: boolean;
deleted?: boolean; deleted?: boolean;
o_id?: number; o_id?: number;
o_ram?: number;
o_name?: string; o_name?: string;
o_device_id?: number | string; o_device_id?: number | string;
o_type_id?: number | string; o_type_id?: number | string;
@@ -343,6 +347,7 @@ export interface EntityItem {
o_value_type?: number; o_value_type?: number;
o_deleted?: boolean; o_deleted?: boolean;
o_writeable?: boolean; o_writeable?: boolean;
o_value?: any;
} }
export interface Entities { export interface Entities {
@@ -398,6 +403,6 @@ export const DeviceValueTypeNames = [
'ULONG', 'ULONG',
'TIME', 'TIME',
'ENUM', 'ENUM',
'STRING', 'RAW',
'CMD' 'CMD'
]; ];

View File

@@ -9,7 +9,6 @@ export interface NTPStatus {
utc_time: string; utc_time: string;
local_time: string; local_time: string;
server: string; server: string;
uptime: number;
} }
export interface NTPSettings { export interface NTPSettings {

View File

@@ -12,10 +12,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@alova/adapter-xhr@npm:^1.0.2": "@alova/adapter-xhr@npm:^1.0.3":
version: 1.0.2 version: 1.0.3
resolution: "@alova/adapter-xhr@npm:1.0.2" resolution: "@alova/adapter-xhr@npm:1.0.3"
checksum: a57d178e89e3b655191bebccbc34d22760813b97b430e16f77b6ad561e3bb4ad8a34948aa2d724f5833d675f21a337ab769a3e5f73878430c3139374c6afb6ea checksum: 53923b0b7f833bbbda662ad28f29bb8226d2126ab7dcc57c9aa5486212cb02f0cfa19760d33ab63334688458138fc3c4713084c2f6a558c969d83efda7828601
languageName: node languageName: node
linkType: hard linkType: hard
@@ -401,12 +401,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.23.6": "@babel/runtime@npm:^7.23.8":
version: 7.23.6 version: 7.23.8
resolution: "@babel/runtime@npm:7.23.6" resolution: "@babel/runtime@npm:7.23.8"
dependencies: dependencies:
regenerator-runtime: "npm:^0.14.0" regenerator-runtime: "npm:^0.14.0"
checksum: 4c4ab16f0361c59fb23956e4d0a29935f1f8a64aa8dd37876ce38355b6f4d8f0e54237aacb89c73b1532def60539ddde2d651523c8fa887b30b19a8cf0c465b0 checksum: ec8f1967a36164da6cac868533ffdff97badd76d23d7d820cc84f0818864accef972f22f9c6a710185db1e3810e353fc18c3da721e5bb3ee8bc61bdbabce03ff
languageName: node languageName: node
linkType: hard linkType: hard
@@ -841,41 +841,41 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/core@npm:^1.4.2": "@floating-ui/core@npm:^1.5.3":
version: 1.5.0
resolution: "@floating-ui/core@npm:1.5.0"
dependencies:
"@floating-ui/utils": "npm:^0.1.3"
checksum: 3182715a30493f44a32158f4d77ab5b6c212064b160cb84b5b8405ec2845bd8a9167c25292709e841cad9e70c6b9efdc30f876044e3b0909139fea8eca00c631
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.5.1":
version: 1.5.3 version: 1.5.3
resolution: "@floating-ui/dom@npm:1.5.3" resolution: "@floating-ui/core@npm:1.5.3"
dependencies: dependencies:
"@floating-ui/core": "npm:^1.4.2" "@floating-ui/utils": "npm:^0.2.0"
"@floating-ui/utils": "npm:^0.1.3" checksum: 7d9feaca2565a2a71bf03d23cd292c03def63097d7fde7d62909cdb8ddb84664781f3922086bcf10443f3310cb92381a0ecf745b2774edb917fa74fe61015c56
checksum: d2d5ae7a0949c0ebf7fbf97a21612bf94dbd29cb6c847e00588b8e2a5575ade27c47cb19f5d230fc21a571d99aa0c714b301c9221d33921047408c0ed9d91a30
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/react-dom@npm:^2.0.4": "@floating-ui/dom@npm:^1.5.4":
version: 2.0.4 version: 1.5.4
resolution: "@floating-ui/react-dom@npm:2.0.4" resolution: "@floating-ui/dom@npm:1.5.4"
dependencies: dependencies:
"@floating-ui/dom": "npm:^1.5.1" "@floating-ui/core": "npm:^1.5.3"
"@floating-ui/utils": "npm:^0.2.0"
checksum: 3ba02ba2b4227c1e18df6ccdd029a1c100058db2e76ca1dac60a593ec72b2d4d995fa5c2d1639a5c38adb17e12398fbfe4f6cf5fd45f2ee6170ed0cf64acea06
languageName: node
linkType: hard
"@floating-ui/react-dom@npm:^2.0.5":
version: 2.0.5
resolution: "@floating-ui/react-dom@npm:2.0.5"
dependencies:
"@floating-ui/dom": "npm:^1.5.4"
peerDependencies: peerDependencies:
react: ">=16.8.0" react: ">=16.8.0"
react-dom: ">=16.8.0" react-dom: ">=16.8.0"
checksum: 4240a718502c797fd2e174cd06dcd7321a6eda9c8966dbaf61864b9e16445e95649a59bfe7c19ee13f68c11f3693724d7970c7e618089a3d3915bd343639cfae checksum: b4fc008c725149b9565949184d844c914a8fa2687636c3c1166a1d52ca58537b3ba9b0a0e2945cf424662c846e60b173df0d325af7e700a31550e5e0b346070a
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/utils@npm:^0.1.3": "@floating-ui/utils@npm:^0.2.0":
version: 0.1.6 version: 0.2.1
resolution: "@floating-ui/utils@npm:0.1.6" resolution: "@floating-ui/utils@npm:0.2.1"
checksum: 450ec4ecc1dd8161b1904d4e1e9d95e653cc06f79af6c3b538b79efb10541d90bcc88646ab3cdffc5b92e00c4804ca727b025d153ad285f42dbbb39aec219ec9 checksum: 33c9ab346e7b05c5a1e6a95bc902aafcfc2c9d513a147e2491468843bd5607531b06d0b9aa56aa491cbf22a6c2495c18ccfc4c0344baec54a689a7bb8e4898d6
languageName: node languageName: node
linkType: hard linkType: hard
@@ -970,16 +970,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/base@npm:5.0.0-beta.30": "@mui/base@npm:5.0.0-beta.32":
version: 5.0.0-beta.30 version: 5.0.0-beta.32
resolution: "@mui/base@npm:5.0.0-beta.30" resolution: "@mui/base@npm:5.0.0-beta.32"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@floating-ui/react-dom": "npm:^2.0.4" "@floating-ui/react-dom": "npm:^2.0.5"
"@mui/types": "npm:^7.2.12" "@mui/types": "npm:^7.2.13"
"@mui/utils": "npm:^5.15.3" "@mui/utils": "npm:^5.15.5"
"@popperjs/core": "npm:^2.11.8" "@popperjs/core": "npm:^2.11.8"
clsx: "npm:^2.0.0" clsx: "npm:^2.1.0"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
peerDependencies: peerDependencies:
"@types/react": ^17.0.0 || ^18.0.0 "@types/react": ^17.0.0 || ^18.0.0
@@ -988,22 +988,22 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 55e18d59ac96f5bbfbfdadd907751f5e6a4f74f611b5e99fe5f8002c76fa117b62c159f52ea0f12574a66460d62734082438cff19cb73e3fca9dc22f82f6eaf2 checksum: c88cd8a412ecaeaf0040e20708b2a607b9594a4462449ad06b90e96465aad0dada23295f801ed72851025fd023ababc410b6a48fcb69d7cdef90b55e62aa9a11
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/core-downloads-tracker@npm:^5.15.3": "@mui/core-downloads-tracker@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/core-downloads-tracker@npm:5.15.3" resolution: "@mui/core-downloads-tracker@npm:5.15.5"
checksum: 002451af1aa555c0163c0ffacde5c15062e0ae0f30b81945e1a0befe7b6c5d14924a2b068b7b5f713c177ee3eecca4fc250d590d11206a6b5465700c399a34d1 checksum: 4c9b1281ebe8d17d402e22f7f50c347c0b3918b1ed17af721f4de5ce282d90bc6d90fe9730595998b2bbb2f7ebe57fc55d4c858f31754fccdb606af472a59dc8
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/icons-material@npm:^5.15.3": "@mui/icons-material@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/icons-material@npm:5.15.3" resolution: "@mui/icons-material@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
peerDependencies: peerDependencies:
"@mui/material": ^5.0.0 "@mui/material": ^5.0.0
"@types/react": ^17.0.0 || ^18.0.0 "@types/react": ^17.0.0 || ^18.0.0
@@ -1011,22 +1011,22 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 2393a9dcd0834cdda728b8ebca5d8f6acbfc34316346aaea257e32961abf7cf578419df196b50223b89b3e2556098aea283786ca4eeedaf58be3d204f499f6bc checksum: 25feb86a76ce83c81391c95d0c1c867e988cc7bc1b5a05c5698b71cb3cd1005fd148b07c2fa8908cda9fc4e44ea8b6e0fd2197bc0abafac0ee4880b477852eea
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/material@npm:^5.15.3": "@mui/material@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/material@npm:5.15.3" resolution: "@mui/material@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@mui/base": "npm:5.0.0-beta.30" "@mui/base": "npm:5.0.0-beta.32"
"@mui/core-downloads-tracker": "npm:^5.15.3" "@mui/core-downloads-tracker": "npm:^5.15.5"
"@mui/system": "npm:^5.15.3" "@mui/system": "npm:^5.15.5"
"@mui/types": "npm:^7.2.12" "@mui/types": "npm:^7.2.13"
"@mui/utils": "npm:^5.15.3" "@mui/utils": "npm:^5.15.5"
"@types/react-transition-group": "npm:^4.4.10" "@types/react-transition-group": "npm:^4.4.10"
clsx: "npm:^2.0.0" clsx: "npm:^2.1.0"
csstype: "npm:^3.1.2" csstype: "npm:^3.1.2"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
react-is: "npm:^18.2.0" react-is: "npm:^18.2.0"
@@ -1044,16 +1044,16 @@ __metadata:
optional: true optional: true
"@types/react": "@types/react":
optional: true optional: true
checksum: fe8d318aed036b649a82e4833254f833ece028b6a25cec001991e6864d9e520752df6a746b6ca856c0310cc9aae16697aa77ddf53a85c889bb5d04c5aa5c1dcb checksum: 2a094d94acfc8f945b6cc73b295799f3174d7292707230e9b9486d810990561778f5f228f2fdc13a064ae234d528fb28c9b53f6c487ca43e65dc17460886165c
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/private-theming@npm:^5.15.3": "@mui/private-theming@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/private-theming@npm:5.15.3" resolution: "@mui/private-theming@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@mui/utils": "npm:^5.15.3" "@mui/utils": "npm:^5.15.5"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
peerDependencies: peerDependencies:
"@types/react": ^17.0.0 || ^18.0.0 "@types/react": ^17.0.0 || ^18.0.0
@@ -1061,15 +1061,15 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 4404a7d9545974631b329f391df72fa54edb5aefa6d32d9656b200284613e8ea1bdd3d0add2abe7278f1343dd5cf7552c7e4d2aaf5593f292c7db3cd63ddff21 checksum: 1b26bc897417dcd91bbc65af3584c3cdf6704e9beb707c97bb7977962536213d7c7bf8e1004cbe86a19625ed5feba82d3ad2997e943138ed36114a8a36bf0fed
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/styled-engine@npm:^5.15.3": "@mui/styled-engine@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/styled-engine@npm:5.15.3" resolution: "@mui/styled-engine@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@emotion/cache": "npm:^11.11.0" "@emotion/cache": "npm:^11.11.0"
csstype: "npm:^3.1.2" csstype: "npm:^3.1.2"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
@@ -1082,20 +1082,20 @@ __metadata:
optional: true optional: true
"@emotion/styled": "@emotion/styled":
optional: true optional: true
checksum: 6775f92cda9f17428baf5b95e5849f31eead92485e332902ff29bd49bd03fbe2f5e762ebcdd122f7f31e102ec42cda29cbb5fdef79f5d03f416705d119b69e75 checksum: 10e38ed39f7defc26d7e14e9634afcd9d540eaa1b9aeb957a6d1154a14a3cca2843e9aa7ead126604728bbf2125203c1f157059c06b397ed0278fc4b7cfae5c5
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/system@npm:^5.15.3": "@mui/system@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/system@npm:5.15.3" resolution: "@mui/system@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@mui/private-theming": "npm:^5.15.3" "@mui/private-theming": "npm:^5.15.5"
"@mui/styled-engine": "npm:^5.15.3" "@mui/styled-engine": "npm:^5.15.5"
"@mui/types": "npm:^7.2.12" "@mui/types": "npm:^7.2.13"
"@mui/utils": "npm:^5.15.3" "@mui/utils": "npm:^5.15.5"
clsx: "npm:^2.0.0" clsx: "npm:^2.1.0"
csstype: "npm:^3.1.2" csstype: "npm:^3.1.2"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
peerDependencies: peerDependencies:
@@ -1110,27 +1110,27 @@ __metadata:
optional: true optional: true
"@types/react": "@types/react":
optional: true optional: true
checksum: 7b71cad3c3b4f8136cf51a9e7040440073201618eaa5d0fcbd8830e3c3f35eb8a38303bb2bc9da84e0c95844193fdb4238af50e1f1d74a8e9fa79500a49c31db checksum: bc40858eff92efe1424b4de5782ca48ec0bccfe2de244b00af8f8607a7f47b5ec7006a0e369d1c52ddb3fe01d7666d1f7ed6d9a9070bee28dfa4ab2cecc4d015
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/types@npm:^7.2.12": "@mui/types@npm:^7.2.13":
version: 7.2.12 version: 7.2.13
resolution: "@mui/types@npm:7.2.12" resolution: "@mui/types@npm:7.2.13"
peerDependencies: peerDependencies:
"@types/react": ^17.0.0 || ^18.0.0 "@types/react": ^17.0.0 || ^18.0.0
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 7d3ef53fee7eddc063d2083dc129f7d6d38b472a9196c3522fc5a75e66849fbf2b824be3f6aee11dc02c4475864e544026e6051ffb9d33f5dc1fc2a2279a8b72 checksum: a35bff025f715073329bd7cbe11ef4ce331ea377adffc0c5cd264bea47283590ce928d1fdbbc27506d1d462151325c81e71f2378ac4335feef3042010bbf3fcd
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/utils@npm:^5.15.3": "@mui/utils@npm:^5.15.5":
version: 5.15.3 version: 5.15.5
resolution: "@mui/utils@npm:5.15.3" resolution: "@mui/utils@npm:5.15.5"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.6" "@babel/runtime": "npm:^7.23.8"
"@types/prop-types": "npm:^15.7.11" "@types/prop-types": "npm:^15.7.11"
prop-types: "npm:^15.8.1" prop-types: "npm:^15.8.1"
react-is: "npm:^18.2.0" react-is: "npm:^18.2.0"
@@ -1140,7 +1140,7 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: c4d66e34332f448527c6dea66a7011f95dc230ccaf5a3ee898a6fd69b77a3584af1fd644d095dc7edb2d480e5c050db06f9b9ec9dea3bc5091a80cf8b676f709 checksum: c8ff39a23ec540c6fd6495e44df6dc5531afca535cbb605f81cd5ef66af946e6c6415290caade8cfa0f61ecfb55703d8065c4968530c0b54c52d44f23a04cbfe
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1277,10 +1277,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@remix-run/router@npm:1.14.1": "@remix-run/router@npm:1.14.2":
version: 1.14.1 version: 1.14.2
resolution: "@remix-run/router@npm:1.14.1" resolution: "@remix-run/router@npm:1.14.2"
checksum: caed61639006444a66ca832f1e500bac2fcf02695183e967ff1452d3172f888f2bb40591b239c85f9003b9628383cfd4c8ef55cde800d14276905c7031c9f0b9 checksum: 422844e88b985f1e287301b302c6cf8169c9eea792f80d40464f97b25393bb2e697228ebd7a7b61444d5a51c5873c4a637aad20acde5886a5caf62e833c5ceee
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1568,12 +1568,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:^20.10.6": "@types/node@npm:^20.11.5":
version: 20.10.6 version: 20.11.5
resolution: "@types/node@npm:20.10.6" resolution: "@types/node@npm:20.11.5"
dependencies: dependencies:
undici-types: "npm:~5.26.4" undici-types: "npm:~5.26.4"
checksum: 08471220d3cbbb6669835c4b78541edf5eface8f2c2e36c550cfa4ff73da73071c90e200a06359fac25d6564127597c23e178128058fb676824ec23d5178a017 checksum: 9f31c471047d7b3e240ce7b77ff29b0d15e83be7e3feafb3d0b0d0931122b438b1eefa302a5a2e1e9849914ff3fd76aafbd8ccb372efb1331ba048da63bce6f8
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1648,14 +1648,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/react@npm:^18.2.47": "@types/react@npm:^18.2.48":
version: 18.2.47 version: 18.2.48
resolution: "@types/react@npm:18.2.47" resolution: "@types/react@npm:18.2.48"
dependencies: dependencies:
"@types/prop-types": "npm:*" "@types/prop-types": "npm:*"
"@types/scheduler": "npm:*" "@types/scheduler": "npm:*"
csstype: "npm:^3.0.2" csstype: "npm:^3.0.2"
checksum: 0a98c2ef8303909f78c973ac9731cb671f3a0b96bc5213b538d1a50cbaae6e51b6befd64845a9cb95af8528767315d5bd99a85608eb716c020393c7d33a9b477 checksum: 2e56ea6bd821ae96bd943f727a59d85384eaf5f8a3e6fce4fa1d34453e32d8eedda742432b3857fa0de7a4214bf84ce4239757eb52918e76452c00384731e585
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1691,15 +1691,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/eslint-plugin@npm:^6.18.0": "@typescript-eslint/eslint-plugin@npm:^6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/eslint-plugin@npm:6.18.0" resolution: "@typescript-eslint/eslint-plugin@npm:6.19.0"
dependencies: dependencies:
"@eslint-community/regexpp": "npm:^4.5.1" "@eslint-community/regexpp": "npm:^4.5.1"
"@typescript-eslint/scope-manager": "npm:6.18.0" "@typescript-eslint/scope-manager": "npm:6.19.0"
"@typescript-eslint/type-utils": "npm:6.18.0" "@typescript-eslint/type-utils": "npm:6.19.0"
"@typescript-eslint/utils": "npm:6.18.0" "@typescript-eslint/utils": "npm:6.19.0"
"@typescript-eslint/visitor-keys": "npm:6.18.0" "@typescript-eslint/visitor-keys": "npm:6.19.0"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
graphemer: "npm:^1.4.0" graphemer: "npm:^1.4.0"
ignore: "npm:^5.2.4" ignore: "npm:^5.2.4"
@@ -1712,44 +1712,44 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 7810a84f6d9cb735f6848aa4a7ef64c77740812afc0248fda63ec182910a1d045cd9a32d8e49b0e5323266db6da12a2fe50171147927b3186815a56f12c16ee7 checksum: 5ed8483d792c4bc6ed697159c84a47ba5c35cd124949883813f2053b972537de3900a7ae26d4d6f370194f2cc7929baa2d09268e0b90118f20ed961cf6c176b9
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/parser@npm:^6.18.0": "@typescript-eslint/parser@npm:^6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/parser@npm:6.18.0" resolution: "@typescript-eslint/parser@npm:6.19.0"
dependencies: dependencies:
"@typescript-eslint/scope-manager": "npm:6.18.0" "@typescript-eslint/scope-manager": "npm:6.19.0"
"@typescript-eslint/types": "npm:6.18.0" "@typescript-eslint/types": "npm:6.19.0"
"@typescript-eslint/typescript-estree": "npm:6.18.0" "@typescript-eslint/typescript-estree": "npm:6.19.0"
"@typescript-eslint/visitor-keys": "npm:6.18.0" "@typescript-eslint/visitor-keys": "npm:6.19.0"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 6798332819f839454a8405e31cfaa0fe908d5966be929bef55e78ac51a0ff3868feb42b38e7772cedf88277f6b2841b3d91f6c573eacb945e2741ecae94047c7 checksum: 0c6280a69127cf521b3403be9877775eecda2b2e4e44a67874b0d9cf82ed95a7971dac2db633e55ec22f8026da2681137110b2924313421a22b7c03eba8cda67
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/scope-manager@npm:6.18.0": "@typescript-eslint/scope-manager@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/scope-manager@npm:6.18.0" resolution: "@typescript-eslint/scope-manager@npm:6.19.0"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.18.0" "@typescript-eslint/types": "npm:6.19.0"
"@typescript-eslint/visitor-keys": "npm:6.18.0" "@typescript-eslint/visitor-keys": "npm:6.19.0"
checksum: c2c465e6803f78d3300142167a8a79dd2613c64cf464a40a9cf6b13a2c10c3d82ca30bb9c2d26aba7f054b8740b38e1d25f377fcdf917aba489d5b5ea2550858 checksum: d36c51c05e14c51ce13181120eeea46d1edd59ed1ff16dc4ec1f5532a975b5faec5c10a373aaa90545f82a12330c6cba18ecedc734e18288f5874855c48ba808
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/type-utils@npm:6.18.0": "@typescript-eslint/type-utils@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/type-utils@npm:6.18.0" resolution: "@typescript-eslint/type-utils@npm:6.19.0"
dependencies: dependencies:
"@typescript-eslint/typescript-estree": "npm:6.18.0" "@typescript-eslint/typescript-estree": "npm:6.19.0"
"@typescript-eslint/utils": "npm:6.18.0" "@typescript-eslint/utils": "npm:6.19.0"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.0.1" ts-api-utils: "npm:^1.0.1"
peerDependencies: peerDependencies:
@@ -1757,23 +1757,23 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 9f4a392fe7b7f6b1fb02acbdaa331e764775f6404c29ca66774419e39552523d878227020526ca9c438d997555a99600f8d711496e9a435fb14a779e25ed094e checksum: f1f20ac28c03dd18546050b63ec0b0fd8c67780265ccb9ef566f16441c3de5deb2607a6046fefdebe8a43ac11fecdf0b009f8e5f70a3d15916d855be74b0f3bb
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/types@npm:6.18.0": "@typescript-eslint/types@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/types@npm:6.18.0" resolution: "@typescript-eslint/types@npm:6.19.0"
checksum: fc507ca7a1bfec04467087165ff6722f7b9aa9a089ecf0c17656824a951b92ca014766e480122de850057c63a3066628985eb0681c5bbb80ab41d94e7bb52288 checksum: 396ad2ad9f2d759dd87bc880a1ffc9d11fda04db8af9402abb4e8eccd58c01fa2d26e38b186526d0b457012f7c912e7afdab2a3798a73aa0ae34abaf50d617ae
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/typescript-estree@npm:6.18.0": "@typescript-eslint/typescript-estree@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/typescript-estree@npm:6.18.0" resolution: "@typescript-eslint/typescript-estree@npm:6.19.0"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.18.0" "@typescript-eslint/types": "npm:6.19.0"
"@typescript-eslint/visitor-keys": "npm:6.18.0" "@typescript-eslint/visitor-keys": "npm:6.19.0"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
globby: "npm:^11.1.0" globby: "npm:^11.1.0"
is-glob: "npm:^4.0.3" is-glob: "npm:^4.0.3"
@@ -1783,34 +1783,34 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: b65392e944baba97ed98e99a1e7122b7b05fa0d9a082b48d0190b377ae9e2ae4ed72d505a2f0f05f2ca78908f0e4b0f1acd44d345c7f4f4415fcec6bb2c57791 checksum: 06e24bb145a302299a6cf86b36652bd4d7080c4e88517ebc24bdc137c57425a68db256ba628ce16b568bfec8020ae2a748ccee93e304efeded329cb3292b17bf
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/utils@npm:6.18.0": "@typescript-eslint/utils@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/utils@npm:6.18.0" resolution: "@typescript-eslint/utils@npm:6.19.0"
dependencies: dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0" "@eslint-community/eslint-utils": "npm:^4.4.0"
"@types/json-schema": "npm:^7.0.12" "@types/json-schema": "npm:^7.0.12"
"@types/semver": "npm:^7.5.0" "@types/semver": "npm:^7.5.0"
"@typescript-eslint/scope-manager": "npm:6.18.0" "@typescript-eslint/scope-manager": "npm:6.19.0"
"@typescript-eslint/types": "npm:6.18.0" "@typescript-eslint/types": "npm:6.19.0"
"@typescript-eslint/typescript-estree": "npm:6.18.0" "@typescript-eslint/typescript-estree": "npm:6.19.0"
semver: "npm:^7.5.4" semver: "npm:^7.5.4"
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0
checksum: f91798069e337ed42b7e415aabded833d540dab9adf66ae88183003428892584946f20a5496aae2dc65fc0f65b66496baed785a3470ee782b6e2dd25b9200c6c checksum: 4080c36331204ffef9f218e29f43da767f17551fa4d3877c3d3b49194f7c7382dd9ae2124e7b5ebd47d5556946bb6ad195b47d7d215553efabacdebf81b9e74d
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/visitor-keys@npm:6.18.0": "@typescript-eslint/visitor-keys@npm:6.19.0":
version: 6.18.0 version: 6.19.0
resolution: "@typescript-eslint/visitor-keys@npm:6.18.0" resolution: "@typescript-eslint/visitor-keys@npm:6.19.0"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.18.0" "@typescript-eslint/types": "npm:6.19.0"
eslint-visitor-keys: "npm:^3.4.1" eslint-visitor-keys: "npm:^3.4.1"
checksum: bf34a357549d515607c761f385b7c7c82aaa07795cada0be2e1e3860c5103fbf731408ac07eaeb0517b51426d77ef9b194dfb94f205c776107a46e0d0027b452 checksum: 8d51c0b8d94c5df044fde958f62741cef55be97c6a3a16c47e4df9af7b2ff13aa1ee03ca5240777481dca53f3b7a9b00b329e50aff5e3ad829d96bc5f63ca2c3
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1825,24 +1825,24 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "EMS-ESP@workspace:." resolution: "EMS-ESP@workspace:."
dependencies: dependencies:
"@alova/adapter-xhr": "npm:^1.0.2" "@alova/adapter-xhr": "npm:^1.0.3"
"@babel/core": "npm:^7.23.7" "@babel/core": "npm:^7.23.7"
"@emotion/react": "npm:^11.11.3" "@emotion/react": "npm:^11.11.3"
"@emotion/styled": "npm:^11.11.0" "@emotion/styled": "npm:^11.11.0"
"@mui/icons-material": "npm:^5.15.3" "@mui/icons-material": "npm:^5.15.5"
"@mui/material": "npm:^5.15.3" "@mui/material": "npm:^5.15.5"
"@preact/compat": "npm:^17.1.2" "@preact/compat": "npm:^17.1.2"
"@preact/preset-vite": "npm:^2.8.1" "@preact/preset-vite": "npm:^2.8.1"
"@table-library/react-table-library": "npm:4.1.7" "@table-library/react-table-library": "npm:4.1.7"
"@types/imagemin": "npm:^8.0.5" "@types/imagemin": "npm:^8.0.5"
"@types/lodash-es": "npm:^4.17.12" "@types/lodash-es": "npm:^4.17.12"
"@types/node": "npm:^20.10.6" "@types/node": "npm:^20.11.5"
"@types/react": "npm:^18.2.47" "@types/react": "npm:^18.2.48"
"@types/react-dom": "npm:^18.2.18" "@types/react-dom": "npm:^18.2.18"
"@types/react-router-dom": "npm:^5.3.3" "@types/react-router-dom": "npm:^5.3.3"
"@typescript-eslint/eslint-plugin": "npm:^6.18.0" "@typescript-eslint/eslint-plugin": "npm:^6.19.0"
"@typescript-eslint/parser": "npm:^6.18.0" "@typescript-eslint/parser": "npm:^6.19.0"
alova: "npm:^2.16.2" alova: "npm:^2.17.0"
async-validator: "npm:^4.2.5" async-validator: "npm:^4.2.5"
concurrently: "npm:^8.2.2" concurrently: "npm:^8.2.2"
eslint: "npm:^8.56.0" eslint: "npm:^8.56.0"
@@ -1861,21 +1861,21 @@ __metadata:
lodash-es: "npm:^4.17.21" lodash-es: "npm:^4.17.21"
mime-types: "npm:^2.1.35" mime-types: "npm:^2.1.35"
preact: "npm:^10.19.3" preact: "npm:^10.19.3"
prettier: "npm:^3.1.1" prettier: "npm:^3.2.4"
react: "npm:latest" react: "npm:latest"
react-dom: "npm:latest" react-dom: "npm:latest"
react-dropzone: "npm:^14.2.3" react-dropzone: "npm:^14.2.3"
react-icons: "npm:^4.12.0" react-icons: "npm:^5.0.1"
react-router-dom: "npm:^6.21.1" react-router-dom: "npm:^6.21.3"
react-toastify: "npm:^9.1.3" react-toastify: "npm:^10.0.3"
rollup-plugin-visualizer: "npm:^5.12.0" rollup-plugin-visualizer: "npm:^5.12.0"
sockette: "npm:^2.0.6" sockette: "npm:^2.0.6"
terser: "npm:^5.26.0" terser: "npm:^5.27.0"
typesafe-i18n: "npm:^5.26.2" typesafe-i18n: "npm:^5.26.2"
typescript: "npm:^5.3.3" typescript: "npm:^5.3.3"
vite: "npm:^5.0.11" vite: "npm:^5.0.12"
vite-plugin-imagemin: "npm:^0.6.1" vite-plugin-imagemin: "npm:^0.6.1"
vite-tsconfig-paths: "npm:^4.2.3" vite-tsconfig-paths: "npm:^4.3.1"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@@ -1944,10 +1944,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"alova@npm:^2.16.2": "alova@npm:^2.17.0":
version: 2.16.2 version: 2.17.0
resolution: "alova@npm:2.16.2" resolution: "alova@npm:2.17.0"
checksum: 06fafddf380d4d8e8e5dd172ebcaa0bc229c76c11b2675cfb2c0ab884a36d4818159267adb14ec7a3cbe681464793085b0386d7741e6a6a732c764b14c8783a8 checksum: ff3bda492ac7dc8665403293644736ab90d7989a8479cfb2fa7fcab8fdb6e92b755851e5bcae07f55f5a5170c66c6486f047d19efb8ca39b6b3298717c3f50d7
languageName: node languageName: node
linkType: hard linkType: hard
@@ -2617,17 +2617,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"clsx@npm:^1.1.1": "clsx@npm:^2.1.0":
version: 1.2.1 version: 2.1.0
resolution: "clsx@npm:1.2.1" resolution: "clsx@npm:2.1.0"
checksum: 5ded6f61f15f1fa0350e691ccec43a28b12fb8e64c8e94715f2a937bc3722d4c3ed41d6e945c971fc4dcc2a7213a43323beaf2e1c28654af63ba70c9968a8643 checksum: 2e0ce7c3b6803d74fc8147c408f88e79245583202ac14abd9691e2aebb9f312de44270b79154320d10bb7804a9197869635d1291741084826cff20820f31542b
languageName: node
linkType: hard
"clsx@npm:^2.0.0":
version: 2.0.0
resolution: "clsx@npm:2.0.0"
checksum: 943766d1b02fee3538c871e56638d87f973fbc2d6291ce221215ea436fdecb9be97ad323f411839c2d52c45640c449b1a53fbfe7e8b3d529b4e263308b630c9a
languageName: node languageName: node
linkType: hard linkType: hard
@@ -7091,12 +7084,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"prettier@npm:^3.1.1": "prettier@npm:^3.2.4":
version: 3.1.1 version: 3.2.4
resolution: "prettier@npm:3.1.1" resolution: "prettier@npm:3.2.4"
bin: bin:
prettier: bin/prettier.cjs prettier: bin/prettier.cjs
checksum: 26a249f321b97d26c04483f1bf2eeb22e082a76f4222a2c922bebdc60111691aad4ec3979610e83942e0b956058ec361d9e9c81c185172264eb6db9aa678082b checksum: e2b735d0552501b3a7ac8bd3ba3b6de2920bb35bd4cd02d08cb9057ebe3e96d83b9a7e4b903d987b7530a50223b12c74d107c154337236ae2c68156ba1e65cd2
languageName: node languageName: node
linkType: hard linkType: hard
@@ -7202,12 +7195,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-icons@npm:^4.12.0": "react-icons@npm:^5.0.1":
version: 4.12.0 version: 5.0.1
resolution: "react-icons@npm:4.12.0" resolution: "react-icons@npm:5.0.1"
peerDependencies: peerDependencies:
react: "*" react: "*"
checksum: 5cc20509ca0b182f1e7bf42c271846c48f688c8922e2439f48728805adc93ba18476a13588cbe91ee43a2d03b2787e0dc0b5cc4b9c4e4ae3426f4464b3c1b734 checksum: c4458c643ae32a793ddebc5fa1235c7ec051be1b131205510e8199d15a4c89221a501f95a71fa21c2da93e8dd225290e2e24bb80abd3fb85801e43009e692098
languageName: node languageName: node
linkType: hard linkType: hard
@@ -7225,39 +7218,39 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-router-dom@npm:^6.21.1": "react-router-dom@npm:^6.21.3":
version: 6.21.1 version: 6.21.3
resolution: "react-router-dom@npm:6.21.1" resolution: "react-router-dom@npm:6.21.3"
dependencies: dependencies:
"@remix-run/router": "npm:1.14.1" "@remix-run/router": "npm:1.14.2"
react-router: "npm:6.21.1" react-router: "npm:6.21.3"
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
react-dom: ">=16.8" react-dom: ">=16.8"
checksum: 2d75bd889828fa5516ad076b44506656d826c365645e7079138cd0ef899db28a1b212f708a6c6e3b543ae11b96b2031f01201cc2fe1733dd4d9c5cbdd4d734ef checksum: 6e23e35d02e5c83847c8e47d7912d1f6c2c42a35f2317802031bdd993a8205468138a045ff34f67fe807fe9f7dc9d0995ee05bab25aedc0bf978e620ac132815
languageName: node languageName: node
linkType: hard linkType: hard
"react-router@npm:6.21.1": "react-router@npm:6.21.3":
version: 6.21.1 version: 6.21.3
resolution: "react-router@npm:6.21.1" resolution: "react-router@npm:6.21.3"
dependencies: dependencies:
"@remix-run/router": "npm:1.14.1" "@remix-run/router": "npm:1.14.2"
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
checksum: 1220cc75e0c915a26dde9dbb6509a8f0b0163d96e5ad591af91d9bb5a92a18401718f8d872a03d1cb366e7a6216c165a5cadd12375adf97943f37d7f5c487a90 checksum: 3d5107cfdb440519d84e6ad6d95454e3bf41ec97677b95f7b2a7f281f8ddf191b765cf1b599ead951f3cd33ed4429f140590d74a01cfdf835dc2f812023a978a
languageName: node languageName: node
linkType: hard linkType: hard
"react-toastify@npm:^9.1.3": "react-toastify@npm:^10.0.3":
version: 9.1.3 version: 10.0.3
resolution: "react-toastify@npm:9.1.3" resolution: "react-toastify@npm:10.0.3"
dependencies: dependencies:
clsx: "npm:^1.1.1" clsx: "npm:^2.1.0"
peerDependencies: peerDependencies:
react: ">=16" react: ">=16"
react-dom: ">=16" react-dom: ">=16"
checksum: 12667aa10e6cf3f74be2e3c704c2d5570dd7de66fff89ae38fbfab1122e9a9f632de1cb712fe44a9a60b8ecca7590578157cb4ca6c4e8105a8cf80936a94e181 checksum: 3c9e9cebef41cff7ea60528d1ca01f03feed98a9bba10bd0749a17d7627fa5e4719b2f1d28dee22c9f9a66df2d9ddf906e180f3f9771607e16d96c889f1bf484
languageName: node languageName: node
linkType: hard linkType: hard
@@ -8345,9 +8338,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"terser@npm:^5.26.0": "terser@npm:^5.27.0":
version: 5.26.0 version: 5.27.0
resolution: "terser@npm:5.26.0" resolution: "terser@npm:5.27.0"
dependencies: dependencies:
"@jridgewell/source-map": "npm:^0.3.3" "@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2" acorn: "npm:^8.8.2"
@@ -8355,7 +8348,7 @@ __metadata:
source-map-support: "npm:~0.5.20" source-map-support: "npm:~0.5.20"
bin: bin:
terser: bin/terser terser: bin/terser
checksum: 0282c5c065cbfa1e725d5609b99579252bc20b83cd1d75e8ab8b46d5da2c9d0fcfc453a12624f2d2d4c1240bfa0017a90fcf1e3b88258e5842fca1b0b82be8d8 checksum: 9b2c5cb00747dea5994034ca064fb3cc7efc1be6b79a35247662d51ab43bdbe9cbf002bbf29170b5f3bd068c811d0212e22d94acd2cf0d8562687b96f1bffc9f
languageName: node languageName: node
linkType: hard linkType: hard
@@ -8444,17 +8437,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tsconfck@npm:^2.1.0": "tsconfck@npm:^3.0.1":
version: 2.1.2 version: 3.0.1
resolution: "tsconfck@npm:2.1.2" resolution: "tsconfck@npm:3.0.1"
peerDependencies: peerDependencies:
typescript: ^4.3.5 || ^5.0.0 typescript: ^5.0.0
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
bin: bin:
tsconfck: bin/tsconfck.js tsconfck: bin/tsconfck.js
checksum: 61df3b03b334a25eabb0a52e67a0c8d85770c631f2739db7703af8fdd102a2ebd598f1c851cc5fc6d6a59f2497a26c845be71c934ea16d838a3ff95a885034fb checksum: c5317404e2a809af31ad093f82365518a5856b2f342371991f729f42cab0def1b87dca8d22df3fb8c82acda7248710d4fb5030270db024c8000bc8272a3e6d58
languageName: node languageName: node
linkType: hard linkType: hard
@@ -8755,25 +8748,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vite-tsconfig-paths@npm:^4.2.3": "vite-tsconfig-paths@npm:^4.3.1":
version: 4.2.3 version: 4.3.1
resolution: "vite-tsconfig-paths@npm:4.2.3" resolution: "vite-tsconfig-paths@npm:4.3.1"
dependencies: dependencies:
debug: "npm:^4.1.1" debug: "npm:^4.1.1"
globrex: "npm:^0.1.2" globrex: "npm:^0.1.2"
tsconfck: "npm:^2.1.0" tsconfck: "npm:^3.0.1"
peerDependencies: peerDependencies:
vite: "*" vite: "*"
peerDependenciesMeta: peerDependenciesMeta:
vite: vite:
optional: true optional: true
checksum: ba6abe5d18fc1c1e494e1f1d8a7db56445c2a40e15aadb5d47a9c66cc5372d6f69b94ff0b1e47b67659d6ecaeddebab0a9d11e40b1c3c36c0115800736a6c760 checksum: 1432f80750f5cbe181c265eb9fc2e9fff8b25a2858f176dc0a02311e3e826333526ee9c16bb0aaaa8555a417ea944d68a2e8225181215cd9502370f913eb3f79
languageName: node languageName: node
linkType: hard linkType: hard
"vite@npm:^5.0.11": "vite@npm:^5.0.12":
version: 5.0.11 version: 5.0.12
resolution: "vite@npm:5.0.11" resolution: "vite@npm:5.0.12"
dependencies: dependencies:
esbuild: "npm:^0.19.3" esbuild: "npm:^0.19.3"
fsevents: "npm:~2.3.3" fsevents: "npm:~2.3.3"
@@ -8807,7 +8800,7 @@ __metadata:
optional: true optional: true
bin: bin:
vite: bin/vite.js vite: bin/vite.js
checksum: f1a8fea35ed9f162d7a10fd13efb2c96637028b0a319d726aeec8b31e20e4d047272bda5df82167618e7774a520236c66f3093ed172802660aec5227814072f4 checksum: ed0bb26a0d0c8e1dae0b70af9e36adffd7e15d80297443fe4da762596dc81570bad7f0291f590a57c1553f5e435338d8c7ffc483bd9431a95c09d9ac90665fad
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -1,12 +0,0 @@
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Google
Standard: c++11
AllowShortFunctionsOnASingleLine: Empty
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash
DerivePointerAlignment: false
# Always break after if to get accurate coverage
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false

View File

@@ -1 +0,0 @@
*.md

View File

@@ -1,6 +1,18 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
v7.0.2 (2024-01-19)
------
* Fix assertion `poolIndex < count_` after `JsonDocument::clear()` (issue #2034)
v7.0.1 (2024-01-10)
------
* Fix "no matching function" with `JsonObjectConst::operator[]` (issue #2019)
* Remove unused files in the PlatformIO package
* Fix `volatile bool` serialized as `1` or `0` instead of `true` or `false` (issue #2029)
v7.0.0 (2024-01-03) v7.0.0 (2024-01-03)
------ ------

View File

@@ -0,0 +1,25 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
cmake_minimum_required(VERSION 3.15)
if(ESP_PLATFORM)
# Build ArduinoJson as an ESP-IDF component
idf_component_register(INCLUDE_DIRS src)
return()
endif()
project(ArduinoJson VERSION 7.0.2)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
add_subdirectory(src)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
include(extras/CompileOptions.cmake)
add_subdirectory(extras/tests)
add_subdirectory(extras/fuzzing)
endif()

View File

@@ -1,10 +0,0 @@
# Contribution to ArduinoJson
First, thank you for taking the time to contribute to this project.
You can submit changes via GitHub Pull Requests.
Please:
1. Update the test suite for any change of behavior
2. Use clang-format in "file" mode to format the code

View File

@@ -1,155 +0,0 @@
<p align="center">
<a href="https://arduinojson.org/"><img alt="ArduinoJson" src="https://arduinojson.org/images/logo.svg" width="200" /></a>
</p>
---
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=7.x&logo=github)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A7.x)
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/7.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
[![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/7.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)
ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
## Features
* [JSON deserialization](https://arduinojson.org/v7/api/json/deserializejson/)
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v7/api/config/decode_unicode/)
* [Optionally supports comments in the input](https://arduinojson.org/v7/api/config/enable_comments/)
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v7/api/json/deserializejson/#filtering)
* Supports single quotes as a string delimiter
* Compatible with [NDJSON](http://ndjson.org/) and [JSON Lines](https://jsonlines.org/)
* [JSON serialization](https://arduinojson.org/v7/api/json/serializejson/)
* [Can write to a buffer or a stream](https://arduinojson.org/v7/api/json/serializejson/)
* [Optionally indents the document (prettified JSON)](https://arduinojson.org/v7/api/json/serializejsonpretty/)
* [MessagePack serialization](https://arduinojson.org/v7/api/msgpack/serializemsgpack/)
* [MessagePack deserialization](https://arduinojson.org/v7/api/msgpack/deserializemsgpack/)
* Efficient
* [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Deduplicates strings](https://arduinojson.org/news/2020/08/01/version-6-16-0/)
* Versatile
* Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v7/how-to/use-external-ram-on-esp32/)
* Supports [`String`](https://arduinojson.org/v7/api/config/enable_arduino_string/), [`std::string`](https://arduinojson.org/v7/api/config/enable_std_string/), and [`std::string_view`](https://arduinojson.org/v7/api/config/enable_string_view/)
* Supports [`Stream`](https://arduinojson.org/v7/api/config/enable_arduino_stream/) and [`std::istream`/`std::ostream`](https://arduinojson.org/v7/api/config/enable_std_stream/)
* Supports [Flash strings](https://arduinojson.org/v7/api/config/enable_progmem/)
* Supports [custom readers](https://arduinojson.org/v7/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v7/api/json/serializejson/#custom-writer)
* Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/)
* Portable
* Usable on any C++ project (not limited to Arduino)
* Compatible with C++11, C++14 and C++17
* Support for C++98/C++03 available on [ArduinoJson 6.20.x](https://github.com/bblanchon/ArduinoJson/tree/6.20.x)
* Zero warnings with `-Wall -Wextra -pedantic` and `/W4`
* [Header-only library](https://en.wikipedia.org/wiki/Header-only)
* Works with virtually any board
* Arduino boards: [Uno](https://amzn.to/38aL2ik), [Due](https://amzn.to/36YkWi2), [Micro](https://amzn.to/35WkdwG), [Nano](https://amzn.to/2QTvwRX), [Mega](https://amzn.to/36XWhuf), [Yun](https://amzn.to/30odURc), [Leonardo](https://amzn.to/36XWjlR)...
* Espressif chips: [ESP8266](https://amzn.to/36YluV8), [ESP32](https://amzn.to/2G4pRCB)
* Lolin (WeMos) boards: [D1 mini](https://amzn.to/2QUpz7q), [D1 Mini Pro](https://amzn.to/36UsGSs)...
* Teensy boards: [4.0](https://amzn.to/30ljXGq), [3.2](https://amzn.to/2FT0EuC), [2.0](https://amzn.to/2QXUMXj)
* Particle boards: [Argon](https://amzn.to/2FQHa9X), [Boron](https://amzn.to/36WgLUd), [Electron](https://amzn.to/30vEc4k), [Photon](https://amzn.to/387F9Cd)...
* Texas Instruments boards: [MSP430](https://amzn.to/30nJWgg)...
* Soft cores: [Nios II](https://en.wikipedia.org/wiki/Nios_II)...
* Tested on all major development environments
* [Arduino IDE](https://www.arduino.cc/en/Main/Software)
* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/)
* [Atollic TrueSTUDIO](https://atollic.com/truestudio/)
* [Energia](http://energia.nu/)
* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/)
* [Keil uVision](http://www.keil.com/)
* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide)
* [Particle](https://www.particle.io/)
* [PlatformIO](http://platformio.org/)
* [Sloeber plugin for Eclipse](https://eclipse.baeyens.it/)
* [Visual Micro](http://www.visualmicro.com/)
* [Visual Studio](https://www.visualstudio.com/)
* [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/RlZSKy17DjJ6HcdN)
* [CMake friendly](https://arduinojson.org/v7/how-to/use-arduinojson-with-cmake/)
* Well designed
* [Elegant API](http://arduinojson.org/v7/example/)
* [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety)
* Self-contained (no external dependency)
* `const` friendly
* [`for` friendly](https://arduinojson.org/v7/api/jsonobject/begin_end/)
* [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming)
* Handles [integer overflows](https://arduinojson.org/v7/api/jsonvariant/as/#integer-overflows)
* Well tested
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
* Continuously tested on
* [Visual Studio 2017, 2019, 2022](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
* [GCC 5, 6, 7, 8, 9, 10, 11, 12](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Clang 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10, 11, 12, 13, 14, 15](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
* Well documented
* [Tutorials](https://arduinojson.org/v7/doc/deserialization/)
* [Examples](https://arduinojson.org/v7/example/)
* [How-tos](https://arduinojson.org/v7/example/)
* [FAQ](https://arduinojson.org/v7/faq/)
* [Troubleshooter](https://arduinojson.org/v7/troubleshooter/)
* [Book](https://arduinojson.org/book/)
* [Changelog](CHANGELOG.md)
* Vibrant user community
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
## Quickstart
### Deserialization
Here is a program that parses a JSON document with ArduinoJson.
```c++
const char* json = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
JsonDocument doc;
deserializeJson(doc, json);
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/deserialization/)
### Serialization
Here is a program that generates a JSON document with ArduinoJson:
```c++
JsonDocument doc;
doc["sensor"] = "gps";
doc["time"] = 1351824120;
doc["data"][0] = 48.756080;
doc["data"][1] = 2.302038;
serializeJson(doc, Serial);
// This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/serialization/)
## Sponsors
ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve it!
<p>
<a href="https://www.programmingelectronics.com/" rel="sponsored">
<img src="https://arduinojson.org/images/2021/10/programmingeleactronicsacademy.png" alt="Programming Electronics Academy" width="200">
</a>
</p>
<p>
<a href="https://github.com/1technophile" rel="sponsored">
<img alt="1technophile" src="https://avatars.githubusercontent.com/u/12672732?s=40&v=4">
</a>
</p>
If you run a commercial project that embeds ArduinoJson, think about [sponsoring the library's development](https://github.com/sponsors/bblanchon): it ensures the code that your products rely on stays actively maintained. It can also give your project some exposure to the makers' community.
If you are an individual user and want to support the development (or give a sign of appreciation), consider purchasing the book [Mastering ArduinoJson](https://arduinojson.org/book/)&nbsp;❤, or simply [cast a star](https://github.com/bblanchon/ArduinoJson/stargazers)&nbsp;⭐.

View File

@@ -1,27 +0,0 @@
# ArduinoJson Support
First off, thank you very much for using ArduinoJson.
We'll be very happy to help you, but first please read the following.
## Before asking for help
1. Read the [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=support)
2. Search in the [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=support)
If you did not find the answer, please create a [new issue on GitHub](https://github.com/bblanchon/ArduinoJson/issues/new).
It is OK to add a comment to a currently opened issue, but please avoid adding comments to a closed issue.
## Before hitting the Submit button
Please provide all the relevant information:
* Good title
* Short description of the problem
* Target platform
* Compiler model and version
* [MVCE](https://stackoverflow.com/help/mcve)
* Compiler output
Good questions get fast answers!

View File

@@ -0,0 +1,13 @@
version: "7.0.2"
description: >-
A simple and efficient JSON library for embedded C++.
⭐ 6444 stars on GitHub!
Supports serialization, deserialization, MessagePack, streams, filtering, and more.
Fully tested and documented.
url: https://arduinojson.org/
files:
exclude:
- "**/.vs/**/*"
- ".devcontainer/**/*"
- "examples/**/*"
- "extras/**/*"

View File

@@ -0,0 +1,23 @@
{
"name": "ArduinoJson",
"keywords": "json, rest, http, web",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6444 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
"repository": {
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "7.0.2",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"
},
"export": {
"include": ["src", "examples", "LICENSE.txt", "ArduinoJson.h"]
},
"frameworks": "*",
"platforms": "*",
"build": {
"libArchive": false
}
}

View File

@@ -0,0 +1,11 @@
name=ArduinoJson
version=7.0.2
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.
paragraph=⭐ 6444 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
category=Data Processing
url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
architectures=*
repository=https://github.com/bblanchon/ArduinoJson.git
license=MIT

View File

@@ -51,7 +51,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
} }
operator JsonVariantConst() const { operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_); return JsonVariantConst(getData(), resources_);
} }
// Returns true if the reference is unbound. // Returns true if the reference is unbound.
@@ -69,7 +69,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns the depth (nesting level) of the array. // Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/nesting/ // https://arduinojson.org/v7/api/jsonarrayconst/nesting/
FORCE_INLINE size_t nesting() const { FORCE_INLINE size_t nesting() const {
return detail::VariantData::nesting(collectionToVariant(data_), resources_); return detail::VariantData::nesting(getData(), resources_);
} }
// Returns the number of elements in the array. // Returns the number of elements in the array.

View File

@@ -65,7 +65,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
} }
// Reduces the capacity of the memory pool to match the current usage. // Reduces the capacity of the memory pool to match the current usage.
// https://arduinojson.org/v7/api/JsonDocument/shrinktofit/ // https://arduinojson.org/v7/api/jsondocument/shrinktofit/
void shrinkToFit() { void shrinkToFit() {
resources_.shrinkToFit(); resources_.shrinkToFit();
} }

View File

@@ -103,6 +103,7 @@ class VariantPoolList {
for (PoolCount i = 0; i < count_; i++) for (PoolCount i = 0; i < count_; i++)
pools_[i].destroy(allocator); pools_[i].destroy(allocator);
count_ = 0; count_ = 0;
freeList_ = NULL_SLOT;
if (pools_ != preallocatedPools_) { if (pools_ != preallocatedPools_) {
allocator->deallocate(pools_); allocator->deallocate(pools_);
pools_ = preallocatedPools_; pools_ = preallocatedPools_;

View File

@@ -27,7 +27,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
: data_(data), resources_(resources) {} : data_(data), resources_(resources) {}
operator JsonVariantConst() const { operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_); return JsonVariantConst(getData(), resources_);
} }
// Returns true if the reference is unbound. // Returns true if the reference is unbound.
@@ -45,7 +45,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// Returns the depth (nesting level) of the object. // Returns the depth (nesting level) of the object.
// https://arduinojson.org/v7/api/jsonobjectconst/nesting/ // https://arduinojson.org/v7/api/jsonobjectconst/nesting/
FORCE_INLINE size_t nesting() const { FORCE_INLINE size_t nesting() const {
return detail::VariantData::nesting(collectionToVariant(data_), resources_); return detail::VariantData::nesting(getData(), resources_);
} }
// Returns the number of members in the object. // Returns the number of members in the object.
@@ -101,8 +101,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value, FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
JsonVariantConst>::type JsonVariantConst>::type
operator[](TChar* key) const { operator[](TChar* key) const {
return JsonVariantConst( return JsonVariantConst(detail::ObjectData::getMember(
detail::ObjectData::getMember(data_, detail::adaptString(key))); data_, detail::adaptString(key), resources_),
resources_);
} }
// DEPRECATED: always returns zero // DEPRECATED: always returns zero

View File

@@ -132,7 +132,8 @@ VariantRefBase<TDerived>::operator[](const TString& key) const {
template <typename TDerived> template <typename TDerived>
template <typename T> template <typename T>
inline bool VariantRefBase<TDerived>::set(const T& value) const { inline bool VariantRefBase<TDerived>::set(const T& value) const {
Converter<T>::toJson(value, getOrCreateVariant()); Converter<typename detail::remove_cv<T>::type>::toJson(value,
getOrCreateVariant());
auto resources = getResourceManager(); auto resources = getResourceManager();
return resources && !resources->overflowed(); return resources && !resources->overflowed();
} }

View File

@@ -4,8 +4,8 @@
#pragma once #pragma once
#define ARDUINOJSON_VERSION "7.0.0" #define ARDUINOJSON_VERSION "7.0.2"
#define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MAJOR 7
#define ARDUINOJSON_VERSION_MINOR 0 #define ARDUINOJSON_VERSION_MINOR 0
#define ARDUINOJSON_VERSION_REVISION 0 #define ARDUINOJSON_VERSION_REVISION 2
#define ARDUINOJSON_VERSION_MACRO V700 #define ARDUINOJSON_VERSION_MACRO V702

View File

@@ -70,7 +70,7 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
~MsgpackAsyncJsonResponse() { ~MsgpackAsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
bool _sourceValid() const { bool _sourceValid() const {
@@ -114,7 +114,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
~AsyncJsonResponse() { ~AsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
bool _sourceValid() const { bool _sourceValid() const {
@@ -158,7 +158,7 @@ class PrettyAsyncJsonResponse : public AsyncJsonResponse {
} }
}; };
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler : public AsyncWebHandler { class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
private: private:

View File

@@ -25,7 +25,7 @@ void AuthenticationService::verifyAuthorization(AsyncWebServerRequest * request)
* Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in * Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in
* subsequent requests. * subsequent requests.
*/ */
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant & json) { void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
String username = json["username"]; String username = json["username"];
String password = json["password"]; String password = json["password"];

View File

@@ -21,7 +21,7 @@ class AuthenticationService {
AsyncCallbackJsonWebHandler _signInHandler; AsyncCallbackJsonWebHandler _signInHandler;
// endpoint functions // endpoint functions
void signIn(AsyncWebServerRequest * request, JsonVariant & json); void signIn(AsyncWebServerRequest * request, JsonVariant json);
void verifyAuthorization(AsyncWebServerRequest * request); void verifyAuthorization(AsyncWebServerRequest * request);
}; };

View File

@@ -54,7 +54,7 @@ void ESP8266React::begin() {
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
} }
DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname); // TODO use hostname DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname);
}); });
_apSettingsService.begin(); _apSettingsService.begin();
_ntpSettingsService.begin(); _ntpSettingsService.begin();

View File

@@ -13,77 +13,48 @@
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
template <class T> template <class T>
class HttpGetEndpoint { class HttpEndpoint {
public:
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN)
: _stateReader(stateReader)
, _statefulService(statefulService) {
server->on(servicePath.c_str(), HTTP_GET, securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, _1), authenticationPredicate));
}
HttpGetEndpoint(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath)
: _stateReader(stateReader)
, _statefulService(statefulService) {
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, _1));
}
protected:
JsonStateReader<T> _stateReader;
StatefulService<T> * _statefulService;
void fetchSettings(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false);
JsonObject jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
response->setLength();
request->send(response);
}
};
template <class T>
class HttpPostEndpoint {
public:
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _updateHandler(servicePath, securityManager->wrapCallback(std::bind(&HttpPostEndpoint::updateSettings, this, _1, _2), authenticationPredicate)) {
_updateHandler.setMethod(HTTP_POST);
server->addHandler(&_updateHandler);
}
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _updateHandler(servicePath, std::bind(&HttpPostEndpoint::updateSettings, this, _1, _2)) {
_updateHandler.setMethod(HTTP_POST);
server->addHandler(&_updateHandler);
}
protected: protected:
JsonStateReader<T> _stateReader; JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
AsyncCallbackJsonWebHandler _updateHandler;
void updateSettings(AsyncWebServerRequest * request, JsonVariant & json) { AsyncCallbackWebHandler * GEThandler;
AsyncCallbackJsonWebHandler * POSThandler;
public:
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService) {
// Create the GET and POST endpoints
// We can't use HTTP_ANY and process one a single endpoint due to the way the ESPAsyncWebServer library works
// Could also use server->on() but this is more efficient
// create the GET
GEThandler = new AsyncCallbackWebHandler();
GEThandler->setUri(servicePath);
GEThandler->setMethod(HTTP_GET);
GEThandler->onRequest(securityManager->wrapRequest(std::bind(&HttpEndpoint::fetchSettings, this, _1), authenticationPredicate));
server->addHandler(GEThandler);
// create the POST
POSThandler =
new AsyncCallbackJsonWebHandler(servicePath,
securityManager->wrapCallback(std::bind(&HttpEndpoint::updateSettings, this, _1, _2), authenticationPredicate));
POSThandler->setMethod(HTTP_POST);
server->addHandler(POSThandler);
}
protected:
// for POST
void updateSettings(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
request->send(400); request->send(400);
return; return;
@@ -105,29 +76,15 @@ class HttpPostEndpoint {
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
};
template <class T> // for GET
class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> { void fetchSettings(AsyncWebServerRequest * request) {
public: AsyncJsonResponse * response = new AsyncJsonResponse(false);
HttpEndpoint(JsonStateReader<T> stateReader, JsonObject jsonObject = response->getRoot().to<JsonObject>();
JsonStateUpdater<T> stateUpdater, _statefulService->read(jsonObject, _stateReader);
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, securityManager, authenticationPredicate)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate) {
}
HttpEndpoint(JsonStateReader<T> stateReader, response->setLength();
JsonStateUpdater<T> stateUpdater, request->send(response);
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath) {
} }
}; };

View File

@@ -61,7 +61,7 @@ void NTPSettingsService::configureNTP() {
} }
} }
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant & json) { void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
struct tm tm = {0}; struct tm tm = {0};
String timeLocal = json["local_time"]; String timeLocal = json["local_time"];

View File

@@ -67,7 +67,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
bool connected_ = false; bool connected_ = false;
void WiFiEvent(WiFiEvent_t event); void WiFiEvent(WiFiEvent_t event);
void configureNTP(); void configureNTP();
void configureTime(AsyncWebServerRequest * request, JsonVariant & json); void configureTime(AsyncWebServerRequest * request, JsonVariant json);
}; };
#endif #endif

View File

@@ -101,7 +101,7 @@ ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFu
} }
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) { ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) { return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant json) {
Authentication authentication = authenticateRequest(request); Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) { if (!predicate(authentication)) {
request->send(401); request->send(401);

View File

@@ -59,7 +59,7 @@ class PrettyAsyncJsonResponse {
~PrettyAsyncJsonResponse() { ~PrettyAsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
@@ -104,7 +104,7 @@ class MsgpackAsyncJsonResponse {
~MsgpackAsyncJsonResponse() { ~MsgpackAsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
@@ -150,7 +150,7 @@ class AsyncJsonResponse {
~AsyncJsonResponse() { ~AsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
@@ -177,7 +177,7 @@ class AsyncJsonResponse {
} }
}; };
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler : public AsyncWebHandler { class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
private: private:

View File

@@ -67,7 +67,7 @@ class HttpPostEndpoint {
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
void updateSettings(AsyncWebServerRequest * request, JsonVariant & json) { void updateSettings(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
return; return;
} }

View File

@@ -100,7 +100,7 @@ ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFu
} }
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) { ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) { return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant json) {
Authentication authentication = authenticateRequest(request); Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) { if (!predicate(authentication)) {
request->send(401); request->send(401);

View File

@@ -414,28 +414,35 @@ const signin = {
}; };
const generate_token = { token: '1234' }; const generate_token = { token: '1234' };
//
// EMS-ESP Project specific // EMS-ESP Project specific
//
const EMSESP_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'settings'; const EMSESP_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'settings';
const EMSESP_CORE_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'coreData'; const EMSESP_CORE_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'coreData';
const EMSESP_SENSOR_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'sensorData'; const EMSESP_SENSOR_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'sensorData';
const EMSESP_DEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'devices'; const EMSESP_DEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'devices';
const EMSESP_SCANDEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'scanDevices'; const EMSESP_SCANDEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'scanDevices';
// const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData/:id'; // const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData/:id';
// const EMSESP_DEVICEENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceEntities/:id'; // const EMSESP_DEVICEENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceEntities/:id';
const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData'; const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData';
const EMSESP_DEVICEENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceEntities'; const EMSESP_DEVICEENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceEntities';
const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'status'; const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'status';
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile'; const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile';
const EMSESP_WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue'; const EMSESP_WRITE_DEVICEVALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue';
const EMSESP_WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeTemperatureSensor'; const EMSESP_WRITE_TEMPSENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeTemperatureSensor';
const EMSESP_WRITE_ANALOG_ENDPOINT = REST_ENDPOINT_ROOT + 'writeAnalogSensor'; const EMSESP_WRITE_ANALOGSENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeAnalogSensor';
const EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customizationEntities'; const EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customizationEntities';
const EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'resetCustomizations'; const EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'resetCustomizations';
const EMSESP_WRITE_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule';
const EMSESP_WRITE_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities'; const EMSESP_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule';
const EMSESP_CUSTOMENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customEntities';
const EMSESP_GET_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'getSettings';
const EMSESP_GET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'getCustomizations';
const EMSESP_GET_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'getEntities';
const EMSESP_GET_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'getSchedule';
const EMSESP_SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info';
const emsesp_info = { const emsesp_info = {
System: { System: {
@@ -566,7 +573,7 @@ const emsesp_allvalues = {
'date/time': '10.12.2023 13:49', 'date/time': '10.12.2023 13:49',
'hc1 how hot lounge should be': 19, 'hc1 how hot lounge should be': 19,
'hc1 current room temp': 19.5, 'hc1 current room temp': 19.5,
'hc1 Discovery current room temperature': 'roomTemp', 'hc1 mqtt discovery current room temperature': 'roomTemp',
'hc1 mode': 'auto', 'hc1 mode': 'auto',
'hc1 manual temperature': 21.5, 'hc1 manual temperature': 21.5,
'hc1 temperature when mode is off': 7, 'hc1 temperature when mode is off': 7,
@@ -742,7 +749,7 @@ const emsesp_coredata = {
d: 1, d: 1,
p: 1, p: 1,
v: '', v: '',
e: 1 e: 2
} }
] ]
}; };
@@ -788,7 +795,7 @@ const status = {
// 1 - RC35 thermo // 1 - RC35 thermo
// 2 - RC20 thermo // 2 - RC20 thermo
// 3 - Buderus GB125 boiler // 3 - Buderus GB125 boiler
// 4 - RC100 themo // 4 - RC100 thermostat
// 5 - Mixer MM10 // 5 - Mixer MM10
// 6 - Solar SM10 // 6 - Solar SM10
// 7 - Nefit Trendline boiler // 7 - Nefit Trendline boiler
@@ -2048,6 +2055,11 @@ const emsesp_devicedata_99 = {
u: 1, u: 1,
id: '00boiler_flowtemp', id: '00boiler_flowtemp',
c: 'boiler_flowtemp' c: 'boiler_flowtemp'
},
{
v: 0,
u: 0,
id: '00wwExtra1'
} }
] ]
}; };
@@ -2058,6 +2070,7 @@ let emsesp_customentities = {
entities: [ entities: [
{ {
id: 0, id: 0,
ram: 0,
device_id: 8, device_id: 8,
type_id: 24, type_id: 24,
offset: 0, offset: 0,
@@ -2065,7 +2078,21 @@ let emsesp_customentities = {
name: 'boiler_flowtemp', name: 'boiler_flowtemp',
uom: 1, uom: 1,
value_type: 1, value_type: 1,
writeable: true writeable: true,
value: 30
},
{
id: 1,
ram: 0,
device_id: 16,
type_id: 797,
offset: 0,
factor: 1,
name: 'wwExtra1',
uom: 0,
value_type: 0,
writeable: false,
value: 0
} }
] ]
}; };
@@ -2345,6 +2372,7 @@ router
// SYSTEM // SYSTEM
router router
.get(SYSTEM_STATUS_ENDPOINT, () => new Response(JSON.stringify(system_status), { headers })) .get(SYSTEM_STATUS_ENDPOINT, () => new Response(JSON.stringify(system_status), { headers }))
.get(SECURITY_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(security_settings), { headers }))
.post(SECURITY_SETTINGS_ENDPOINT, async (request: any) => { .post(SECURITY_SETTINGS_ENDPOINT, async (request: any) => {
security_settings = await request.json(); security_settings = await request.json();
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
@@ -2458,15 +2486,16 @@ router
// EMS-ESP Project stuff // EMS-ESP Project stuff
// //
router router
.post(EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT, async (request: any) => {
return new Response('OK', { status: 200 }); // EMS-ESP Settings
})
.get(EMSESP_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(settings), { headers })) .get(EMSESP_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(settings), { headers }))
.post(EMSESP_SETTINGS_ENDPOINT, async (request: any) => { .post(EMSESP_SETTINGS_ENDPOINT, async (request: any) => {
settings = await request.json(); settings = await request.json();
return new Response('OK', { status: 200 }); // no restart needed return new Response('OK', { status: 200 }); // no restart needed
// return new Response('OK', { status: 205 }); // restart needed // return new Response('OK', { status: 205 }); // restart needed
}) })
// Device Dashboard Data
.get(EMSESP_CORE_DATA_ENDPOINT, () => new Response(JSON.stringify(emsesp_coredata), { headers })) .get(EMSESP_CORE_DATA_ENDPOINT, () => new Response(JSON.stringify(emsesp_coredata), { headers }))
.get(EMSESP_SENSOR_DATA_ENDPOINT, () => new Response(JSON.stringify(emsesp_sensordata), { headers })) .get(EMSESP_SENSOR_DATA_ENDPOINT, () => new Response(JSON.stringify(emsesp_sensordata), { headers }))
.get(EMSESP_DEVICES_ENDPOINT, () => new Response(JSON.stringify(emsesp_devices), { headers })) .get(EMSESP_DEVICES_ENDPOINT, () => new Response(JSON.stringify(emsesp_devices), { headers }))
@@ -2527,6 +2556,8 @@ router
return new Response(encoder.encode(emsesp_deviceentities_7), { headers }); return new Response(encoder.encode(emsesp_deviceentities_7), { headers });
} }
}) })
// Customization
.post(EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT, async (request: any) => { .post(EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT, async (request: any) => {
const content = await request.json(); const content = await request.json();
const id = content.id; const id = content.id;
@@ -2549,17 +2580,28 @@ router
} }
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
}) })
.post(EMSESP_WRITE_SCHEDULE_ENDPOINT, async (request: any) => { .post(EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT, async (request: any) => {
return new Response('OK', { status: 200 });
})
// Scheduler
.post(EMSESP_SCHEDULE_ENDPOINT, async (request: any) => {
const content = await request.json(); const content = await request.json();
emsesp_schedule = content; emsesp_schedule = content;
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
}) })
.post(EMSESP_WRITE_ENTITIES_ENDPOINT, async (request: any) => { .get(EMSESP_SCHEDULE_ENDPOINT, () => new Response(JSON.stringify(emsesp_schedule), { headers }))
// Custom Entities
.post(EMSESP_CUSTOMENTITIES_ENDPOINT, async (request: any) => {
const content = await request.json(); const content = await request.json();
emsesp_customentities = content; emsesp_customentities = content;
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
}) })
.post(EMSESP_WRITE_VALUE_ENDPOINT, async (request: any) => { .get(EMSESP_CUSTOMENTITIES_ENDPOINT, () => new Response(JSON.stringify(emsesp_customentities), { headers }))
// Device Dashboard
.post(EMSESP_WRITE_DEVICEVALUE_ENDPOINT, async (request: any) => {
const content = await request.json(); const content = await request.json();
const command = content.c; const command = content.c;
const value = content.v; const value = content.v;
@@ -2603,7 +2645,9 @@ router
await delay(1000); // wait to show spinner await delay(1000); // wait to show spinner
return new Response('OK', { status: 200 }); // or 400 for bad request return new Response('OK', { status: 200 }); // or 400 for bad request
}) })
.post(EMSESP_WRITE_SENSOR_ENDPOINT, async (request: any) => {
// Temperature & Analog Sensors
.post(EMSESP_WRITE_TEMPSENSOR_ENDPOINT, async (request: any) => {
const ts = await request.json(); const ts = await request.json();
var objIndex = emsesp_sensordata.ts.findIndex((obj) => obj.id == ts.id_str); var objIndex = emsesp_sensordata.ts.findIndex((obj) => obj.id == ts.id_str);
if (objIndex !== -1) { if (objIndex !== -1) {
@@ -2612,7 +2656,7 @@ router
} }
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
}) })
.post(EMSESP_WRITE_ANALOG_ENDPOINT, async (request: any) => { .post(EMSESP_WRITE_ANALOGSENSOR_ENDPOINT, async (request: any) => {
const as = await request.json(); const as = await request.json();
var objIndex = emsesp_sensordata.as.findIndex((obj) => obj.g == as.gpio); var objIndex = emsesp_sensordata.as.findIndex((obj) => obj.g == as.gpio);
if (objIndex === -1) { if (objIndex === -1) {
@@ -2645,6 +2689,8 @@ router
return new Response('OK', { status: 200 }); return new Response('OK', { status: 200 });
}) })
// Settings - board profile
.post(EMSESP_BOARDPROFILE_ENDPOINT, async (request: any) => { .post(EMSESP_BOARDPROFILE_ENDPOINT, async (request: any) => {
const content = await request.json(); const content = await request.json();
const board_profile = content.code; const board_profile = content.code;
@@ -2774,26 +2820,18 @@ router
} }
return new Response(JSON.stringify(data), { headers }); return new Response(JSON.stringify(data), { headers });
}); })
// API and calls // Download Settings
const SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info'; .get(EMSESP_GET_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers }))
const GET_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'getSettings'; .get(EMSESP_GET_CUSTOMIZATIONS_ENDPOINT, () => new Response(JSON.stringify(emsesp_deviceentities_1), { headers }))
const GET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'getCustomizations'; .get(EMSESP_GET_ENTITIES_ENDPOINT, () => new Response(JSON.stringify(emsesp_customentities), { headers }))
const GET_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'getEntities'; .get(EMSESP_GET_SCHEDULE_ENDPOINT, () => new Response(JSON.stringify(emsesp_schedule), { headers }));
const GET_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'getSchedule';
const SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule';
const ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customentities';
// API which are usually POST for security
router router
.post(SYSTEM_INFO_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers })) .post(EMSESP_SYSTEM_INFO_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers }))
.get(SYSTEM_INFO_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers })) .get(EMSESP_SYSTEM_INFO_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers }))
.get(GET_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(emsesp_info), { headers }))
.get(GET_CUSTOMIZATIONS_ENDPOINT, () => new Response(JSON.stringify(emsesp_deviceentities_1), { headers }))
.get(GET_ENTITIES_ENDPOINT, () => new Response(JSON.stringify(emsesp_customentities), { headers }))
.get(GET_SCHEDULE_ENDPOINT, () => new Response(JSON.stringify(emsesp_schedule), { headers }))
.get(SCHEDULE_ENDPOINT, () => new Response(JSON.stringify(emsesp_schedule), { headers }))
.get(ENTITIES_ENDPOINT, () => new Response(JSON.stringify(emsesp_customentities), { headers }))
.post(API_ENDPOINT_ROOT, async (request: any) => { .post(API_ENDPOINT_ROOT, async (request: any) => {
const data = await request.json(); const data = await request.json();
if (data.device === 'system') { if (data.device === 'system') {
@@ -2807,7 +2845,9 @@ router
return new Response('Not Found', { status: 404 }); return new Response('Not Found', { status: 404 });
}); });
//
// Event Source // TODO fix event source later // Event Source // TODO fix event source later
//
// const data = { // const data = {
// t: '000+00:00:00.000', // t: '000+00:00:00.000',

View File

@@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"@msgpack/msgpack": "^2.8.0", "@msgpack/msgpack": "^2.8.0",
"busboy": "^1.6.0", "busboy": "^1.6.0",
"itty-router": "^4.0.26" "itty-router": "^4.0.27"
}, },
"packageManager": "yarn@4.0.2", "packageManager": "yarn@4.0.2",
"devDependencies": { "devDependencies": {

View File

@@ -129,7 +129,7 @@ __metadata:
"@msgpack/msgpack": "npm:^2.8.0" "@msgpack/msgpack": "npm:^2.8.0"
"@types/multer": "npm:^1.4.11" "@types/multer": "npm:^1.4.11"
busboy: "npm:^1.6.0" busboy: "npm:^1.6.0"
itty-router: "npm:^4.0.26" itty-router: "npm:^4.0.27"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@@ -142,10 +142,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"itty-router@npm:^4.0.26": "itty-router@npm:^4.0.27":
version: 4.0.26 version: 4.0.27
resolution: "itty-router@npm:4.0.26" resolution: "itty-router@npm:4.0.27"
checksum: 6bd64bdd89a3508e7dd806fafcd86e0887daf306a83149d34c12443de0e518da83f76598f02caff375120aae8e40eae2234c9885e0bb3c3aaa0273a807eb4dd2 checksum: ebb959388b1033f3d80ba2575c2d90fa649c1d5370d977879513cc46e8fd78159b7140d2a66853af6be98f7d740f8609a2c5aa7381506eaa1f1a46268fd2a95f
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -20,7 +20,7 @@
; default_envs = lolin_s3 ; default_envs = lolin_s3
; default_envs = standalone ; default_envs = standalone
; default_envs = debug ; default_envs = debug
default_envs = custom ; default_envs = custom
[env:esp32_4M] [env:esp32_4M]
; if using OTA enter your details below ; if using OTA enter your details below
@@ -29,16 +29,19 @@ default_envs = custom
; --port=8266 ; --port=8266
; --auth=ems-esp-neo ; --auth=ems-esp-neo
; upload_port = ems-esp.local ; upload_port = ems-esp.local
; for USB use one of these: ; for USB, here are some examples:
; upload_port = /dev/ttyUSB* ; upload_port = /dev/ttyUSB*
; upload_port = COM5 ; upload_port = COM5
extra_scripts =
; pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
scripts/rename_fw.py
[env:esp32_16M] [env:esp32_16M]
[env:custom] [env:custom]
; use for ESP-S boards with 4MB flash ; use for baisc ESP boards with 4MB flash
; make sure -D TASMOTA_SDK is also enabled ; make sure -D TASMOTA_SDK is also enabled
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.01.00/platform-espressif32.zip
; use for S3 boards: ; use for S3 boards:
; platform = espressif32 ; platform = espressif32
framework = arduino framework = arduino
@@ -68,13 +71,13 @@ build_flags =
-D EMSESP_TEST -D EMSESP_TEST
-D EMSESP_DEBUG -D EMSESP_DEBUG
-D CONFIG_ETH_ENABLED -D CONFIG_ETH_ENABLED
; -D TASMOTA_SDK -D TASMOTA_SDK
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"' '-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
[env:lolin_s3] [env:lolin_s3]
upload_port = /dev/ttyACM0 upload_port = /dev/ttyUSB0
extra_scripts = extra_scripts =
pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time ; pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
scripts/rename_fw.py scripts/rename_fw.py
; pio run -e debug ; pio run -e debug

View File

@@ -2,8 +2,8 @@
; override any settings with your own local ones in pio_local.ini ; override any settings with your own local ones in pio_local.ini
[platformio] [platformio]
; default_envs = esp32_4M default_envs = esp32_4M
default_envs = lolin_s3 ; default_envs = lolin_s3
; default_envs = esp32_16M ; default_envs = esp32_16M
; default_envs = standalone ; default_envs = standalone
@@ -50,11 +50,9 @@ extra_scripts =
[espressi32_base_tasmota] [espressi32_base_tasmota]
; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap ; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap
; platform = https://github.com/tasmota/platform-espressif32.git ; latest development ; platform = https://github.com/tasmota/platform-espressif32.git ; latest development
; latest release with WiFi_secure.h ; latest release with WiFi_secure.h, Arduino 2.0.14
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip ; latest stable platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.01.00/platform-espressif32.zip
; latest arduino 2.xx release: ; latest Arduino 3.0/IDF 5.1.(alpha 3):
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.11.01/platform-espressif32.zip
; latest arduino 3.0/IDF 5.1.(alpha 3):
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.12.10/platform-espressif32.zip ; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.12.10/platform-espressif32.zip
framework = arduino framework = arduino
board_build.filesystem = littlefs board_build.filesystem = littlefs

BIN
scripts/bootloader.bin Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -679,7 +679,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
// if we're filtering on an attribute, go find it // if we're filtering on an attribute, go find it
if (attribute_s) { if (attribute_s) {
if (output.containsKey(attribute_s)) { if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s]; String data = output[attribute_s].as<String>();
output.clear(); output.clear();
output["api_data"] = data; output["api_data"] = data;
return true; return true;

View File

@@ -128,14 +128,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
commands->add_command(ShellContext::MAIN, commands->add_command(ShellContext::MAIN,
CommandFlags::USER, CommandFlags::USER,
string_vector{"test"}, string_vector{"test"},
string_vector{F_(name_optional), F_(data_optional)}, string_vector{F_(name_optional), F_(data_optional), F_(id_optional)},
[=](Shell & shell, const std::vector<std::string> & arguments) { [=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) { if (arguments.empty()) {
Test::run_test(shell, "default"); Test::run_test(shell, "default");
} else if (arguments.size() == 1) { } else if (arguments.size() == 1) {
Test::run_test(shell, arguments.front()); Test::run_test(shell, arguments.front());
} else { } else if (arguments.size() == 2) {
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str()); Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str());
} else {
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str(), arguments[2].c_str());
} }
}); });
commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{"t"}, [=](Shell & shell, const std::vector<std::string> & arguments) { commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{"t"}, [=](Shell & shell, const std::vector<std::string> & arguments) {
@@ -531,15 +533,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
if (return_code == CommandRet::OK && json.size()) { if (return_code == CommandRet::OK && json.size()) {
if (json.containsKey("api_data")) { if (json.containsKey("api_data")) {
JsonVariant data = json["api_data"]; String data = json["api_data"].as<String>();
if (data.is<int>()) { shell.println(data.c_str());
shell.printfln("%d", data.as<int>());
} else if (data.is<float>()) {
char s[10];
shell.println(Helpers::render_value(s, data.as<float>(), 1));
} else {
shell.println(data.as<const char *>());
}
return; return;
} }
serializeJsonPretty(doc, shell); serializeJsonPretty(doc, shell);
@@ -568,9 +563,9 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
} }
return devices_list; return devices_list;
} else if (current_arguments.size() == 1) { } else if (current_arguments.size() == 1) {
std::vector<std::string> command_list;
uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str()); uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str());
if (Command::device_has_commands(device_type)) { if (Command::device_has_commands(device_type)) {
std::vector<std::string> command_list;
for (const auto & cf : Command::commands()) { for (const auto & cf : Command::commands()) {
if (cf.device_type_ == device_type) { if (cf.device_type_ == device_type) {
command_list.emplace_back(cf.cmd_); command_list.emplace_back(cf.cmd_);

View File

@@ -482,6 +482,15 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(hpMaxPower), FL_(hpMaxPower),
DeviceValueUOM::PERCENT, DeviceValueUOM::PERCENT,
MAKE_CF_CB(set_hpMaxPower)); MAKE_CF_CB(set_hpMaxPower));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpSetDiffPress_,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_MUL50,
FL_(hpSetDiffPress),
DeviceValueUOM::MBAR,
MAKE_CF_CB(set_hpDiffPress),
150,
750);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompOn_, DeviceValueType::BOOL, FL_(hpCompOn), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompOn_, DeviceValueType::BOOL, FL_(hpCompOn), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), DeviceValueUOM::NONE);
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpHeatingOn_, DeviceValueType::BOOL, FL_(hpHeatingOn), DeviceValueUOM::NONE); // register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpHeatingOn_, DeviceValueType::BOOL, FL_(hpHeatingOn), DeviceValueUOM::NONE);
@@ -1935,6 +1944,10 @@ void Boiler::process_HpMeters(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, meterHeat_, 24); has_update(telegram, meterHeat_, 24);
} }
void Boiler::process_HpPressure(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpSetDiffPress_, 9);
}
// HIU unit // HIU unit
// boiler(0x08) -B-> All(0x00), ?(0x0779), data: 06 05 01 01 AD 02 EF FF FF 00 00 7F FF // boiler(0x08) -B-> All(0x00), ?(0x0779), data: 06 05 01 01 AD 02 EF FF FF 00 00 7F FF
@@ -2939,6 +2952,15 @@ bool Boiler::set_hpMaxPower(const char * value, const int8_t id) {
return false; return false;
} }
bool Boiler::set_hpDiffPress(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
write_command(0x2CC, 9, (uint8_t)(v / 50), 0x2CC);
return true;
}
return false;
}
bool Boiler::set_vp_cooling(const char * value, const int8_t id) { bool Boiler::set_vp_cooling(const char * value, const int8_t id) {
bool v; bool v;
if (Helpers::value2bool(value, v)) { if (Helpers::value2bool(value, v)) {

View File

@@ -223,6 +223,7 @@ class Boiler : public EMSdevice {
uint32_t meterHeat_; uint32_t meterHeat_;
uint8_t hpEA0_; uint8_t hpEA0_;
uint8_t hpPumpMode_; uint8_t hpPumpMode_;
uint8_t hpSetDiffPress_;
// Pool unit // Pool unit
int8_t poolSetTemp_; int8_t poolSetTemp_;
@@ -334,6 +335,7 @@ class Boiler : public EMSdevice {
void process_HpPool(std::shared_ptr<const Telegram> telegram); void process_HpPool(std::shared_ptr<const Telegram> telegram);
void process_HpInput(std::shared_ptr<const Telegram> telegram); void process_HpInput(std::shared_ptr<const Telegram> telegram);
void process_HpInConfig(std::shared_ptr<const Telegram> telegram); void process_HpInConfig(std::shared_ptr<const Telegram> telegram);
void process_HpPressure(std::shared_ptr<const Telegram> telegram);
void process_HpCooling(std::shared_ptr<const Telegram> telegram); void process_HpCooling(std::shared_ptr<const Telegram> telegram);
void process_HpHeaterConfig(std::shared_ptr<const Telegram> telegram); void process_HpHeaterConfig(std::shared_ptr<const Telegram> telegram);
void process_HybridHp(std::shared_ptr<const Telegram> telegram); void process_HybridHp(std::shared_ptr<const Telegram> telegram);
@@ -437,6 +439,7 @@ class Boiler : public EMSdevice {
bool set_hpCircPumpWw(const char * value, const int8_t id); bool set_hpCircPumpWw(const char * value, const int8_t id);
bool set_hpPumpMode(const char * value, const int8_t id); bool set_hpPumpMode(const char * value, const int8_t id);
bool set_hpMaxPower(const char * value, const int8_t id); bool set_hpMaxPower(const char * value, const int8_t id);
bool set_hpDiffPress(const char * value, const int8_t id);
bool set_auxLimit(const char * value, const int8_t id); bool set_auxLimit(const char * value, const int8_t id);
inline bool set_auxMaxLimit(const char * value, const int8_t id) { inline bool set_auxMaxLimit(const char * value, const int8_t id) {

View File

@@ -30,7 +30,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC)); register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC)); // register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type(device_id - 0x20 + 0x02CD, "MMPLUSSetMessage_HC", false, MAKE_PF_CB(process_MMPLUSSetMessage_HC)); register_telegram_type((device_id - 0x20) * 2 + 0x02CC, "MMPLUSSetMessage_HC", false, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
hc_ = device_id - 0x20 + 1; hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1; uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES); register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES);
@@ -151,7 +151,7 @@ void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
} }
// Mixer Setting 0x2CD // Mixer Setting 0x2CC
void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) { void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, activated_, 0); // on = 0xFF has_update(telegram, activated_, 0); // on = 0xFF
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
@@ -241,7 +241,7 @@ bool Mixer::set_activated(const char * value, const int8_t id) {
} }
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
uint8_t hc = device_id() - 0x20; uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 0, b ? 0xFF : 0, 0x2CD + hc); write_command(0x2CC + hc * 2, 0, b ? 0xFF : 0, 0x2CC + hc * 2);
return true; return true;
} }
return false; return false;
@@ -260,7 +260,7 @@ bool Mixer::set_setValveTime(const char * value, const int8_t id) {
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
v = (v + 5) / 10; v = (v + 5) / 10;
uint8_t hc = device_id() - 0x20; uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 1, v, 0x2CD + hc); write_command(0x2CC + hc * 2, 1, v, 0x2CC + hc * 2);
return true; return true;
} }
return false; return false;
@@ -273,7 +273,7 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
} }
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
uint8_t hc = device_id() - 0x20; uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 2, v, 0x2CD + hc); write_command(0x2CC + hc * 2, 2, v, 0x2CC + hc * 2);
return true; return true;
} }
return false; return false;

View File

@@ -583,6 +583,7 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
} }
} }
}); });
if (ignore) { if (ignore) {
return; return;
} }
@@ -943,11 +944,10 @@ void EMSdevice::generate_values_web(JsonObject output) {
auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string
// add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique // add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique
if (dv.has_tag()) { obj["id"] = dv.has_tag() ? mask + tag_to_string(dv.tag) + " " + fullname : mask + fullname; // suffix tag
obj["id"] = mask + tag_to_string(dv.tag) + " " + fullname;
} else { // TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
obj["id"] = mask + fullname; // obj["id"] = dv.has_tag() ? mask + fullname + " " + tag_to_string(dv.tag) : mask + fullname; // suffix tag
}
// add commands and options // add commands and options
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) { if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
@@ -1001,7 +1001,7 @@ void EMSdevice::generate_values_web(JsonObject output) {
// as generate_values_web() but stripped down to only show all entities and their state // as generate_values_web() but stripped down to only show all entities and their state
// this is used only for WebCustomizationService::device_entities() // this is used only for WebCustomizationService::device_entities()
void EMSdevice::generate_values_web_customization(JsonArray & output) { void EMSdevice::generate_values_web_customization(JsonArray output) {
for (auto & dv : devicevalues_) { for (auto & dv : devicevalues_) {
// also show commands and entities that have an empty full name // also show commands and entities that have an empty full name
JsonObject obj = output.add<JsonObject>(); JsonObject obj = output.add<JsonObject>();
@@ -1058,13 +1058,10 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
auto fullname = Helpers::translated_word(dv.fullname); auto fullname = Helpers::translated_word(dv.fullname);
if (dv.type != DeviceValueType::CMD) { if (dv.type != DeviceValueType::CMD) {
if (fullname) { if (fullname) {
if (dv.has_tag()) { obj["n"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname : fullname; // prefix tag
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname); // TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
obj["n"] = name; // obj["n"] = (dv.has_tag()) ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
} else {
obj["n"] = fullname;
}
} }
// add the custom name, is optional // add the custom name, is optional
@@ -1409,13 +1406,10 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
auto fullname = dv.get_fullname(); auto fullname = dv.get_fullname();
if (!fullname.empty()) { if (!fullname.empty()) {
if (dv.has_tag()) { json["fullname"] = dv.has_tag() ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname.c_str()); // TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
json["fullname"] = name; json["fullname"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname.c_str() : fullname; // prefix tag
} else {
json["fullname"] = fullname;
}
} }
if (dv.tag != DeviceValueTAG::TAG_NONE) { if (dv.tag != DeviceValueTAG::TAG_NONE) {
@@ -1549,7 +1543,7 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
EMSESP::logger().debug("Attribute '%s'", attribute_s); EMSESP::logger().debug("Attribute '%s'", attribute_s);
#endif #endif
if (json.containsKey(attribute_s)) { if (json.containsKey(attribute_s)) {
JsonVariant data = json[attribute_s]; String data = json[attribute_s].as<String>();
output.clear(); output.clear();
output["api_data"] = data; output["api_data"] = data;
return true; return true;
@@ -1610,16 +1604,20 @@ bool EMSdevice::generate_values(JsonObject output, const uint8_t tag_filter, con
char name[80]; char name[80];
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) { if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
char short_name[20]; // char short_name[20];
if (output_target == OUTPUT_TARGET::CONSOLE) { // if (output_target == OUTPUT_TARGET::CONSOLE) {
snprintf(short_name, sizeof(short_name), " (%s)", dv.short_name); // snprintf(short_name, sizeof(short_name), "(%s)", dv.short_name);
} else { // } else {
strcpy(short_name, ""); // strcpy(short_name, "");
} // }
// add tag
if (have_tag) { if (have_tag) {
snprintf(name, sizeof(name), "%s %s%s", tag_to_string(dv.tag), fullname.c_str(), short_name); // prefix the tag snprintf(name, sizeof(name), "%s %s (%s)", tag_to_string(dv.tag), fullname.c_str(), dv.short_name); // prefix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// snprintf(name, sizeof(name), "%s %s (%s)", fullname.c_str(), tag_to_string(dv.tag), dv.short_name); // sufix tag
} else { } else {
snprintf(name, sizeof(name), "%s%s", fullname.c_str(), short_name); snprintf(name, sizeof(name), "%s (%s)", fullname.c_str(), dv.short_name);
} }
} else { } else {
strlcpy(name, (dv.short_name), sizeof(name)); // use short name strlcpy(name, (dv.short_name), sizeof(name)); // use short name

View File

@@ -222,7 +222,7 @@ class EMSdevice {
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE }; enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
bool generate_values(JsonObject output, const uint8_t tag_filter, const bool nested, const uint8_t output_target); bool generate_values(JsonObject output, const uint8_t tag_filter, const bool nested, const uint8_t output_target);
void generate_values_web(JsonObject output); void generate_values_web(JsonObject output);
void generate_values_web_customization(JsonArray & output); void generate_values_web_customization(JsonArray output);
void add_device_value(uint8_t tag, void add_device_value(uint8_t tag,
void * value_p, void * value_p,

View File

@@ -110,7 +110,7 @@ const char * DeviceValue::DeviceValueUOM_s[] = {
F_(uom_blank), // 0 F_(uom_blank), // 0
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], F_(uom_ua), F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], F_(uom_ua),
F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm),
F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_blank) F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_blank)
}; };

View File

@@ -71,7 +71,8 @@ class DeviceValue {
KMIN, // 21 - K*min KMIN, // 21 - K*min
K, // 22 - K K, // 22 - K
VOLTS, // 23 - V VOLTS, // 23 - V
CONNECTIVITY // 24 - used in HA MBAR, // 24 - mbar
CONNECTIVITY // 25 - used in HA
}; };
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp // TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
@@ -143,7 +144,8 @@ class DeviceValue {
DV_NUMOP_DIV100 = 100, DV_NUMOP_DIV100 = 100,
DV_NUMOP_MUL5 = -5, DV_NUMOP_MUL5 = -5,
DV_NUMOP_MUL10 = -10, DV_NUMOP_MUL10 = -10,
DV_NUMOP_MUL15 = -15 DV_NUMOP_MUL15 = -15,
DV_NUMOP_MUL50 = -50
}; };
uint8_t device_type; // EMSdevice::DeviceType uint8_t device_type; // EMSdevice::DeviceType

View File

@@ -392,7 +392,6 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
// extract the shortname from the key, which is in brackets // extract the shortname from the key, which is in brackets
std::size_t first_bracket = key.find_last_of('('); std::size_t first_bracket = key.find_last_of('(');
std::size_t last_bracket = key.find_last_of(')'); std::size_t last_bracket = key.find_last_of(')');
std::string shortname = key.substr(first_bracket + 1, last_bracket - first_bracket - 1);
std::string uom = emsdevice->get_value_uom(key.substr(first_bracket + 1, last_bracket - first_bracket - 1)); std::string uom = emsdevice->get_value_uom(key.substr(first_bracket + 1, last_bracket - first_bracket - 1));
shell.printfln(" %s: %s%s %s%s", key.c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), uom.c_str(), COLOR_RESET); shell.printfln(" %s: %s%s %s%s", key.c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), uom.c_str(), COLOR_RESET);
@@ -705,7 +704,7 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8
// search for recognized device_ids : Me, All, otherwise print hex value // search for recognized device_ids : Me, All, otherwise print hex value
std::string EMSESP::device_tostring(const uint8_t device_id) { std::string EMSESP::device_tostring(const uint8_t device_id) {
if ((device_id & 0x7F) == rxservice_.ems_bus_id()) { if ((device_id & 0x7F) == EMSbus::ems_bus_id()) {
return "Me"; return "Me";
} else if (device_id == 0x00) { } else if (device_id == 0x00) {
return "All"; return "All";
@@ -734,12 +733,16 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
} else if (emsdevice->is_device_id(dest)) { } else if (emsdevice->is_device_id(dest)) {
dest_name = emsdevice->device_type_name(); dest_name = emsdevice->device_type_name();
} }
// get the type name, any match will do // get the type name
if (type_name.empty()) { if (type_name.empty()) {
if ((telegram->operation == Telegram::Operation::RX_READ && emsdevice->is_device_id(dest))
|| (telegram->operation != Telegram::Operation::RX_READ && dest == 0 && emsdevice->is_device_id(src))
|| (telegram->operation != Telegram::Operation::RX_READ && src == EMSbus::ems_bus_id() && emsdevice->is_device_id(dest))) {
type_name = emsdevice->telegram_type_name(telegram); type_name = emsdevice->telegram_type_name(telegram);
} }
} }
} }
}
// if we can't find names for the devices, use their hex values // if we can't find names for the devices, use their hex values
if (src_name.empty()) { if (src_name.empty()) {
@@ -867,7 +870,7 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// returns false if there are none found // returns false if there are none found
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) { bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// if watching or reading... // if watching or reading...
if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == txservice_.ems_bus_id())) { if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == EMSbus::ems_bus_id())) {
if (telegram->type_id == response_id_) { if (telegram->type_id == response_id_) {
if (!trace_raw_) { if (!trace_raw_) {
LOG_TRACE("%s", pretty_telegram(telegram).c_str()); LOG_TRACE("%s", pretty_telegram(telegram).c_str());
@@ -896,7 +899,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
} }
// only process broadcast telegrams or ones sent to us on request // only process broadcast telegrams or ones sent to us on request
// if ((telegram->dest != 0x00) && (telegram->dest != rxservice_.ems_bus_id())) { // if ((telegram->dest != 0x00) && (telegram->dest != EMSbus::ems_bus_id())) {
if (telegram->operation == Telegram::Operation::RX_READ) { if (telegram->operation == Telegram::Operation::RX_READ) {
// LOG_DEBUG("read telegram received, not processing"); // LOG_DEBUG("read telegram received, not processing");
return false; return false;
@@ -924,13 +927,13 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
bool found = false; bool found = false;
bool knowndevice = false; bool knowndevice = false;
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice->is_device_id(telegram->src)) { if (emsdevice->is_device_id(telegram->src) && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) {
knowndevice = true; knowndevice = true;
found = emsdevice->handle_telegram(telegram); found = emsdevice->handle_telegram(telegram);
// if we correctly processed the telegram then follow up with sending it via MQTT (if enabled) // if we correctly processed the telegram then follow up with sending it via MQTT (if enabled)
if (found && Mqtt::connected()) { if (found && Mqtt::connected()) {
if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update()) if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update())
|| (telegram->type_id == publish_id_ && telegram->dest == txservice_.ems_bus_id())) { || (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id())) {
if (telegram->type_id == publish_id_) { if (telegram->type_id == publish_id_) {
publish_id_ = 0; publish_id_ = 0;
} }
@@ -947,7 +950,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
emsdevice->add_handlers_ignored(telegram->type_id); emsdevice->add_handlers_ignored(telegram->type_id);
} }
break; break;
} else if (emsdevice->is_device_id(telegram->dest)) { } else if (emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) {
emsdevice->handle_telegram(telegram); emsdevice->handle_telegram(telegram);
} }
} }
@@ -1021,7 +1024,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// if its not in our database, we don't add it // if its not in our database, we don't add it
bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand) { bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand) {
// don't add ourselves! // don't add ourselves!
if (device_id == rxservice_.ems_bus_id()) { if (device_id == EMSbus::ems_bus_id()) {
return false; return false;
} }
@@ -1062,7 +1065,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
device_p = &device; device_p = &device;
break; break;
} }
if ((device_id >= EMSdevice::EMS_DEVICE_ID_HS1 && device_id <= EMSdevice::EMS_DEVICE_ID_HS16)) { if (device_id >= EMSdevice::EMS_DEVICE_ID_HS1 && device_id <= EMSdevice::EMS_DEVICE_ID_HS16) {
device_p = &device; device_p = &device;
device_p->device_type = DeviceType::HEATSOURCE; device_p->device_type = DeviceType::HEATSOURCE;
break; break;
@@ -1265,7 +1268,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
#endif #endif
// check first for echo // check first for echo
uint8_t first_value = data[0]; uint8_t first_value = data[0];
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) { if (((first_value & 0x7F) == EMSbus::ems_bus_id()) && (length > 1)) {
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20 // if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length); Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length);
#ifdef EMSESP_UART_DEBUG #ifdef EMSESP_UART_DEBUG
@@ -1336,7 +1339,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
wait_km_ = true; wait_km_ = true;
connect_time = uuid::get_uptime_sec(); connect_time = uuid::get_uptime_sec();
} }
if (poll_id == txservice_.ems_bus_id()) { if (poll_id == EMSbus::ems_bus_id()) {
EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
} }
if (wait_km_) { if (wait_km_) {

View File

@@ -249,6 +249,7 @@ MAKE_WORD_CUSTOM(uom_l, "l")
MAKE_WORD_CUSTOM(uom_kmin, "K*min") MAKE_WORD_CUSTOM(uom_kmin, "K*min")
MAKE_WORD_CUSTOM(uom_k, "K") MAKE_WORD_CUSTOM(uom_k, "K")
MAKE_WORD_CUSTOM(uom_volts, "V") MAKE_WORD_CUSTOM(uom_volts, "V")
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
// MQTT topics and prefixes // MQTT topics and prefixes
MAKE_WORD_CUSTOM(heating_active, "heating_active") MAKE_WORD_CUSTOM(heating_active, "heating_active")

View File

@@ -286,7 +286,7 @@ MAKE_WORD_TRANSLATION(partymode, "party", "Party", "party", "", "impreza", "", "
MAKE_WORD_TRANSLATION(fireplace, "fireplace", "Kamin", "haard", "", "kominek", "", "", "şömine", "camino", "krb") // TODO translate MAKE_WORD_TRANSLATION(fireplace, "fireplace", "Kamin", "haard", "", "kominek", "", "", "şömine", "camino", "krb") // TODO translate
// MQTT Discovery - this is special device entity for 'climate' // MQTT Discovery - this is special device entity for 'climate'
MAKE_TRANSLATION(haclimate, "haclimate", "Discovery current room temperature", "Discovery Temperatur", "Discovery huidige kamertemperatuur", "", "termostat w HA", "HA Avlest temp", "", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "Zistiť aktuálnu teplotu v miestnosti") // TODO translate MAKE_TRANSLATION(haclimate, "haclimate", "mqtt discovery current room temperature", "Discovery Temperatur", "Discovery huidige kamertemperatuur", "", "termostat w HA", "HA Avlest temp", "", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "Zistiť aktuálnu teplotu v miestnosti") // TODO translate
// Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk // Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk
// Boiler // Boiler
@@ -463,6 +463,7 @@ MAKE_TRANSLATION(hpPumpMode, "hppumpmode", "primary heatpump mode", "Modus Haupt
MAKE_TRANSLATION(instantstart, "instantstart", "instant start", "Sofortstart", "", "", "natychmiastowy start", "", "", "", "", "") // TODO translate MAKE_TRANSLATION(instantstart, "instantstart", "instant start", "Sofortstart", "", "", "natychmiastowy start", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(heatondelay, "heatondelay", "heat-on delay", "Einschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate MAKE_TRANSLATION(heatondelay, "heatondelay", "heat-on delay", "Einschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(heatoffdelay, "heatoffdelay", "heat-off delay", "Ausschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate MAKE_TRANSLATION(heatoffdelay, "heatoffdelay", "heat-off delay", "Ausschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(hpSetDiffPress, "hpsetdiffpress", "set differental pressure", "Pumpensolldruck", "", "", "", "", "", "", "", "") // TODO translate
// hybrid heatpump // hybrid heatpump
MAKE_TRANSLATION(hybridStrategy, "hybridstrategy", "hybrid control strategy", "Hybrid Strategie", "Hybride strategie", "Hybrid kontrollstrategi", "strategia sterowania hybrydowego", "hybrid kontrollstrategi", "stratégie contrôle hybride", "hibrit kontrol stratejisi", "strategia comtrollo ibrido", "hybridná stratégia riadenia") MAKE_TRANSLATION(hybridStrategy, "hybridstrategy", "hybrid control strategy", "Hybrid Strategie", "Hybride strategie", "Hybrid kontrollstrategi", "strategia sterowania hybrydowego", "hybrid kontrollstrategi", "stratégie contrôle hybride", "hibrit kontrol stratejisi", "strategia comtrollo ibrido", "hybridná stratégia riadenia")

View File

@@ -683,12 +683,12 @@ bool Mqtt::queue_publish(const char * topic, const std::string & payload) {
return queue_publish_message((topic), payload, mqtt_retain_); return queue_publish_message((topic), payload, mqtt_retain_);
} }
bool Mqtt::queue_publish(const char * topic, const JsonObjectConst & payload) { bool Mqtt::queue_publish(const char * topic, const JsonObjectConst payload) {
return queue_publish_retain(topic, payload, mqtt_retain_); return queue_publish_retain(topic, payload, mqtt_retain_);
} }
// publish json doc, only if its not empty // publish json doc, only if its not empty
bool Mqtt::queue_publish(const std::string & topic, const JsonObjectConst & payload) { bool Mqtt::queue_publish(const std::string & topic, const JsonObjectConst payload) {
return queue_publish_retain(topic, payload, mqtt_retain_); return queue_publish_retain(topic, payload, mqtt_retain_);
} }
@@ -698,11 +698,11 @@ bool Mqtt::queue_publish_retain(const char * topic, const std::string & payload,
} }
// publish json doc, only if its not empty, using the retain flag // publish json doc, only if its not empty, using the retain flag
bool Mqtt::queue_publish_retain(const std::string & topic, const JsonObjectConst & payload, const bool retain) { bool Mqtt::queue_publish_retain(const std::string & topic, const JsonObjectConst payload, const bool retain) {
return queue_publish_retain(topic.c_str(), payload, retain); return queue_publish_retain(topic.c_str(), payload, retain);
} }
bool Mqtt::queue_publish_retain(const char * topic, const JsonObjectConst & payload, const bool retain) { bool Mqtt::queue_publish_retain(const char * topic, const JsonObjectConst payload, const bool retain) {
if (payload.size()) { if (payload.size()) {
std::string payload_text; std::string payload_text;
payload_text.reserve(measureJson(payload) + 1); payload_text.reserve(measureJson(payload) + 1);
@@ -722,7 +722,7 @@ bool Mqtt::queue_remove_topic(const char * topic) {
} }
// queue a Home Assistant config topic and payload, with retain flag off. // queue a Home Assistant config topic and payload, with retain flag off.
bool Mqtt::queue_ha(const char * topic, const JsonObjectConst & payload) { bool Mqtt::queue_ha(const char * topic, const JsonObjectConst payload) {
if (!enabled()) { if (!enabled()) {
return false; return false;
} }
@@ -817,7 +817,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSd
const int16_t dv_set_min, const int16_t dv_set_min,
const uint32_t dv_set_max, const uint32_t dv_set_max,
const int8_t num_op, const int8_t num_op,
const JsonObjectConst & dev_json) { const JsonObjectConst dev_json) {
// ignore if name (fullname) is empty // ignore if name (fullname) is empty
if (!fullname || !en_name) { if (!fullname || !en_name) {
return false; return false;

View File

@@ -68,13 +68,13 @@ class Mqtt {
static bool queue_publish(const std::string & topic, const std::string & payload); static bool queue_publish(const std::string & topic, const std::string & payload);
static bool queue_publish(const char * topic, const char * payload); static bool queue_publish(const char * topic, const char * payload);
static bool queue_publish(const std::string & topic, const JsonObjectConst & payload); static bool queue_publish(const std::string & topic, const JsonObjectConst payload);
static bool queue_publish(const char * topic, const JsonObjectConst & payload); static bool queue_publish(const char * topic, const JsonObjectConst payload);
static bool queue_publish(const char * topic, const std::string & payload); static bool queue_publish(const char * topic, const std::string & payload);
static bool queue_publish_retain(const std::string & topic, const JsonObjectConst & payload, const bool retain); static bool queue_publish_retain(const std::string & topic, const JsonObjectConst payload, const bool retain);
static bool queue_publish_retain(const char * topic, const std::string & payload, const bool retain); static bool queue_publish_retain(const char * topic, const std::string & payload, const bool retain);
static bool queue_publish_retain(const char * topic, const JsonObjectConst & payload, const bool retain); static bool queue_publish_retain(const char * topic, const JsonObjectConst payload, const bool retain);
static bool queue_ha(const char * topic, const JsonObjectConst & payload); static bool queue_ha(const char * topic, const JsonObjectConst payload);
static bool queue_remove_topic(const char * topic); static bool queue_remove_topic(const char * topic);
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false); static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
@@ -92,7 +92,7 @@ class Mqtt {
const int16_t dv_set_min, const int16_t dv_set_min,
const uint32_t dv_set_max, const uint32_t dv_set_max,
const int8_t num_op, const int8_t num_op,
const JsonObjectConst & dev_json); const JsonObjectConst dev_json);
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom); static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30); static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
@@ -168,6 +168,10 @@ class Mqtt {
return entity_format_; return entity_format_;
} }
static void entity_format(uint8_t n) {
entity_format_ = n;
}
static uint8_t discovery_type() { static uint8_t discovery_type() {
return discovery_type_; return discovery_type_;
} }

View File

@@ -632,7 +632,7 @@ void System::send_info_mqtt() {
} }
// create the json for heartbeat // create the json for heartbeat
bool System::heartbeat_json(JsonObject output) { void System::heartbeat_json(JsonObject output) {
uint8_t bus_status = EMSESP::bus_status(); uint8_t bus_status = EMSESP::bus_status();
if (bus_status == EMSESP::BUS_STATUS_TX_ERRORS) { if (bus_status == EMSESP::BUS_STATUS_TX_ERRORS) {
output["bus_status"] = "txerror"; output["bus_status"] = "txerror";
@@ -684,8 +684,6 @@ bool System::heartbeat_json(JsonObject output) {
output["wifistrength"] = wifi_quality(rssi); output["wifistrength"] = wifi_quality(rssi);
} }
#endif #endif
return true;
} }
// send periodic MQTT message with system information // send periodic MQTT message with system information
@@ -700,9 +698,8 @@ void System::send_heartbeat() {
JsonDocument doc; JsonDocument doc;
JsonObject json = doc.to<JsonObject>(); JsonObject json = doc.to<JsonObject>();
if (heartbeat_json(json)) { heartbeat_json(json);
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue. Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
}
} }
// initializes network // initializes network
@@ -1471,7 +1468,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
#if defined(EMSESP_TEST) #if defined(EMSESP_TEST)
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler // run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
bool System::command_test(const char * value, const int8_t id) { bool System::command_test(const char * value, const int8_t id) {
return Test::run_test(value, id); return Test::test(value, id);
} }
#endif #endif

View File

@@ -79,7 +79,7 @@ class System {
void syslog_init(); void syslog_init();
bool check_upgrade(bool factory_settings); bool check_upgrade(bool factory_settings);
bool check_restore(); bool check_restore();
bool heartbeat_json(JsonObject output); void heartbeat_json(JsonObject output);
void send_heartbeat(); void send_heartbeat();
void send_info_mqtt(); void send_info_mqtt();

View File

@@ -414,8 +414,8 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
if (Helpers::toLower(command_s) == Helpers::toLower(sensor.name().c_str()) || Helpers::toLower(command_s) == Helpers::toLower(sensor.id().c_str())) { if (Helpers::toLower(command_s) == Helpers::toLower(sensor.name().c_str()) || Helpers::toLower(command_s) == Helpers::toLower(sensor.id().c_str())) {
output["id"] = sensor.id(); output["id"] = sensor.id();
output["name"] = sensor.name(); output["name"] = sensor.name();
char val[10];
if (Helpers::hasValue(sensor.temperature_c)) { if (Helpers::hasValue(sensor.temperature_c)) {
char val[10];
output["value"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0)); output["value"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
} }
@@ -426,7 +426,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
// if we're filtering on an attribute, go find it // if we're filtering on an attribute, go find it
if (attribute_s) { if (attribute_s) {
if (output.containsKey(attribute_s)) { if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s]; String data = output[attribute_s].as<String>();
output.clear(); output.clear();
output["api_data"] = data; output["api_data"] = data;
return true; return true;

View File

@@ -1,7 +1,6 @@
/* /*
* EMS-ESP - https://github.com/emsesp/EMS-ESP * EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire * Copyright 2020-2023 Paul Derbyshire
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,12 +24,19 @@ namespace emsesp {
// no shell, called via the API or 'call system test' command // no shell, called via the API or 'call system test' command
// or http://ems-esp/api?device=system&cmd=test&data=boiler // or http://ems-esp/api?device=system&cmd=test&data=boiler
bool Test::run_test(const char * command, int8_t id) { bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
if ((command == nullptr) || (strlen(command) == 0)) { if (cmd.empty()) {
return false; return false;
} }
if (strcmp(command, "memory") == 0) { if (cmd == "add") {
Mqtt::entity_format(Mqtt::entityFormat::SINGLE_LONG); // SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT
System::test_set_all_active(true); // include all entities and give them fake values
add_device(id1, id2);
return true;
}
if (cmd == "memory") {
EMSESP::logger().notice("Testing memory by adding lots of devices and entities..."); EMSESP::logger().notice("Testing memory by adding lots of devices and entities...");
System::test_set_all_active(true); // include all entities and give them fake values System::test_set_all_active(true); // include all entities and give them fake values
@@ -43,7 +49,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "general") == 0) { if (cmd == "general") {
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat"); EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
// System::test_set_all_active(true); // uncomment if we want to show all entities and give them fake values // System::test_set_all_active(true); // uncomment if we want to show all entities and give them fake values
@@ -77,7 +83,7 @@ bool Test::run_test(const char * command, int8_t id) {
// //
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
if (strcmp(command, "heat_exchange") == 0) { if (cmd == "heat_exchange") {
EMSESP::logger().info("Testing heating exchange..."); EMSESP::logger().info("Testing heating exchange...");
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170 add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
@@ -89,7 +95,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "2thermostats") == 0) { if (cmd == "2thermostats") {
EMSESP::logger().info("Testing with multiple thermostats..."); EMSESP::logger().info("Testing with multiple thermostats...");
add_device(0x08, 123); // GB072 add_device(0x08, 123); // GB072
@@ -121,7 +127,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "310") == 0) { if (cmd == "310") {
EMSESP::logger().info("Adding a GB072/RC310 combo..."); EMSESP::logger().info("Adding a GB072/RC310 combo...");
add_device(0x08, 123); // GB072 add_device(0x08, 123); // GB072
@@ -148,7 +154,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "gateway") == 0) { if (cmd == "gateway") {
EMSESP::logger().info("Adding a Gateway..."); EMSESP::logger().info("Adding a Gateway...");
// add 0x48 KM200, via a version command // add 0x48 KM200, via a version command
@@ -168,7 +174,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "mixer") == 0) { if (cmd == "mixer") {
EMSESP::logger().info("Adding a mixer..."); EMSESP::logger().info("Adding a mixer...");
// add controller // add controller
@@ -190,7 +196,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "boiler") == 0) { if (cmd == "boiler") {
EMSESP::logger().info("Adding boiler..."); EMSESP::logger().info("Adding boiler...");
add_device(0x08, 123); // Nefit Trendline add_device(0x08, 123); // Nefit Trendline
@@ -207,7 +213,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "thermostat") == 0) { if (cmd == "thermostat") {
EMSESP::logger().info("Adding thermostat..."); EMSESP::logger().info("Adding thermostat...");
add_device(0x10, 192); // FW120 add_device(0x10, 192); // FW120
@@ -220,7 +226,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "solar") == 0) { if (cmd == "solar") {
EMSESP::logger().info("Adding solar..."); EMSESP::logger().info("Adding solar...");
add_device(0x30, 163); // SM100 add_device(0x30, 163); // SM100
@@ -239,7 +245,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true; return true;
} }
if (strcmp(command, "heatpump") == 0) { if (cmd == "heatpump") {
EMSESP::logger().info("Adding heatpump..."); EMSESP::logger().info("Adding heatpump...");
add_device(0x38, 200); // Enviline module add_device(0x38, 200); // Enviline module
@@ -258,11 +264,13 @@ bool Test::run_test(const char * command, int8_t id) {
} }
// These next tests are run from the Consol via the test command, so inherit the Shell // These next tests are run from the Consol via the test command, so inherit the Shell
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & data) { void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & id1_s, const std::string & id2_s) {
shell.add_flags(CommandFlags::ADMIN); // switch to su shell.add_flags(CommandFlags::ADMIN); // switch to su
// init stuff // init stuff
Mqtt::ha_enabled(true); Mqtt::ha_enabled(true);
Mqtt::entity_format(Mqtt::entityFormat::SINGLE_SHORT); // SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
// EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw mode // EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw mode
@@ -275,11 +283,32 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
command = cmd; command = cmd;
} }
// extract params
int8_t id1 = -1;
int8_t id2 = -1;
if (!id1_s.empty()) {
if (id1_s[0] == '0' && id1_s[1] == 'x') {
id1 = Helpers::hextoint(id1_s.c_str());
} else {
id1 = Helpers::atoint(id1_s.c_str());
}
}
if (!id2_s.empty()) {
id2 = Helpers::atoint(id2_s.c_str());
}
bool ok = false; bool ok = false;
if (command == "add") {
shell.printfln("Testing Adding a device (product_id %d), with all values...", id2);
test("add", id1, id2); // e.g. 8 172
shell.invoke_command("show values");
ok = true;
}
if (command == "general") { if (command == "general") {
shell.printfln("Testing adding a boiler, thermostat and sensors..."); shell.printfln("Testing adding a boiler, thermostat and sensors...");
run_test("general"); test("general");
// add sensors // add sensors
emsesp::EMSESP::analogsensor_.test(); emsesp::EMSESP::analogsensor_.test();
@@ -296,14 +325,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// https://github.com/emsesp/EMS-ESP32/issues/869 // https://github.com/emsesp/EMS-ESP32/issues/869
if (command == "memory") { if (command == "memory") {
shell.printfln("Testing memory by adding lots of devices and entities..."); shell.printfln("Testing memory by adding lots of devices and entities...");
run_test("memory"); test("memory");
shell.invoke_command("show values"); shell.invoke_command("show values");
ok = true; ok = true;
} }
if (command == "custom_entities") { if (command == "custom_entities") {
shell.printfln("custom entities..."); shell.printfln("custom entities...");
run_test("general"); test("general");
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
AsyncWebServerRequest request; AsyncWebServerRequest request;
@@ -318,7 +347,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "coldshot") { if (command == "coldshot") {
shell.printfln("Testing coldshot..."); shell.printfln("Testing coldshot...");
run_test("general"); test("general");
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
AsyncWebServerRequest request; AsyncWebServerRequest request;
@@ -370,7 +399,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "modes") { if (command == "modes") {
shell.printfln("Testing thermostat modes..."); shell.printfln("Testing thermostat modes...");
run_test("general"); test("general");
shell.invoke_command("call thermostat mode auto"); shell.invoke_command("call thermostat mode auto");
shell.invoke_command("call thermostat mode Manuell"); // DE shell.invoke_command("call thermostat mode Manuell"); // DE
shell.invoke_command("call thermostat mode 1"); shell.invoke_command("call thermostat mode 1");
@@ -506,13 +535,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "gateway") { if (command == "gateway") {
shell.printfln("Testing Gateway..."); shell.printfln("Testing Gateway...");
run_test("gateway"); test("gateway");
ok = true; ok = true;
} }
if (command == "310") { if (command == "310") {
shell.printfln("Testing RC310..."); shell.printfln("Testing RC310...");
run_test("310"); test("310");
shell.invoke_command("show devices"); shell.invoke_command("show devices");
shell.invoke_command("show values"); shell.invoke_command("show values");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
@@ -522,7 +551,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "heat_exchange") { if (command == "heat_exchange") {
shell.printfln("Testing heat exchange..."); shell.printfln("Testing heat exchange...");
run_test("heat_exchange"); test("heat_exchange");
shell.invoke_command("show devices"); shell.invoke_command("show devices");
shell.invoke_command("show values"); shell.invoke_command("show values");
ok = true; ok = true;
@@ -530,7 +559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "2thermostats") { if (command == "2thermostats") {
shell.printfln("Testing multiple thermostats..."); shell.printfln("Testing multiple thermostats...");
run_test("2thermostats"); test("2thermostats");
shell.invoke_command("show values"); shell.invoke_command("show values");
shell.invoke_command("show devices"); shell.invoke_command("show devices");
ok = true; ok = true;
@@ -542,8 +571,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::enabled(false); // turn off mqtt Mqtt::enabled(false); // turn off mqtt
Mqtt::ha_enabled(false); // turn off ha Mqtt::ha_enabled(false); // turn off ha
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
JsonDocument doc; // some absurd high number JsonDocument doc; // some absurd high number
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
@@ -596,7 +625,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true); Mqtt::ha_enabled(true);
Mqtt::nested_format(1); Mqtt::nested_format(1);
run_test("boiler"); test("boiler");
shell.invoke_command("show devices"); shell.invoke_command("show devices");
shell.invoke_command("show values"); shell.invoke_command("show values");
shell.invoke_command("call boiler info"); shell.invoke_command("call boiler info");
@@ -622,7 +651,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "shower_alert") { if (command == "shower_alert") {
shell.printfln("Testing Shower Alert..."); shell.printfln("Testing Shower Alert...");
run_test("boiler"); test("boiler");
// device type, command, data // device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false"); Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
@@ -650,10 +679,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1); // is nested Mqtt::nested_format(1); // is nested
// Mqtt::nested_format(2); // not nested // Mqtt::nested_format(2); // not nested
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
run_test("solar"); test("solar");
run_test("mixer"); test("mixer");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
shell.invoke_command("show mqtt"); shell.invoke_command("show mqtt");
@@ -672,8 +701,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1); Mqtt::nested_format(1);
// Mqtt::send_response(false); // Mqtt::send_response(false);
run_test("boiler"); test("boiler");
// run_test("thermostat"); // test("thermostat");
// 0xC2 // 0xC2
// [emsesp] Boiler(0x08) -> Me(0x0B), UBAErrorMessage3(0xC2), data: 08 AC 00 10 31 48 30 31 15 80 95 0B 0E 10 38 00 7F FF FF FF 08 AC 00 10 09 41 30 // [emsesp] Boiler(0x08) -> Me(0x0B), UBAErrorMessage3(0xC2), data: 08 AC 00 10 31 48 30 31 15 80 95 0B 0E 10 38 00 7F FF FF FF 08 AC 00 10 09 41 30
@@ -692,8 +721,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1); Mqtt::nested_format(1);
// Mqtt::send_response(false); // Mqtt::send_response(false);
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
shell.invoke_command("call boiler wwseltemp"); shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
@@ -745,15 +774,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
} }
if (command == "healthcheck") { if (command == "healthcheck") {
uint8_t n = 0;
if (!data.empty()) {
n = Helpers::atoint(data.c_str());
}
// n=1 = EMSESP::system_.HEALTHCHECK_NO_BUS // n=1 = EMSESP::system_.HEALTHCHECK_NO_BUS
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK // n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
shell.printfln("Testing healthcheck with %d", n); if (id1 == -1) {
EMSESP::system_.healthcheck(n); id1 = 0;
}
shell.printfln("Testing healthcheck with %d", id1);
EMSESP::system_.healthcheck(id1);
ok = true; ok = true;
} }
@@ -763,7 +790,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true); Mqtt::ha_enabled(true);
// Mqtt::send_response(false); // Mqtt::send_response(false);
run_test("thermostat"); test("thermostat");
// shell.invoke_command("call thermostat seltemp"); // shell.invoke_command("call thermostat seltemp");
// shell.invoke_command("call system publish"); // shell.invoke_command("call system publish");
@@ -794,7 +821,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true); Mqtt::ha_enabled(true);
// Mqtt::send_response(false); // Mqtt::send_response(false);
run_test("boiler"); test("boiler");
shell.invoke_command("call boiler wwseltemp"); shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
@@ -819,7 +846,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true); Mqtt::ha_enabled(true);
// Mqtt::send_response(false); // Mqtt::send_response(false);
run_test("boiler"); test("boiler");
shell.invoke_command("call boiler wwseltemp"); shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
@@ -842,8 +869,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// EMSESP::bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR // EMSESP::bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
EMSESP::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR EMSESP::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
AsyncWebServerRequest request; AsyncWebServerRequest request;
JsonDocument doc; JsonDocument doc;
@@ -875,8 +902,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
EMSESP::system_.bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR EMSESP::system_.bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
// EMSESP::bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR // EMSESP::bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59"); EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
ok = true; ok = true;
@@ -887,15 +914,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing API wwmode"); shell.printfln("Testing API wwmode");
Mqtt::ha_enabled(false); Mqtt::ha_enabled(false);
Mqtt::nested_format(1); Mqtt::nested_format(1);
run_test("310"); test("310");
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_POST); request.method(HTTP_POST);
JsonDocument doc; JsonDocument doc;
JsonVariant json; JsonVariant json;
char data[] = "{\"value\":\"off\"}"; char odata[] = "{\"value\":\"off\"}";
deserializeJson(doc, data); deserializeJson(doc, odata);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/thermostat/wwmode"); request.url("/api/thermostat/wwmode");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService_post(&request, json);
@@ -911,8 +938,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1); Mqtt::nested_format(1);
// Mqtt::send_response(true); // Mqtt::send_response(true);
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
AsyncWebServerRequest requestX; AsyncWebServerRequest requestX;
JsonDocument docX; JsonDocument docX;
@@ -1181,10 +1208,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing nested MQTT"); shell.printfln("Testing nested MQTT");
Mqtt::ha_enabled(false); // turn off HA Discovery to stop the chatter Mqtt::ha_enabled(false); // turn off HA Discovery to stop the chatter
run_test("boiler"); test("boiler");
run_test("thermostat"); test("thermostat");
run_test("solar"); test("solar");
run_test("mixer"); test("mixer");
// first with nested // first with nested
Mqtt::nested_format(1); Mqtt::nested_format(1);
@@ -1201,7 +1228,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "thermostat") { if (command == "thermostat") {
shell.printfln("Testing adding a thermostat FW120..."); shell.printfln("Testing adding a thermostat FW120...");
run_test("thermostat"); test("thermostat");
shell.invoke_command("show values"); shell.invoke_command("show values");
shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
@@ -1229,7 +1256,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "solar") { if (command == "solar") {
shell.printfln("Testing Solar"); shell.printfln("Testing Solar");
run_test("solar"); test("solar");
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on (1)sh uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on (1)sh
EMSESP::show_device_values(shell); EMSESP::show_device_values(shell);
@@ -1243,7 +1270,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "heatpump") { if (command == "heatpump") {
shell.printfln("Testing Heat Pump"); shell.printfln("Testing Heat Pump");
run_test("heatpump"); test("heatpump");
shell.invoke_command("call"); shell.invoke_command("call");
shell.invoke_command("call heatpump info"); shell.invoke_command("call heatpump info");
ok = true; ok = true;
@@ -1698,7 +1725,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "mixer") { if (command == "mixer") {
shell.printfln("Testing Mixer..."); shell.printfln("Testing Mixer...");
run_test("mixer"); test("mixer");
// check for error "No telegram type handler found for ID 0x255 (src 0x20)" // check for error "No telegram type handler found for ID 0x255 (src 0x20)"
uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A}); uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});

View File

@@ -22,6 +22,7 @@
#define EMSESP_TEST_H #define EMSESP_TEST_H
#include "emsesp.h" #include "emsesp.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
namespace emsesp { namespace emsesp {
@@ -60,8 +61,8 @@ namespace emsesp {
class Test { class Test {
public: public:
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & data = ""); static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & id1 = "", const std::string & id2 = "");
static bool run_test(const char * command, int8_t id = 0); static bool test(const std::string & command, int8_t id1 = -1, int8_t id2 = -1);
static void dummy_mqtt_commands(const char * message); static void dummy_mqtt_commands(const char * message);
static void rx_telegram(const std::vector<uint8_t> & data); static void rx_telegram(const std::vector<uint8_t> & data);
static void uart_telegram(const std::vector<uint8_t> & rx_data); static void uart_telegram(const std::vector<uint8_t> & rx_data);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.5-test.8" #define EMSESP_APP_VERSION "3.6.5-test.10"

View File

@@ -53,7 +53,7 @@ void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
// For HTTP POSTS with an optional JSON body // For HTTP POSTS with an optional JSON body
// HTTP_POST | HTTP_PUT | HTTP_PATCH // HTTP_POST | HTTP_PUT | HTTP_PATCH
// POST /{device}[/{hc|id}][/{name}] // POST /{device}[/{hc|id}][/{name}]
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) { void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant json) {
// if no body then treat it as a secure GET // if no body then treat it as a secure GET
if (!json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
webAPIService_get(request); webAPIService_get(request);
@@ -134,8 +134,8 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
// if we're returning single values, just sent as plain text // if we're returning single values, just sent as plain text
// https://github.com/emsesp/EMS-ESP32/issues/462#issuecomment-1093877210 // https://github.com/emsesp/EMS-ESP32/issues/462#issuecomment-1093877210
if (output.containsKey("api_data")) { if (output.containsKey("api_data")) {
JsonVariant data = output["api_data"]; String data = output["api_data"].as<String>();
request->send(200, "text/plain; charset=utf-8", data.as<String>()); request->send(200, "text/plain; charset=utf-8", data);
api_count_++; api_count_++;
delete response; delete response;
return; return;

View File

@@ -31,7 +31,7 @@ class WebAPIService {
public: public:
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager); WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json); // for POSTs void webAPIService_post(AsyncWebServerRequest * request, JsonVariant json); // for POSTs
void webAPIService_get(AsyncWebServerRequest * request); // for GETs void webAPIService_get(AsyncWebServerRequest * request); // for GETs
static uint32_t api_count() { static uint32_t api_count() {

View File

@@ -48,6 +48,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) {
for (const CustomEntityItem & entityItem : webEntity.customEntityItems) { for (const CustomEntityItem & entityItem : webEntity.customEntityItems) {
JsonObject ei = entity.add<JsonObject>(); JsonObject ei = entity.add<JsonObject>();
ei["id"] = counter++; // id is only used to render the table and must be unique ei["id"] = counter++; // id is only used to render the table and must be unique
ei["ram"] = entityItem.ram;
ei["device_id"] = entityItem.device_id; ei["device_id"] = entityItem.device_id;
ei["type_id"] = entityItem.type_id; ei["type_id"] = entityItem.type_id;
ei["offset"] = entityItem.offset; ei["offset"] = entityItem.offset;
@@ -73,7 +74,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, json); deserializeJson(doc, json);
root = doc.as<JsonObject>(); root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA); Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake custom entity file: "); Serial.print(" Using fake custom entity file: ");
serializeJson(root, Serial); serializeJson(root, Serial);
Serial.println(COLOR_RESET); Serial.println(COLOR_RESET);
@@ -88,6 +89,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
if (root["entities"].is<JsonArray>()) { if (root["entities"].is<JsonArray>()) {
for (const JsonObject ei : root["entities"].as<JsonArray>()) { for (const JsonObject ei : root["entities"].as<JsonArray>()) {
auto entityItem = CustomEntityItem(); auto entityItem = CustomEntityItem();
entityItem.ram = ei["ram"] | 0;
entityItem.device_id = ei["device_id"]; // send as numeric, will be converted to string in web entityItem.device_id = ei["device_id"]; // send as numeric, will be converted to string in web
entityItem.type_id = ei["type_id"]; entityItem.type_id = ei["type_id"];
entityItem.offset = ei["offset"]; entityItem.offset = ei["offset"];
@@ -96,6 +98,14 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.uom = ei["uom"]; entityItem.uom = ei["uom"];
entityItem.value_type = ei["value_type"]; entityItem.value_type = ei["value_type"];
entityItem.writeable = ei["writeable"]; entityItem.writeable = ei["writeable"];
entityItem.data = ei["value"].as<std::string>();
if (entityItem.ram == 1) {
entityItem.device_id = 0;
entityItem.type_id = 0;
entityItem.uom = 0;
entityItem.value_type = DeviceValueType::STRING;
entityItem.writeable = true;
}
if (entityItem.value_type == DeviceValueType::BOOL) { if (entityItem.value_type == DeviceValueType::BOOL) {
entityItem.value = EMS_VALUE_DEFAULT_BOOL; entityItem.value = EMS_VALUE_DEFAULT_BOOL;
@@ -107,7 +117,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.value = EMS_VALUE_DEFAULT_SHORT; entityItem.value = EMS_VALUE_DEFAULT_SHORT;
} else if (entityItem.value_type == DeviceValueType::USHORT) { } else if (entityItem.value_type == DeviceValueType::USHORT) {
entityItem.value = EMS_VALUE_DEFAULT_USHORT; entityItem.value = EMS_VALUE_DEFAULT_USHORT;
} else { // if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) { } else if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) {
entityItem.value = EMS_VALUE_DEFAULT_ULONG; entityItem.value = EMS_VALUE_DEFAULT_ULONG;
} }
if (entityItem.factor == 0) { if (entityItem.factor == 0) {
@@ -134,7 +144,9 @@ bool WebCustomEntityService::command_setvalue(const char * value, const std::str
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
for (CustomEntityItem & entityItem : *customEntityItems) { for (CustomEntityItem & entityItem : *customEntityItems) {
if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) { if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) {
if (entityItem.value_type == DeviceValueType::STRING) { if (entityItem.ram == 1) {
entityItem.data = value;
} else if (entityItem.value_type == DeviceValueType::STRING) {
char telegram[84]; char telegram[84];
strlcpy(telegram, value, sizeof(telegram)); strlcpy(telegram, value, sizeof(telegram));
uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
@@ -274,7 +286,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
for (const CustomEntityItem & entity : *customEntityItems) { for (const CustomEntityItem & entity : *customEntityItems) {
render_value(output, entity); render_value(output, entity);
} }
return (output.size() != 0); return true;
} }
char command_s[30]; char command_s[30];
@@ -297,6 +309,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
output["readable"] = true; output["readable"] = true;
output["writeable"] = entity.writeable; output["writeable"] = entity.writeable;
output["visible"] = true; output["visible"] = true;
if (entity.ram == 0) {
output["device_id"] = Helpers::hextoa(entity.device_id); output["device_id"] = Helpers::hextoa(entity.device_id);
output["type_id"] = Helpers::hextoa(entity.type_id); output["type_id"] = Helpers::hextoa(entity.type_id);
output["offset"] = entity.offset; output["offset"] = entity.offset;
@@ -305,10 +318,11 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
} else if (entity.value_type == DeviceValueType::STRING) { } else if (entity.value_type == DeviceValueType::STRING) {
output["bytes"] = (uint8_t)entity.factor; output["bytes"] = (uint8_t)entity.factor;
} }
}
render_value(output, entity, true); render_value(output, entity, true);
if (attribute_s) { if (attribute_s) {
if (output.containsKey(attribute_s)) { if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s]; String data = output[attribute_s].as<String>();
output.clear(); output.clear();
output["api_data"] = data; output["api_data"] = data;
return true; return true;
@@ -547,11 +561,22 @@ void WebCustomEntityService::fetch() {
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3}; const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
for (auto & entity : *customEntityItems) { for (auto & entity : *customEntityItems) {
if (entity.device_id > 0 && entity.type_id > 0) { // ths excludes also RAM type
bool needFetch = true;
for (const auto & emsdevice : EMSESP::emsdevices) {
if (entity.value_type != DeviceValueType::STRING && emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id)) {
needFetch = false;
break;
}
}
if (needFetch) {
EMSESP::send_read_request(entity.type_id, EMSESP::send_read_request(entity.type_id,
entity.device_id, entity.device_id,
entity.offset, entity.offset,
entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]); entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]);
} }
}
}
// EMSESP::logger().debug("fetch custom entities"); // EMSESP::logger().debug("fetch custom entities");
} }
@@ -563,8 +588,8 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3}; const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
for (auto & entity : *customEntityItems) { for (auto & entity : *customEntityItems) {
if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset == entity.offset) { && telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + (uint8_t)entity.factor)) {
auto data = Helpers::data_to_hex(telegram->message_data, telegram->message_length); auto data = Helpers::data_to_hex(telegram->message_data, (uint8_t)entity.factor);
if (entity.data != data) { if (entity.data != data) {
entity.data = data; entity.data = data;
if (Mqtt::publish_single()) { if (Mqtt::publish_single()) {
@@ -573,8 +598,7 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
has_change = true; has_change = true;
} }
} }
} } else if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) { && telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) {
uint32_t value = 0; uint32_t value = 0;
for (uint8_t i = 0; i < len[entity.value_type]; i++) { for (uint8_t i = 0; i < len[entity.value_type]; i++) {

View File

@@ -21,7 +21,7 @@
#define WebCustomEntityService_h #define WebCustomEntityService_h
#define EMSESP_CUSTOMENTITY_FILE "/config/emsespEntity.json" #define EMSESP_CUSTOMENTITY_FILE "/config/emsespEntity.json"
#define EMSESP_CUSTOMENTITY_SERVICE_PATH "/rest/customentities" // GET and POST #define EMSESP_CUSTOMENTITY_SERVICE_PATH "/rest/customEntities" // GET and POST
namespace emsesp { namespace emsesp {
@@ -38,6 +38,7 @@ class CustomEntityItem {
bool writeable; bool writeable;
uint32_t value; uint32_t value;
std::string data; std::string data;
uint8_t ram;
}; };
class WebCustomEntity { class WebCustomEntity {

View File

@@ -25,14 +25,7 @@ using namespace std::placeholders; // for `_1` etc
bool WebCustomization::_start = true; bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebCustomization::read, : _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
WebCustomization::update,
this,
server,
EMSESP_CUSTOMIZATION_SERVICE_PATH,
securityManager,
AuthenticationPredicates::IS_AUTHENTICATED)
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
, _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH, , _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH,
securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2), securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2),
AuthenticationPredicates::IS_AUTHENTICATED)) { AuthenticationPredicates::IS_AUTHENTICATED)) {
@@ -85,7 +78,7 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
entityJson["product_id"] = entityCustomization.product_id; entityJson["product_id"] = entityCustomization.product_id;
entityJson["device_id"] = entityCustomization.device_id; entityJson["device_id"] = entityCustomization.device_id;
// entries are in the form <XX><shortname>[|optional customname] e.g "08heatingactive|heating is on" // entries are in the form <XX><shortname>[optional customname] e.g "08heatingactive|heating is on"
JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>(); JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>();
for (std::string entity_id : entityCustomization.entity_ids) { for (std::string entity_id : entityCustomization.entity_ids) {
masked_entityJson.add(entity_id); masked_entityJson.add(entity_id);
@@ -103,7 +96,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, json); deserializeJson(doc, json);
root = doc.as<JsonObject>(); root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA); Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake customization file: "); Serial.print(" Using fake customization file: ");
serializeJson(root, Serial); serializeJson(root, Serial);
Serial.println(COLOR_RESET); Serial.println(COLOR_RESET);
@@ -239,7 +232,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
// takes a list of updated entities with new masks from the web UI // takes a list of updated entities with new masks from the web UI
// saves it in the customization service // saves it in the customization service
// and updates the entity list real-time // and updates the entity list real-time
void WebCustomizationService::customization_entities(AsyncWebServerRequest * request, JsonVariant & json) { void WebCustomizationService::customization_entities(AsyncWebServerRequest * request, JsonVariant json) {
bool need_reboot = false; bool need_reboot = false;
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
// find the device using the unique_id // find the device using the unique_id

View File

@@ -23,7 +23,6 @@
// GET // GET
#define DEVICES_SERVICE_PATH "/rest/devices" #define DEVICES_SERVICE_PATH "/rest/devices"
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities" #define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
// POST // POST
@@ -89,7 +88,6 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
private: private:
#endif #endif
HttpEndpoint<WebCustomization> _httpEndpoint;
FSPersistence<WebCustomization> _fsPersistence; FSPersistence<WebCustomization> _fsPersistence;
// GET // GET
@@ -97,7 +95,7 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
void device_entities(AsyncWebServerRequest * request); void device_entities(AsyncWebServerRequest * request);
// POST // POST
void customization_entities(AsyncWebServerRequest * request, JsonVariant & json); void customization_entities(AsyncWebServerRequest * request, JsonVariant json);
void reset_customization(AsyncWebServerRequest * request); // command void reset_customization(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _masked_entities_handler; AsyncCallbackJsonWebHandler _masked_entities_handler;

View File

@@ -227,7 +227,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
} }
// assumes the service has been checked for admin authentication // assumes the service has been checked for admin authentication
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) { void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
uint8_t unique_id = json["id"]; // unique ID uint8_t unique_id = json["id"]; // unique ID
const char * cmd = json["c"]; // the command const char * cmd = json["c"]; // the command
@@ -323,7 +323,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
// takes a temperaturesensor name and optional offset from the WebUI and update the customization settings // takes a temperaturesensor name and optional offset from the WebUI and update the customization settings
// via the temperaturesensor service // via the temperaturesensor service
void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json) { void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json) {
bool ok = false; bool ok = false;
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
JsonObject sensor = json; JsonObject sensor = json;
@@ -346,7 +346,7 @@ void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, J
} }
// update the analog record, or create a new one // update the analog record, or create a new one
void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json) { void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json) {
bool ok = false; bool ok = false;
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
JsonObject analog = json; JsonObject analog = json;

View File

@@ -47,9 +47,9 @@ class WebDataService {
void device_data(AsyncWebServerRequest * request); void device_data(AsyncWebServerRequest * request);
// POST // POST
void write_device_value(AsyncWebServerRequest * request, JsonVariant & json); void write_device_value(AsyncWebServerRequest * request, JsonVariant json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json); void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json); void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json);
void scan_devices(AsyncWebServerRequest * request); // command void scan_devices(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler; AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;

View File

@@ -211,7 +211,7 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
} }
// sets the values like level after a POST // sets the values like level after a POST
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) { void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
return; return;
} }

View File

@@ -64,7 +64,7 @@ class WebLogService : public uuid::log::Handler {
char * messagetime(char * out, const uint64_t t, const size_t bufsize); char * messagetime(char * out, const uint64_t t, const size_t bufsize);
void setValues(AsyncWebServerRequest * request, JsonVariant & json); void setValues(AsyncWebServerRequest * request, JsonVariant json);
AsyncCallbackJsonWebHandler setValues_; // for POSTs AsyncCallbackJsonWebHandler setValues_; // for POSTs

View File

@@ -62,7 +62,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, json); deserializeJson(doc, json);
root = doc.as<JsonObject>(); root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA); Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake scheduler file: "); Serial.print(" Using fake scheduler file: ");
serializeJson(root, Serial); serializeJson(root, Serial);
Serial.println(COLOR_RESET); Serial.println(COLOR_RESET);
@@ -177,7 +177,6 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
attribute_s = breakp + 1; attribute_s = breakp + 1;
} }
JsonVariant data;
for (const ScheduleItem & scheduleItem : *scheduleItems) { for (const ScheduleItem & scheduleItem : *scheduleItems) {
if (Helpers::toLower(scheduleItem.name) == Helpers::toLower(command_s)) { if (Helpers::toLower(scheduleItem.name) == Helpers::toLower(command_s)) {
output["name"] = scheduleItem.name; output["name"] = scheduleItem.name;
@@ -199,7 +198,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
} }
if (attribute_s && output.containsKey(attribute_s)) { if (attribute_s && output.containsKey(attribute_s)) {
data = output[attribute_s]; String data = output[attribute_s].as<String>();
output.clear(); output.clear();
output["api_data"] = data; output["api_data"] = data;
} }