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

View File

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

View File

@@ -37,7 +37,8 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
if (open) {
void generateToken();
}
}, [open, generateToken]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
return (
<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 SettingsApplication from './SettingsApplication';
import SettingsCustomEntities from './SettingsCustomEntities';
import SettingsCustomization from './SettingsCustomization';
import SettingsEntities from './SettingsEntities';
import SettingsScheduler from './SettingsScheduler';
import type { FC } from 'react';
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
@@ -27,7 +27,7 @@ const Settings: FC = () => {
<Route path="application" element={<SettingsApplication />} />
<Route path="customization" element={<SettingsCustomization />} />
<Route path="scheduler" element={<SettingsScheduler />} />
<Route path="customentities" element={<SettingsEntities />} />
<Route path="customentities" element={<SettingsCustomEntities />} />
<Route path="*" element={<Navigate replace to="/settings/application" />} />
</Routes>
</>

View File

@@ -1,5 +1,7 @@
import AddIcon from '@mui/icons-material/Add';
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 { Button, Typography, Box } from '@mui/material';
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 SettingsEntitiesDialog from './SettingsEntitiesDialog';
import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog';
import * as EMSESP from './api';
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import { entityItemValidation } from './validators';
@@ -21,7 +23,7 @@ import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'componen
import { useI18nContext } from 'i18n/i18n-react';
const SettingsEntities: FC = () => {
const SettingsCustomEntities: FC = () => {
const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
@@ -43,6 +45,7 @@ const SettingsEntities: FC = () => {
function hasEntityChanged(ei: EntityItem) {
return (
ei.id !== ei.o_id ||
ei.ram !== ei.o_ram ||
(ei?.name || '') !== (ei?.o_name || '') ||
ei.device_id !== ei.o_device_id ||
ei.type_id !== ei.o_type_id ||
@@ -51,7 +54,8 @@ const SettingsEntities: FC = () => {
ei.factor !== ei.o_factor ||
ei.value_type !== ei.o_value_type ||
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)
.map((condensed_ei) => ({
id: condensed_ei.id,
ram: condensed_ei.ram,
name: condensed_ei.name,
device_id: condensed_ei.device_id,
type_id: condensed_ei.type_id,
@@ -125,7 +130,8 @@ const SettingsEntities: FC = () => {
factor: condensed_ei.factor,
uom: condensed_ei.uom,
writeable: condensed_ei.writeable,
value_type: condensed_ei.value_type
value_type: condensed_ei.value_type,
value: condensed_ei.value
}))
})
.then(() => {
@@ -173,14 +179,16 @@ const SettingsEntities: FC = () => {
setSelectedEntityItem({
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
name: '',
device_id: '',
type_id: '',
ram: 0,
device_id: '0',
type_id: '0',
offset: 0,
factor: 1,
uom: 0,
value_type: 0,
writeable: false,
deleted: false
deleted: false,
value: ''
});
setDialogOpen(true);
};
@@ -212,18 +220,21 @@ const SettingsEntities: FC = () => {
<HeaderCell stiff>{LL.ID_OF(LL.DEVICE())}</HeaderCell>
<HeaderCell stiff>{LL.ID_OF(LL.TYPE(1))}</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>
</HeaderRow>
</Header>
<Body>
{tableList.map((ei: EntityItem) => (
<Row key={ei.name} item={ei} onClick={() => editEntityItem(ei)}>
<Cell>{ei.name}</Cell>
<Cell>{showHex(ei.device_id as number, 2)}</Cell>
<Cell>{showHex(ei.type_id as number, 3)}</Cell>
<Cell>{ei.offset}</Cell>
<Cell>{DeviceValueTypeNames[ei.value_type]}</Cell>
<Cell>
{ei.name}&nbsp;
{ei.writeable && <EditOutlinedIcon color="primary" sx={{ fontSize: 12 }} />}
</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>
</Row>
))}
@@ -244,7 +255,7 @@ const SettingsEntities: FC = () => {
{renderEntity()}
{selectedEntityItem && (
<SettingsEntitiesDialog
<SettingsCustomEntitiesDialog
open={dialogOpen}
creating={creating}
onClose={onDialogClose}
@@ -274,7 +285,10 @@ const SettingsEntities: FC = () => {
</Box>
<Box flexWrap="nowrap" whiteSpace="nowrap">
<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)}
</Button>
</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 { validate } from 'validators';
type SettingsEntitiesDialogProps = {
type SettingsCustomEntitiesDialogProps = {
open: boolean;
creating: boolean;
onClose: () => void;
@@ -39,14 +39,14 @@ type SettingsEntitiesDialogProps = {
validator: Schema;
};
const SettingsEntitiesDialog = ({
const SettingsCustomEntitiesDialog = ({
open,
creating,
onClose,
onSave,
selectedItem,
validator
}: SettingsEntitiesDialogProps) => {
}: SettingsCustomEntitiesDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -100,7 +100,7 @@ const SettingsEntitiesDialog = ({
<Box flexWrap="nowrap" whiteSpace="nowrap" />
</Box>
<Grid container spacing={2}>
<Grid item xs={8}>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="name"
@@ -111,6 +111,36 @@ const SettingsEntitiesDialog = ({
onChange={updateFormValue}
/>
</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}>
<BlockFormControlLabel
control={<Checkbox checked={editItem.writeable} onChange={updateFormValue} name="writeable" />}
@@ -212,7 +242,7 @@ const SettingsEntitiesDialog = ({
</Grid>
</>
)}
{editItem.value_type === DeviceValueType.STRING && (
{editItem.value_type === DeviceValueType.STRING && editItem.device_id !== '0' && (
<Grid item xs={4}>
<TextField
name="factor"
@@ -227,6 +257,8 @@ const SettingsEntitiesDialog = ({
/>
</Grid>
)}
</>
)}
</Grid>
</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
export const readCustomEntities = () =>
alovaInstance.Get<EntityItem[]>('/rest/customentities', {
alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
name: 'entities',
transformData(data: any) {
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,
KMIN,
K,
VOLTS
VOLTS,
MBAR
}
export const DeviceValueUOM_s = [
@@ -204,7 +205,8 @@ export const DeviceValueUOM_s = [
'l',
'K*min',
'K',
'V'
'V',
'mbar'
];
export enum AnalogType {
@@ -323,6 +325,7 @@ export enum ScheduleFlag {
export interface EntityItem {
id: number; // unique number
ram: number;
name: string;
device_id: number | string;
type_id: number | string;
@@ -334,6 +337,7 @@ export interface EntityItem {
writeable: boolean;
deleted?: boolean;
o_id?: number;
o_ram?: number;
o_name?: string;
o_device_id?: number | string;
o_type_id?: number | string;
@@ -343,6 +347,7 @@ export interface EntityItem {
o_value_type?: number;
o_deleted?: boolean;
o_writeable?: boolean;
o_value?: any;
}
export interface Entities {
@@ -398,6 +403,6 @@ export const DeviceValueTypeNames = [
'ULONG',
'TIME',
'ENUM',
'STRING',
'RAW',
'CMD'
];

View File

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

View File

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

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 {
return JsonVariantConst(collectionToVariant(data_), resources_);
return JsonVariantConst(getData(), resources_);
}
// 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.
// https://arduinojson.org/v7/api/jsonarrayconst/nesting/
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.

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.
// https://arduinojson.org/v7/api/JsonDocument/shrinktofit/
// https://arduinojson.org/v7/api/jsondocument/shrinktofit/
void shrinkToFit() {
resources_.shrinkToFit();
}

View File

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

View File

@@ -27,7 +27,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
: data_(data), resources_(resources) {}
operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_);
return JsonVariantConst(getData(), resources_);
}
// 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.
// https://arduinojson.org/v7/api/jsonobjectconst/nesting/
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.
@@ -101,8 +101,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
JsonVariantConst>::type
operator[](TChar* key) const {
return JsonVariantConst(
detail::ObjectData::getMember(data_, detail::adaptString(key)));
return JsonVariantConst(detail::ObjectData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
// DEPRECATED: always returns zero

View File

@@ -132,7 +132,8 @@ VariantRefBase<TDerived>::operator[](const TString& key) const {
template <typename TDerived>
template <typename T>
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();
return resources && !resources->overflowed();
}

View File

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

View File

@@ -70,7 +70,7 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
~MsgpackAsyncJsonResponse() {
}
JsonVariant & getRoot() {
JsonVariant getRoot() {
return _root;
}
bool _sourceValid() const {
@@ -114,7 +114,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
~AsyncJsonResponse() {
}
JsonVariant & getRoot() {
JsonVariant getRoot() {
return _root;
}
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 {
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
* subsequent requests.
*/
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant & json) {
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) {
String username = json["username"];
String password = json["password"];

View File

@@ -21,7 +21,7 @@ class AuthenticationService {
AsyncCallbackJsonWebHandler _signInHandler;
// endpoint functions
void signIn(AsyncWebServerRequest * request, JsonVariant & json);
void signIn(AsyncWebServerRequest * request, JsonVariant json);
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-Credentials", "true");
}
DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname); // TODO use hostname
DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname);
});
_apSettingsService.begin();
_ntpSettingsService.begin();

View File

@@ -13,77 +13,48 @@
using namespace std::placeholders; // for `_1` etc
template <class T>
class HttpGetEndpoint {
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);
}
class HttpEndpoint {
protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
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>()) {
request->send(400);
return;
@@ -105,29 +76,15 @@ class HttpPostEndpoint {
response->setLength();
request->send(response);
}
};
template <class T>
class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
public:
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
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) {
}
// for GET
void fetchSettings(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false);
JsonObject jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath) {
response->setLength();
request->send(response);
}
};

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>()) {
struct tm tm = {0};
String timeLocal = json["local_time"];

View File

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

View File

@@ -101,7 +101,7 @@ ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFu
}
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);
if (!predicate(authentication)) {
request->send(401);

View File

@@ -59,7 +59,7 @@ class PrettyAsyncJsonResponse {
~PrettyAsyncJsonResponse() {
}
JsonVariant & getRoot() {
JsonVariant getRoot() {
return _root;
}
@@ -104,7 +104,7 @@ class MsgpackAsyncJsonResponse {
~MsgpackAsyncJsonResponse() {
}
JsonVariant & getRoot() {
JsonVariant getRoot() {
return _root;
}
@@ -150,7 +150,7 @@ class AsyncJsonResponse {
~AsyncJsonResponse() {
}
JsonVariant & getRoot() {
JsonVariant getRoot() {
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 {
private:

View File

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

View File

@@ -100,7 +100,7 @@ ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFu
}
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);
if (!predicate(authentication)) {
request->send(401);

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@
; default_envs = lolin_s3
; default_envs = standalone
; default_envs = debug
default_envs = custom
; default_envs = custom
[env:esp32_4M]
; if using OTA enter your details below
@@ -29,16 +29,19 @@ default_envs = custom
; --port=8266
; --auth=ems-esp-neo
; upload_port = ems-esp.local
; for USB use one of these:
; for USB, here are some examples:
; upload_port = /dev/ttyUSB*
; 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: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
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:
; platform = espressif32
framework = arduino
@@ -68,13 +71,13 @@ build_flags =
-D EMSESP_TEST
-D EMSESP_DEBUG
-D CONFIG_ETH_ENABLED
; -D TASMOTA_SDK
-D TASMOTA_SDK
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
[env:lolin_s3]
upload_port = /dev/ttyACM0
upload_port = /dev/ttyUSB0
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
; pio run -e debug

View File

@@ -2,8 +2,8 @@
; override any settings with your own local ones in pio_local.ini
[platformio]
; default_envs = esp32_4M
default_envs = lolin_s3
default_envs = esp32_4M
; default_envs = lolin_s3
; default_envs = esp32_16M
; default_envs = standalone
@@ -50,11 +50,9 @@ extra_scripts =
[espressi32_base_tasmota]
; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap
; platform = https://github.com/tasmota/platform-espressif32.git ; latest development
; latest release with WiFi_secure.h
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip ; latest stable
; latest arduino 2.xx release:
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.11.01/platform-espressif32.zip
; latest arduino 3.0/IDF 5.1.(alpha 3):
; latest release with WiFi_secure.h, Arduino 2.0.14
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.01.00/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
framework = arduino
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 (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;

View File

@@ -128,14 +128,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
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) {
if (arguments.empty()) {
Test::run_test(shell, "default");
} else if (arguments.size() == 1) {
Test::run_test(shell, arguments.front());
} else {
} else if (arguments.size() == 2) {
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) {
@@ -531,15 +533,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
if (return_code == CommandRet::OK && json.size()) {
if (json.containsKey("api_data")) {
JsonVariant data = json["api_data"];
if (data.is<int>()) {
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 *>());
}
String data = json["api_data"].as<String>();
shell.println(data.c_str());
return;
}
serializeJsonPretty(doc, shell);
@@ -568,9 +563,9 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
}
return devices_list;
} 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());
if (Command::device_has_commands(device_type)) {
std::vector<std::string> command_list;
for (const auto & cf : Command::commands()) {
if (cf.device_type_ == device_type) {
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),
DeviceValueUOM::PERCENT,
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, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), 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);
}
void Boiler::process_HpPressure(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpSetDiffPress_, 9);
}
// HIU unit
// 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;
}
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 v;
if (Helpers::value2bool(value, v)) {

View File

@@ -223,6 +223,7 @@ class Boiler : public EMSdevice {
uint32_t meterHeat_;
uint8_t hpEA0_;
uint8_t hpPumpMode_;
uint8_t hpSetDiffPress_;
// Pool unit
int8_t poolSetTemp_;
@@ -334,6 +335,7 @@ class Boiler : public EMSdevice {
void process_HpPool(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_HpPressure(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_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_hpPumpMode(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);
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) {
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 + 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;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
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
}
// Mixer Setting 0x2CD
// Mixer Setting 0x2CC
void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, activated_, 0); // on = 0xFF
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) {
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 false;
@@ -260,7 +260,7 @@ bool Mixer::set_setValveTime(const char * value, const int8_t id) {
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
v = (v + 5) / 10;
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 false;
@@ -273,7 +273,7 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
}
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
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 false;

View File

@@ -583,6 +583,7 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
}
}
});
if (ignore) {
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
// 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"] = mask + tag_to_string(dv.tag) + " " + fullname;
} else {
obj["id"] = mask + fullname;
}
obj["id"] = dv.has_tag() ? mask + tag_to_string(dv.tag) + " " + fullname : mask + fullname; // suffix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// obj["id"] = dv.has_tag() ? mask + fullname + " " + tag_to_string(dv.tag) : mask + fullname; // suffix tag
// add commands and options
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
// 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_) {
// also show commands and entities that have an empty full name
JsonObject obj = output.add<JsonObject>();
@@ -1058,13 +1058,10 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
auto fullname = Helpers::translated_word(dv.fullname);
if (dv.type != DeviceValueType::CMD) {
if (fullname) {
if (dv.has_tag()) {
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname);
obj["n"] = name;
} else {
obj["n"] = fullname;
}
obj["n"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname : fullname; // prefix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// obj["n"] = (dv.has_tag()) ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
}
// 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();
if (!fullname.empty()) {
if (dv.has_tag()) {
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname.c_str());
json["fullname"] = name;
} else {
json["fullname"] = fullname;
}
json["fullname"] = dv.has_tag() ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
json["fullname"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname.c_str() : fullname; // prefix tag
}
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);
#endif
if (json.containsKey(attribute_s)) {
JsonVariant data = json[attribute_s];
String data = json[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;
@@ -1610,16 +1604,20 @@ bool EMSdevice::generate_values(JsonObject output, const uint8_t tag_filter, con
char name[80];
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
char short_name[20];
if (output_target == OUTPUT_TARGET::CONSOLE) {
snprintf(short_name, sizeof(short_name), " (%s)", dv.short_name);
} else {
strcpy(short_name, "");
}
// char short_name[20];
// if (output_target == OUTPUT_TARGET::CONSOLE) {
// snprintf(short_name, sizeof(short_name), "(%s)", dv.short_name);
// } else {
// strcpy(short_name, "");
// }
// add 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 {
snprintf(name, sizeof(name), "%s%s", fullname.c_str(), short_name);
snprintf(name, sizeof(name), "%s (%s)", fullname.c_str(), dv.short_name);
}
} else {
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 };
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_customization(JsonArray & output);
void generate_values_web_customization(JsonArray output);
void add_device_value(uint8_t tag,
void * value_p,

View File

@@ -110,7 +110,7 @@ const char * DeviceValue::DeviceValueUOM_s[] = {
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_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
K, // 22 - K
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
@@ -143,7 +144,8 @@ class DeviceValue {
DV_NUMOP_DIV100 = 100,
DV_NUMOP_MUL5 = -5,
DV_NUMOP_MUL10 = -10,
DV_NUMOP_MUL15 = -15
DV_NUMOP_MUL15 = -15,
DV_NUMOP_MUL50 = -50
};
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
std::size_t first_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));
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
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";
} else if (device_id == 0x00) {
return "All";
@@ -734,12 +733,16 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
} else if (emsdevice->is_device_id(dest)) {
dest_name = emsdevice->device_type_name();
}
// get the type name, any match will do
// get the type name
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);
}
}
}
}
// if we can't find names for the devices, use their hex values
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
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// 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 (!trace_raw_) {
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
// 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) {
// LOG_DEBUG("read telegram received, not processing");
return false;
@@ -924,13 +927,13 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
bool found = false;
bool knowndevice = false;
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;
found = emsdevice->handle_telegram(telegram);
// if we correctly processed the telegram then follow up with sending it via MQTT (if enabled)
if (found && Mqtt::connected()) {
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_) {
publish_id_ = 0;
}
@@ -947,7 +950,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
emsdevice->add_handlers_ignored(telegram->type_id);
}
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);
}
}
@@ -1021,7 +1024,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// 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) {
// don't add ourselves!
if (device_id == rxservice_.ems_bus_id()) {
if (device_id == EMSbus::ems_bus_id()) {
return false;
}
@@ -1062,7 +1065,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
device_p = &device;
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_type = DeviceType::HEATSOURCE;
break;
@@ -1265,7 +1268,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
#endif
// check first for echo
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
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length);
#ifdef EMSESP_UART_DEBUG
@@ -1336,7 +1339,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
wait_km_ = true;
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
}
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_k, "K")
MAKE_WORD_CUSTOM(uom_volts, "V")
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
// MQTT topics and prefixes
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
// 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
// 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(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(hpSetDiffPress, "hpsetdiffpress", "set differental pressure", "Pumpensolldruck", "", "", "", "", "", "", "", "") // TODO translate
// 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")

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_);
}
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_);
}
// 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_);
}
@@ -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
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);
}
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()) {
std::string payload_text;
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.
bool Mqtt::queue_ha(const char * topic, const JsonObjectConst & payload) {
bool Mqtt::queue_ha(const char * topic, const JsonObjectConst payload) {
if (!enabled()) {
return false;
}
@@ -817,7 +817,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSd
const int16_t dv_set_min,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObjectConst & dev_json) {
const JsonObjectConst dev_json) {
// ignore if name (fullname) is empty
if (!fullname || !en_name) {
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 char * topic, const char * 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 std::string & 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_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 JsonObjectConst & payload, const bool retain);
static bool queue_ha(const char * topic, const JsonObjectConst & payload);
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_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);
@@ -92,7 +92,7 @@ class Mqtt {
const int16_t dv_set_min,
const uint32_t dv_set_max,
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_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_;
}
static void entity_format(uint8_t n) {
entity_format_ = n;
}
static uint8_t discovery_type() {
return discovery_type_;
}

View File

@@ -632,7 +632,7 @@ void System::send_info_mqtt() {
}
// create the json for heartbeat
bool System::heartbeat_json(JsonObject output) {
void System::heartbeat_json(JsonObject output) {
uint8_t bus_status = EMSESP::bus_status();
if (bus_status == EMSESP::BUS_STATUS_TX_ERRORS) {
output["bus_status"] = "txerror";
@@ -684,8 +684,6 @@ bool System::heartbeat_json(JsonObject output) {
output["wifistrength"] = wifi_quality(rssi);
}
#endif
return true;
}
// send periodic MQTT message with system information
@@ -700,9 +698,8 @@ void System::send_heartbeat() {
JsonDocument doc;
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.
}
}
// initializes network
@@ -1471,7 +1468,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
#if defined(EMSESP_TEST)
// 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) {
return Test::run_test(value, id);
return Test::test(value, id);
}
#endif

View File

@@ -79,7 +79,7 @@ class System {
void syslog_init();
bool check_upgrade(bool factory_settings);
bool check_restore();
bool heartbeat_json(JsonObject output);
void heartbeat_json(JsonObject output);
void send_heartbeat();
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())) {
output["id"] = sensor.id();
output["name"] = sensor.name();
char val[10];
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));
}
@@ -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 (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;

View File

@@ -1,7 +1,6 @@
/*
* 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
* 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
// or http://ems-esp/api?device=system&cmd=test&data=boiler
bool Test::run_test(const char * command, int8_t id) {
if ((command == nullptr) || (strlen(command) == 0)) {
bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
if (cmd.empty()) {
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...");
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;
}
if (strcmp(command, "general") == 0) {
if (cmd == "general") {
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
@@ -77,7 +83,7 @@ bool Test::run_test(const char * command, int8_t id) {
//
#ifdef EMSESP_STANDALONE
if (strcmp(command, "heat_exchange") == 0) {
if (cmd == "heat_exchange") {
EMSESP::logger().info("Testing heating exchange...");
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;
}
if (strcmp(command, "2thermostats") == 0) {
if (cmd == "2thermostats") {
EMSESP::logger().info("Testing with multiple thermostats...");
add_device(0x08, 123); // GB072
@@ -121,7 +127,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "310") == 0) {
if (cmd == "310") {
EMSESP::logger().info("Adding a GB072/RC310 combo...");
add_device(0x08, 123); // GB072
@@ -148,7 +154,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "gateway") == 0) {
if (cmd == "gateway") {
EMSESP::logger().info("Adding a Gateway...");
// add 0x48 KM200, via a version command
@@ -168,7 +174,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "mixer") == 0) {
if (cmd == "mixer") {
EMSESP::logger().info("Adding a mixer...");
// add controller
@@ -190,7 +196,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "boiler") == 0) {
if (cmd == "boiler") {
EMSESP::logger().info("Adding boiler...");
add_device(0x08, 123); // Nefit Trendline
@@ -207,7 +213,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "thermostat") == 0) {
if (cmd == "thermostat") {
EMSESP::logger().info("Adding thermostat...");
add_device(0x10, 192); // FW120
@@ -220,7 +226,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "solar") == 0) {
if (cmd == "solar") {
EMSESP::logger().info("Adding solar...");
add_device(0x30, 163); // SM100
@@ -239,7 +245,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "heatpump") == 0) {
if (cmd == "heatpump") {
EMSESP::logger().info("Adding heatpump...");
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
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
// init stuff
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::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;
}
// 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;
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") {
shell.printfln("Testing adding a boiler, thermostat and sensors...");
run_test("general");
test("general");
// add sensors
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
if (command == "memory") {
shell.printfln("Testing memory by adding lots of devices and entities...");
run_test("memory");
test("memory");
shell.invoke_command("show values");
ok = true;
}
if (command == "custom_entities") {
shell.printfln("custom entities...");
run_test("general");
test("general");
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -318,7 +347,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "coldshot") {
shell.printfln("Testing coldshot...");
run_test("general");
test("general");
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -370,7 +399,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "modes") {
shell.printfln("Testing thermostat modes...");
run_test("general");
test("general");
shell.invoke_command("call thermostat mode auto");
shell.invoke_command("call thermostat mode Manuell"); // DE
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") {
shell.printfln("Testing Gateway...");
run_test("gateway");
test("gateway");
ok = true;
}
if (command == "310") {
shell.printfln("Testing RC310...");
run_test("310");
test("310");
shell.invoke_command("show devices");
shell.invoke_command("show values");
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") {
shell.printfln("Testing heat exchange...");
run_test("heat_exchange");
test("heat_exchange");
shell.invoke_command("show devices");
shell.invoke_command("show values");
ok = true;
@@ -530,7 +559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "2thermostats") {
shell.printfln("Testing multiple thermostats...");
run_test("2thermostats");
test("2thermostats");
shell.invoke_command("show values");
shell.invoke_command("show devices");
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::ha_enabled(false); // turn off ha
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
JsonDocument doc; // some absurd high number
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::nested_format(1);
run_test("boiler");
test("boiler");
shell.invoke_command("show devices");
shell.invoke_command("show values");
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") {
shell.printfln("Testing Shower Alert...");
run_test("boiler");
test("boiler");
// device type, command, data
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(2); // not nested
run_test("boiler");
run_test("thermostat");
run_test("solar");
run_test("mixer");
test("boiler");
test("thermostat");
test("solar");
test("mixer");
shell.invoke_command("call system publish");
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::send_response(false);
run_test("boiler");
// run_test("thermostat");
test("boiler");
// test("thermostat");
// 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
@@ -692,8 +721,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1);
// Mqtt::send_response(false);
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
shell.invoke_command("call boiler wwseltemp");
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") {
uint8_t n = 0;
if (!data.empty()) {
n = Helpers::atoint(data.c_str());
}
// n=1 = EMSESP::system_.HEALTHCHECK_NO_BUS
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
shell.printfln("Testing healthcheck with %d", n);
EMSESP::system_.healthcheck(n);
if (id1 == -1) {
id1 = 0;
}
shell.printfln("Testing healthcheck with %d", id1);
EMSESP::system_.healthcheck(id1);
ok = true;
}
@@ -763,7 +790,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true);
// Mqtt::send_response(false);
run_test("thermostat");
test("thermostat");
// shell.invoke_command("call thermostat seltemp");
// 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::send_response(false);
run_test("boiler");
test("boiler");
shell.invoke_command("call boiler wwseltemp");
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::send_response(false);
run_test("boiler");
test("boiler");
shell.invoke_command("call boiler wwseltemp");
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::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
AsyncWebServerRequest request;
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::bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
ok = true;
@@ -887,15 +914,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing API wwmode");
Mqtt::ha_enabled(false);
Mqtt::nested_format(1);
run_test("310");
test("310");
AsyncWebServerRequest request;
request.method(HTTP_POST);
JsonDocument doc;
JsonVariant json;
char data[] = "{\"value\":\"off\"}";
deserializeJson(doc, data);
char odata[] = "{\"value\":\"off\"}";
deserializeJson(doc, odata);
json = doc.as<JsonVariant>();
request.url("/api/thermostat/wwmode");
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::send_response(true);
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
AsyncWebServerRequest requestX;
JsonDocument docX;
@@ -1181,10 +1208,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing nested MQTT");
Mqtt::ha_enabled(false); // turn off HA Discovery to stop the chatter
run_test("boiler");
run_test("thermostat");
run_test("solar");
run_test("mixer");
test("boiler");
test("thermostat");
test("solar");
test("mixer");
// first with nested
Mqtt::nested_format(1);
@@ -1201,7 +1228,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "thermostat") {
shell.printfln("Testing adding a thermostat FW120...");
run_test("thermostat");
test("thermostat");
shell.invoke_command("show values");
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") {
shell.printfln("Testing Solar");
run_test("solar");
test("solar");
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on (1)sh
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") {
shell.printfln("Testing Heat Pump");
run_test("heatpump");
test("heatpump");
shell.invoke_command("call");
shell.invoke_command("call heatpump info");
ok = true;
@@ -1698,7 +1725,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "mixer") {
shell.printfln("Testing Mixer...");
run_test("mixer");
test("mixer");
// check for error "No telegram type handler found for ID 0x255 (src 0x20)"
uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});

View File

@@ -22,6 +22,7 @@
#define EMSESP_TEST_H
#include "emsesp.h"
#include <ESPAsyncWebServer.h>
namespace emsesp {
@@ -60,8 +61,8 @@ namespace emsesp {
class Test {
public:
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & data = "");
static bool run_test(const char * command, int8_t id = 0);
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & id1 = "", const std::string & id2 = "");
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 rx_telegram(const std::vector<uint8_t> & 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
// HTTP_POST | HTTP_PUT | HTTP_PATCH
// 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 (!json.is<JsonObject>()) {
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
// https://github.com/emsesp/EMS-ESP32/issues/462#issuecomment-1093877210
if (output.containsKey("api_data")) {
JsonVariant data = output["api_data"];
request->send(200, "text/plain; charset=utf-8", data.as<String>());
String data = output["api_data"].as<String>();
request->send(200, "text/plain; charset=utf-8", data);
api_count_++;
delete response;
return;

View File

@@ -31,7 +31,7 @@ class WebAPIService {
public:
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
static uint32_t api_count() {

View File

@@ -48,6 +48,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) {
for (const CustomEntityItem & entityItem : webEntity.customEntityItems) {
JsonObject ei = entity.add<JsonObject>();
ei["id"] = counter++; // id is only used to render the table and must be unique
ei["ram"] = entityItem.ram;
ei["device_id"] = entityItem.device_id;
ei["type_id"] = entityItem.type_id;
ei["offset"] = entityItem.offset;
@@ -73,7 +74,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
JsonDocument doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake custom entity file: ");
serializeJson(root, Serial);
Serial.println(COLOR_RESET);
@@ -88,6 +89,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
if (root["entities"].is<JsonArray>()) {
for (const JsonObject ei : root["entities"].as<JsonArray>()) {
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.type_id = ei["type_id"];
entityItem.offset = ei["offset"];
@@ -96,6 +98,14 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.uom = ei["uom"];
entityItem.value_type = ei["value_type"];
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) {
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
@@ -107,7 +117,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.value = EMS_VALUE_DEFAULT_SHORT;
} else if (entityItem.value_type == DeviceValueType::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;
}
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; });
for (CustomEntityItem & entityItem : *customEntityItems) {
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];
strlcpy(telegram, value, sizeof(telegram));
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) {
render_value(output, entity);
}
return (output.size() != 0);
return true;
}
char command_s[30];
@@ -297,6 +309,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
output["readable"] = true;
output["writeable"] = entity.writeable;
output["visible"] = true;
if (entity.ram == 0) {
output["device_id"] = Helpers::hextoa(entity.device_id);
output["type_id"] = Helpers::hextoa(entity.type_id);
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) {
output["bytes"] = (uint8_t)entity.factor;
}
}
render_value(output, entity, true);
if (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;
@@ -547,11 +561,22 @@ void WebCustomEntityService::fetch() {
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
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,
entity.device_id,
entity.offset,
entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]);
}
}
}
// 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};
for (auto & entity : *customEntityItems) {
if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset == entity.offset) {
auto data = Helpers::data_to_hex(telegram->message_data, telegram->message_length);
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + (uint8_t)entity.factor)) {
auto data = Helpers::data_to_hex(telegram->message_data, (uint8_t)entity.factor);
if (entity.data != data) {
entity.data = data;
if (Mqtt::publish_single()) {
@@ -573,8 +598,7 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
has_change = true;
}
}
}
if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
} else 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])) {
uint32_t value = 0;
for (uint8_t i = 0; i < len[entity.value_type]; i++) {

View File

@@ -21,7 +21,7 @@
#define WebCustomEntityService_h
#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 {
@@ -38,6 +38,7 @@ class CustomEntityItem {
bool writeable;
uint32_t value;
std::string data;
uint8_t ram;
};
class WebCustomEntity {

View File

@@ -25,14 +25,7 @@ using namespace std::placeholders; // for `_1` etc
bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebCustomization::read,
WebCustomization::update,
this,
server,
EMSESP_CUSTOMIZATION_SERVICE_PATH,
securityManager,
AuthenticationPredicates::IS_AUTHENTICATED)
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
: _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
, _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH,
securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2),
AuthenticationPredicates::IS_AUTHENTICATED)) {
@@ -85,7 +78,7 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
entityJson["product_id"] = entityCustomization.product_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>();
for (std::string entity_id : entityCustomization.entity_ids) {
masked_entityJson.add(entity_id);
@@ -103,7 +96,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
JsonDocument doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake customization file: ");
serializeJson(root, Serial);
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
// saves it in the customization service
// 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;
if (json.is<JsonObject>()) {
// find the device using the unique_id

View File

@@ -23,7 +23,6 @@
// GET
#define DEVICES_SERVICE_PATH "/rest/devices"
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
// POST
@@ -89,7 +88,6 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
private:
#endif
HttpEndpoint<WebCustomization> _httpEndpoint;
FSPersistence<WebCustomization> _fsPersistence;
// GET
@@ -97,7 +95,7 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
void device_entities(AsyncWebServerRequest * request);
// POST
void customization_entities(AsyncWebServerRequest * request, JsonVariant & json);
void customization_entities(AsyncWebServerRequest * request, JsonVariant json);
void reset_customization(AsyncWebServerRequest * request); // command
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
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) {
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) {
uint8_t unique_id = json["id"]; // unique ID
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
// 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;
if (json.is<JsonObject>()) {
JsonObject sensor = json;
@@ -346,7 +346,7 @@ void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, J
}
// 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;
if (json.is<JsonObject>()) {
JsonObject analog = json;

View File

@@ -47,9 +47,9 @@ class WebDataService {
void device_data(AsyncWebServerRequest * request);
// POST
void write_device_value(AsyncWebServerRequest * request, JsonVariant & json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void write_device_value(AsyncWebServerRequest * request, JsonVariant json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json);
void scan_devices(AsyncWebServerRequest * request); // command
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
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) {
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) {
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);
void setValues(AsyncWebServerRequest * request, JsonVariant & json);
void setValues(AsyncWebServerRequest * request, JsonVariant json);
AsyncCallbackJsonWebHandler setValues_; // for POSTs

View File

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