mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -71,3 +71,4 @@ words-found-verbose.txt
|
|||||||
|
|
||||||
# sonarlint
|
# sonarlint
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
package.json
|
||||||
|
|||||||
@@ -36,13 +36,14 @@ For more details go to [www.emsesp.org](https://www.emsesp.org/).
|
|||||||
- RT800 remote emulation [#1867](https://github.com/emsesp/EMS-ESP32/issues/1867)
|
- RT800 remote emulation [#1867](https://github.com/emsesp/EMS-ESP32/issues/1867)
|
||||||
- RC310 cooling parameters [#1857](https://github.com/emsesp/EMS-ESP32/issues/1857)
|
- RC310 cooling parameters [#1857](https://github.com/emsesp/EMS-ESP32/issues/1857)
|
||||||
- command `api/device/entities` [#1897](https://github.com/emsesp/EMS-ESP32/issues/1897)
|
- command `api/device/entities` [#1897](https://github.com/emsesp/EMS-ESP32/issues/1897)
|
||||||
- switchprogmode [#1903]<https://github.com/emsesp/EMS-ESP32/discussions/1903>
|
- switchprogmode [#1903](https://github.com/emsesp/EMS-ESP32/discussions/1903)
|
||||||
- autodetect and download firmware upgrades via the WebUI
|
- autodetect and download firmware upgrades via the WebUI
|
||||||
- command 'show log' that lists out the current weblog buffer, showing last messages.
|
- command 'show log' that lists out the current weblog buffer, showing last messages.
|
||||||
- default web log buffer to 25 lines for ESP32s with no PSRAM
|
- default web log buffer to 25 lines for ESP32s with no PSRAM
|
||||||
- try and determine correct board profile if none is set during boot
|
- try and determine correct board profile if none is set during boot
|
||||||
- auto Scroll in WebLog UI - reduced delay so incoming logs are faster
|
- auto Scroll in WebLog UI - reduced delay so incoming logs are faster
|
||||||
- uploading custom support info for Guest users [#2054]<https://github.com/emsesp/EMS-ESP32/issues/2054>
|
- uploading custom support info, shown to Guest users in Help page [#2054](https://github.com/emsesp/EMS-ESP32/issues/2054)
|
||||||
|
- feature: Dashboard showing all data (favorites, sensors, custom) [#1958](https://github.com/emsesp/EMS-ESP32/issues/1958)
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ For more details go to [www.emsesp.org](https://www.emsesp.org/).
|
|||||||
- Change key-names in JSON to be compliant and consistent [#1860](https://github.com/emsesp/EMS-ESP32/issues/1860)
|
- Change key-names in JSON to be compliant and consistent [#1860](https://github.com/emsesp/EMS-ESP32/issues/1860)
|
||||||
- Updates to webUI [#1920](https://github.com/emsesp/EMS-ESP32/issues/1920)
|
- Updates to webUI [#1920](https://github.com/emsesp/EMS-ESP32/issues/1920)
|
||||||
- Correct firmware naming #1933 [#1933](https://github.com/emsesp/EMS-ESP32/issues/1933)
|
- Correct firmware naming #1933 [#1933](https://github.com/emsesp/EMS-ESP32/issues/1933)
|
||||||
- Don't start Serial console if not connected to a Serial port. Will initiate manually after a CTRL-C
|
- Don't start Serial console if not connected to a Serial port. Will initiate manually after a CTRL-C/CTRL-S
|
||||||
- WebLog UI matches color schema of the terminal console correctly
|
- WebLog UI matches color schema of the terminal console correctly
|
||||||
- Updated Web libraries, ArduinoJson
|
- Updated Web libraries, ArduinoJson
|
||||||
- Help page doesn't show detailed tech info if the user is not 'admin' role [#2054](https://github.com/emsesp/EMS-ESP32/issues/2054)
|
- Help page doesn't show detailed tech info if the user is not 'admin' role [#2054](https://github.com/emsesp/EMS-ESP32/issues/2054)
|
||||||
|
|||||||
@@ -16,7 +16,15 @@ export default tseslint.config(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ignores: ['dist/*', 'build/*', '*.js', '**/*.cjs', '**/unpack.ts', 'i18n*.*']
|
ignores: [
|
||||||
|
'dist/*',
|
||||||
|
'*.mjs',
|
||||||
|
'build/*',
|
||||||
|
'*.js',
|
||||||
|
'**/*.cjs',
|
||||||
|
'**/unpack.ts',
|
||||||
|
'i18n*.*'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
"lint": "eslint . --fix"
|
"lint": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-xhr": "2.0.7",
|
"@alova/adapter-xhr": "2.0.8",
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
"@mui/icons-material": "^6.1.2",
|
"@mui/icons-material": "^6.1.3",
|
||||||
"@mui/material": "^6.1.2",
|
"@mui/material": "^6.1.3",
|
||||||
"@table-library/react-table-library": "4.1.7",
|
"@table-library/react-table-library": "4.1.7",
|
||||||
"alova": "3.0.17",
|
"alova": "3.1.0",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
@@ -35,21 +35,21 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.27.0",
|
||||||
"react-toastify": "^10.0.5",
|
"react-toastify": "^10.0.6",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.26.2",
|
||||||
"typescript": "^5.6.2"
|
"typescript": "^5.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.7",
|
"@babel/core": "^7.25.8",
|
||||||
"@eslint/js": "^9.12.0",
|
"@eslint/js": "^9.12.0",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.1",
|
||||||
"@preact/preset-vite": "^2.9.1",
|
"@preact/preset-vite": "^2.9.1",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@types/formidable": "^3",
|
"@types/formidable": "^3",
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.7.5",
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"eslint": "^9.12.0",
|
"eslint": "^9.12.0",
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"terser": "^5.34.1",
|
"terser": "^5.34.1",
|
||||||
"typescript-eslint": "8.8.0",
|
"typescript-eslint": "8.8.1",
|
||||||
"vite": "^5.4.8",
|
"vite": "^5.4.8",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1",
|
||||||
"vite-tsconfig-paths": "^5.0.1"
|
"vite-tsconfig-paths": "^5.0.1"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const App = () => {
|
|||||||
<CustomTheme>
|
<CustomTheme>
|
||||||
<AppRouting />
|
<AppRouting />
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="bottom-right"
|
position="bottom-left"
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
hideProgressBar={false}
|
hideProgressBar={false}
|
||||||
newestOnTop={false}
|
newestOnTop={false}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Navigate, Route, Routes } from 'react-router-dom';
|
|||||||
|
|
||||||
import CustomEntities from 'app/main/CustomEntities';
|
import CustomEntities from 'app/main/CustomEntities';
|
||||||
import Customizations from 'app/main/Customizations';
|
import Customizations from 'app/main/Customizations';
|
||||||
|
import Dashboard from 'app/main/Dashboard';
|
||||||
import Devices from 'app/main/Devices';
|
import Devices from 'app/main/Devices';
|
||||||
import Help from 'app/main/Help';
|
import Help from 'app/main/Help';
|
||||||
import Modules from 'app/main/Modules';
|
import Modules from 'app/main/Modules';
|
||||||
@@ -32,6 +33,7 @@ const AuthenticatedRouting = () => {
|
|||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||||
<Route path="/devices/*" element={<Devices />} />
|
<Route path="/devices/*" element={<Devices />} />
|
||||||
<Route path="/sensors/*" element={<Sensors />} />
|
<Route path="/sensors/*" element={<Sensors />} />
|
||||||
<Route path="/status/*" element={<Status />} />
|
<Route path="/status/*" element={<Status />} />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
Action,
|
Action,
|
||||||
Activity,
|
Activity,
|
||||||
CoreData,
|
CoreData,
|
||||||
|
DashboardItem,
|
||||||
DeviceData,
|
DeviceData,
|
||||||
DeviceEntity,
|
DeviceEntity,
|
||||||
Entities,
|
Entities,
|
||||||
@@ -19,7 +20,13 @@ import type {
|
|||||||
WriteTemperatureSensor
|
WriteTemperatureSensor
|
||||||
} from '../app/main/types';
|
} from '../app/main/types';
|
||||||
|
|
||||||
// DashboardDevices
|
// Dashboard
|
||||||
|
export const readDashboard = () =>
|
||||||
|
alovaInstance.Get<DashboardItem[]>('/rest/dashboardData', {
|
||||||
|
responseType: 'arraybuffer' // uses msgpack
|
||||||
|
});
|
||||||
|
|
||||||
|
// Devices
|
||||||
export const readCoreData = () => alovaInstance.Get<CoreData>(`/rest/coreData`);
|
export const readCoreData = () => alovaInstance.Get<CoreData>(`/rest/coreData`);
|
||||||
export const readDeviceData = (id: number) =>
|
export const readDeviceData = (id: number) =>
|
||||||
alovaInstance.Get<DeviceData>('/rest/deviceData', {
|
alovaInstance.Get<DeviceData>('/rest/deviceData', {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useBlocker } from 'react-router-dom';
|
import { useBlocker } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import { useInterval } from 'utils';
|
||||||
|
|
||||||
import { readCustomEntities, writeCustomEntities } from '../../api/app';
|
import { readCustomEntities, writeCustomEntities } from '../../api/app';
|
||||||
import SettingsCustomEntitiesDialog from './CustomEntitiesDialog';
|
import SettingsCustomEntitiesDialog from './CustomEntitiesDialog';
|
||||||
@@ -52,17 +53,11 @@ const CustomEntities = () => {
|
|||||||
initialData: []
|
initialData: []
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useInterval(() => {
|
||||||
const timer = setInterval(async () => {
|
if (!dialogOpen && !numChanges) {
|
||||||
if (dialogOpen || numChanges > 0) {
|
void fetchEntities();
|
||||||
return;
|
}
|
||||||
}
|
}, 3000);
|
||||||
await fetchEntities();
|
|
||||||
}, 2000);
|
|
||||||
return () => {
|
|
||||||
clearInterval(timer);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const { send: writeEntities } = useRequest(
|
const { send: writeEntities } = useRequest(
|
||||||
(data: Entities) => writeCustomEntities(data),
|
(data: Entities) => writeCustomEntities(data),
|
||||||
@@ -130,15 +125,10 @@ const CustomEntities = () => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.td {
|
.td {
|
||||||
border-top: 1px solid #565656;
|
|
||||||
border-bottom: 1px solid #565656;
|
border-bottom: 1px solid #565656;
|
||||||
}
|
}
|
||||||
&:hover .td {
|
&:hover .td {
|
||||||
border-top: 1px solid #177ac9;
|
background-color: #177ac9;
|
||||||
border-bottom: 1px solid #177ac9;
|
|
||||||
}
|
|
||||||
&:nth-of-type(odd) .td {
|
|
||||||
background-color: #303030;
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
});
|
});
|
||||||
@@ -295,7 +285,7 @@ const CustomEntities = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={2} color="warning.main">
|
||||||
<Typography variant="body2">{LL.ENTITIES_HELP_1()}</Typography>
|
<Typography variant="body1">{LL.ENTITIES_HELP_1()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{renderEntity()}
|
{renderEntity()}
|
||||||
|
|||||||
@@ -190,10 +190,7 @@ const Customizations = () => {
|
|||||||
}
|
}
|
||||||
&:hover .td {
|
&:hover .td {
|
||||||
border-top: 1px solid #177ac9;
|
border-top: 1px solid #177ac9;
|
||||||
border-bottom: 1px solid #177ac9;
|
background-color: #177ac9;
|
||||||
}
|
|
||||||
&:nth-of-type(odd) .td {
|
|
||||||
background-color: #303030;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Cell: `
|
Cell: `
|
||||||
@@ -427,7 +424,7 @@ const Customizations = () => {
|
|||||||
const renderDeviceList = () => (
|
const renderDeviceList = () => (
|
||||||
<>
|
<>
|
||||||
<Box mb={1} color="warning.main">
|
<Box mb={1} color="warning.main">
|
||||||
<Typography variant="body2">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
||||||
{rename ? (
|
{rename ? (
|
||||||
|
|||||||
363
interface/src/app/main/Dashboard.tsx
Normal file
363
interface/src/app/main/Dashboard.tsx
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { IconContext } from 'react-icons/lib';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
|
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
||||||
|
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
IconButton,
|
||||||
|
ToggleButton,
|
||||||
|
ToggleButtonGroup,
|
||||||
|
Tooltip,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
import Grid from '@mui/material/Grid2';
|
||||||
|
|
||||||
|
import { Body, Cell, Row, Table } from '@table-library/react-table-library/table';
|
||||||
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
|
import { CellTree, useTree } from '@table-library/react-table-library/tree';
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
|
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import { useInterval, usePersistState } from 'utils';
|
||||||
|
|
||||||
|
import { readDashboard, writeDeviceValue } from '../../api/app';
|
||||||
|
import DeviceIcon from './DeviceIcon';
|
||||||
|
import DevicesDialog from './DevicesDialog';
|
||||||
|
import { formatValue } from './deviceValue';
|
||||||
|
import {
|
||||||
|
type DashboardItem,
|
||||||
|
DeviceEntityMask,
|
||||||
|
DeviceType,
|
||||||
|
type DeviceValue
|
||||||
|
} from './types';
|
||||||
|
import { deviceValueItemValidation } from './validators';
|
||||||
|
|
||||||
|
const Dashboard = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
|
useLayoutTitle(LL.DASHBOARD());
|
||||||
|
|
||||||
|
const [showAll, setShowAll] = usePersistState(true, 'showAll');
|
||||||
|
|
||||||
|
const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState<boolean>(false);
|
||||||
|
const [parentNodes, setParentNodes] = useState<number>(0);
|
||||||
|
const [selectedDashboardItem, setSelectedDashboardItem] =
|
||||||
|
useState<DashboardItem>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
send: fetchDashboard,
|
||||||
|
error,
|
||||||
|
loading
|
||||||
|
} = useRequest(readDashboard, {
|
||||||
|
initialData: []
|
||||||
|
}).onSuccess((event) => {
|
||||||
|
if (event.data.length !== parentNodes) {
|
||||||
|
setParentNodes(event.data.length); // count number of parents/devices
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loading: submitting, send: sendDeviceValue } = useRequest(
|
||||||
|
(data: { id: number; c: string; v: unknown }) => writeDeviceValue(data),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
||||||
|
if (!selectedDashboardItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = selectedDashboardItem.parentNode.id; // this is the parent ID
|
||||||
|
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
|
||||||
|
.then(() => {
|
||||||
|
toast.success(LL.WRITE_CMD_SENT());
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setDeviceValueDialogOpen(false);
|
||||||
|
setSelectedDashboardItem(undefined);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const dashboard_theme = useTheme({
|
||||||
|
Table: `
|
||||||
|
--data-table-library_grid-template-columns: minmax(80px, auto) 120px 32px;
|
||||||
|
`,
|
||||||
|
BaseRow: `
|
||||||
|
font-size: 14px;
|
||||||
|
.td {
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Row: `
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
&:hover .td {
|
||||||
|
background-color: #177ac9;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
BaseCell: `
|
||||||
|
&:nth-of-type(2) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
&:nth-of-type(3) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = useTree(
|
||||||
|
{ nodes: data },
|
||||||
|
{
|
||||||
|
onChange: undefined // not used but needed
|
||||||
|
},
|
||||||
|
{
|
||||||
|
treeIcon: {
|
||||||
|
margin: '4px',
|
||||||
|
iconDefault: null,
|
||||||
|
iconRight: (
|
||||||
|
<ChevronRightIcon
|
||||||
|
sx={{ fontSize: 16, verticalAlign: 'middle' }}
|
||||||
|
color="info"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
iconDown: (
|
||||||
|
<ExpandMoreIcon
|
||||||
|
sx={{ fontSize: 16, verticalAlign: 'middle' }}
|
||||||
|
color="info"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
indentation: 45
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
useInterval(() => {
|
||||||
|
if (!deviceValueDialogOpen) {
|
||||||
|
void fetchDashboard();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showAll
|
||||||
|
? tree.fns.onAddAll(data.map((item: DashboardItem) => item.id)) // expand tree
|
||||||
|
: tree.fns.onRemoveAll(); // collapse tree
|
||||||
|
}, [parentNodes]);
|
||||||
|
|
||||||
|
const showType = (n?: string, t?: number) => {
|
||||||
|
// if we have a name show it
|
||||||
|
if (n) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
if (t) {
|
||||||
|
// otherwise pick translation based on type
|
||||||
|
switch (t) {
|
||||||
|
case DeviceType.CUSTOM:
|
||||||
|
return LL.CUSTOM_ENTITIES(0);
|
||||||
|
case DeviceType.ANALOGSENSOR:
|
||||||
|
return LL.ANALOG_SENSORS();
|
||||||
|
case DeviceType.TEMPERATURESENSOR:
|
||||||
|
return LL.TEMP_SENSORS();
|
||||||
|
case DeviceType.SCHEDULER:
|
||||||
|
return LL.SCHEDULER();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const showName = (di: DashboardItem) => {
|
||||||
|
if (di.id < 100) {
|
||||||
|
// if its a device (parent node) and has entities
|
||||||
|
if (di.nodes?.length) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span style="font-size: 14px">
|
||||||
|
<DeviceIcon type_id={di.t ?? 0} />
|
||||||
|
{showType(di.n, di.t)}
|
||||||
|
</span>
|
||||||
|
<span style={{ color: 'lightblue' }}> ({di.nodes?.length})</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (di.dv) {
|
||||||
|
return <span style="color:lightgrey">{di.dv.id.slice(2)}</span>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasMask = (id: string, mask: number) =>
|
||||||
|
(parseInt(id.slice(0, 2), 16) & mask) === mask;
|
||||||
|
|
||||||
|
const editDashboardValue = (di: DashboardItem) => {
|
||||||
|
if (me.admin && di.dv?.c) {
|
||||||
|
setSelectedDashboardItem(di);
|
||||||
|
setDeviceValueDialogOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleShowAll = (
|
||||||
|
event: React.MouseEvent<HTMLElement>,
|
||||||
|
toggle: boolean | null
|
||||||
|
) => {
|
||||||
|
if (toggle !== null) {
|
||||||
|
tree.fns.onToggleAll({});
|
||||||
|
setShowAll(toggle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderContent = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={fetchDashboard} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: 'black',
|
||||||
|
pt: 1,
|
||||||
|
pl: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container spacing={0} justifyContent="flex-start">
|
||||||
|
<Grid size={11}>
|
||||||
|
<Typography mb={2} variant="body1" color="warning">
|
||||||
|
{LL.DASHBOARD_1()}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={1} alignItems="end">
|
||||||
|
<ToggleButtonGroup
|
||||||
|
color="primary"
|
||||||
|
size="small"
|
||||||
|
value={showAll}
|
||||||
|
exclusive
|
||||||
|
onChange={handleShowAll}
|
||||||
|
>
|
||||||
|
<ToggleButton value={true}>
|
||||||
|
<UnfoldMoreIcon sx={{ fontSize: 14 }} />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value={false}>
|
||||||
|
<UnfoldLessIcon sx={{ fontSize: 14 }} />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
padding={1}
|
||||||
|
justifyContent="center"
|
||||||
|
flexDirection="column"
|
||||||
|
sx={{
|
||||||
|
borderRadius: 1,
|
||||||
|
border: '1px solid grey'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconContext.Provider
|
||||||
|
value={{
|
||||||
|
color: 'lightblue',
|
||||||
|
size: '16',
|
||||||
|
style: { verticalAlign: 'middle' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!loading && data.length === 0 ? (
|
||||||
|
<Typography variant="subtitle2" color="warning">
|
||||||
|
{LL.NO_DATA()}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Table
|
||||||
|
data={{ nodes: data }}
|
||||||
|
theme={dashboard_theme}
|
||||||
|
layout={{ custom: true }}
|
||||||
|
tree={tree}
|
||||||
|
>
|
||||||
|
{(tableList: DashboardItem[]) => (
|
||||||
|
<Body>
|
||||||
|
{tableList.map((di: DashboardItem) => (
|
||||||
|
<Row
|
||||||
|
key={di.id}
|
||||||
|
item={di}
|
||||||
|
onClick={() => editDashboardValue(di)}
|
||||||
|
>
|
||||||
|
{di.id > 99 ? (
|
||||||
|
<>
|
||||||
|
<Cell>{showName(di)}</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
title={formatValue(LL, di.dv?.v, di.dv?.u)}
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
<span style={{ color: 'lightgrey' }}>
|
||||||
|
{formatValue(LL, di.dv?.v, di.dv?.u)}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
{me.admin &&
|
||||||
|
di.dv?.c &&
|
||||||
|
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && (
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => editDashboardValue(di)}
|
||||||
|
>
|
||||||
|
<EditIcon
|
||||||
|
color="primary"
|
||||||
|
sx={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</Cell>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<CellTree item={di}>{showName(di)}</CellTree>
|
||||||
|
<Cell />
|
||||||
|
<Cell />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
</Body>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</IconContext.Provider>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContent>
|
||||||
|
{renderContent()}
|
||||||
|
{selectedDashboardItem && selectedDashboardItem.dv && (
|
||||||
|
<DevicesDialog
|
||||||
|
open={deviceValueDialogOpen}
|
||||||
|
onClose={() => setDeviceValueDialogOpen(false)}
|
||||||
|
onSave={deviceValueDialogSave}
|
||||||
|
selectedItem={selectedDashboardItem.dv}
|
||||||
|
writeable={true}
|
||||||
|
validator={deviceValueItemValidation(selectedDashboardItem.dv)}
|
||||||
|
progress={submitting}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dashboard;
|
||||||
@@ -2,59 +2,52 @@ import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/
|
|||||||
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
||||||
import { FaSolarPanel } from 'react-icons/fa';
|
import { FaSolarPanel } from 'react-icons/fa';
|
||||||
import { GiHeatHaze, GiTap } from 'react-icons/gi';
|
import { GiHeatHaze, GiTap } from 'react-icons/gi';
|
||||||
|
import { MdPlaylistAdd } from 'react-icons/md';
|
||||||
|
import { MdMoreTime } from 'react-icons/md';
|
||||||
import {
|
import {
|
||||||
MdOutlineDevices,
|
MdOutlineDevices,
|
||||||
MdOutlinePool,
|
MdOutlinePool,
|
||||||
MdOutlineSensors,
|
MdOutlineSensors,
|
||||||
MdThermostatAuto
|
MdThermostatAuto
|
||||||
} from 'react-icons/md';
|
} from 'react-icons/md';
|
||||||
import { TiFlowSwitch } from 'react-icons/ti';
|
import { PiFan, PiGauge } from 'react-icons/pi';
|
||||||
|
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
|
||||||
import { VscVmConnect } from 'react-icons/vsc';
|
import { VscVmConnect } from 'react-icons/vsc';
|
||||||
|
|
||||||
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
import type { SvgIconProps } from '@mui/material';
|
||||||
|
|
||||||
import { DeviceType } from './types';
|
import { DeviceType } from './types';
|
||||||
|
|
||||||
|
const deviceIconLookup: {
|
||||||
|
[key in DeviceType]: React.ComponentType<SvgIconProps> | undefined;
|
||||||
|
} = {
|
||||||
|
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
|
||||||
|
[DeviceType.ANALOGSENSOR]: PiGauge,
|
||||||
|
[DeviceType.BOILER]: CgSmartHomeBoiler,
|
||||||
|
[DeviceType.HEATSOURCE]: CgSmartHomeBoiler,
|
||||||
|
[DeviceType.THERMOSTAT]: MdThermostatAuto,
|
||||||
|
[DeviceType.MIXER]: AiOutlineControl,
|
||||||
|
[DeviceType.SOLAR]: FaSolarPanel,
|
||||||
|
[DeviceType.HEATPUMP]: GiHeatHaze,
|
||||||
|
[DeviceType.GATEWAY]: AiOutlineGateway,
|
||||||
|
[DeviceType.SWITCH]: TiFlowSwitch,
|
||||||
|
[DeviceType.CONTROLLER]: VscVmConnect,
|
||||||
|
[DeviceType.CONNECT]: VscVmConnect,
|
||||||
|
[DeviceType.ALERT]: AiOutlineAlert,
|
||||||
|
[DeviceType.EXTENSION]: MdOutlineDevices,
|
||||||
|
[DeviceType.WATER]: GiTap,
|
||||||
|
[DeviceType.POOL]: MdOutlinePool,
|
||||||
|
[DeviceType.CUSTOM]: MdPlaylistAdd,
|
||||||
|
[DeviceType.UNKNOWN]: MdOutlineSensors,
|
||||||
|
[DeviceType.SYSTEM]: undefined,
|
||||||
|
[DeviceType.SCHEDULER]: MdMoreTime,
|
||||||
|
[DeviceType.GENERIC]: MdOutlineSensors,
|
||||||
|
[DeviceType.VENTILATION]: PiFan
|
||||||
|
};
|
||||||
|
|
||||||
const DeviceIcon = ({ type_id }: { type_id: DeviceType }) => {
|
const DeviceIcon = ({ type_id }: { type_id: DeviceType }) => {
|
||||||
switch (type_id) {
|
const Icon = deviceIconLookup[type_id];
|
||||||
case DeviceType.TEMPERATURESENSOR:
|
return Icon ? <Icon /> : null;
|
||||||
case DeviceType.ANALOGSENSOR:
|
|
||||||
return <MdOutlineSensors />;
|
|
||||||
case DeviceType.BOILER:
|
|
||||||
case DeviceType.HEATSOURCE:
|
|
||||||
return <CgSmartHomeBoiler />;
|
|
||||||
case DeviceType.THERMOSTAT:
|
|
||||||
return <MdThermostatAuto />;
|
|
||||||
case DeviceType.MIXER:
|
|
||||||
return <AiOutlineControl />;
|
|
||||||
case DeviceType.SOLAR:
|
|
||||||
return <FaSolarPanel />;
|
|
||||||
case DeviceType.HEATPUMP:
|
|
||||||
return <GiHeatHaze />;
|
|
||||||
case DeviceType.GATEWAY:
|
|
||||||
return <AiOutlineGateway />;
|
|
||||||
case DeviceType.SWITCH:
|
|
||||||
return <TiFlowSwitch />;
|
|
||||||
case DeviceType.CONTROLLER:
|
|
||||||
case DeviceType.CONNECT:
|
|
||||||
return <VscVmConnect />;
|
|
||||||
case DeviceType.ALERT:
|
|
||||||
return <AiOutlineAlert />;
|
|
||||||
case DeviceType.EXTENSION:
|
|
||||||
return <MdOutlineDevices />;
|
|
||||||
case DeviceType.WATER:
|
|
||||||
return <GiTap />;
|
|
||||||
case DeviceType.POOL:
|
|
||||||
return <MdOutlinePool />;
|
|
||||||
case DeviceType.CUSTOM:
|
|
||||||
return (
|
|
||||||
<PlaylistAddIcon
|
|
||||||
sx={{ color: 'lightblue', fontSize: 22, verticalAlign: 'middle' }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeviceIcon;
|
export default DeviceIcon;
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ import { useRequest } from 'alova/client';
|
|||||||
import { MessageBox, SectionContent, useLayoutTitle } from 'components';
|
import { MessageBox, SectionContent, useLayoutTitle } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import { useInterval } from 'utils';
|
||||||
|
|
||||||
import { readCoreData, readDeviceData, writeDeviceValue } from '../../api/app';
|
import { readCoreData, readDeviceData, writeDeviceValue } from '../../api/app';
|
||||||
import DeviceIcon from './DeviceIcon';
|
import DeviceIcon from './DeviceIcon';
|
||||||
import DashboardDevicesDialog from './DevicesDialog';
|
import DevicesDialog from './DevicesDialog';
|
||||||
import { formatValue } from './deviceValue';
|
import { formatValue } from './deviceValue';
|
||||||
import { DeviceEntityMask, DeviceType, DeviceValueUOM_s } from './types';
|
import { DeviceEntityMask, DeviceType, DeviceValueUOM_s } from './types';
|
||||||
import type { Device, DeviceValue } from './types';
|
import type { Device, DeviceValue } from './types';
|
||||||
@@ -77,7 +78,7 @@ const Devices = () => {
|
|||||||
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
|
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
|
||||||
const [onlyFav, setOnlyFav] = useState(false);
|
const [onlyFav, setOnlyFav] = useState(false);
|
||||||
const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false);
|
const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false);
|
||||||
const [showDeviceInfo, setShowDeviceInfo] = useState<boolean>(false);
|
const [showDeviceInfo, setShowDeviceInfo] = useState(false);
|
||||||
const [selectedDevice, setSelectedDevice] = useState<number>();
|
const [selectedDevice, setSelectedDevice] = useState<number>();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -95,7 +96,7 @@ const Devices = () => {
|
|||||||
(id: number) => readDeviceData(id),
|
(id: number) => readDeviceData(id),
|
||||||
{
|
{
|
||||||
initialData: {
|
initialData: {
|
||||||
data: []
|
nodes: []
|
||||||
},
|
},
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
@@ -147,22 +148,15 @@ const Devices = () => {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Row: `
|
Row: `
|
||||||
background-color: #1E1E1E;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background-color: #1E1E1E;
|
||||||
.td {
|
.td {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-top: 1px solid #565656;
|
|
||||||
border-bottom: 1px solid #565656;
|
|
||||||
}
|
}
|
||||||
&.tr.tr-body.row-select.row-select-single-selected {
|
&.tr.tr-body.row-select.row-select-single-selected {
|
||||||
background-color: #3d4752;
|
background-color: #177ac9;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
&:hover .td {
|
|
||||||
border-top: 1px solid #177ac9;
|
|
||||||
border-bottom: 1px solid #177ac9;
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -170,17 +164,21 @@ const Devices = () => {
|
|||||||
common_theme,
|
common_theme,
|
||||||
{
|
{
|
||||||
Table: `
|
Table: `
|
||||||
--data-table-library_grid-template-columns: 40px repeat(1, minmax(0, 1fr)) 130px;
|
--data-table-library_grid-template-columns: repeat(1, minmax(0, 1fr)) 130px;
|
||||||
`,
|
`,
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
.td {
|
// .td {
|
||||||
height: 42px;
|
// height: 42px;
|
||||||
}
|
// }
|
||||||
`,
|
`,
|
||||||
HeaderRow: `
|
HeaderRow: `
|
||||||
.th {
|
.th {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
`,
|
||||||
|
Row: `
|
||||||
|
&:hover .td {
|
||||||
|
background-color: #177ac9;
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -221,7 +219,10 @@ const Devices = () => {
|
|||||||
Row: `
|
Row: `
|
||||||
&:nth-of-type(odd) .td {
|
&:nth-of-type(odd) .td {
|
||||||
background-color: #303030;
|
background-color: #303030;
|
||||||
}
|
},
|
||||||
|
&:hover .td {
|
||||||
|
background-color: #177ac9;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -251,7 +252,7 @@ const Devices = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const dv_sort = useSort(
|
const dv_sort = useSort(
|
||||||
{ nodes: deviceData.data },
|
{ nodes: deviceData.nodes },
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
sortIcon: {
|
sortIcon: {
|
||||||
@@ -383,8 +384,8 @@ const Devices = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const data = onlyFav
|
const data = onlyFav
|
||||||
? deviceData.data.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
|
? deviceData.nodes.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
|
||||||
: deviceData.data;
|
: deviceData.nodes;
|
||||||
|
|
||||||
const csvData = data.reduce(
|
const csvData = data.reduce(
|
||||||
(csvString: string, rowItem: DeviceValue) =>
|
(csvString: string, rowItem: DeviceValue) =>
|
||||||
@@ -418,17 +419,11 @@ const Devices = () => {
|
|||||||
downloadBlob(new Blob([csvData], { type: 'text/csv;charset:utf-8' }));
|
downloadBlob(new Blob([csvData], { type: 'text/csv;charset:utf-8' }));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useInterval(() => {
|
||||||
const timer = setInterval(() => {
|
if (!deviceValueDialogOpen) {
|
||||||
if (deviceValueDialogOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedDevice ? void sendDeviceData(selectedDevice) : void sendCoreData();
|
selectedDevice ? void sendDeviceData(selectedDevice) : void sendCoreData();
|
||||||
}, 2000);
|
}
|
||||||
return () => {
|
}, 3000);
|
||||||
clearInterval(timer);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
||||||
const id = Number(device_select.state.id);
|
const id = Number(device_select.state.id);
|
||||||
@@ -527,57 +522,57 @@ const Devices = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderCoreData = () => (
|
const renderCoreData = () => (
|
||||||
<IconContext.Provider
|
<>
|
||||||
value={{
|
<IconContext.Provider
|
||||||
color: 'lightblue',
|
value={{
|
||||||
size: '18',
|
color: 'lightblue',
|
||||||
style: { verticalAlign: 'middle' }
|
size: '18',
|
||||||
}}
|
style: { verticalAlign: 'middle' }
|
||||||
>
|
}}
|
||||||
{!coreData.connected && (
|
>
|
||||||
<MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />
|
{!coreData.connected && (
|
||||||
)}
|
<MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />
|
||||||
|
)}
|
||||||
|
|
||||||
{coreData.connected && (
|
{coreData.connected && (
|
||||||
<Table
|
<Table
|
||||||
data={{ nodes: coreData.devices }}
|
data={{ nodes: coreData.devices }}
|
||||||
select={device_select}
|
select={device_select}
|
||||||
theme={device_theme}
|
theme={device_theme}
|
||||||
layout={{ custom: true }}
|
layout={{ custom: true }}
|
||||||
>
|
>
|
||||||
{(tableList: Device[]) => (
|
{(tableList: Device[]) => (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<Header>
|
||||||
<HeaderRow>
|
<HeaderRow>
|
||||||
<HeaderCell stiff />
|
<HeaderCell resize>{LL.DESCRIPTION()}</HeaderCell>
|
||||||
<HeaderCell resize>{LL.DESCRIPTION()}</HeaderCell>
|
<HeaderCell stiff>{LL.TYPE(0)}</HeaderCell>
|
||||||
<HeaderCell stiff>{LL.TYPE(0)}</HeaderCell>
|
</HeaderRow>
|
||||||
</HeaderRow>
|
</Header>
|
||||||
</Header>
|
<Body>
|
||||||
<Body>
|
{tableList.length === 0 && (
|
||||||
{tableList.length === 0 && (
|
<CircularProgress sx={{ margin: 1 }} size={18} />
|
||||||
<CircularProgress sx={{ margin: 1 }} size={18} />
|
)}
|
||||||
)}
|
{tableList.map((device: Device) => (
|
||||||
{tableList.map((device: Device) => (
|
<Row key={device.id} item={device}>
|
||||||
<Row key={device.id} item={device}>
|
<Cell>
|
||||||
<Cell stiff>
|
<DeviceIcon type_id={device.t} />
|
||||||
<DeviceIcon type_id={device.t} />
|
|
||||||
</Cell>
|
{device.n}
|
||||||
<Cell>
|
<span style={{ color: 'lightblue' }}>
|
||||||
{device.n}
|
({device.e})
|
||||||
<span style={{ color: 'lightblue' }}>
|
</span>
|
||||||
({device.e})
|
</Cell>
|
||||||
</span>
|
<Cell stiff>{device.tn}</Cell>
|
||||||
</Cell>
|
</Row>
|
||||||
<Cell stiff>{device.tn}</Cell>
|
))}
|
||||||
</Row>
|
</Body>
|
||||||
))}
|
</>
|
||||||
</Body>
|
)}
|
||||||
</>
|
</Table>
|
||||||
)}
|
)}
|
||||||
</Table>
|
</IconContext.Provider>
|
||||||
)}
|
</>
|
||||||
</IconContext.Provider>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const deviceValueDialogClose = () => {
|
const deviceValueDialogClose = () => {
|
||||||
@@ -611,8 +606,8 @@ const Devices = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const shown_data = onlyFav
|
const shown_data = onlyFav
|
||||||
? deviceData.data.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
|
? deviceData.nodes.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
|
||||||
: deviceData.data;
|
: deviceData.nodes;
|
||||||
|
|
||||||
const deviceIndex = coreData.devices.findIndex(
|
const deviceIndex = coreData.devices.findIndex(
|
||||||
(d) => d.id === device_select.state.id
|
(d) => d.id === device_select.state.id
|
||||||
@@ -733,7 +728,7 @@ const Devices = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={() => showDeviceValue(dv)}
|
onClick={() => showDeviceValue(dv)}
|
||||||
>
|
>
|
||||||
{dv.v === '' && dv.c ? (
|
{dv.v === '' ? (
|
||||||
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
||||||
) : (
|
) : (
|
||||||
<EditIcon color="primary" sx={{ fontSize: 16 }} />
|
<EditIcon color="primary" sx={{ fontSize: 16 }} />
|
||||||
@@ -757,7 +752,7 @@ const Devices = () => {
|
|||||||
{renderDeviceData()}
|
{renderDeviceData()}
|
||||||
{renderDeviceDetails()}
|
{renderDeviceDetails()}
|
||||||
{selectedDeviceValue && (
|
{selectedDeviceValue && (
|
||||||
<DashboardDevicesDialog
|
<DevicesDialog
|
||||||
open={deviceValueDialogOpen}
|
open={deviceValueDialogOpen}
|
||||||
onClose={deviceValueDialogClose}
|
onClose={deviceValueDialogClose}
|
||||||
onSave={deviceValueDialogSave}
|
onSave={deviceValueDialogSave}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { validate } from 'validators';
|
|||||||
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
||||||
import type { DeviceValue } from './types';
|
import type { DeviceValue } from './types';
|
||||||
|
|
||||||
interface DashboardDevicesDialogProps {
|
interface DevicesDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (as: DeviceValue) => void;
|
onSave: (as: DeviceValue) => void;
|
||||||
@@ -47,7 +47,7 @@ const DevicesDialog = ({
|
|||||||
writeable,
|
writeable,
|
||||||
validator,
|
validator,
|
||||||
progress
|
progress
|
||||||
}: DashboardDevicesDialogProps) => {
|
}: DevicesDialogProps) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [editItem, setEditItem] = useState<DeviceValue>(selectedItem);
|
const [editItem, setEditItem] = useState<DeviceValue>(selectedItem);
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
@@ -75,7 +75,10 @@ const DevicesDialog = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setUom = (uom: DeviceValueUOM) => {
|
const setUom = (uom?: DeviceValueUOM) => {
|
||||||
|
if (uom === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (uom) {
|
switch (uom) {
|
||||||
case DeviceValueUOM.HOURS:
|
case DeviceValueUOM.HOURS:
|
||||||
return LL.HOURS();
|
return LL.HOURS();
|
||||||
@@ -195,9 +198,9 @@ const DevicesDialog = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<WarningIcon color="warning" />}
|
startIcon={<WarningIcon color="warning" />}
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
onClick={save}
|
onClick={save}
|
||||||
color="info"
|
color="primary"
|
||||||
>
|
>
|
||||||
{selectedItem.v === '' && selectedItem.c ? LL.EXECUTE() : LL.UPDATE()}
|
{selectedItem.v === '' && selectedItem.c ? LL.EXECUTE() : LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { useRequest } from 'alova/client';
|
|||||||
import { SectionContent, useLayoutTitle } from 'components';
|
import { SectionContent, useLayoutTitle } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { saveFile } from 'utils/file';
|
import { saveFile } from 'utils';
|
||||||
|
|
||||||
import { API, callAction } from '../../api/app';
|
import { API, callAction } from '../../api/app';
|
||||||
import type { APIcall } from './types';
|
import type { APIcall } from './types';
|
||||||
@@ -147,7 +147,7 @@ const Help = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Box p={2} color="warning.main">
|
<Box p={2} color="warning.main">
|
||||||
<Typography mb={1} variant="body2">
|
<Typography mb={1} variant="body1">
|
||||||
{LL.HELP_INFORMATION_4()}
|
{LL.HELP_INFORMATION_4()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ const Modules = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={2} color="warning.main">
|
||||||
<Typography variant="body2">{LL.MODULES_DESCRIPTION()}</Typography>
|
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Table
|
<Table
|
||||||
data={{ nodes: modules }}
|
data={{ nodes: modules }}
|
||||||
|
|||||||
@@ -117,15 +117,10 @@ const Scheduler = () => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.td {
|
.td {
|
||||||
border-top: 1px solid #565656;
|
|
||||||
border-bottom: 1px solid #565656;
|
border-bottom: 1px solid #565656;
|
||||||
}
|
}
|
||||||
&:hover .td {
|
&:hover .td {
|
||||||
border-top: 1px solid #177ac9;
|
background-color: #177ac9;
|
||||||
border-bottom: 1px solid #177ac9;
|
|
||||||
}
|
|
||||||
&:nth-of-type(odd) .td {
|
|
||||||
background-color: #303030;
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
});
|
});
|
||||||
@@ -318,7 +313,7 @@ const Scheduler = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={2} color="warning.main">
|
||||||
<Typography variant="body2">{LL.SCHEDULER_HELP_1()}</Typography>
|
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{renderSchedule()}
|
{renderSchedule()}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ import {
|
|||||||
} from '@table-library/react-table-library/table';
|
} from '@table-library/react-table-library/table';
|
||||||
import { useTheme } from '@table-library/react-table-library/theme';
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
import type { State } from '@table-library/react-table-library/types/common';
|
import type { State } from '@table-library/react-table-library/types/common';
|
||||||
import { useAutoRequest, useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
import { SectionContent, useLayoutTitle } from 'components';
|
import { SectionContent, useLayoutTitle } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import { useInterval } from 'utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
readSensorData,
|
readSensorData,
|
||||||
@@ -59,7 +60,7 @@ const Sensors = () => {
|
|||||||
const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false);
|
const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false);
|
||||||
const [creating, setCreating] = useState<boolean>(false);
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
|
|
||||||
const { data: sensorData, send: fetchSensorData } = useAutoRequest(
|
const { data: sensorData, send: fetchSensorData } = useRequest(
|
||||||
() => readSensorData(),
|
() => readSensorData(),
|
||||||
{
|
{
|
||||||
initialData: {
|
initialData: {
|
||||||
@@ -67,8 +68,7 @@ const Sensors = () => {
|
|||||||
as: [],
|
as: [],
|
||||||
analog_enabled: false,
|
analog_enabled: false,
|
||||||
platform: 'ESP32'
|
platform: 'ESP32'
|
||||||
},
|
}
|
||||||
pollingTime: 2000
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -86,6 +86,12 @@ const Sensors = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useInterval(() => {
|
||||||
|
if (!temperatureDialogOpen && !analogDialogOpen) {
|
||||||
|
void fetchSensorData();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
const common_theme = useTheme({
|
const common_theme = useTheme({
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -110,19 +116,10 @@ const Sensors = () => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.td {
|
.td {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-top: 1px solid #565656;
|
|
||||||
border-bottom: 1px solid #565656;
|
border-bottom: 1px solid #565656;
|
||||||
}
|
}
|
||||||
&.tr.tr-body.row-select.row-select-single-selected {
|
|
||||||
background-color: #3d4752;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
&:hover .td {
|
&:hover .td {
|
||||||
border-top: 1px solid #177ac9;
|
background-color: #177ac9;
|
||||||
border-bottom: 1px solid #177ac9;
|
|
||||||
}
|
|
||||||
&:nth-of-type(odd) .td {
|
|
||||||
background-color: #303030;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Cell: `
|
Cell: `
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ const SensorsAnalogDialog = ({
|
|||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="error"
|
color="warning"
|
||||||
onClick={remove}
|
onClick={remove}
|
||||||
>
|
>
|
||||||
{LL.REMOVE()}
|
{LL.REMOVE()}
|
||||||
@@ -339,9 +339,9 @@ const SensorsAnalogDialog = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<WarningIcon color="warning" />}
|
startIcon={<WarningIcon color="warning" />}
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
onClick={save}
|
onClick={save}
|
||||||
color="info"
|
color="primary"
|
||||||
>
|
>
|
||||||
{creating ? LL.ADD(0) : LL.UPDATE()}
|
{creating ? LL.ADD(0) : LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -122,9 +122,9 @@ const SensorsTemperatureDialog = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<WarningIcon color="warning" />}
|
startIcon={<WarningIcon color="warning" />}
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
onClick={save}
|
onClick={save}
|
||||||
color="info"
|
color="primary"
|
||||||
>
|
>
|
||||||
{LL.UPDATE()}
|
{LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ const formatDurationMin = (LL: TranslationFunctions, duration_min: number) => {
|
|||||||
|
|
||||||
export function formatValue(
|
export function formatValue(
|
||||||
LL: TranslationFunctions,
|
LL: TranslationFunctions,
|
||||||
value: unknown,
|
value?: unknown,
|
||||||
uom: DeviceValueUOM
|
uom?: DeviceValueUOM
|
||||||
) {
|
) {
|
||||||
if (typeof value !== 'number') {
|
if (typeof value !== 'number' || uom === undefined || value === undefined) {
|
||||||
return (value === undefined ? '' : value) as string;
|
if (value === undefined || typeof value === 'boolean') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return value as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (uom) {
|
switch (uom) {
|
||||||
case DeviceValueUOM.HOURS:
|
case DeviceValueUOM.HOURS:
|
||||||
return value ? formatDurationMin(LL, value * 60) : LL.NUM_HOURS({ num: 0 });
|
return value ? formatDurationMin(LL, value * 60) : LL.NUM_HOURS({ num: 0 });
|
||||||
|
|||||||
@@ -114,10 +114,18 @@ export interface CoreData {
|
|||||||
devices: Device[];
|
devices: Device[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DashboardItem {
|
||||||
|
id: number; // unique index
|
||||||
|
t?: number; // type from DeviceType
|
||||||
|
n?: string; // name, optional
|
||||||
|
dv?: DeviceValue; // device value, optional
|
||||||
|
nodes?: DashboardItem[]; // children nodes, optional
|
||||||
|
}
|
||||||
|
|
||||||
export interface DeviceValue {
|
export interface DeviceValue {
|
||||||
id: string; // index, contains mask+name
|
id: string; // index, contains mask+name
|
||||||
v: unknown; // value, Number or String
|
v?: unknown; // value, Number, String or Boolean - can be undefined
|
||||||
u: number; // uom
|
u?: number; // uom, optional
|
||||||
c?: string; // command, optional
|
c?: string; // command, optional
|
||||||
l?: string[]; // list, optional
|
l?: string[]; // list, optional
|
||||||
h?: string; // help text, optional
|
h?: string; // help text, optional
|
||||||
@@ -125,8 +133,9 @@ export interface DeviceValue {
|
|||||||
m?: number; // min, optional
|
m?: number; // min, optional
|
||||||
x?: number; // max, optional
|
x?: number; // max, optional
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceData {
|
export interface DeviceData {
|
||||||
data: DeviceValue[];
|
nodes: DeviceValue[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceEntity {
|
export interface DeviceEntity {
|
||||||
@@ -299,7 +308,7 @@ export interface ScheduleItem {
|
|||||||
time: string; // also used for Condition and On Change
|
time: string; // also used for Condition and On Change
|
||||||
cmd: string;
|
cmd: string;
|
||||||
value: string;
|
value: string;
|
||||||
name: string; // is optional
|
name: string; // can be empty
|
||||||
o_id?: number;
|
o_id?: number;
|
||||||
o_active?: boolean;
|
o_active?: boolean;
|
||||||
o_deleted?: boolean;
|
o_deleted?: boolean;
|
||||||
@@ -382,10 +391,10 @@ export interface Entities {
|
|||||||
// matches emsdevice.h DeviceType
|
// matches emsdevice.h DeviceType
|
||||||
export const enum DeviceType {
|
export const enum DeviceType {
|
||||||
SYSTEM = 0,
|
SYSTEM = 0,
|
||||||
TEMPERATURESENSOR,
|
TEMPERATURESENSOR = 1,
|
||||||
ANALOGSENSOR,
|
ANALOGSENSOR = 2,
|
||||||
SCHEDULER,
|
SCHEDULER = 3,
|
||||||
CUSTOM,
|
CUSTOM = 4,
|
||||||
BOILER,
|
BOILER,
|
||||||
THERMOSTAT,
|
THERMOSTAT,
|
||||||
MIXER,
|
MIXER,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import {
|
|||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { saveFile } from 'utils/file';
|
import { saveFile } from 'utils';
|
||||||
|
|
||||||
const DownloadUpload = () => {
|
const DownloadUpload = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -221,7 +221,7 @@ const DownloadUpload = () => {
|
|||||||
{LL.DOWNLOAD(0)}
|
{LL.DOWNLOAD(0)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography mb={1} variant="body2" color="warning">
|
<Typography mb={1} variant="body1" color="warning">
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
@@ -269,7 +269,7 @@ const DownloadUpload = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box color="warning.main" sx={{ pb: 2 }}>
|
<Box color="warning.main" sx={{ pb: 2 }}>
|
||||||
<Typography variant="body2">{LL.UPLOAD_TEXT()}</Typography>
|
<Typography variant="body1">{LL.UPLOAD_TEXT()}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<SingleUpload doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const APStatus = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(APApi.readAPStatus, { pollingTime: 5000 });
|
} = useAutoRequest(APApi.readAPStatus, { pollingTime: 3000 });
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.STATUS_OF(LL.ACCESS_POINT(0)));
|
useLayoutTitle(LL.STATUS_OF(LL.ACCESS_POINT(0)));
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const SystemActivity = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(readActivity, { pollingTime: 2000 });
|
} = useAutoRequest(readActivity, { pollingTime: 3000 });
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const HardwareStatus = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(SystemApi.readSystemStatus, { pollingTime: 2000 });
|
} = useAutoRequest(SystemApi.readSystemStatus, { pollingTime: 3000 });
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const MqttStatus = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(MqttApi.readMqttStatus, { pollingTime: 5000 });
|
} = useAutoRequest(MqttApi.readMqttStatus, { pollingTime: 3000 });
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.STATUS_OF('MQTT'));
|
useLayoutTitle(LL.STATUS_OF('MQTT'));
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const NTPStatus = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(NTPApi.readNTPStatus, { pollingTime: 5000 });
|
} = useAutoRequest(NTPApi.readNTPStatus, { pollingTime: 3000 });
|
||||||
|
|
||||||
const [localTime, setLocalTime] = useState<string>('');
|
const [localTime, setLocalTime] = useState<string>('');
|
||||||
const [settingTime, setSettingTime] = useState<boolean>(false);
|
const [settingTime, setSettingTime] = useState<boolean>(false);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const NetworkStatus = () => {
|
|||||||
data,
|
data,
|
||||||
send: loadData,
|
send: loadData,
|
||||||
error
|
error
|
||||||
} = useAutoRequest(NetworkApi.readNetworkStatus, { pollingTime: 5000 });
|
} = useAutoRequest(NetworkApi.readNetworkStatus, { pollingTime: 3000 });
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.STATUS_OF(LL.NETWORK(1)));
|
useLayoutTitle(LL.STATUS_OF(LL.NETWORK(1)));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const RestartMonitor = () => {
|
|||||||
initialData: { status: 'Getting ready...' },
|
initialData: { status: 'Getting ready...' },
|
||||||
async middleware(_, next) {
|
async middleware(_, next) {
|
||||||
if (count++ >= 1) {
|
if (count++ >= 1) {
|
||||||
// skip first request (1 seconds) to allow AsyncWS to send its response
|
// skip first request (1 second) to allow AsyncWS to send its response
|
||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const SystemStatus = () => {
|
|||||||
error
|
error
|
||||||
} = useAutoRequest(readSystemStatus, {
|
} = useAutoRequest(readSystemStatus, {
|
||||||
initialData: [],
|
initialData: [],
|
||||||
pollingTime: 5000,
|
pollingTime: 3000,
|
||||||
async middleware(_, next) {
|
async middleware(_, next) {
|
||||||
if (!restarting) {
|
if (!restarting) {
|
||||||
await next();
|
await next();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -24,9 +24,11 @@ const Layout: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
|
|
||||||
useEffect(() => setMobileOpen(false), [pathname]);
|
useEffect(() => setMobileOpen(false), [pathname]);
|
||||||
|
|
||||||
|
// cache the object to prevent unnecessary re-renders
|
||||||
|
const obj = useMemo(() => ({ title, setTitle }), [title]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// TODO wrap title/setTitle in a useMemo()
|
<LayoutContext.Provider value={obj}>
|
||||||
<LayoutContext.Provider value={{ title, setTitle }}>
|
|
||||||
<LayoutAppBar title={title} onToggleDrawer={handleDrawerToggle} />
|
<LayoutAppBar title={title} onToggleDrawer={handleDrawerToggle} />
|
||||||
<LayoutDrawer mobileOpen={mobileOpen} onClose={handleDrawerToggle} />
|
<LayoutDrawer mobileOpen={mobileOpen} onClose={handleDrawerToggle} />
|
||||||
<Box component="main" sx={{ marginLeft: { md: `${DRAWER_WIDTH}px` } }}>
|
<Box component="main" sx={{ marginLeft: { md: `${DRAWER_WIDTH}px` } }}>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import PersonIcon from '@mui/icons-material/Person';
|
|||||||
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
||||||
import SensorsIcon from '@mui/icons-material/Sensors';
|
import SensorsIcon from '@mui/icons-material/Sensors';
|
||||||
import SettingsIcon from '@mui/icons-material/Settings';
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@@ -51,8 +52,8 @@ const LayoutMenu = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<List component="nav">
|
<List component="nav">
|
||||||
|
<LayoutMenuItem icon={StarIcon} label="Dashboard" to={`/dashboard`} />
|
||||||
<LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} />
|
<LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} />
|
||||||
<LayoutMenuItem icon={SensorsIcon} label={LL.SENSORS()} to={`/sensors`} />
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@@ -77,19 +78,19 @@ const LayoutMenu = () => {
|
|||||||
mb: '2px',
|
mb: '2px',
|
||||||
color: 'lightblue'
|
color: 'lightblue'
|
||||||
}}
|
}}
|
||||||
secondary={
|
// secondary={
|
||||||
LL.CUSTOMIZATIONS() +
|
// LL.CUSTOMIZATIONS() +
|
||||||
', ' +
|
// ', ' +
|
||||||
LL.SCHEDULER() +
|
// LL.SCHEDULER() +
|
||||||
', ' +
|
// ', ' +
|
||||||
LL.CUSTOM_ENTITIES(0) +
|
// LL.CUSTOM_ENTITIES(0) +
|
||||||
'...'
|
// '...'
|
||||||
}
|
// }
|
||||||
secondaryTypographyProps={{
|
// secondaryTypographyProps={{
|
||||||
noWrap: true,
|
// noWrap: true,
|
||||||
fontSize: 12,
|
// fontSize: 12,
|
||||||
color: menuOpen ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)'
|
// color: menuOpen ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)'
|
||||||
}}
|
// }}
|
||||||
sx={{ my: 0 }}
|
sx={{ my: 0 }}
|
||||||
/>
|
/>
|
||||||
<KeyboardArrowDown
|
<KeyboardArrowDown
|
||||||
@@ -103,6 +104,12 @@ const LayoutMenu = () => {
|
|||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
{menuOpen && (
|
{menuOpen && (
|
||||||
<>
|
<>
|
||||||
|
<LayoutMenuItem
|
||||||
|
icon={SensorsIcon}
|
||||||
|
label={LL.SENSORS()}
|
||||||
|
to={`/sensors`}
|
||||||
|
/>
|
||||||
|
|
||||||
<LayoutMenuItem
|
<LayoutMenuItem
|
||||||
icon={ConstructionIcon}
|
icon={ConstructionIcon}
|
||||||
label={LL.CUSTOMIZATIONS()}
|
label={LL.CUSTOMIZATIONS()}
|
||||||
@@ -142,8 +149,8 @@ const LayoutMenu = () => {
|
|||||||
</List>
|
</List>
|
||||||
<Divider />
|
<Divider />
|
||||||
<List>
|
<List>
|
||||||
<ListItem disablePadding onClick={handleClick}>
|
<ListItem disablePadding>
|
||||||
<ListItemButton>
|
<ListItemButton component="button" onClick={handleClick}>
|
||||||
<ListItemIcon sx={{ color: '#9e9e9e' }}>
|
<ListItemIcon sx={{ color: '#9e9e9e' }}>
|
||||||
<AccountCircleIcon />
|
<AccountCircleIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function fetchLoginRedirect(): Partial<Path> {
|
|||||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
||||||
clearLoginRedirect();
|
clearLoginRedirect();
|
||||||
return {
|
return {
|
||||||
pathname: signInPathname || `/devices`,
|
pathname: signInPathname || `/dashboard`,
|
||||||
search: (signInPathname && signInSearch) || undefined
|
search: (signInPathname && signInSearch) || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { redirect } from 'react-router-dom';
|
import { redirect } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@@ -67,17 +67,15 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
void refresh();
|
void refresh();
|
||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
|
// cache object to prevent re-renders
|
||||||
|
const obj = useMemo(
|
||||||
|
() => ({ signIn, signOut, me, refresh }),
|
||||||
|
[signIn, signOut, me, refresh]
|
||||||
|
);
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return (
|
return (
|
||||||
// TODO useMemo?
|
<AuthenticationContext.Provider value={obj}>
|
||||||
<AuthenticationContext.Provider
|
|
||||||
value={{
|
|
||||||
signIn,
|
|
||||||
signOut,
|
|
||||||
me,
|
|
||||||
refresh
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</AuthenticationContext.Provider>
|
</AuthenticationContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const de: Translation = {
|
|||||||
PLEASE_WAIT: 'Bitte warten',
|
PLEASE_WAIT: 'Bitte warten',
|
||||||
RESTARTING_PRE: 'Initialisierung',
|
RESTARTING_PRE: 'Initialisierung',
|
||||||
RESTARTING_POST: 'Vorbereitung',
|
RESTARTING_POST: 'Vorbereitung',
|
||||||
AUTO_SCROLL: 'Automatisches Scrollen'
|
AUTO_SCROLL: 'Automatisches Scrollen',
|
||||||
|
DASHBOARD: 'Dashboard',
|
||||||
|
NO_DATA: 'Keine Daten verfügbar',
|
||||||
|
DASHBOARD_1: 'Passen Sie Ihr Dashboard an, indem Sie EMS-Entitäten mithilfe des Moduls „Anpassungen“ als Favorit markieren.'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const en: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait',
|
PLEASE_WAIT: 'Please wait',
|
||||||
RESTARTING_PRE: 'Initializing',
|
RESTARTING_PRE: 'Initializing',
|
||||||
RESTARTING_POST: 'Preparing',
|
RESTARTING_POST: 'Preparing',
|
||||||
AUTO_SCROLL: 'Auto Scroll'
|
AUTO_SCROLL: 'Auto Scroll',
|
||||||
|
DASHBOARD: 'Dashboard',
|
||||||
|
NO_DATA: 'No data available',
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const fr: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const it: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const nl: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const no: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const pl: BaseTranslation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const sk: Translation = {
|
|||||||
PLEASE_WAIT: 'Čakajte prosím',
|
PLEASE_WAIT: 'Čakajte prosím',
|
||||||
RESTARTING_PRE: 'Prebieha inicializácia',
|
RESTARTING_PRE: 'Prebieha inicializácia',
|
||||||
RESTARTING_POST: 'Príprava',
|
RESTARTING_POST: 'Príprava',
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const sv: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -340,7 +340,10 @@ const tr: Translation = {
|
|||||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||||
RESTARTING_POST: 'Preparing', // TODO translate
|
RESTARTING_POST: 'Preparing', // TODO translate
|
||||||
AUTO_SCROLL: 'Auto Scroll' // TODO translate
|
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||||
|
DASHBOARD: 'Dashboard', // TODO translate
|
||||||
|
NO_DATA: 'No data available', // TODO translate
|
||||||
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module.' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -3,4 +3,7 @@ export * from './route';
|
|||||||
export * from './submit';
|
export * from './submit';
|
||||||
export * from './time';
|
export * from './time';
|
||||||
export * from './useRest';
|
export * from './useRest';
|
||||||
|
export * from './useInterval';
|
||||||
export * from './props';
|
export * from './props';
|
||||||
|
export * from './file';
|
||||||
|
export * from './usePersistState';
|
||||||
|
|||||||
25
interface/src/utils/useInterval.ts
Normal file
25
interface/src/utils/useInterval.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
// adapted from https://www.joshwcomeau.com/snippets/react-hooks/use-interval/
|
||||||
|
export const useInterval = (callback: () => void, delay: number) => {
|
||||||
|
const intervalRef = useRef<number | null>(null);
|
||||||
|
const savedCallback = useRef<() => void>(callback);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
savedCallback.current = callback;
|
||||||
|
}, [callback]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tick = () => savedCallback.current();
|
||||||
|
if (typeof delay === 'number') {
|
||||||
|
intervalRef.current = window.setInterval(tick, delay);
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current !== null) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [delay]);
|
||||||
|
|
||||||
|
return intervalRef;
|
||||||
|
};
|
||||||
26
interface/src/utils/usePersistState.ts
Normal file
26
interface/src/utils/usePersistState.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
export const usePersistState = <T>(
|
||||||
|
initial_value: T,
|
||||||
|
id: string
|
||||||
|
): [T, (new_state: T) => void] => {
|
||||||
|
// Set initial value
|
||||||
|
const _initial_value = useMemo(() => {
|
||||||
|
const local_storage_value_str = localStorage.getItem('state:' + id);
|
||||||
|
// If there is a value stored in localStorage, use that
|
||||||
|
if (local_storage_value_str) {
|
||||||
|
return JSON.parse(local_storage_value_str);
|
||||||
|
}
|
||||||
|
// Otherwise use initial_value that was passed to the function
|
||||||
|
return initial_value;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [state, setState] = useState(_initial_value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const state_str = JSON.stringify(state); // Stringified state
|
||||||
|
localStorage.setItem('state:' + id, state_str); // Set stringified state as item in localStorage
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
return [state, setState];
|
||||||
|
};
|
||||||
@@ -5,21 +5,21 @@ __metadata:
|
|||||||
version: 8
|
version: 8
|
||||||
cacheKey: 10c0
|
cacheKey: 10c0
|
||||||
|
|
||||||
"@alova/adapter-xhr@npm:2.0.7":
|
"@alova/adapter-xhr@npm:2.0.8":
|
||||||
version: 2.0.7
|
version: 2.0.8
|
||||||
resolution: "@alova/adapter-xhr@npm:2.0.7"
|
resolution: "@alova/adapter-xhr@npm:2.0.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@alova/shared": "npm:^1.0.5"
|
"@alova/shared": "npm:^1.0.6"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
alova: ^3.0.16
|
alova: ^3.0.20
|
||||||
checksum: 10c0/2c21313ef963df22af08ae652c310381f681948e2edca2f863cc92becd7c6e815771649c9a0b77e4193eb507a4bb5bb9e77f332fe194f86f00a4144845b7757e
|
checksum: 10c0/84b2f8f33733af3ed9418e7eb4d6195115b318717afc588147514d474632eaa703f72f753f18198d1a90da0a688c60a29345aabe0f93941c4ef089b7ac636204
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@alova/shared@npm:^1.0.5":
|
"@alova/shared@npm:^1.0.6":
|
||||||
version: 1.0.5
|
version: 1.0.6
|
||||||
resolution: "@alova/shared@npm:1.0.5"
|
resolution: "@alova/shared@npm:1.0.6"
|
||||||
checksum: 10c0/58d99d8b6b026e60c7184c55bdccd43d3b39bec598722135b272fd3c355942facc40f9433dceb6fcabb6631b87766c7227432c2a2bf3ae281d9a26b2eba2a69d
|
checksum: 10c0/7779bef946a6c9cca9a6369a9fa664be9fe929b0132a94fe60836b787844f884aef1eb69a1f8850003b92169d7495b772b59e40d8de138019386f32cdc689aad
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -90,9 +90,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/core@npm:^7.25.7":
|
"@babel/core@npm:^7.25.8":
|
||||||
version: 7.25.7
|
version: 7.25.8
|
||||||
resolution: "@babel/core@npm:7.25.7"
|
resolution: "@babel/core@npm:7.25.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ampproject/remapping": "npm:^2.2.0"
|
"@ampproject/remapping": "npm:^2.2.0"
|
||||||
"@babel/code-frame": "npm:^7.25.7"
|
"@babel/code-frame": "npm:^7.25.7"
|
||||||
@@ -100,16 +100,16 @@ __metadata:
|
|||||||
"@babel/helper-compilation-targets": "npm:^7.25.7"
|
"@babel/helper-compilation-targets": "npm:^7.25.7"
|
||||||
"@babel/helper-module-transforms": "npm:^7.25.7"
|
"@babel/helper-module-transforms": "npm:^7.25.7"
|
||||||
"@babel/helpers": "npm:^7.25.7"
|
"@babel/helpers": "npm:^7.25.7"
|
||||||
"@babel/parser": "npm:^7.25.7"
|
"@babel/parser": "npm:^7.25.8"
|
||||||
"@babel/template": "npm:^7.25.7"
|
"@babel/template": "npm:^7.25.7"
|
||||||
"@babel/traverse": "npm:^7.25.7"
|
"@babel/traverse": "npm:^7.25.7"
|
||||||
"@babel/types": "npm:^7.25.7"
|
"@babel/types": "npm:^7.25.8"
|
||||||
convert-source-map: "npm:^2.0.0"
|
convert-source-map: "npm:^2.0.0"
|
||||||
debug: "npm:^4.1.0"
|
debug: "npm:^4.1.0"
|
||||||
gensync: "npm:^1.0.0-beta.2"
|
gensync: "npm:^1.0.0-beta.2"
|
||||||
json5: "npm:^2.2.3"
|
json5: "npm:^2.2.3"
|
||||||
semver: "npm:^6.3.1"
|
semver: "npm:^6.3.1"
|
||||||
checksum: 10c0/dad20af39624086afc3a0910bd97ae712c9ad0e9dda09fc5da93876e8ea1802b63ddd81c44f4aa8a9834db46de801eaab1ce9b81ab54b4fe907ae052c24de136
|
checksum: 10c0/8411ea506e6f7c8a39ab5c1524b00589fa3b087edb47389708f7fe07170929192171734666e3ea10b95a951643a531a6d09eedfe071572c9ea28516646265086
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -403,6 +403,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@babel/parser@npm:^7.25.8":
|
||||||
|
version: 7.25.8
|
||||||
|
resolution: "@babel/parser@npm:7.25.8"
|
||||||
|
dependencies:
|
||||||
|
"@babel/types": "npm:^7.25.8"
|
||||||
|
bin:
|
||||||
|
parser: ./bin/babel-parser.js
|
||||||
|
checksum: 10c0/a1a13845b7e8dda4c970791814a4bbf60004969882f18f470e260ad822d2e1f8941948f851e9335895563610f240fa6c98481ce8019865e469502bbf21daafa4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@npm:^7.24.7":
|
"@babel/plugin-syntax-jsx@npm:^7.24.7":
|
||||||
version: 7.24.7
|
version: 7.24.7
|
||||||
resolution: "@babel/plugin-syntax-jsx@npm:7.24.7"
|
resolution: "@babel/plugin-syntax-jsx@npm:7.24.7"
|
||||||
@@ -551,6 +562,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@babel/types@npm:^7.25.8":
|
||||||
|
version: 7.25.8
|
||||||
|
resolution: "@babel/types@npm:7.25.8"
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser": "npm:^7.25.7"
|
||||||
|
"@babel/helper-validator-identifier": "npm:^7.25.7"
|
||||||
|
to-fast-properties: "npm:^2.0.0"
|
||||||
|
checksum: 10c0/55ca2d6df6426c98db2769ce884ce5e9de83a512ea2dd7bcf56c811984dc14351cacf42932a723630c5afcff2455809323decd645820762182f10b7b5252b59f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/babel-plugin@npm:^11.12.0":
|
"@emotion/babel-plugin@npm:^11.12.0":
|
||||||
version: 11.12.0
|
version: 11.12.0
|
||||||
resolution: "@emotion/babel-plugin@npm:11.12.0"
|
resolution: "@emotion/babel-plugin@npm:11.12.0"
|
||||||
@@ -627,7 +649,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/serialize@npm:^1.2.0, @emotion/serialize@npm:^1.3.0, @emotion/serialize@npm:^1.3.1":
|
"@emotion/serialize@npm:^1.2.0, @emotion/serialize@npm:^1.3.0, @emotion/serialize@npm:^1.3.1, @emotion/serialize@npm:^1.3.2":
|
||||||
version: 1.3.2
|
version: 1.3.2
|
||||||
resolution: "@emotion/serialize@npm:1.3.2"
|
resolution: "@emotion/serialize@npm:1.3.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1045,38 +1067,38 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/core-downloads-tracker@npm:^6.1.2":
|
"@mui/core-downloads-tracker@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/core-downloads-tracker@npm:6.1.2"
|
resolution: "@mui/core-downloads-tracker@npm:6.1.3"
|
||||||
checksum: 10c0/a685ac90a614be07c07bba752a6772be1caf06329e278c731f2a60b7712381c467adb3f5e0cfe7f2bc51b744ba76138f451853ff767c4739fa6379c8bc49d407
|
checksum: 10c0/250df05f7cb497fb950d20680c67ce5c0e33d32fb7c56dfd2edfe28c298542c35fc79039123f64ae51a379cd205d182f310bde613d3a36e0da40079d24998515
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/icons-material@npm:^6.1.2":
|
"@mui/icons-material@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/icons-material@npm:6.1.2"
|
resolution: "@mui/icons-material@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mui/material": ^6.1.2
|
"@mui/material": ^6.1.3
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/ae5f1134e3ac45914c2bfef76a31848a8dc655237a9e478959580f778c31d77e6e54a588ec0ec552d8c6a904298f02d7cf482a5d6df294389d9ea9da8109ef94
|
checksum: 10c0/38a47fcb22c80f2beeb1897aad66ac8641aad236169e9b324bae63596c1fab69a55e39f233d0c4976f2cf492cd135e4203a24e8b047baa2918d7ca8382358400
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/material@npm:^6.1.2":
|
"@mui/material@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/material@npm:6.1.2"
|
resolution: "@mui/material@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
"@mui/core-downloads-tracker": "npm:^6.1.2"
|
"@mui/core-downloads-tracker": "npm:^6.1.3"
|
||||||
"@mui/system": "npm:^6.1.2"
|
"@mui/system": "npm:^6.1.3"
|
||||||
"@mui/types": "npm:^7.2.17"
|
"@mui/types": "npm:^7.2.18"
|
||||||
"@mui/utils": "npm:^6.1.2"
|
"@mui/utils": "npm:^6.1.3"
|
||||||
"@popperjs/core": "npm:^2.11.8"
|
"@popperjs/core": "npm:^2.11.8"
|
||||||
"@types/react-transition-group": "npm:^4.4.11"
|
"@types/react-transition-group": "npm:^4.4.11"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
@@ -1087,7 +1109,7 @@ __metadata:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@emotion/react": ^11.5.0
|
"@emotion/react": ^11.5.0
|
||||||
"@emotion/styled": ^11.3.0
|
"@emotion/styled": ^11.3.0
|
||||||
"@mui/material-pigment-css": ^6.1.2
|
"@mui/material-pigment-css": ^6.1.3
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
@@ -1100,16 +1122,16 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/7825be6ec47f82b3b3469fd0724b818bcc488f9b42de275fed38de314c216dfcefeec1cd23a0e8f8e6355ca51fbb2554ee4011834d6188a9245a8d60e7be2398
|
checksum: 10c0/657f8d501b368208f69f02c4058f7d93d68d9b50c01812ea5c16117929448fa0e9ead6f49b9828f927b379075af72a991565632de926976a266b1162b3ea0f89
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/private-theming@npm:^6.1.2":
|
"@mui/private-theming@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/private-theming@npm:6.1.2"
|
resolution: "@mui/private-theming@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
"@mui/utils": "npm:^6.1.2"
|
"@mui/utils": "npm:^6.1.3"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
@@ -1117,16 +1139,17 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/b8f8ee447dde7ecd34b9b96c19d68cff6771a7d5a133b85c3ff2dcb19fc8f2375375de6695c439dcfb6cb94e28d0a7c0889e132bfa467700a883e0dd834e8080
|
checksum: 10c0/b84ca829a9f1fbcc15a8b43410108cc03bb65fff80b8ec908352dde5666c3722df60af8130ded9b6eabf02d202c536393adccb6ba13cad08da7edce76200a271
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/styled-engine@npm:^6.1.2":
|
"@mui/styled-engine@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/styled-engine@npm:6.1.2"
|
resolution: "@mui/styled-engine@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
"@emotion/cache": "npm:^11.13.1"
|
"@emotion/cache": "npm:^11.13.1"
|
||||||
|
"@emotion/serialize": "npm:^1.3.2"
|
||||||
"@emotion/sheet": "npm:^1.4.0"
|
"@emotion/sheet": "npm:^1.4.0"
|
||||||
csstype: "npm:^3.1.3"
|
csstype: "npm:^3.1.3"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
@@ -1139,19 +1162,19 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@emotion/styled":
|
"@emotion/styled":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/cad5a768d039a8e6652c5f33371ff68e82afedec75f340a0c4154ca4bf29877f23e89c041b0cbdad7d81c4fe4e356882d0de3d24157a05323a88e9703ed85d30
|
checksum: 10c0/3788657bfae25dc78bfc82f0077919a29ee18c5659e82f9b73de29afd57447397e0dee1416c3f25360b584b26d8116287512bf3a5563a16f5d8eabb03f990a95
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/system@npm:^6.1.2":
|
"@mui/system@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/system@npm:6.1.2"
|
resolution: "@mui/system@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
"@mui/private-theming": "npm:^6.1.2"
|
"@mui/private-theming": "npm:^6.1.3"
|
||||||
"@mui/styled-engine": "npm:^6.1.2"
|
"@mui/styled-engine": "npm:^6.1.3"
|
||||||
"@mui/types": "npm:^7.2.17"
|
"@mui/types": "npm:^7.2.18"
|
||||||
"@mui/utils": "npm:^6.1.2"
|
"@mui/utils": "npm:^6.1.3"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
csstype: "npm:^3.1.3"
|
csstype: "npm:^3.1.3"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
@@ -1167,28 +1190,28 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/b9240574f5db5a79d1ad1fc5dead2fc2b0dcaee2bc5515e6b8ea88cddd374f58adb18611fee65ae38ef165434712de425a6a5fc4d8d717d9b4f5a3716e2c3c46
|
checksum: 10c0/53934c4abcae93e1474cf3457e7fc2a43c6a57b51c0b5aba7561746dbab71192dd2716dde86230be7fd56bbef130260e4b8af61462a0453d5ba9da240dafefed
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/types@npm:^7.2.17":
|
"@mui/types@npm:^7.2.18":
|
||||||
version: 7.2.17
|
version: 7.2.18
|
||||||
resolution: "@mui/types@npm:7.2.17"
|
resolution: "@mui/types@npm:7.2.18"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/2dd77e88490d364f1f8408a5270583d323919461812768b804cae3874143d79714a862f14c7167f5f65109f7f1e90252ffee14473e3470dd1ae12d7b7308c6c4
|
checksum: 10c0/338404bdef7c7f9ebcd389ebbf429c44d2cc9c25c65d8669dc900a24b2c8718240482273bf6cd953578965e3838ad40a8e7376c71d3d9146be3afb88bff1b67a
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/utils@npm:^6.1.2":
|
"@mui/utils@npm:^6.1.3":
|
||||||
version: 6.1.2
|
version: 6.1.3
|
||||||
resolution: "@mui/utils@npm:6.1.2"
|
resolution: "@mui/utils@npm:6.1.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.25.6"
|
"@babel/runtime": "npm:^7.25.6"
|
||||||
"@mui/types": "npm:^7.2.17"
|
"@mui/types": "npm:^7.2.18"
|
||||||
"@types/prop-types": "npm:^15.7.13"
|
"@types/prop-types": "npm:^15.7.13"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
@@ -1199,7 +1222,7 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/ed56688610f3c04ad219066b5bd94b16c2c1318e2fc4d49e6d6d7b3800d10b4751185cbcc88431a3a1070a236f61e41238c06ef2998a3d65724ac6245a8b64e6
|
checksum: 10c0/2879a47b309565a3aa83177c94a9dddb67fcad5286ec1e5cec498543e07d7ee3c8930586a5ec36dc54a33c12c451ea8eb913d83a85127074c6cf8289b75d1ea9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1337,10 +1360,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@remix-run/router@npm:1.19.2":
|
"@remix-run/router@npm:1.20.0":
|
||||||
version: 1.19.2
|
version: 1.20.0
|
||||||
resolution: "@remix-run/router@npm:1.19.2"
|
resolution: "@remix-run/router@npm:1.20.0"
|
||||||
checksum: 10c0/ac7fc813350686705f2c29219e70e1e299d9a8e3b301e9e81f7e84f578c40c6462b590cf0d78863bac40dbc325b68c71ae070f4a1465793d1d1971b619618295
|
checksum: 10c0/2e017dea530717a6e93a16d478714c4c9165313a1c48e39172ec609bc20324ca6362e8ee2243602df6343644c9268d82a3f50f154d3bb8a17dddde6c37be6e83
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1644,7 +1667,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*, @types/node@npm:^22.7.4":
|
"@types/node@npm:*":
|
||||||
version: 22.7.4
|
version: 22.7.4
|
||||||
resolution: "@types/node@npm:22.7.4"
|
resolution: "@types/node@npm:22.7.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1653,6 +1676,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/node@npm:^22.7.5":
|
||||||
|
version: 22.7.5
|
||||||
|
resolution: "@types/node@npm:22.7.5"
|
||||||
|
dependencies:
|
||||||
|
undici-types: "npm:~6.19.2"
|
||||||
|
checksum: 10c0/cf11f74f1a26053ec58066616e3a8685b6bcd7259bc569738b8f752009f9f0f7f85a1b2d24908e5b0f752482d1e8b6babdf1fbb25758711ec7bb9500bfcd6e60
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/parse-json@npm:^4.0.0":
|
"@types/parse-json@npm:^4.0.0":
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
resolution: "@types/parse-json@npm:4.0.2"
|
resolution: "@types/parse-json@npm:4.0.2"
|
||||||
@@ -1667,12 +1699,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react-dom@npm:^18.3.0":
|
"@types/react-dom@npm:^18.3.1":
|
||||||
version: 18.3.0
|
version: 18.3.1
|
||||||
resolution: "@types/react-dom@npm:18.3.0"
|
resolution: "@types/react-dom@npm:18.3.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react": "npm:*"
|
"@types/react": "npm:*"
|
||||||
checksum: 10c0/6c90d2ed72c5a0e440d2c75d99287e4b5df3e7b011838cdc03ae5cd518ab52164d86990e73246b9d812eaf02ec351d74e3b4f5bd325bf341e13bf980392fd53b
|
checksum: 10c0/8b416551c60bb6bd8ec10e198c957910cfb271bc3922463040b0d57cf4739cdcd24b13224f8d68f10318926e1ec3cd69af0af79f0291b599a992f8c80d47f1eb
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1744,15 +1776,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@npm:8.8.0":
|
"@typescript-eslint/eslint-plugin@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.8.0"
|
resolution: "@typescript-eslint/eslint-plugin@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp": "npm:^4.10.0"
|
"@eslint-community/regexpp": "npm:^4.10.0"
|
||||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
"@typescript-eslint/scope-manager": "npm:8.8.1"
|
||||||
"@typescript-eslint/type-utils": "npm:8.8.0"
|
"@typescript-eslint/type-utils": "npm:8.8.1"
|
||||||
"@typescript-eslint/utils": "npm:8.8.0"
|
"@typescript-eslint/utils": "npm:8.8.1"
|
||||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
"@typescript-eslint/visitor-keys": "npm:8.8.1"
|
||||||
graphemer: "npm:^1.4.0"
|
graphemer: "npm:^1.4.0"
|
||||||
ignore: "npm:^5.3.1"
|
ignore: "npm:^5.3.1"
|
||||||
natural-compare: "npm:^1.4.0"
|
natural-compare: "npm:^1.4.0"
|
||||||
@@ -1763,66 +1795,66 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/98ac37587eda02a713710f0a62ca979833482024968f1d1735881718abe102a6b49707db4f1dac0d7c731d1cbf8111d829c5125348d4829ab6fad7a7b3b344e4
|
checksum: 10c0/020a0a482202b34c6665a56ec5902e38ae1870b2600ec1b2092de352b23099dde553781ee8323974f63962ebe164a6304f0019e937afb5cf7854b0e0163ad1ca
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/parser@npm:8.8.0":
|
"@typescript-eslint/parser@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/parser@npm:8.8.0"
|
resolution: "@typescript-eslint/parser@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
"@typescript-eslint/scope-manager": "npm:8.8.1"
|
||||||
"@typescript-eslint/types": "npm:8.8.0"
|
"@typescript-eslint/types": "npm:8.8.1"
|
||||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
"@typescript-eslint/typescript-estree": "npm:8.8.1"
|
||||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
"@typescript-eslint/visitor-keys": "npm:8.8.1"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/cf72a644b89c62cd55b09fa1d22b51a2c726714aac344a797f0c2ad80bfbabcb7567000fadd4ea8188aa1d923675bebdca06acc1d28ac1b8360bf28a36b46f3a
|
checksum: 10c0/2afd147ccec6754316d6837d6108a5d822eb6071e1a7355073288c232530bc3e49901d3f08755ce02d497110c531f3b3658eb46d0ff875a69d4f360b5f938cb4
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@npm:8.8.0":
|
"@typescript-eslint/scope-manager@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/scope-manager@npm:8.8.0"
|
resolution: "@typescript-eslint/scope-manager@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:8.8.0"
|
"@typescript-eslint/types": "npm:8.8.1"
|
||||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
"@typescript-eslint/visitor-keys": "npm:8.8.1"
|
||||||
checksum: 10c0/29ddf589ff0e465dbbf3eb87b79a29face4ec5a6cb617bbaafbac6ae8340d376b5b405bca762ee1c7a40cbdf7912a32734f9119f6864df048c7a0b2de21bdd3d
|
checksum: 10c0/6f697baf087aedc3f0f228ff964fd108a9dd33fe4e5cc6c914be6367c324cee55629e099832668042bedfec8cdc72c6ef2ca960ee26966dbcc75753059a1352f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@npm:8.8.0":
|
"@typescript-eslint/type-utils@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/type-utils@npm:8.8.0"
|
resolution: "@typescript-eslint/type-utils@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
"@typescript-eslint/typescript-estree": "npm:8.8.1"
|
||||||
"@typescript-eslint/utils": "npm:8.8.0"
|
"@typescript-eslint/utils": "npm:8.8.1"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
ts-api-utils: "npm:^1.3.0"
|
ts-api-utils: "npm:^1.3.0"
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/d6ee11f4686fb54daea1f436f73b96eb31a95f6e535abc0534abf5794e7597669a92d12300969c8afee0fc1912dbc1591664f7e37f0da5935016cc981b2921a8
|
checksum: 10c0/6edfc2b9fca5233dd922141f080377b677db1093ec3e702a3ab52d58f77b91c0fb69479d4d42f125536b8fc0ffa85c07c7de2f17cc4c6fa1df1226ec01e5608c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/types@npm:8.8.0":
|
"@typescript-eslint/types@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/types@npm:8.8.0"
|
resolution: "@typescript-eslint/types@npm:8.8.1"
|
||||||
checksum: 10c0/cd168fafcaf77641b023c4405ea3a8c30fbad1737abb5aec9fce67fe2ae20224b624b5a2e3e84900ba81dc7dd33343add3653763703a225326cc81356b182d09
|
checksum: 10c0/4b44857332a0b1bfafbeccb8be157f8266d9e226ac723f6af1272b9b670b49444423ddac733655163eb3b90e8c88393a68ab2d7f326f5775371eaf4b9ca31d7b
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@npm:8.8.0":
|
"@typescript-eslint/typescript-estree@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/typescript-estree@npm:8.8.0"
|
resolution: "@typescript-eslint/typescript-estree@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:8.8.0"
|
"@typescript-eslint/types": "npm:8.8.1"
|
||||||
"@typescript-eslint/visitor-keys": "npm:8.8.0"
|
"@typescript-eslint/visitor-keys": "npm:8.8.1"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
fast-glob: "npm:^3.3.2"
|
fast-glob: "npm:^3.3.2"
|
||||||
is-glob: "npm:^4.0.3"
|
is-glob: "npm:^4.0.3"
|
||||||
@@ -1832,31 +1864,31 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/9b9e849f6b2d4e250840ef8e05f55a97d6598adaf48c1e6df83084b94c30feca6a3e7916ee1c235178188d0db6364a877cbf8fe218c36d5f8d5acb50767f3273
|
checksum: 10c0/e3b9bc1e925c07833237044271cdc9bd8bdba3e2143dcfc5bf3bf481c89731b666a6fad25333a4b1980ac2f4c6f5e6e42c71206f73f3704e319f6b3b67463a6a
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/utils@npm:8.8.0":
|
"@typescript-eslint/utils@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/utils@npm:8.8.0"
|
resolution: "@typescript-eslint/utils@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||||
"@typescript-eslint/scope-manager": "npm:8.8.0"
|
"@typescript-eslint/scope-manager": "npm:8.8.1"
|
||||||
"@typescript-eslint/types": "npm:8.8.0"
|
"@typescript-eslint/types": "npm:8.8.1"
|
||||||
"@typescript-eslint/typescript-estree": "npm:8.8.0"
|
"@typescript-eslint/typescript-estree": "npm:8.8.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
checksum: 10c0/fcf2dfd4a2d9491aa096a29c2c1fdd891ca3c13933d20cfea44e51b3d10a397e7ed9a9cd71ac9a29e8c4706264ae00c25a29394e2a6bda3291be298062901f2c
|
checksum: 10c0/954a2e85ae56a3ebefb6e41fb33c59ffa886963860536e9729a35ecea55eefdc58858c7aa126048c4a61f4fd9997b4f7601e7884ed2b3e4e7a46c9e4617a9f29
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@npm:8.8.0":
|
"@typescript-eslint/visitor-keys@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "@typescript-eslint/visitor-keys@npm:8.8.0"
|
resolution: "@typescript-eslint/visitor-keys@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:8.8.0"
|
"@typescript-eslint/types": "npm:8.8.1"
|
||||||
eslint-visitor-keys: "npm:^3.4.3"
|
eslint-visitor-keys: "npm:^3.4.3"
|
||||||
checksum: 10c0/580ce74c9b09b9e6a6f3f0ac2d2f0c6a6b983a78ce3b2544822ee08107c57142858d674897f61ff32a9a5e8fca00c916545c159bb75d134f4380884642542d38
|
checksum: 10c0/6f917090b61277bd443aa851c532c4a9cc91ad57aedf185c5dff0c530f158cce84ef815833bd8deffa87f0bbf7a9f1abd1e02e30af2463c4e7f27c0c08f59080
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1864,23 +1896,23 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "EMS-ESP@workspace:."
|
resolution: "EMS-ESP@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@alova/adapter-xhr": "npm:2.0.7"
|
"@alova/adapter-xhr": "npm:2.0.8"
|
||||||
"@babel/core": "npm:^7.25.7"
|
"@babel/core": "npm:^7.25.8"
|
||||||
"@emotion/react": "npm:^11.13.3"
|
"@emotion/react": "npm:^11.13.3"
|
||||||
"@emotion/styled": "npm:^11.13.0"
|
"@emotion/styled": "npm:^11.13.0"
|
||||||
"@eslint/js": "npm:^9.12.0"
|
"@eslint/js": "npm:^9.12.0"
|
||||||
"@mui/icons-material": "npm:^6.1.2"
|
"@mui/icons-material": "npm:^6.1.3"
|
||||||
"@mui/material": "npm:^6.1.2"
|
"@mui/material": "npm:^6.1.3"
|
||||||
"@preact/compat": "npm:^18.3.1"
|
"@preact/compat": "npm:^18.3.1"
|
||||||
"@preact/preset-vite": "npm:^2.9.1"
|
"@preact/preset-vite": "npm:^2.9.1"
|
||||||
"@table-library/react-table-library": "npm:4.1.7"
|
"@table-library/react-table-library": "npm:4.1.7"
|
||||||
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
|
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
|
||||||
"@types/formidable": "npm:^3"
|
"@types/formidable": "npm:^3"
|
||||||
"@types/node": "npm:^22.7.4"
|
"@types/node": "npm:^22.7.5"
|
||||||
"@types/react": "npm:^18.3.11"
|
"@types/react": "npm:^18.3.11"
|
||||||
"@types/react-dom": "npm:^18.3.0"
|
"@types/react-dom": "npm:^18.3.1"
|
||||||
"@types/react-router-dom": "npm:^5.3.3"
|
"@types/react-router-dom": "npm:^5.3.3"
|
||||||
alova: "npm:3.0.17"
|
alova: "npm:3.1.0"
|
||||||
async-validator: "npm:^4.2.5"
|
async-validator: "npm:^4.2.5"
|
||||||
concurrently: "npm:^9.0.1"
|
concurrently: "npm:^9.0.1"
|
||||||
eslint: "npm:^9.12.0"
|
eslint: "npm:^9.12.0"
|
||||||
@@ -1893,13 +1925,13 @@ __metadata:
|
|||||||
react: "npm:^18.3.1"
|
react: "npm:^18.3.1"
|
||||||
react-dom: "npm:^18.3.1"
|
react-dom: "npm:^18.3.1"
|
||||||
react-icons: "npm:^5.3.0"
|
react-icons: "npm:^5.3.0"
|
||||||
react-router-dom: "npm:^6.26.2"
|
react-router-dom: "npm:^6.27.0"
|
||||||
react-toastify: "npm:^10.0.5"
|
react-toastify: "npm:^10.0.6"
|
||||||
rollup-plugin-visualizer: "npm:^5.12.0"
|
rollup-plugin-visualizer: "npm:^5.12.0"
|
||||||
terser: "npm:^5.34.1"
|
terser: "npm:^5.34.1"
|
||||||
typesafe-i18n: "npm:^5.26.2"
|
typesafe-i18n: "npm:^5.26.2"
|
||||||
typescript: "npm:^5.6.2"
|
typescript: "npm:^5.6.3"
|
||||||
typescript-eslint: "npm:8.8.0"
|
typescript-eslint: "npm:8.8.1"
|
||||||
vite: "npm:^5.4.8"
|
vite: "npm:^5.4.8"
|
||||||
vite-plugin-imagemin: "npm:^0.6.1"
|
vite-plugin-imagemin: "npm:^0.6.1"
|
||||||
vite-tsconfig-paths: "npm:^5.0.1"
|
vite-tsconfig-paths: "npm:^5.0.1"
|
||||||
@@ -1962,13 +1994,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"alova@npm:3.0.17":
|
"alova@npm:3.1.0":
|
||||||
version: 3.0.17
|
version: 3.1.0
|
||||||
resolution: "alova@npm:3.0.17"
|
resolution: "alova@npm:3.1.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@alova/shared": "npm:^1.0.5"
|
"@alova/shared": "npm:^1.0.6"
|
||||||
rate-limiter-flexible: "npm:^5.0.3"
|
rate-limiter-flexible: "npm:^5.0.3"
|
||||||
checksum: 10c0/e8a2ae885a3ff44dafec230d9388dc22b6445bb0cf8511fc9855b5a98ad9961941b0d33a7da874df23db4af0dba75872a470e3edebbdcc5ead8aecbc7fcc3d6b
|
checksum: 10c0/4f073e37fae112dff2c8b168d37220fa4c9475305d7689869c1338a91d1368f2b941c1131bc46214ddebfa72489b6c449b246e2b4ff484d1ff1486dd7172f694
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5971,39 +6003,39 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-router-dom@npm:^6.26.2":
|
"react-router-dom@npm:^6.27.0":
|
||||||
version: 6.26.2
|
version: 6.27.0
|
||||||
resolution: "react-router-dom@npm:6.26.2"
|
resolution: "react-router-dom@npm:6.27.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@remix-run/router": "npm:1.19.2"
|
"@remix-run/router": "npm:1.20.0"
|
||||||
react-router: "npm:6.26.2"
|
react-router: "npm:6.27.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=16.8"
|
react: ">=16.8"
|
||||||
react-dom: ">=16.8"
|
react-dom: ">=16.8"
|
||||||
checksum: 10c0/7515128a98eef0a6b2bf354ef9dfefad03556a06be00fa9220eda6526aaada8a42f294911083473d7ced6d7128c3088bd193218bbb3d62593f9f4f7053781c23
|
checksum: 10c0/7db48ffd0b387af0eed060ceaf42075d074e63fbd30f4cf60993526b3610883a9ff82615965001165ed69d2bf2f1bce05c594a21c8d0d845e7b9bf203201116e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-router@npm:6.26.2":
|
"react-router@npm:6.27.0":
|
||||||
version: 6.26.2
|
version: 6.27.0
|
||||||
resolution: "react-router@npm:6.26.2"
|
resolution: "react-router@npm:6.27.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@remix-run/router": "npm:1.19.2"
|
"@remix-run/router": "npm:1.20.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=16.8"
|
react: ">=16.8"
|
||||||
checksum: 10c0/0d15a39b419c99fb5ccad76388bfc4ee2b01323b3b1b694595a9f9ea28e1fbeea25486b5398f5d3d93922f5c6a9aa751b6bb27419488d85279f6ca5ff9e0a6bb
|
checksum: 10c0/440d6ee00890cec92a0c2183164149fbb96363efccf52bb132a964f44e51aec2f4b5a0520c67f6f17faddaa4097090fd76f7efe58263947532fceeb11dd4cdf3
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-toastify@npm:^10.0.5":
|
"react-toastify@npm:^10.0.6":
|
||||||
version: 10.0.5
|
version: 10.0.6
|
||||||
resolution: "react-toastify@npm:10.0.5"
|
resolution: "react-toastify@npm:10.0.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: "npm:^2.1.0"
|
clsx: "npm:^2.1.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=18"
|
react: ">=18"
|
||||||
react-dom: ">=18"
|
react-dom: ">=18"
|
||||||
checksum: 10c0/66c68ec3d6c017d9f32652d73bb925224921c6a80b629b9d481430d5b4fd504abb7a99995a64b9aef0fc31326c74f3cbe088b3287b978dd0c355079c4bbf4158
|
checksum: 10c0/4042b716d008295d0feab32488d1e88ec655a1b7a9176fa7d253c70387578a8a0c04aca0ff86d20e1722f3b4baadae8970f50f462940d67a90453c307dd350a9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -7001,37 +7033,37 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript-eslint@npm:8.8.0":
|
"typescript-eslint@npm:8.8.1":
|
||||||
version: 8.8.0
|
version: 8.8.1
|
||||||
resolution: "typescript-eslint@npm:8.8.0"
|
resolution: "typescript-eslint@npm:8.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/eslint-plugin": "npm:8.8.0"
|
"@typescript-eslint/eslint-plugin": "npm:8.8.1"
|
||||||
"@typescript-eslint/parser": "npm:8.8.0"
|
"@typescript-eslint/parser": "npm:8.8.1"
|
||||||
"@typescript-eslint/utils": "npm:8.8.0"
|
"@typescript-eslint/utils": "npm:8.8.1"
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/545f0ce051282921aff56288baf288cffe6f7bafee5149f1b87af2c67f81f8c2088924a2e0fc0f0dcd12692b6a97eca10149a619c8c85d4aaef2fe763938da8d
|
checksum: 10c0/d6793697fce239ef8838ced6e1e59940c30579c8f62c49bc605fdeda9f3f7a5c24bfddd997b142f8c411859dc0b9985ecdae569814dd4f8e6775e1899d55e9cc
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@npm:^5.6.2":
|
"typescript@npm:^5.6.3":
|
||||||
version: 5.6.2
|
version: 5.6.3
|
||||||
resolution: "typescript@npm:5.6.2"
|
resolution: "typescript@npm:5.6.3"
|
||||||
bin:
|
bin:
|
||||||
tsc: bin/tsc
|
tsc: bin/tsc
|
||||||
tsserver: bin/tsserver
|
tsserver: bin/tsserver
|
||||||
checksum: 10c0/3ed8297a8c7c56b7fec282532503d1ac795239d06e7c4966b42d4330c6cf433a170b53bcf93a130a7f14ccc5235de5560df4f1045eb7f3550b46ebed16d3c5e5
|
checksum: 10c0/44f61d3fb15c35359bc60399cb8127c30bae554cd555b8e2b46d68fa79d680354b83320ad419ff1b81a0bdf324197b29affe6cc28988cd6a74d4ac60c94f9799
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@patch:typescript@npm%3A^5.6.2#optional!builtin<compat/typescript>":
|
"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin<compat/typescript>":
|
||||||
version: 5.6.2
|
version: 5.6.3
|
||||||
resolution: "typescript@patch:typescript@npm%3A5.6.2#optional!builtin<compat/typescript>::version=5.6.2&hash=8c6c40"
|
resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin<compat/typescript>::version=5.6.3&hash=8c6c40"
|
||||||
bin:
|
bin:
|
||||||
tsc: bin/tsc
|
tsc: bin/tsc
|
||||||
tsserver: bin/tsserver
|
tsserver: bin/tsserver
|
||||||
checksum: 10c0/94eb47e130d3edd964b76da85975601dcb3604b0c848a36f63ac448d0104e93819d94c8bdf6b07c00120f2ce9c05256b8b6092d23cf5cf1c6fa911159e4d572f
|
checksum: 10c0/7c9d2e07c81226d60435939618c91ec2ff0b75fbfa106eec3430f0fcf93a584bc6c73176676f532d78c3594fe28a54b36eb40b3d75593071a7ec91301533ace7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,10 @@ GET {{host_standalone}}/api/system/info
|
|||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
GET {{host_standalone}}/rest/dashboardData
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
POST {{host_standalone2}}/rest/uploadFile
|
POST {{host_standalone2}}/rest/uploadFile
|
||||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
|
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,39 @@ const headers = {
|
|||||||
let countWifiScanPoll = 0; // wifi network scan
|
let countWifiScanPoll = 0; // wifi network scan
|
||||||
let countHardwarePoll = 0; // for during an upload
|
let countHardwarePoll = 0; // for during an upload
|
||||||
|
|
||||||
|
// DeviceTypes
|
||||||
|
const enum DeviceType {
|
||||||
|
SYSTEM = 0,
|
||||||
|
TEMPERATURESENSOR,
|
||||||
|
ANALOGSENSOR,
|
||||||
|
SCHEDULER,
|
||||||
|
CUSTOM,
|
||||||
|
BOILER,
|
||||||
|
THERMOSTAT,
|
||||||
|
MIXER,
|
||||||
|
SOLAR,
|
||||||
|
HEATPUMP,
|
||||||
|
GATEWAY,
|
||||||
|
SWITCH,
|
||||||
|
CONTROLLER,
|
||||||
|
CONNECT,
|
||||||
|
ALERT,
|
||||||
|
EXTENSION,
|
||||||
|
GENERIC,
|
||||||
|
HEATSOURCE,
|
||||||
|
VENTILATION,
|
||||||
|
WATER,
|
||||||
|
POOL,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum DeviceTypeUniqueID {
|
||||||
|
SCHEDULER_UID = 96,
|
||||||
|
ANALOGSENSOR_UID = 97,
|
||||||
|
TEMPERATURESENSOR_UID = 98,
|
||||||
|
CUSTOM_UID = 99 // always 99
|
||||||
|
}
|
||||||
|
|
||||||
function updateMask(entity: any, de: any, dd: any) {
|
function updateMask(entity: any, de: any, dd: any) {
|
||||||
const current_mask = parseInt(entity.slice(0, 2), 16);
|
const current_mask = parseInt(entity.slice(0, 2), 16);
|
||||||
|
|
||||||
@@ -54,22 +87,22 @@ function updateMask(entity: any, de: any, dd: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find in dd, either looking for fullname or custom name
|
// find in dd, either looking for fullname or custom name
|
||||||
// console.log('looking for ' + fullname + ' in ' + dd.data);
|
// console.log('looking for ' + fullname + ' in ' + dd.nodes);
|
||||||
const dd_objIndex = dd.data.findIndex(
|
const dd_objIndex = dd.nodes.findIndex(
|
||||||
(obj: any) => obj.id.slice(2) === fullname
|
(obj: any) => obj.id.slice(2) === fullname
|
||||||
);
|
);
|
||||||
if (dd_objIndex !== -1) {
|
if (dd_objIndex !== -1) {
|
||||||
let changed = new Boolean(false);
|
let changed = new Boolean(false);
|
||||||
|
|
||||||
// see if the mask has changed
|
// see if the mask has changed
|
||||||
const old_mask = parseInt(dd.data[dd_objIndex].id.slice(0, 2), 16);
|
const old_mask = parseInt(dd.nodes[dd_objIndex].id.slice(0, 2), 16);
|
||||||
if (old_mask !== current_mask) {
|
if (old_mask !== current_mask) {
|
||||||
changed = true;
|
changed = true;
|
||||||
console.log('mask has changed to ' + current_mask.toString(16));
|
console.log('mask has changed to ' + current_mask.toString(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if the custom name has changed
|
// see if the custom name has changed
|
||||||
const old_custom_name = dd.data[dd_objIndex].cn;
|
const old_custom_name = dd.nodes[dd_objIndex].cn;
|
||||||
console.log(
|
console.log(
|
||||||
'comparing names, old (' +
|
'comparing names, old (' +
|
||||||
old_custom_name +
|
old_custom_name +
|
||||||
@@ -87,8 +120,8 @@ function updateMask(entity: any, de: any, dd: any) {
|
|||||||
|
|
||||||
// see if min or max has changed
|
// see if min or max has changed
|
||||||
// get current min/max values if they exist
|
// get current min/max values if they exist
|
||||||
const current_min = dd.data[dd_objIndex].min;
|
const current_min = dd.nodes[dd_objIndex].min;
|
||||||
const current_max = dd.data[dd_objIndex].max;
|
const current_max = dd.nodes[dd_objIndex].max;
|
||||||
let new_min = current_min;
|
let new_min = current_min;
|
||||||
let new_max = current_max;
|
let new_max = current_max;
|
||||||
if (has_min_max) {
|
if (has_min_max) {
|
||||||
@@ -109,9 +142,9 @@ function updateMask(entity: any, de: any, dd: any) {
|
|||||||
if (new_max) {
|
if (new_max) {
|
||||||
de[de_objIndex].ma = new_max;
|
de[de_objIndex].ma = new_max;
|
||||||
}
|
}
|
||||||
dd.data[dd_objIndex].id =
|
dd.nodes[dd_objIndex].id =
|
||||||
current_mask.toString(16).padStart(2, '0') + new_fullname;
|
current_mask.toString(16).padStart(2, '0') + new_fullname;
|
||||||
dd.data[dd_objIndex].cn = new_fullname;
|
dd.nodes[dd_objIndex].cn = new_fullname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,6 +490,8 @@ const EMSESP_DEVICEDATA_ENDPOINT2 = REST_ENDPOINT_ROOT + 'deviceData/:id?';
|
|||||||
const EMSESP_DEVICEENTITIES_ENDPOINT1 = REST_ENDPOINT_ROOT + 'deviceEntities';
|
const EMSESP_DEVICEENTITIES_ENDPOINT1 = REST_ENDPOINT_ROOT + 'deviceEntities';
|
||||||
const EMSESP_DEVICEENTITIES_ENDPOINT2 = REST_ENDPOINT_ROOT + 'deviceEntities/:id?';
|
const EMSESP_DEVICEENTITIES_ENDPOINT2 = REST_ENDPOINT_ROOT + 'deviceEntities/:id?';
|
||||||
|
|
||||||
|
const EMSESP_DASHBOARD_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'dashboardData';
|
||||||
|
|
||||||
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile';
|
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile';
|
||||||
const EMSESP_WRITE_DEVICEVALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue';
|
const EMSESP_WRITE_DEVICEVALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue';
|
||||||
const EMSESP_WRITE_DEVICENAME_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceName';
|
const EMSESP_WRITE_DEVICENAME_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceName';
|
||||||
@@ -858,7 +893,7 @@ const activity = {
|
|||||||
// 99 - Custom
|
// 99 - Custom
|
||||||
|
|
||||||
const emsesp_devicedata_1 = {
|
const emsesp_devicedata_1 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: '22(816) 01.05.2023 13:07 (1 min)',
|
v: '22(816) 01.05.2023 13:07 (1 min)',
|
||||||
u: 0,
|
u: 0,
|
||||||
@@ -1302,7 +1337,7 @@ const emsesp_devicedata_1 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_2 = {
|
const emsesp_devicedata_2 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: '(0)',
|
v: '(0)',
|
||||||
u: 0,
|
u: 0,
|
||||||
@@ -1316,7 +1351,7 @@ const emsesp_devicedata_2 = {
|
|||||||
{
|
{
|
||||||
v: 18.2,
|
v: 18.2,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00Chosen Room Temperature',
|
id: '08Chosen Room Temperature',
|
||||||
c: 'hc1/seltemp',
|
c: 'hc1/seltemp',
|
||||||
m: 5,
|
m: 5,
|
||||||
x: 52,
|
x: 52,
|
||||||
@@ -1325,7 +1360,7 @@ const emsesp_devicedata_2 = {
|
|||||||
{
|
{
|
||||||
v: 22.6,
|
v: 22.6,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00hc1 current room temperature'
|
id: '08hc1 current room temperature'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 'auto',
|
v: 'auto',
|
||||||
@@ -1345,7 +1380,7 @@ const emsesp_devicedata_2 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_3 = {
|
const emsesp_devicedata_3 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: '',
|
v: '',
|
||||||
u: 0,
|
u: 0,
|
||||||
@@ -1686,7 +1721,7 @@ const emsesp_devicedata_3 = {
|
|||||||
{
|
{
|
||||||
v: 'hot',
|
v: 'hot',
|
||||||
u: 0,
|
u: 0,
|
||||||
id: '00dhw comfort',
|
id: '08dhw comfort',
|
||||||
c: 'dhw/comfort',
|
c: 'dhw/comfort',
|
||||||
l: ['hot', 'eco', 'intelligent']
|
l: ['hot', 'eco', 'intelligent']
|
||||||
},
|
},
|
||||||
@@ -1841,7 +1876,7 @@ const emsesp_devicedata_3 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_4 = {
|
const emsesp_devicedata_4 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: 16,
|
v: 16,
|
||||||
u: 1,
|
u: 1,
|
||||||
@@ -1857,7 +1892,7 @@ const emsesp_devicedata_4 = {
|
|||||||
{
|
{
|
||||||
v: 'off',
|
v: 'off',
|
||||||
u: 0,
|
u: 0,
|
||||||
id: '02hc2 mode',
|
id: '03hc2 mode',
|
||||||
c: 'hc2/mode',
|
c: 'hc2/mode',
|
||||||
l: ['off', 'on', 'auto']
|
l: ['off', 'on', 'auto']
|
||||||
}
|
}
|
||||||
@@ -1865,7 +1900,7 @@ const emsesp_devicedata_4 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_5 = {
|
const emsesp_devicedata_5 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: 30,
|
v: 30,
|
||||||
u: 1,
|
u: 1,
|
||||||
@@ -1912,7 +1947,7 @@ const emsesp_devicedata_5 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_6 = {
|
const emsesp_devicedata_6 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: 43.9,
|
v: 43.9,
|
||||||
u: 1,
|
u: 1,
|
||||||
@@ -2061,7 +2096,7 @@ const emsesp_devicedata_6 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_7 = {
|
const emsesp_devicedata_7 = {
|
||||||
data: [
|
nodes: [
|
||||||
{ v: '', u: 0, id: '08reset', c: 'reset', l: ['-', 'maintenance', 'error'] },
|
{ v: '', u: 0, id: '08reset', c: 'reset', l: ['-', 'maintenance', 'error'] },
|
||||||
{ v: 'off', u: 0, id: '08heating active' },
|
{ v: 'off', u: 0, id: '08heating active' },
|
||||||
{ v: 'off', u: 0, id: '04tapwater active' },
|
{ v: 'off', u: 0, id: '04tapwater active' },
|
||||||
@@ -2204,7 +2239,7 @@ const emsesp_devicedata_7 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_8 = {
|
const emsesp_devicedata_8 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: '',
|
v: '',
|
||||||
u: 0,
|
u: 0,
|
||||||
@@ -2244,22 +2279,22 @@ const emsesp_devicedata_8 = {
|
|||||||
id: '00heating pump modulation'
|
id: '00heating pump modulation'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 30.299999237060547,
|
v: 30.29,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00outside temperature'
|
id: '00outside temperature'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 18.700000762939453,
|
v: 18.7,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00current flow temperature'
|
id: '00current flow temperature'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 21.399999618530273,
|
v: 21.39,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00return temperature'
|
id: '00return temperature'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 18.700000762939453,
|
v: 18.7,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00low loss header'
|
id: '00low loss header'
|
||||||
},
|
},
|
||||||
@@ -2544,7 +2579,7 @@ const emsesp_devicedata_8 = {
|
|||||||
id: '00brine out/condenser'
|
id: '00brine out/condenser'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 21.399999618530273,
|
v: 21.39,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00heat carrier return (TC0)'
|
id: '00heat carrier return (TC0)'
|
||||||
},
|
},
|
||||||
@@ -2559,12 +2594,12 @@ const emsesp_devicedata_8 = {
|
|||||||
id: '00condenser temperature (TC3)'
|
id: '00condenser temperature (TC3)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 51.599998474121094,
|
v: 51.59,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00compressor temperature (TR1)'
|
id: '00compressor temperature (TR1)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 14.600000381469727,
|
v: 14.6,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00refrigerant temperature liquid side (condenser output) (TR3)'
|
id: '00refrigerant temperature liquid side (condenser output) (TR3)'
|
||||||
},
|
},
|
||||||
@@ -2574,32 +2609,32 @@ const emsesp_devicedata_8 = {
|
|||||||
id: '00evaporator inlet temperature (TR4)'
|
id: '00evaporator inlet temperature (TR4)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 20.200000762939453,
|
v: 20.2,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00compressor inlet temperature (TR5)'
|
id: '00compressor inlet temperature (TR5)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 54.599998474121094,
|
v: 54.59,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00compressor outlet temperature (TR6)'
|
id: '00compressor outlet temperature (TR6)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 29.600000381469727,
|
v: 29.6,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00air inlet temperature (TL2)'
|
id: '00air inlet temperature (TL2)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 13.899999618530273,
|
v: 13.89,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00low pressure side temperature (PL1)'
|
id: '00low pressure side temperature (PL1)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 37.79999923706055,
|
v: 37.79,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00high pressure side temperature (PH1)'
|
id: '00high pressure side temperature (PH1)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 25.600000381469727,
|
v: 25.6,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00drain pan temp (TA4)'
|
id: '00drain pan temp (TA4)'
|
||||||
},
|
},
|
||||||
@@ -2743,7 +2778,7 @@ const emsesp_devicedata_8 = {
|
|||||||
s: '0.1'
|
s: '0.1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 0.10000000149011612,
|
v: 0.1,
|
||||||
u: 22,
|
u: 22,
|
||||||
id: '00aux heater limit start',
|
id: '00aux heater limit start',
|
||||||
c: 'auxlimitstart',
|
c: 'auxlimitstart',
|
||||||
@@ -3109,7 +3144,7 @@ const emsesp_devicedata_8 = {
|
|||||||
l: ['off', 'on']
|
l: ['off', 'on']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
v: 58.70000076293945,
|
v: 58.7,
|
||||||
u: 1,
|
u: 1,
|
||||||
id: '00dhw current intern temperature'
|
id: '00dhw current intern temperature'
|
||||||
},
|
},
|
||||||
@@ -3178,7 +3213,7 @@ const emsesp_devicedata_8 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_9 = {
|
const emsesp_devicedata_9 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: 24,
|
v: 24,
|
||||||
u: 1,
|
u: 1,
|
||||||
@@ -3207,7 +3242,7 @@ const emsesp_devicedata_9 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_10 = {
|
const emsesp_devicedata_10 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: '26.06.2024 14:49',
|
v: '26.06.2024 14:49',
|
||||||
u: 0,
|
u: 0,
|
||||||
@@ -3696,7 +3731,7 @@ const emsesp_devicedata_10 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emsesp_devicedata_99 = {
|
const emsesp_devicedata_99 = {
|
||||||
data: [
|
nodes: [
|
||||||
{
|
{
|
||||||
v: 5,
|
v: 5,
|
||||||
u: 1,
|
u: 1,
|
||||||
@@ -3821,7 +3856,7 @@ let emsesp_schedule = {
|
|||||||
time: '',
|
time: '',
|
||||||
cmd: 'system/message',
|
cmd: 'system/message',
|
||||||
value: '"hello world"',
|
value: '"hello world"',
|
||||||
name: 'immediate'
|
name: '' // empty
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -3896,7 +3931,7 @@ const emsesp_deviceentities_2 = [
|
|||||||
v: 18.2,
|
v: 18.2,
|
||||||
n: 'Chosen Room Temperature',
|
n: 'Chosen Room Temperature',
|
||||||
id: 'hc1/seltemp',
|
id: 'hc1/seltemp',
|
||||||
m: 0,
|
m: 8,
|
||||||
mi: 5,
|
mi: 5,
|
||||||
ma: 52,
|
ma: 52,
|
||||||
w: true
|
w: true
|
||||||
@@ -3905,7 +3940,7 @@ const emsesp_deviceentities_2 = [
|
|||||||
v: 22.6,
|
v: 22.6,
|
||||||
n: 'hc1 current room temperature',
|
n: 'hc1 current room temperature',
|
||||||
id: 'hc1/curtemp',
|
id: 'hc1/curtemp',
|
||||||
m: 0,
|
m: 8,
|
||||||
w: false
|
w: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3936,7 +3971,7 @@ const emsesp_deviceentities_4 = [
|
|||||||
v: 'off',
|
v: 'off',
|
||||||
n: 'hc2 mode',
|
n: 'hc2 mode',
|
||||||
id: 'hc2/mode',
|
id: 'hc2/mode',
|
||||||
m: 2,
|
m: 3,
|
||||||
w: true
|
w: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -4182,7 +4217,7 @@ function deviceData(id: number) {
|
|||||||
}
|
}
|
||||||
if (id == 8) {
|
if (id == 8) {
|
||||||
// test changing the selected flow temp on a Bosch Compress 7000i AW Heat Pump (Boiler/HP)
|
// test changing the selected flow temp on a Bosch Compress 7000i AW Heat Pump (Boiler/HP)
|
||||||
emsesp_devicedata_8.data[4].v = Math.floor(Math.random() * 100);
|
emsesp_devicedata_8.nodes[4].v = Math.floor(Math.random() * 100);
|
||||||
return new Response(encoder.encode(emsesp_devicedata_8), { headers });
|
return new Response(encoder.encode(emsesp_devicedata_8), { headers });
|
||||||
}
|
}
|
||||||
if (id == 9) {
|
if (id == 9) {
|
||||||
@@ -4231,7 +4266,34 @@ function deviceEntities(id: number) {
|
|||||||
return new Response(encoder.encode(emsesp_deviceentities_none), { headers });
|
return new Response(encoder.encode(emsesp_deviceentities_none), { headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router starts here...
|
// prepare dashboard data
|
||||||
|
function getDashboardEntityData(id: number) {
|
||||||
|
let device_data = {};
|
||||||
|
if (id == 1) device_data = emsesp_devicedata_1;
|
||||||
|
else if (id == 2) device_data = emsesp_devicedata_2;
|
||||||
|
else if (id == 3) device_data = emsesp_devicedata_3;
|
||||||
|
else if (id == 4) device_data = emsesp_devicedata_4;
|
||||||
|
else if (id == 5) device_data = emsesp_devicedata_5;
|
||||||
|
else if (id == 6) device_data = emsesp_devicedata_6;
|
||||||
|
else if (id == 7) device_data = emsesp_devicedata_7;
|
||||||
|
else if (id == 8) device_data = emsesp_devicedata_8;
|
||||||
|
else if (id == 9) device_data = emsesp_devicedata_9;
|
||||||
|
else if (id == 10) device_data = emsesp_devicedata_10;
|
||||||
|
else if (id == 99) device_data = emsesp_devicedata_99;
|
||||||
|
|
||||||
|
// filter device_data on
|
||||||
|
// - only add favorite (mask has bit 8 set) except for Custom Entities (type 99)
|
||||||
|
let new_data = (device_data as any).nodes
|
||||||
|
.filter((item) => id === 99 || parseInt(item.id.slice(0, 2), 16) & 0x08)
|
||||||
|
.map((item, index) => ({
|
||||||
|
id: id * 100 + index, // mandatory unique id for table
|
||||||
|
dv: item // devicevalue
|
||||||
|
}));
|
||||||
|
|
||||||
|
return new_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router routing starts here...
|
||||||
router
|
router
|
||||||
// EMS-ESP Settings
|
// EMS-ESP Settings
|
||||||
.get(EMSESP_SETTINGS_ENDPOINT, () => settings)
|
.get(EMSESP_SETTINGS_ENDPOINT, () => settings)
|
||||||
@@ -4242,7 +4304,7 @@ router
|
|||||||
// return status(205); // restart needed
|
// return status(205); // restart needed
|
||||||
})
|
})
|
||||||
|
|
||||||
// Device Dashboard Data
|
// Device Data
|
||||||
.get(EMSESP_CORE_DATA_ENDPOINT, () => {
|
.get(EMSESP_CORE_DATA_ENDPOINT, () => {
|
||||||
// sort by type, like its done in the C++ code
|
// sort by type, like its done in the C++ code
|
||||||
let sorted_devices = [...emsesp_coredata.devices].sort((a, b) => a.t - b.t);
|
let sorted_devices = [...emsesp_coredata.devices].sort((a, b) => a.t - b.t);
|
||||||
@@ -4268,6 +4330,146 @@ router
|
|||||||
.get(EMSESP_DEVICEENTITIES_ENDPOINT2, ({ params }) =>
|
.get(EMSESP_DEVICEENTITIES_ENDPOINT2, ({ params }) =>
|
||||||
params.id ? deviceEntities(Number(params.id)) : status(404)
|
params.id ? deviceEntities(Number(params.id)) : status(404)
|
||||||
)
|
)
|
||||||
|
.get(EMSESP_DASHBOARD_DATA_ENDPOINT, () => {
|
||||||
|
let dashboard_data: { id?: number; n?: string; t?: number; nodes?: any[] }[] =
|
||||||
|
[];
|
||||||
|
let dashboard_object: { id?: number; n?: string; t?: number; nodes?: any[] } =
|
||||||
|
{};
|
||||||
|
|
||||||
|
let fake = false;
|
||||||
|
|
||||||
|
// fake = true; // for testing
|
||||||
|
|
||||||
|
if (!fake) {
|
||||||
|
// pick EMS devices from coredata
|
||||||
|
for (const element of emsesp_coredata.devices) {
|
||||||
|
const id = element.id;
|
||||||
|
|
||||||
|
dashboard_object = {
|
||||||
|
id: id,
|
||||||
|
n: element.n,
|
||||||
|
t: element.t,
|
||||||
|
nodes: getDashboardEntityData(id)
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the custom entity data
|
||||||
|
dashboard_object = {
|
||||||
|
id: DeviceTypeUniqueID.CUSTOM_UID, // unique ID for custom entities
|
||||||
|
t: DeviceType.CUSTOM,
|
||||||
|
nodes: getDashboardEntityData(99)
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add temperature sensor data. no command c
|
||||||
|
let sensor_data: any[] = [];
|
||||||
|
sensor_data = emsesp_sensordata.ts.map((item, index) => ({
|
||||||
|
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index,
|
||||||
|
dv: {
|
||||||
|
id: '00' + item.n,
|
||||||
|
v: item.t, // value is called t in ts (temperature)
|
||||||
|
u: item.u
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
dashboard_object = {
|
||||||
|
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID,
|
||||||
|
t: DeviceType.TEMPERATURESENSOR,
|
||||||
|
nodes: sensor_data
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add analog sensor data. no command c
|
||||||
|
// remove disabled sensors first (t = 0)
|
||||||
|
sensor_data = emsesp_sensordata.as.filter((item) => item.t !== 0);
|
||||||
|
sensor_data = sensor_data.map((item, index) => ({
|
||||||
|
id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index,
|
||||||
|
dv: {
|
||||||
|
id: '00' + item.n,
|
||||||
|
v: item.v,
|
||||||
|
u: item.u
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
dashboard_object = {
|
||||||
|
id: DeviceTypeUniqueID.ANALOGSENSOR_UID,
|
||||||
|
t: DeviceType.ANALOGSENSOR,
|
||||||
|
nodes: sensor_data
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the scheduler data
|
||||||
|
// filter emsesp_schedule with only if it has a name
|
||||||
|
let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name);
|
||||||
|
let scheduler_data2 = scheduler_data.map((item, index) => ({
|
||||||
|
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
|
||||||
|
dv: {
|
||||||
|
id: '00' + item.name,
|
||||||
|
v: item.active ? 'on' : 'off',
|
||||||
|
c: item.name,
|
||||||
|
l: ['off', 'on']
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
dashboard_object = {
|
||||||
|
id: DeviceTypeUniqueID.SCHEDULER_UID,
|
||||||
|
t: DeviceType.SCHEDULER,
|
||||||
|
nodes: scheduler_data2
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for testing only
|
||||||
|
|
||||||
|
// add the custom entity data
|
||||||
|
// dashboard_object = {
|
||||||
|
// id: DeviceTypeUniqueID.CUSTOM_UID, // unique ID for custom entities
|
||||||
|
// t: DeviceType.CUSTOM,
|
||||||
|
// nodes: getDashboardEntityData(99)
|
||||||
|
// };
|
||||||
|
// // only add to dashboard if we have values
|
||||||
|
// if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
// dashboard_data.push(dashboard_object);
|
||||||
|
// }
|
||||||
|
|
||||||
|
let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name);
|
||||||
|
let scheduler_data2 = scheduler_data.map((item, index) => ({
|
||||||
|
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
|
||||||
|
dv: {
|
||||||
|
id: '00' + item.name,
|
||||||
|
v: item.active ? 'on' : 'off',
|
||||||
|
c: item.name,
|
||||||
|
l: ['off', 'on']
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
dashboard_object = {
|
||||||
|
id: DeviceTypeUniqueID.SCHEDULER_UID,
|
||||||
|
t: DeviceType.SCHEDULER,
|
||||||
|
nodes: scheduler_data2
|
||||||
|
};
|
||||||
|
// only add to dashboard if we have values
|
||||||
|
if ((dashboard_object.nodes ?? []).length > 0) {
|
||||||
|
dashboard_data.push(dashboard_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('dashboard_data: ', dashboard_data);
|
||||||
|
|
||||||
|
// return dashboard_data; // if not using msgpack
|
||||||
|
return new Response(encoder.encode(dashboard_data), { headers }); // msgpack it
|
||||||
|
})
|
||||||
|
|
||||||
// Customizations
|
// Customizations
|
||||||
.post(EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT, async (request: any) => {
|
.post(EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT, async (request: any) => {
|
||||||
@@ -4346,62 +4548,79 @@ router
|
|||||||
})
|
})
|
||||||
.get(EMSESP_CUSTOMENTITIES_ENDPOINT, () => emsesp_customentities)
|
.get(EMSESP_CUSTOMENTITIES_ENDPOINT, () => emsesp_customentities)
|
||||||
|
|
||||||
// Device Dashboard
|
// Devices page
|
||||||
.post(EMSESP_WRITE_DEVICEVALUE_ENDPOINT, async (request: any) => {
|
.post(EMSESP_WRITE_DEVICEVALUE_ENDPOINT, async (request: any) => {
|
||||||
const content = await request.json();
|
const content = await request.json();
|
||||||
const command = content.c;
|
const command = content.c;
|
||||||
const value = content.v;
|
const value = content.v;
|
||||||
const id = content.id;
|
const id = content.id;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'write device value, id: ' + id + ' command: ' + command + ' value: ' + value
|
||||||
|
);
|
||||||
|
|
||||||
var objIndex;
|
var objIndex;
|
||||||
if (id === 1) {
|
if (id === 1) {
|
||||||
objIndex = emsesp_devicedata_1.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_1.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_1.data[objIndex].v = value;
|
emsesp_devicedata_1.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 2) {
|
if (id === 2) {
|
||||||
objIndex = emsesp_devicedata_2.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_2.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_2.data[objIndex].v = value;
|
emsesp_devicedata_2.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 3) {
|
if (id === 3) {
|
||||||
objIndex = emsesp_devicedata_3.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_3.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_3.data[objIndex].v = value;
|
emsesp_devicedata_3.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 4) {
|
if (id === 4) {
|
||||||
objIndex = emsesp_devicedata_4.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_4.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_4.data[objIndex].v = value;
|
emsesp_devicedata_4.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 5) {
|
if (id === 5) {
|
||||||
objIndex = emsesp_devicedata_5.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_5.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_5.data[objIndex].v = value;
|
emsesp_devicedata_5.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 6) {
|
if (id === 6) {
|
||||||
objIndex = emsesp_devicedata_6.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_6.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_6.data[objIndex].v = value;
|
emsesp_devicedata_6.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 7) {
|
if (id === 7) {
|
||||||
objIndex = emsesp_devicedata_7.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_7.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_7.data[objIndex].v = value;
|
emsesp_devicedata_7.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 8) {
|
if (id === 8) {
|
||||||
objIndex = emsesp_devicedata_8.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_8.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_8.data[objIndex].v = value;
|
emsesp_devicedata_8.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 9) {
|
if (id === 9) {
|
||||||
objIndex = emsesp_devicedata_9.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_9.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_9.data[objIndex].v = value;
|
emsesp_devicedata_9.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 10) {
|
if (id === 10) {
|
||||||
objIndex = emsesp_devicedata_10.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_10.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_10.data[objIndex].v = value;
|
emsesp_devicedata_10.nodes[objIndex].v = value;
|
||||||
}
|
}
|
||||||
if (id === 99) {
|
if (id === DeviceTypeUniqueID.CUSTOM_UID) {
|
||||||
// custom entities
|
// custom entities
|
||||||
objIndex = emsesp_devicedata_99.data.findIndex((obj) => obj.c == command);
|
objIndex = emsesp_devicedata_99.nodes.findIndex((obj) => obj.c == command);
|
||||||
emsesp_devicedata_99.data[objIndex].v = value;
|
emsesp_devicedata_99.nodes[objIndex].v = value;
|
||||||
|
}
|
||||||
|
if (id === DeviceTypeUniqueID.SCHEDULER_UID) {
|
||||||
|
// toggle scheduler
|
||||||
|
// find the schedule in emsesp_schedule via the name and toggle the active
|
||||||
|
const objIndex = emsesp_schedule.schedule.findIndex(
|
||||||
|
(obj) => obj.name === command
|
||||||
|
);
|
||||||
|
if (objIndex !== -1) {
|
||||||
|
emsesp_schedule.schedule[objIndex].active = value;
|
||||||
|
console.log("Toggle schedule '" + command + "' to " + value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// await delay(1000); // wait to show spinner
|
// await delay(1000); // wait to show spinner
|
||||||
console.log('device value saved', content);
|
// console.log(
|
||||||
|
// 'Device Value updated. command:' + command + ' value:' + value + ' id:' + id
|
||||||
|
// );
|
||||||
return status(200);
|
return status(200);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ extra_scripts =
|
|||||||
[env]
|
[env]
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
|
; on MacBook default upload speed is 921600, which is too fast for USB. Lower for Mac.
|
||||||
|
; see https://docs.platformio.org/en/latest/projectconf/sections/env/options/upload/upload_speed.html
|
||||||
|
; upload_speed = 460800
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
build_type = release
|
build_type = release
|
||||||
check_tool = cppcheck, clangtidy
|
check_tool = cppcheck, clangtidy
|
||||||
@@ -109,10 +112,7 @@ extra_scripts = scripts/rename_fw.py
|
|||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_upload.flash_size = 16MB
|
board_upload.flash_size = 16MB
|
||||||
board_build.partitions = esp32_partition_16M.csv
|
board_build.partitions = esp32_partition_16M.csv
|
||||||
board_build.extra_flags =
|
board_build.extra_flags = -DBOARD_HAS_PSRAM
|
||||||
-DARDUINO_ESP32_DEV
|
|
||||||
-DBOARD_HAS_PSRAM
|
|
||||||
|
|
||||||
|
|
||||||
[env:ci_s3_16M_P]
|
[env:ci_s3_16M_P]
|
||||||
; 16MB ESP32-S3 - with PSRAM - like BBQKees S3
|
; 16MB ESP32-S3 - with PSRAM - like BBQKees S3
|
||||||
@@ -146,9 +146,7 @@ extends = espressif32_base
|
|||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_upload.flash_size = 16MB
|
board_upload.flash_size = 16MB
|
||||||
board_build.partitions = esp32_partition_16M.csv
|
board_build.partitions = esp32_partition_16M.csv
|
||||||
board_build.extra_flags =
|
board_build.extra_flags = -DBOARD_HAS_PSRAM
|
||||||
-DARDUINO_ESP32_DEV
|
|
||||||
-DBOARD_HAS_PSRAM
|
|
||||||
|
|
||||||
[env:c3_mini_4M]
|
[env:c3_mini_4M]
|
||||||
extends = espressif32_base_tasmota
|
extends = espressif32_base_tasmota
|
||||||
@@ -165,6 +163,7 @@ board_upload.flash_size = 4MB
|
|||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
|
-DTASMOTA_SDK
|
||||||
-DBOARD_C3_MINI_V1
|
-DBOARD_C3_MINI_V1
|
||||||
|
|
||||||
[env:s2_4M]
|
[env:s2_4M]
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
// first check if the GPIO is valid. If not, force set it to disabled
|
// first check if the GPIO is valid. If not, force set it to disabled
|
||||||
if (!System::is_valid_gpio(sensor.gpio())) {
|
if (!System::is_valid_gpio(sensor.gpio())) {
|
||||||
LOG_WARNING("Bad GPIO %d for Sensor %s. Disabling.", sensor.gpio(), sensor.name().c_str());
|
LOG_WARNING("Bad GPIO %d for Sensor %s. Disabling.", sensor.gpio(), sensor.name().c_str());
|
||||||
sensor.set_type(AnalogType::NOTUSED);
|
sensor.set_type(AnalogType::NOTUSED); // set disabled
|
||||||
continue; // skip this loop pass
|
continue; // skip this loop pass
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sensor.type() == AnalogType::ADC) {
|
if (sensor.type() == AnalogType::ADC) {
|
||||||
@@ -402,7 +402,9 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
|
|||||||
// reloads the sensors in the customizations file into the sensors list
|
// reloads the sensors in the customizations file into the sensors list
|
||||||
reload();
|
reload();
|
||||||
|
|
||||||
return true;
|
// return false if it's an invalid GPIO, an error will show in WebUI
|
||||||
|
// and reported as an error in the log
|
||||||
|
return System::is_valid_gpio(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if values have been updated
|
// check to see if values have been updated
|
||||||
|
|||||||
@@ -153,7 +153,11 @@ class AnalogSensor {
|
|||||||
return (!sensors_.empty());
|
return (!sensors_.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t count_entities() const {
|
size_t count_entities(bool include_disabled = true) const {
|
||||||
|
if (!include_disabled) {
|
||||||
|
// count number of items in sensors_ where type is not set to disabled
|
||||||
|
return std::count_if(sensors_.begin(), sensors_.end(), [](const Sensor & sensor) { return sensor.type() != AnalogSensor::AnalogType::NOTUSED; });
|
||||||
|
}
|
||||||
return sensors_.size();
|
return sensors_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{ 72, DeviceType::BOILER, "Logano GB1*5, Logamatic MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS},
|
{ 72, DeviceType::BOILER, "Logano GB1*5, Logamatic MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS},
|
||||||
{ 81, DeviceType::BOILER, "Cascade CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 81, DeviceType::BOILER, "Cascade CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{ 84, DeviceType::BOILER, "Logamax Plus GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 84, DeviceType::BOILER, "Logamax Plus GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{ 95, DeviceType::BOILER, "Condens 2500/5000W, Logamax/Logomatic, Cerapur Top, Greenstar, Generic HT3", DeviceFlags::EMS_DEVICE_FLAG_HT3},
|
{ 95, DeviceType::BOILER, "Condens, Logamax/Logomatic, Cerapur Top, Greenstar, Generic HT3", DeviceFlags::EMS_DEVICE_FLAG_HT3},
|
||||||
{115, DeviceType::BOILER, "Topline, GB162", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{115, DeviceType::BOILER, "Topline, GB162", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{121, DeviceType::BOILER, "Cascade MCM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{121, DeviceType::BOILER, "Cascade MCM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{122, DeviceType::BOILER, "Proline", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{122, DeviceType::BOILER, "Proline", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
|
|||||||
@@ -33,7 +33,16 @@ uint8_t EMSdevice::count_entities() {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if there are entities, excluding any commands
|
// count favorites, used in Dashboard
|
||||||
|
uint8_t EMSdevice::count_entities_fav() {
|
||||||
|
uint8_t count = 0;
|
||||||
|
for (const auto & dv : devicevalues_) {
|
||||||
|
count += dv.has_state(DeviceValueState::DV_FAVORITE);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if there are customized entities, excluding any commands
|
||||||
bool EMSdevice::has_entities() const {
|
bool EMSdevice::has_entities() const {
|
||||||
for (const auto & dv : devicevalues_) {
|
for (const auto & dv : devicevalues_) {
|
||||||
if (dv.type != DeviceValueType::CMD) {
|
if (dv.type != DeviceValueType::CMD) {
|
||||||
@@ -516,6 +525,7 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add to device value library, also know now as a "device entity"
|
// add to device value library, also know now as a "device entity"
|
||||||
|
// this function will also apply any customizations to the entity
|
||||||
void EMSdevice::add_device_value(int8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
void EMSdevice::add_device_value(int8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
||||||
void * value_p, // pointer to the value from the .h file
|
void * value_p, // pointer to the value from the .h file
|
||||||
uint8_t type, // one of DeviceValueType
|
uint8_t type, // one of DeviceValueType
|
||||||
@@ -903,24 +913,39 @@ bool EMSdevice::export_values(uint8_t device_type, JsonObject output, const int8
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepare array of device values used for the WebUI
|
// prepare array of device values used for the WebUI
|
||||||
|
// this is used for the Dashboard and also the Devices page
|
||||||
// this is loosely based of the function generate_values used for the MQTT and Console
|
// this is loosely based of the function generate_values used for the MQTT and Console
|
||||||
// except additional data is stored in the JSON document needed for the Web UI like the UOM and command
|
// except additional data is stored in the JSON document needed for the Web UI like the UOM and command
|
||||||
// v=value, u=uom, n=name, c=cmd, h=help string, s=step, m=min, x=max
|
// v=value, u=uom, n=name, c=cmd, h=help string, s=step, m=min, x=max
|
||||||
void EMSdevice::generate_values_web(JsonObject output) {
|
// see types.ts::DeviceValue for the structure
|
||||||
|
void EMSdevice::generate_values_web(JsonObject output, const bool is_dashboard) {
|
||||||
// output["label"] = to_string_short();
|
// output["label"] = to_string_short();
|
||||||
// output["label"] = name_;
|
// output["label"] = name_;
|
||||||
JsonArray data = output["data"].to<JsonArray>();
|
|
||||||
|
JsonArray nodes = output["nodes"].to<JsonArray>();
|
||||||
|
uint8_t count = 0;
|
||||||
|
|
||||||
for (auto & dv : devicevalues_) {
|
for (auto & dv : devicevalues_) {
|
||||||
auto fullname = dv.get_fullname();
|
auto fullname = dv.get_fullname();
|
||||||
|
|
||||||
// check conditions:
|
// check conditions:
|
||||||
// 1. fullname cannot be empty
|
// 1. fullname cannot be empty
|
||||||
// 2. it must have a valid value, if it is not a command like 'reset'
|
// 2. it must have a valid value, unless its a command like 'reset'
|
||||||
// 3. show favorites first
|
// 3. if is_dashboard then only show favs
|
||||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && !fullname.empty() && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) {
|
bool matching_states = (is_dashboard) ? dv.has_state(DeviceValueState::DV_FAVORITE) : !dv.has_state(DeviceValueState::DV_WEB_EXCLUDE);
|
||||||
JsonObject obj = data.add<JsonObject>(); // create the object, we know there is a value
|
|
||||||
uint8_t fahrenheit = 0;
|
if (matching_states && !fullname.empty() && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) {
|
||||||
|
JsonObject root_obj = nodes.add<JsonObject>(); // create the object, we know there is a value
|
||||||
|
|
||||||
|
JsonObject obj;
|
||||||
|
if (is_dashboard) {
|
||||||
|
root_obj["id"] = (unique_id() * 100) + count++; // make unique
|
||||||
|
obj = root_obj["dv"].to<JsonObject>();
|
||||||
|
} else {
|
||||||
|
obj = root_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t fahrenheit = 0;
|
||||||
|
|
||||||
// handle Booleans (true, false), output as strings according to the user settings
|
// handle Booleans (true, false), output as strings according to the user settings
|
||||||
if (dv.type == DeviceValueType::BOOL) {
|
if (dv.type == DeviceValueType::BOOL) {
|
||||||
@@ -1024,11 +1049,11 @@ void EMSdevice::generate_values_web(JsonObject output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// as generate_values_web() but stripped down to only show all entities and their state
|
// as generate_values_web() but with extra data for WebCustomizationService::device_entities() (rest/deviceEntities?id=n)
|
||||||
// this is used only for WebCustomizationService::device_entities()
|
// also show commands and entities that have an empty fullname
|
||||||
|
// see types.ts::DeviceEntity for the structure
|
||||||
void EMSdevice::generate_values_web_customization(JsonArray output) {
|
void EMSdevice::generate_values_web_customization(JsonArray output) {
|
||||||
for (auto & dv : devicevalues_) {
|
for (auto & dv : devicevalues_) {
|
||||||
// also show commands and entities that have an empty fullname
|
|
||||||
JsonObject obj = output.add<JsonObject>();
|
JsonObject obj = output.add<JsonObject>();
|
||||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||||
|
|
||||||
@@ -1069,7 +1094,8 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// id holds the shortname and must always have a value for the WebUI table to work
|
// create the id
|
||||||
|
// it holds the shortname and must always have a unique value for the WebUI table to work
|
||||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||||
char id_s[50];
|
char id_s[50];
|
||||||
snprintf(id_s, sizeof(id_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
snprintf(id_s, sizeof(id_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||||
@@ -1085,7 +1111,6 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
|||||||
if (fullname) {
|
if (fullname) {
|
||||||
// obj["n"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname : fullname; // prefix tag
|
// obj["n"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname : fullname; // prefix tag
|
||||||
obj["n"] = fullname;
|
obj["n"] = fullname;
|
||||||
|
|
||||||
// TAG https://github.com/emsesp/EMS-ESP32/issues/1338
|
// TAG https://github.com/emsesp/EMS-ESP32/issues/1338
|
||||||
// obj["n"] = (dv.has_tag()) ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
|
// obj["n"] = (dv.has_tag()) ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
|
||||||
}
|
}
|
||||||
@@ -1101,8 +1126,15 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
|||||||
if (dv.has_tag()) {
|
if (dv.has_tag()) {
|
||||||
obj["t"] = tag_to_string(dv.tag);
|
obj["t"] = tag_to_string(dv.tag);
|
||||||
}
|
}
|
||||||
obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble
|
|
||||||
obj["w"] = dv.has_cmd; // if writable
|
// the mask state. We're only interested in the high nibble which contains the flags, so shift right
|
||||||
|
// 0x80 = 128 = DV_FAVORITE
|
||||||
|
// 0x40 = 64 = DV_READONLY
|
||||||
|
// 0x20 = 32 = DV_API_MQTT_EXCLUDE
|
||||||
|
// 0x10 = 16 = DV_WEB_EXCLUDE
|
||||||
|
obj["m"] = dv.state >> 4;
|
||||||
|
|
||||||
|
obj["w"] = dv.has_cmd; // if writable
|
||||||
|
|
||||||
if (dv.has_cmd && (obj["v"].is<float>() || obj["v"].is<int>())) {
|
if (dv.has_cmd && (obj["v"].is<float>() || obj["v"].is<int>())) {
|
||||||
// set the min and max values if there are any and if entity has a value
|
// set the min and max values if there are any and if entity has a value
|
||||||
@@ -1115,16 +1147,20 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply and blacklisted/removed entities
|
||||||
|
// this is when the mask has it's high bit (0x80) set
|
||||||
|
// https://github.com/emsesp/EMS-ESP32/issues/891
|
||||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||||
if (entityCustomization.device_id == device_id()) {
|
if (entityCustomization.device_id == device_id()) {
|
||||||
|
// entity_ids is a list of all entities with the mask prefixed in the string
|
||||||
for (const std::string & entity_id : entityCustomization.entity_ids) {
|
for (const std::string & entity_id : entityCustomization.entity_ids) {
|
||||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||||
if (mask & 0x80) {
|
if (mask & 0x80) {
|
||||||
JsonObject obj = output.add<JsonObject>();
|
JsonObject obj = output.add<JsonObject>();
|
||||||
obj["id"] = DeviceValue::get_name(entity_id);
|
obj["id"] = DeviceValue::get_name(entity_id); // set the name, it could be custom following a '|'
|
||||||
obj["m"] = mask;
|
obj["m"] = mask; // update the mask
|
||||||
obj["w"] = false;
|
obj["w"] = false; // not writeable as it won't be shown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ class EMSdevice {
|
|||||||
|
|
||||||
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
|
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
|
||||||
bool generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target);
|
bool generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target);
|
||||||
void generate_values_web(JsonObject output);
|
void generate_values_web(JsonObject output, const bool is_dashboard = false);
|
||||||
void generate_values_web_customization(JsonArray output);
|
void generate_values_web_customization(JsonArray output);
|
||||||
|
|
||||||
void add_device_value(int8_t tag,
|
void add_device_value(int8_t tag,
|
||||||
@@ -350,6 +350,15 @@ class EMSdevice {
|
|||||||
IVT // 13
|
IVT // 13
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Unique Identifiers for each Device type, used in Dashboard table
|
||||||
|
// 100 and above is reserved for DeviceType
|
||||||
|
enum DeviceTypeUniqueID : uint8_t {
|
||||||
|
SCHEDULER_UID = 96,
|
||||||
|
ANALOGSENSOR_UID = 97,
|
||||||
|
TEMPERATURESENSOR_UID = 98,
|
||||||
|
CUSTOM_UID = 99 // always 99
|
||||||
|
};
|
||||||
|
|
||||||
enum DeviceType : uint8_t {
|
enum DeviceType : uint8_t {
|
||||||
SYSTEM = 0, // this is us (EMS-ESP)
|
SYSTEM = 0, // this is us (EMS-ESP)
|
||||||
TEMPERATURESENSOR, // for internal temperature sensors
|
TEMPERATURESENSOR, // for internal temperature sensors
|
||||||
@@ -456,6 +465,7 @@ class EMSdevice {
|
|||||||
static constexpr uint8_t EMS_DEVICE_FLAG_CR120 = 16; // mostly like RC300, but some changes
|
static constexpr uint8_t EMS_DEVICE_FLAG_CR120 = 16; // mostly like RC300, but some changes
|
||||||
|
|
||||||
uint8_t count_entities();
|
uint8_t count_entities();
|
||||||
|
uint8_t count_entities_fav();
|
||||||
bool has_entities() const;
|
bool has_entities() const;
|
||||||
|
|
||||||
// void reserve_device_values(uint8_t elements) {
|
// void reserve_device_values(uint8_t elements) {
|
||||||
@@ -514,9 +524,12 @@ class EMSdevice {
|
|||||||
|
|
||||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||||
|
|
||||||
std::vector<DeviceValue> devicevalues_; // all the device values
|
|
||||||
|
|
||||||
std::vector<uint16_t> handlers_ignored_;
|
std::vector<uint16_t> handlers_ignored_;
|
||||||
|
|
||||||
|
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||||
|
public: // so we can call it from WebCustomizationService::test()
|
||||||
|
#endif
|
||||||
|
std::vector<DeviceValue> devicevalues_; // all the device values
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ std::string DeviceValue::get_fullname() const {
|
|||||||
return customname;
|
return customname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns any custom name defined in the entity_id
|
||||||
std::string DeviceValue::get_name(const std::string & entity) {
|
std::string DeviceValue::get_name(const std::string & entity) {
|
||||||
auto pos = entity.find('|');
|
auto pos = entity.find('|');
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class DeviceValue {
|
|||||||
DV_WEB_EXCLUDE = (1 << 4), // 16 - not shown on web
|
DV_WEB_EXCLUDE = (1 << 4), // 16 - not shown on web
|
||||||
DV_API_MQTT_EXCLUDE = (1 << 5), // 32 - not shown on mqtt, API
|
DV_API_MQTT_EXCLUDE = (1 << 5), // 32 - not shown on mqtt, API
|
||||||
DV_READONLY = (1 << 6), // 64 - read only
|
DV_READONLY = (1 << 6), // 64 - read only
|
||||||
DV_FAVORITE = (1 << 7) // 128 - sort to front
|
DV_FAVORITE = (1 << 7) // 128 - marked as a favorite
|
||||||
};
|
};
|
||||||
|
|
||||||
// numeric operators
|
// numeric operators
|
||||||
@@ -154,7 +154,6 @@ class DeviceValue {
|
|||||||
const char * const ** options; // options as a flash char array
|
const char * const ** options; // options as a flash char array
|
||||||
const char * const * options_single; // options are not translated
|
const char * const * options_single; // options are not translated
|
||||||
int8_t numeric_operator;
|
int8_t numeric_operator;
|
||||||
uint8_t options_size; // number of options in the char array, calculated
|
|
||||||
const char * const short_name; // used in MQTT and API
|
const char * const short_name; // used in MQTT and API
|
||||||
const char * const * fullname; // used in Web and Console, is translated
|
const char * const * fullname; // used in Web and Console, is translated
|
||||||
std::string custom_fullname; // optional, from customization
|
std::string custom_fullname; // optional, from customization
|
||||||
@@ -164,21 +163,24 @@ class DeviceValue {
|
|||||||
uint32_t max; // max range
|
uint32_t max; // max range
|
||||||
uint8_t state; // DeviceValueState::*
|
uint8_t state; // DeviceValueState::*
|
||||||
|
|
||||||
DeviceValue(uint8_t device_type,
|
uint8_t options_size; // number of options in the char array, calculated at class initialization
|
||||||
int8_t tag,
|
|
||||||
void * value_p,
|
DeviceValue(uint8_t device_type, // EMSdevice::DeviceType
|
||||||
uint8_t type,
|
int8_t tag, // DeviceValueTAG::*
|
||||||
const char * const ** options,
|
void * value_p, // pointer to variable of any type
|
||||||
const char * const * options_single,
|
uint8_t type, // DeviceValueType::*
|
||||||
|
const char * const ** options, // options as a flash char array
|
||||||
|
const char * const * options_single, // options are not translated
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const short_name,
|
const char * const short_name, // used in MQTT and API
|
||||||
const char * const * fullname,
|
const char * const * fullname, // used in Web and Console, is translated
|
||||||
std::string & custom_fullname,
|
std::string & custom_fullname, // optional, from customization
|
||||||
uint8_t uom,
|
uint8_t uom, // DeviceValueUOM::*
|
||||||
bool has_cmd,
|
bool has_cmd, // true if there is a Console/MQTT command which matches the short_name
|
||||||
int16_t min,
|
int16_t min, // min range
|
||||||
uint32_t max,
|
uint32_t max, // max range
|
||||||
uint8_t state);
|
uint8_t state // DeviceValueState::* (also known as the mask)
|
||||||
|
);
|
||||||
|
|
||||||
bool hasValue() const;
|
bool hasValue() const;
|
||||||
bool has_tag() const;
|
bool has_tag() const;
|
||||||
|
|||||||
@@ -1698,7 +1698,7 @@ void EMSESP::loop() {
|
|||||||
|
|
||||||
static bool show_prompt = true;
|
static bool show_prompt = true;
|
||||||
|
|
||||||
// user has to ctrl-c to create a serial console stream, exit command will close it
|
// user has to CTRL-D to create a serial console stream, exit command will close it
|
||||||
// this saves around 2kb of heap memory
|
// this saves around 2kb of heap memory
|
||||||
if (shell_) {
|
if (shell_) {
|
||||||
if (!shell_->running()) {
|
if (!shell_->running()) {
|
||||||
@@ -1715,9 +1715,11 @@ void EMSESP::loop() {
|
|||||||
int c = serial_console_.read();
|
int c = serial_console_.read();
|
||||||
if (c != -1) {
|
if (c != -1) {
|
||||||
show_prompt = true;
|
show_prompt = true;
|
||||||
|
Serial.println(c);
|
||||||
}
|
}
|
||||||
// https://daleswanson.org/ascii.htm#:~:text=0
|
// https://daleswanson.org/ascii.htm#:~:text=0
|
||||||
if (c == '\x03') {
|
// CTRL-D to open
|
||||||
|
if (c == '\x04') {
|
||||||
start_serial_console();
|
start_serial_console();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1738,7 +1740,7 @@ void EMSESP::start_serial_console() {
|
|||||||
void EMSESP::shell_prompt() {
|
void EMSESP::shell_prompt() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
serial_console_.println();
|
serial_console_.println();
|
||||||
serial_console_.printf("EMS-ESP %s: press CTRL-C to activate this serial console", EMSESP_APP_VERSION);
|
serial_console_.printf("EMS-ESP %s: press CTRL-D to activate this serial console", EMSESP_APP_VERSION);
|
||||||
serial_console_.println();
|
serial_console_.println();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/info");
|
request.url("/api/analogsensor/info");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/test_analog1");
|
request.url("/api/analogsensor/test_analogsensor1");
|
||||||
request.url("/api/analogsensor/36");
|
request.url("/api/analogsensor/36");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
@@ -973,48 +973,55 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
if (single) {
|
if (single) {
|
||||||
// run dedicated tests only
|
// run dedicated tests only
|
||||||
|
|
||||||
// EMSESP::webCustomEntityService.test(); // custom entities
|
EMSESP::webCustomEntityService.test(); // custom entities
|
||||||
// EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
|
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
|
||||||
// EMSESP::temperaturesensor_.test(); // add temperature sensors
|
EMSESP::temperaturesensor_.test(); // add temperature sensors
|
||||||
// EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
|
EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
|
||||||
|
|
||||||
|
// request.url("/rest/deviceEntities");
|
||||||
|
// EMSESP::webCustomizationService.device_entities(&request);
|
||||||
|
|
||||||
|
request.url("/rest/dashboardData");
|
||||||
|
EMSESP::webDataService.dashboard_data(&request);
|
||||||
|
|
||||||
|
// COMMANDS
|
||||||
// shell.invoke_command("call system fetch");
|
// shell.invoke_command("call system fetch");
|
||||||
// request.url("/api/system/fetch");
|
// request.url("/api/system/fetch");
|
||||||
// EMSESP::webAPIService.webAPIService(&request);
|
// EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
// request.url("/api/system/restart");
|
// request.url("/api/system/restart");
|
||||||
// EMSESP::webAPIService.webAPIService(&request);
|
// EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
// request.url("/api/system/format");
|
// request.url("/api/system/format");
|
||||||
// EMSESP::webAPIService.webAPIService(&request);
|
// EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
request.method(HTTP_POST);
|
|
||||||
|
|
||||||
char data1[] = "{\"device\":\"system\", \"cmd\":\"restart\",\"id\":-1}";
|
|
||||||
deserializeJson(doc, data1);
|
|
||||||
request.url("/api");
|
|
||||||
EMSESP::webAPIService.webAPIService(&request, doc.as<JsonVariant>());
|
|
||||||
|
|
||||||
char data2[] = "{\"action\":\"customSupport\", \"param\":\"hello\"}";
|
|
||||||
deserializeJson(doc, data2);
|
|
||||||
request.url("/rest/action");
|
|
||||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
|
||||||
|
|
||||||
char data3[] = "{\"action\":\"export\", \"param\":\"schedule\"}";
|
|
||||||
deserializeJson(doc, data3);
|
|
||||||
request.url("/rest/action");
|
|
||||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
|
||||||
|
|
||||||
char data4[] = "{\"action\":\"export\", \"param\":\"allvalues\"}";
|
|
||||||
deserializeJson(doc, data4);
|
|
||||||
request.url("/rest/action");
|
|
||||||
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
|
||||||
|
|
||||||
// request.url("/api/thermostat");
|
// request.url("/api/thermostat");
|
||||||
// EMSESP::webAPIService.webAPIService(&request);
|
// EMSESP::webAPIService.webAPIService(&request);
|
||||||
// request.url("/api/thermostat/hc1");
|
// request.url("/api/thermostat/hc1");
|
||||||
// EMSESP::webAPIService.webAPIService(&request);
|
// EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
|
// POST COMMANDS
|
||||||
|
// request.method(HTTP_POST);
|
||||||
|
|
||||||
|
// char data1[] = "{\"device\":\"system\", \"cmd\":\"restart\",\"id\":-1}";
|
||||||
|
// deserializeJson(doc, data1);
|
||||||
|
// request.url("/api");
|
||||||
|
// EMSESP::webAPIService.webAPIService(&request, doc.as<JsonVariant>());
|
||||||
|
|
||||||
|
// char data2[] = "{\"action\":\"customSupport\", \"param\":\"hello\"}";
|
||||||
|
// deserializeJson(doc, data2);
|
||||||
|
// request.url("/rest/action");
|
||||||
|
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||||
|
|
||||||
|
// char data3[] = "{\"action\":\"export\", \"param\":\"schedule\"}";
|
||||||
|
// deserializeJson(doc, data3);
|
||||||
|
// request.url("/rest/action");
|
||||||
|
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||||
|
|
||||||
|
// char data4[] = "{\"action\":\"export\", \"param\":\"allvalues\"}";
|
||||||
|
// deserializeJson(doc, data4);
|
||||||
|
// request.url("/rest/action");
|
||||||
|
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
EMSESP::webCustomEntityService.test(); // custom entities
|
EMSESP::webCustomEntityService.test(); // custom entities
|
||||||
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
|
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
|
||||||
@@ -1092,11 +1099,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/info");
|
request.url("/api/temperaturesensor/info");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/test_sensor2");
|
request.url("/api/temperaturesensor/test_tempsensor2");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/0B_0C0D_0E0F_1011");
|
request.url("/api/temperaturesensor/0B_0C0D_0E0F_1011");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/test_sensor2/value");
|
request.url("/api/temperaturesensor/test_tempsensor2/value");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
// analogsensor
|
// analogsensor
|
||||||
@@ -1104,9 +1111,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/info");
|
request.url("/api/analogsensor/info");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/test_analog1");
|
request.url("/api/analogsensor/test_analogsensor1");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/test_analog1/offset");
|
request.url("/api/analogsensor/test_analogsensor1/offset");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
// system calls with POST
|
// system calls with POST
|
||||||
@@ -1183,11 +1190,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX");
|
request.url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/temperaturesensor/test_sensor2/bad");
|
request.url("/api/temperaturesensor/test_tempsensor2/bad");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|
||||||
// analogsensor
|
// analogsensor
|
||||||
request.url("/api/analogsensor/test_analog1/bad");
|
request.url("/api/analogsensor/test_analogsensor1/bad");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
request.url("/api/analogsensor/test_analog10");
|
request.url("/api/analogsensor/test_analog10");
|
||||||
EMSESP::webAPIService.webAPIService(&request);
|
EMSESP::webAPIService.webAPIService(&request);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.7.0-dev.43"
|
#define EMSESP_APP_VERSION "3.7.0-dev.44"
|
||||||
@@ -473,15 +473,24 @@ uint8_t WebCustomEntityService::count_entities() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send to dashboard, msgpack don't like serialized, use number
|
// send to dashboard, msgpack don't like serialized, use number
|
||||||
void WebCustomEntityService::generate_value_web(JsonObject output) {
|
void WebCustomEntityService::generate_value_web(JsonObject output, const bool is_dashboard) {
|
||||||
JsonArray data = output["data"].to<JsonArray>();
|
JsonArray nodes = output["nodes"].to<JsonArray>();
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
|
|
||||||
for (const CustomEntityItem & entity : *customEntityItems_) {
|
for (const CustomEntityItem & entity : *customEntityItems_) {
|
||||||
bool include = false;
|
bool include = false;
|
||||||
JsonObject obj = data.add<JsonObject>(); // create the object, we know there is a value
|
JsonObject root_obj = nodes.add<JsonObject>(); // create the object, we know there is a value
|
||||||
obj["id"] = "00" + entity.name;
|
|
||||||
obj["u"] = entity.uom;
|
JsonObject obj;
|
||||||
|
if (is_dashboard) {
|
||||||
|
root_obj["id"] = (EMSdevice::DeviceTypeUniqueID::CUSTOM_UID * 100) + index; // make unique
|
||||||
|
obj = root_obj["dv"].to<JsonObject>();
|
||||||
|
} else {
|
||||||
|
obj = root_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj["id"] = "00" + entity.name;
|
||||||
|
obj["u"] = entity.uom;
|
||||||
|
|
||||||
if (entity.writeable) {
|
if (entity.writeable) {
|
||||||
obj["c"] = entity.name;
|
obj["c"] = entity.name;
|
||||||
@@ -549,7 +558,7 @@ void WebCustomEntityService::generate_value_web(JsonObject output) {
|
|||||||
if (include) {
|
if (include) {
|
||||||
index++;
|
index++;
|
||||||
} else {
|
} else {
|
||||||
data.remove(index);
|
nodes.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
|
|||||||
void fetch();
|
void fetch();
|
||||||
void render_value(JsonObject output, CustomEntityItem & entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
|
void render_value(JsonObject output, CustomEntityItem & entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
|
||||||
void show_values(JsonObject output);
|
void show_values(JsonObject output);
|
||||||
void generate_value_web(JsonObject output);
|
void generate_value_web(JsonObject output, const bool is_dashboard = false);
|
||||||
|
|
||||||
uint8_t count_entities();
|
uint8_t count_entities();
|
||||||
void ha_reset() {
|
void ha_reset() {
|
||||||
|
|||||||
@@ -162,8 +162,15 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques
|
|||||||
// send back list of device entities
|
// send back list of device entities
|
||||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
|
void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
|
|
||||||
|
// for testing we hardcode the id to 1 - the boiler
|
||||||
|
#if defined(EMSESP_STANDALONE) && defined(EMSESP_TEST)
|
||||||
|
if (1) {
|
||||||
|
id = 1;
|
||||||
|
#else
|
||||||
if (request->hasParam(F_(id))) {
|
if (request->hasParam(F_(id))) {
|
||||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||||
|
#endif
|
||||||
|
|
||||||
auto * response = new AsyncJsonResponse(true, true); // array and msgpack
|
auto * response = new AsyncJsonResponse(true, true); // array and msgpack
|
||||||
|
|
||||||
@@ -178,7 +185,12 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
|
|||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
JsonArray output = response->getRoot();
|
JsonArray output = response->getRoot();
|
||||||
emsdevice->generate_values_web_customization(output);
|
emsdevice->generate_values_web_customization(output);
|
||||||
|
#else
|
||||||
|
JsonDocument doc;
|
||||||
|
JsonArray output = doc.to<JsonArray>();
|
||||||
|
emsdevice->generate_values_web_customization(output);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
size_t length = response->setLength();
|
size_t length = response->setLength();
|
||||||
EMSESP::logger().debug("Customizations buffer used: %d", length);
|
EMSESP::logger().debug("Customizations buffer used: %d", length);
|
||||||
@@ -348,13 +360,13 @@ void WebCustomizationService::test() {
|
|||||||
webCustomization.sensorCustomizations.clear();
|
webCustomization.sensorCustomizations.clear();
|
||||||
auto sensor = SensorCustomization();
|
auto sensor = SensorCustomization();
|
||||||
sensor.id = "01_0203_0405_0607";
|
sensor.id = "01_0203_0405_0607";
|
||||||
sensor.name = "test_sensor1";
|
sensor.name = "test_tempsensor1";
|
||||||
sensor.offset = 0;
|
sensor.offset = 0;
|
||||||
webCustomization.sensorCustomizations.push_back(sensor);
|
webCustomization.sensorCustomizations.push_back(sensor);
|
||||||
|
|
||||||
auto sensor2 = SensorCustomization();
|
auto sensor2 = SensorCustomization();
|
||||||
sensor2.id = "0B_0C0D_0E0F_1011";
|
sensor2.id = "0B_0C0D_0E0F_1011";
|
||||||
sensor2.name = "test_sensor2";
|
sensor2.name = "test_tempsensor2";
|
||||||
sensor2.offset = 4;
|
sensor2.offset = 4;
|
||||||
webCustomization.sensorCustomizations.push_back(sensor2);
|
webCustomization.sensorCustomizations.push_back(sensor2);
|
||||||
|
|
||||||
@@ -363,7 +375,7 @@ void WebCustomizationService::test() {
|
|||||||
webCustomization.analogCustomizations.clear();
|
webCustomization.analogCustomizations.clear();
|
||||||
auto analog = AnalogCustomization();
|
auto analog = AnalogCustomization();
|
||||||
analog.gpio = 36;
|
analog.gpio = 36;
|
||||||
analog.name = "test_analog1";
|
analog.name = "test_analogsensor1";
|
||||||
analog.offset = 0;
|
analog.offset = 0;
|
||||||
analog.factor = 0.1;
|
analog.factor = 0.1;
|
||||||
analog.uom = 17;
|
analog.uom = 17;
|
||||||
@@ -372,22 +384,60 @@ void WebCustomizationService::test() {
|
|||||||
|
|
||||||
analog = AnalogCustomization();
|
analog = AnalogCustomization();
|
||||||
analog.gpio = 37;
|
analog.gpio = 37;
|
||||||
analog.name = "test_analog2";
|
analog.name = "test_analogsensor2";
|
||||||
analog.offset = 0;
|
analog.offset = 0;
|
||||||
analog.factor = 1;
|
analog.factor = 1;
|
||||||
analog.uom = 0;
|
analog.uom = 0;
|
||||||
analog.type = 1;
|
analog.type = 1;
|
||||||
webCustomization.analogCustomizations.push_back(analog);
|
webCustomization.analogCustomizations.push_back(analog);
|
||||||
|
|
||||||
// EMS entities
|
analog = AnalogCustomization();
|
||||||
|
analog.gpio = 38;
|
||||||
|
analog.name = "test_analogsensor3";
|
||||||
|
analog.offset = 0;
|
||||||
|
analog.factor = 1;
|
||||||
|
analog.uom = 0;
|
||||||
|
analog.type = 0; // disabled, not-used
|
||||||
|
webCustomization.analogCustomizations.push_back(analog);
|
||||||
|
|
||||||
|
// EMS entities, mark some as favorites
|
||||||
webCustomization.entityCustomizations.clear();
|
webCustomization.entityCustomizations.clear();
|
||||||
auto emsEntity = EntityCustomization();
|
auto emsEntity = EntityCustomization();
|
||||||
emsEntity.product_id = 123;
|
emsEntity.product_id = 123;
|
||||||
emsEntity.device_id = 8;
|
emsEntity.device_id = 8;
|
||||||
emsEntity.custom_name = "Custom Name!!";
|
emsEntity.custom_name = "My Custom Boiler";
|
||||||
emsEntity.entity_ids.push_back("08heatingactive|is my heating on?");
|
emsEntity.entity_ids.push_back("08heatingactive|is my heating on?");
|
||||||
|
emsEntity.entity_ids.push_back("08tapwateractive");
|
||||||
|
emsEntity.entity_ids.push_back("08selflowtemp|<90");
|
||||||
webCustomization.entityCustomizations.push_back(emsEntity);
|
webCustomization.entityCustomizations.push_back(emsEntity);
|
||||||
|
|
||||||
|
// since custom device name is loaded at discovery, we need to force it here
|
||||||
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
|
if (emsdevice->is_device_id(emsEntity.device_id)) {
|
||||||
|
emsdevice->custom_name(emsEntity.custom_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and the same with the custom masks and names for entity values. It's done in EMSdevice::add_device_value()
|
||||||
|
// so we need to force it here
|
||||||
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
|
if (emsdevice->is_device_id(emsEntity.device_id)) {
|
||||||
|
// find the device value and set the mask and custom name to match the above fake data
|
||||||
|
for (auto & dv : emsdevice->devicevalues_) {
|
||||||
|
if (strcmp(dv.short_name, "heatingactive") == 0) {
|
||||||
|
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||||
|
dv.custom_fullname = "is my heating on?";
|
||||||
|
} else if (strcmp(dv.short_name, "tapwateractive") == 0) {
|
||||||
|
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||||
|
} else if (strcmp(dv.short_name, "selflowtemp") == 0) {
|
||||||
|
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return StateUpdateResult::CHANGED; // persist the changes
|
return StateUpdateResult::CHANGED; // persist the changes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,13 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
|
|||||||
server->on(EMSESP_SENSOR_DATA_SERVICE_PATH,
|
server->on(EMSESP_SENSOR_DATA_SERVICE_PATH,
|
||||||
HTTP_GET,
|
HTTP_GET,
|
||||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { sensor_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { sensor_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
|
|
||||||
|
server->on(EMSESP_DASHBOARD_DATA_SERVICE_PATH,
|
||||||
|
HTTP_GET,
|
||||||
|
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { dashboard_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is used in the dashboard and contains all ems device information
|
// this is used in the Devices page and contains all EMS device information
|
||||||
// /coreData endpoint
|
// /coreData endpoint
|
||||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||||
auto * response = new AsyncJsonResponse(false);
|
auto * response = new AsyncJsonResponse(false);
|
||||||
@@ -74,7 +78,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
|||||||
uint8_t customEntities = EMSESP::webCustomEntityService.count_entities();
|
uint8_t customEntities = EMSESP::webCustomEntityService.count_entities();
|
||||||
if (customEntities) {
|
if (customEntities) {
|
||||||
JsonObject obj = devices.add<JsonObject>();
|
JsonObject obj = devices.add<JsonObject>();
|
||||||
obj["id"] = 99; // the last unique id
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::CUSTOM_UID;
|
||||||
obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name
|
obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name
|
||||||
obj["t"] = EMSdevice::DeviceType::CUSTOM; // device type number
|
obj["t"] = EMSdevice::DeviceType::CUSTOM; // device type number
|
||||||
obj["b"] = Helpers::translated_word(FL_(na)); // brand
|
obj["b"] = Helpers::translated_word(FL_(na)); // brand
|
||||||
@@ -150,6 +154,7 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The unique_id is the unique record ID from the Web table to identify which device to load
|
// The unique_id is the unique record ID from the Web table to identify which device to load
|
||||||
|
// endpoint /rest/deviceData?id=n
|
||||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||||
void WebDataService::device_data(AsyncWebServerRequest * request) {
|
void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
@@ -189,7 +194,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (id == 99) {
|
if (id == EMSdevice::DeviceTypeUniqueID::CUSTOM_UID) {
|
||||||
JsonObject output = response->getRoot();
|
JsonObject output = response->getRoot();
|
||||||
EMSESP::webCustomEntityService.generate_value_web(output);
|
EMSESP::webCustomEntityService.generate_value_web(output);
|
||||||
response->setLength();
|
response->setLength();
|
||||||
@@ -219,84 +224,76 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// using the unique ID from the web find the real device type
|
// using the unique ID from the web find the real device type
|
||||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
uint8_t device_type = EMSdevice::DeviceType::UNKNOWN;
|
||||||
if (emsdevice->unique_id() == unique_id) {
|
switch (unique_id) {
|
||||||
// create JSON for output
|
case EMSdevice::DeviceTypeUniqueID::CUSTOM_UID:
|
||||||
auto * response = new AsyncJsonResponse(false);
|
device_type = EMSdevice::DeviceType::CUSTOM;
|
||||||
JsonObject output = response->getRoot();
|
break;
|
||||||
|
case EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID:
|
||||||
// the data could be in any format, but we need string
|
device_type = EMSdevice::DeviceType::SCHEDULER;
|
||||||
// authenticated is always true
|
break;
|
||||||
uint8_t return_code = CommandRet::NOT_FOUND;
|
case EMSdevice::DeviceTypeUniqueID::TEMPERATURESENSOR_UID:
|
||||||
uint8_t device_type = emsdevice->device_type();
|
device_type = EMSdevice::DeviceType::TEMPERATURESENSOR;
|
||||||
// parse the command as it could have a hc or dhw prefixed, e.g. hc2/seltemp
|
break;
|
||||||
int8_t id = -1; // default
|
case EMSdevice::DeviceTypeUniqueID::ANALOGSENSOR_UID:
|
||||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
device_type = EMSdevice::DeviceType::ANALOGSENSOR;
|
||||||
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
break;
|
||||||
|
default:
|
||||||
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
|
if (emsdevice->unique_id() == unique_id) {
|
||||||
|
device_type = emsdevice->device_type();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (data.is<const char *>()) {
|
|
||||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
|
||||||
} else if (data.is<int>()) {
|
|
||||||
char s[10];
|
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int32_t>(), 0), true, id, output);
|
|
||||||
} else if (data.is<float>()) {
|
|
||||||
char s[10];
|
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
|
||||||
} else if (data.is<bool>()) {
|
|
||||||
return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write log
|
|
||||||
if (return_code != CommandRet::OK) {
|
|
||||||
EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code));
|
|
||||||
} else {
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("Write command successful");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
response->setCode((return_code == CommandRet::OK) ? 200 : 400); // bad request
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (device_type == EMSdevice::DeviceType::UNKNOWN) {
|
||||||
// special check for custom entities (which have a unique id of 99)
|
EMSESP::logger().warning("Write command failed, bad device id: %d", unique_id);
|
||||||
if (unique_id == 99) {
|
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
||||||
auto * response = new AsyncJsonResponse(false);
|
|
||||||
JsonObject output = response->getRoot();
|
|
||||||
uint8_t return_code = CommandRet::NOT_FOUND;
|
|
||||||
uint8_t device_type = EMSdevice::DeviceType::CUSTOM;
|
|
||||||
// parse the command as it could have a hc or dhw prefixed, e.g. hc2/seltemp
|
|
||||||
int8_t id = -1;
|
|
||||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
|
||||||
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
|
||||||
}
|
|
||||||
if (data.is<const char *>()) {
|
|
||||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
|
||||||
} else if (data.is<int>()) {
|
|
||||||
char s[10];
|
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int32_t>(), 0), true, id, output);
|
|
||||||
} else if (data.is<float>()) {
|
|
||||||
char s[10];
|
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
|
||||||
}
|
|
||||||
if (return_code != CommandRet::OK) {
|
|
||||||
EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code));
|
|
||||||
} else {
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("Write command successful");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
response->setCode((return_code == CommandRet::OK) ? 200 : 400); // bad request
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// create JSON for output
|
||||||
|
auto * response = new AsyncJsonResponse(false);
|
||||||
|
JsonObject output = response->getRoot();
|
||||||
|
// the data could be in any format, but we need string
|
||||||
|
// authenticated is always true
|
||||||
|
uint8_t return_code = CommandRet::NOT_FOUND;
|
||||||
|
// parse the command as it could have a hc or dhw prefixed, e.g. hc2/seltemp
|
||||||
|
int8_t id = -1; // default
|
||||||
|
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||||
|
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
||||||
|
}
|
||||||
|
if (data.is<const char *>()) {
|
||||||
|
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
||||||
|
} else if (data.is<int>()) {
|
||||||
|
char s[10];
|
||||||
|
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int32_t>(), 0), true, id, output);
|
||||||
|
} else if (data.is<float>()) {
|
||||||
|
char s[10];
|
||||||
|
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
||||||
|
} else if (data.is<bool>()) {
|
||||||
|
return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write log
|
||||||
|
if (return_code != CommandRet::OK) {
|
||||||
|
// is already logged by command and message contains code
|
||||||
|
// EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code));
|
||||||
|
} else {
|
||||||
|
#if defined(EMSESP_DEBUG)
|
||||||
|
EMSESP::logger().debug("Write command successful");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
response->setCode((return_code == CommandRet::OK) ? 200 : 400); // bad request
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSESP::logger().warning("Write command failed, bad json");
|
||||||
|
|
||||||
// if we reach here, fail
|
// if we reach here, fail
|
||||||
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -346,4 +343,131 @@ void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVa
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is used in the dashboard and contains all ems device information
|
||||||
|
// /dashboardData endpoint
|
||||||
|
void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||||
|
auto * response = new AsyncJsonResponse(true, true); // its an Array and also msgpack'd
|
||||||
|
|
||||||
|
#if defined(EMSESP_STANDALONE)
|
||||||
|
JsonDocument doc;
|
||||||
|
JsonArray root = doc.to<JsonArray>();
|
||||||
|
#else
|
||||||
|
JsonArray root = response->getRoot();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
|
if (emsdevice->count_entities_fav()) {
|
||||||
|
JsonObject obj = root.add<JsonObject>();
|
||||||
|
obj["id"] = emsdevice->unique_id(); // it's unique id
|
||||||
|
obj["n"] = emsdevice->name(); // custom name
|
||||||
|
obj["t"] = emsdevice->device_type(); // device type number
|
||||||
|
emsdevice->generate_values_web(obj, true); // is_dashboard = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add custom entities, if we have any
|
||||||
|
if (EMSESP::webCustomEntityService.count_entities()) {
|
||||||
|
JsonObject obj = root.add<JsonObject>();
|
||||||
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::CUSTOM_UID; // it's unique id
|
||||||
|
obj["t"] = EMSdevice::DeviceType::CUSTOM; // device type number
|
||||||
|
EMSESP::webCustomEntityService.generate_value_web(obj, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add temperature sensors
|
||||||
|
if (EMSESP::temperaturesensor_.have_sensors()) {
|
||||||
|
JsonObject obj = root.add<JsonObject>();
|
||||||
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::TEMPERATURESENSOR_UID; // it's unique id
|
||||||
|
obj["t"] = EMSdevice::DeviceType::TEMPERATURESENSOR; // device type number
|
||||||
|
JsonArray nodes = obj["nodes"].to<JsonArray>();
|
||||||
|
uint8_t count = 0;
|
||||||
|
for (const auto & sensor : EMSESP::temperaturesensor_.sensors()) {
|
||||||
|
JsonObject node = nodes.add<JsonObject>();
|
||||||
|
node["id"] = (EMSdevice::DeviceTypeUniqueID::TEMPERATURESENSOR_UID * 100) + count++;
|
||||||
|
|
||||||
|
JsonObject dv = node["dv"].to<JsonObject>();
|
||||||
|
dv["id"] = "00" + sensor.name();
|
||||||
|
if (EMSESP::system_.fahrenheit()) {
|
||||||
|
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||||
|
dv["v"] = (float)sensor.temperature_c * 0.18 + 32;
|
||||||
|
}
|
||||||
|
dv["u"] = DeviceValueUOM::FAHRENHEIT;
|
||||||
|
} else {
|
||||||
|
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||||
|
dv["v"] = (float)sensor.temperature_c / 10;
|
||||||
|
}
|
||||||
|
dv["u"] = DeviceValueUOM::DEGREES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add analog sensors, count excludes disabled entries
|
||||||
|
if (EMSESP::analog_enabled() && EMSESP::analogsensor_.count_entities(false)) {
|
||||||
|
JsonObject obj = root.add<JsonObject>();
|
||||||
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::ANALOGSENSOR_UID; // it's unique id
|
||||||
|
obj["t"] = EMSdevice::DeviceType::ANALOGSENSOR; // device type number
|
||||||
|
JsonArray nodes = obj["nodes"].to<JsonArray>();
|
||||||
|
uint8_t count = 0;
|
||||||
|
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||||
|
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) { // ignore disabled
|
||||||
|
JsonObject node = nodes.add<JsonObject>();
|
||||||
|
node["id"] = (EMSdevice::DeviceTypeUniqueID::ANALOGSENSOR_UID * 100) + count++;
|
||||||
|
|
||||||
|
JsonObject dv = node["dv"].to<JsonObject>();
|
||||||
|
dv["id"] = "00" + sensor.name();
|
||||||
|
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN) {
|
||||||
|
char s[12];
|
||||||
|
dv["v"] = Helpers::render_boolean(s, sensor.value() != 0, true);
|
||||||
|
JsonArray l = dv["l"].to<JsonArray>();
|
||||||
|
l.add(Helpers::render_boolean(s, false, true));
|
||||||
|
l.add(Helpers::render_boolean(s, true, true));
|
||||||
|
} else {
|
||||||
|
dv["v"] = Helpers::transformNumFloat(sensor.value(), 0);
|
||||||
|
dv["u"] = sensor.uom();
|
||||||
|
}
|
||||||
|
if (sensor.type() == AnalogSensor::AnalogType::COUNTER || sensor.type() >= AnalogSensor::AnalogType::DIGITAL_OUT) {
|
||||||
|
dv["c"] = sensor.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// show scheduler, with name, on/off
|
||||||
|
if (EMSESP::webSchedulerService.count_entities()) {
|
||||||
|
JsonObject obj = root.add<JsonObject>();
|
||||||
|
obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id
|
||||||
|
obj["t"] = EMSdevice::DeviceType::SCHEDULER; // device type number
|
||||||
|
JsonArray nodes = obj["nodes"].to<JsonArray>();
|
||||||
|
uint8_t count = 0;
|
||||||
|
|
||||||
|
EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) {
|
||||||
|
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||||
|
// only add if we have a name - we don't need a u (UOM) for this
|
||||||
|
if (!scheduleItem.name.empty()) {
|
||||||
|
JsonObject node = nodes.add<JsonObject>();
|
||||||
|
node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++;
|
||||||
|
|
||||||
|
JsonObject dv = node["dv"].to<JsonObject>();
|
||||||
|
dv["id"] = "00" + scheduleItem.name;
|
||||||
|
dv["c"] = scheduleItem.name;
|
||||||
|
char s[12];
|
||||||
|
dv["v"] = Helpers::render_boolean(s, scheduleItem.active, true);
|
||||||
|
JsonArray l = dv["l"].to<JsonArray>();
|
||||||
|
l.add(Helpers::render_boolean(s, false, true));
|
||||||
|
l.add(Helpers::render_boolean(s, true, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(EMSESP_TEST) && defined(EMSESP_STANDALONE)
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("All dashboard_data: ");
|
||||||
|
serializeJson(root, Serial);
|
||||||
|
Serial.println();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#define EMSESP_CORE_DATA_SERVICE_PATH "/rest/coreData"
|
#define EMSESP_CORE_DATA_SERVICE_PATH "/rest/coreData"
|
||||||
#define EMSESP_DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
#define EMSESP_DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
||||||
#define EMSESP_SENSOR_DATA_SERVICE_PATH "/rest/sensorData"
|
#define EMSESP_SENSOR_DATA_SERVICE_PATH "/rest/sensorData"
|
||||||
|
#define EMSESP_DASHBOARD_DATA_SERVICE_PATH "/rest/dashboardData"
|
||||||
|
|
||||||
// POST
|
// POST
|
||||||
#define EMSESP_WRITE_DEVICE_VALUE_SERVICE_PATH "/rest/writeDeviceValue"
|
#define EMSESP_WRITE_DEVICE_VALUE_SERVICE_PATH "/rest/writeDeviceValue"
|
||||||
@@ -44,6 +45,7 @@ class WebDataService {
|
|||||||
void core_data(AsyncWebServerRequest * request);
|
void core_data(AsyncWebServerRequest * request);
|
||||||
void sensor_data(AsyncWebServerRequest * request);
|
void sensor_data(AsyncWebServerRequest * request);
|
||||||
void device_data(AsyncWebServerRequest * request);
|
void device_data(AsyncWebServerRequest * request);
|
||||||
|
void dashboard_data(AsyncWebServerRequest * request);
|
||||||
|
|
||||||
// POST
|
// POST
|
||||||
void write_device_value(AsyncWebServerRequest * request, JsonVariant json);
|
void write_device_value(AsyncWebServerRequest * request, JsonVariant json);
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ void WebSchedulerService::begin() {
|
|||||||
// and also calls when the Scheduler web page is refreshed
|
// and also calls when the Scheduler web page is refreshed
|
||||||
void WebScheduler::read(WebScheduler & webScheduler, JsonObject root) {
|
void WebScheduler::read(WebScheduler & webScheduler, JsonObject root) {
|
||||||
JsonArray schedule = root["schedule"].to<JsonArray>();
|
JsonArray schedule = root["schedule"].to<JsonArray>();
|
||||||
uint8_t counter = 0;
|
uint8_t counter = 1;
|
||||||
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||||
JsonObject si = schedule.add<JsonObject>();
|
JsonObject si = schedule.add<JsonObject>();
|
||||||
si["id"] = counter++; // id is only used to render the table and must be unique
|
si["id"] = counter++; // id is only used to render the table and must be unique. 0 is for Dashboard
|
||||||
si["flags"] = scheduleItem.flags;
|
si["flags"] = scheduleItem.flags;
|
||||||
si["active"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.active : false;
|
si["active"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.active : false;
|
||||||
si["time"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.time : "";
|
si["time"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.time : "";
|
||||||
@@ -71,33 +71,31 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
|||||||
EMSESP::webSchedulerService.ha_reset();
|
EMSESP::webSchedulerService.ha_reset();
|
||||||
|
|
||||||
// build up the list of schedule items
|
// build up the list of schedule items
|
||||||
if (root["schedule"].is<JsonArray>()) {
|
for (const JsonObject schedule : root["schedule"].as<JsonArray>()) {
|
||||||
for (const JsonObject schedule : root["schedule"].as<JsonArray>()) {
|
// create each schedule item, overwriting any previous settings
|
||||||
// create each schedule item, overwriting any previous settings
|
// ignore the id (as this is only used in the web for table rendering)
|
||||||
// ignore the id (as this is only used in the web for table rendering)
|
auto si = ScheduleItem();
|
||||||
auto si = ScheduleItem();
|
si.active = schedule["active"];
|
||||||
si.active = schedule["active"];
|
si.flags = schedule["flags"];
|
||||||
si.flags = schedule["flags"];
|
si.time = si.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? "" : schedule["time"].as<std::string>();
|
||||||
si.time = si.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? "" : schedule["time"].as<std::string>();
|
si.cmd = schedule["cmd"].as<std::string>();
|
||||||
si.cmd = schedule["cmd"].as<std::string>();
|
si.value = schedule["value"].as<std::string>();
|
||||||
si.value = schedule["value"].as<std::string>();
|
si.name = schedule["name"].as<std::string>();
|
||||||
si.name = schedule["name"].as<std::string>();
|
|
||||||
|
|
||||||
// calculated elapsed minutes
|
// calculated elapsed minutes
|
||||||
si.elapsed_min = Helpers::string2minutes(si.time);
|
si.elapsed_min = Helpers::string2minutes(si.time);
|
||||||
si.retry_cnt = 0xFF; // no startup retries
|
si.retry_cnt = 0xFF; // no startup retries
|
||||||
|
|
||||||
webScheduler.scheduleItems.push_back(si); // add to list
|
webScheduler.scheduleItems.push_back(si); // add to list
|
||||||
if (!webScheduler.scheduleItems.back().name.empty()) {
|
if (!webScheduler.scheduleItems.back().name.empty()) {
|
||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::SCHEDULER,
|
EMSdevice::DeviceType::SCHEDULER,
|
||||||
webScheduler.scheduleItems.back().name.c_str(),
|
webScheduler.scheduleItems.back().name.c_str(),
|
||||||
[webScheduler](const char * value, const int8_t id) {
|
[webScheduler](const char * value, const int8_t id) {
|
||||||
return EMSESP::webSchedulerService.command_setvalue(value, id, webScheduler.scheduleItems.back().name.c_str());
|
return EMSESP::webSchedulerService.command_setvalue(value, id, webScheduler.scheduleItems.back().name.c_str());
|
||||||
},
|
},
|
||||||
FL_(schedule_cmd),
|
FL_(schedule_cmd),
|
||||||
CommandFlag::ADMIN_ONLY);
|
CommandFlag::ADMIN_ONLY);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,6 +549,19 @@ void WebSchedulerService::test() {
|
|||||||
si.elapsed_min = 0;
|
si.elapsed_min = 0;
|
||||||
si.retry_cnt = 0xFF; // no startup retries
|
si.retry_cnt = 0xFF; // no startup retries
|
||||||
|
|
||||||
|
webScheduler.scheduleItems.push_back(si);
|
||||||
|
|
||||||
|
// test 2
|
||||||
|
si = ScheduleItem();
|
||||||
|
si.active = false;
|
||||||
|
si.flags = 1;
|
||||||
|
si.time = "13:00";
|
||||||
|
si.cmd = "system/message";
|
||||||
|
si.value = "20";
|
||||||
|
si.name = ""; // to make sure its excluded from Dashboard
|
||||||
|
si.elapsed_min = 0;
|
||||||
|
si.retry_cnt = 0xFF; // no startup retries
|
||||||
|
|
||||||
webScheduler.scheduleItems.push_back(si);
|
webScheduler.scheduleItems.push_back(si);
|
||||||
already_added = true;
|
already_added = true;
|
||||||
|
|
||||||
|
|||||||
@@ -359,15 +359,15 @@ void create_tests() {
|
|||||||
// temperaturesensor
|
// temperaturesensor
|
||||||
capture("/api/temperaturesensor");
|
capture("/api/temperaturesensor");
|
||||||
capture("/api/temperaturesensor/info");
|
capture("/api/temperaturesensor/info");
|
||||||
capture("/api/temperaturesensor/test_sensor2");
|
capture("/api/temperaturesensor/test_tempsensor2");
|
||||||
capture("/api/temperaturesensor/0B_0C0D_0E0F_1011");
|
capture("/api/temperaturesensor/0B_0C0D_0E0F_1011");
|
||||||
capture("/api/temperaturesensor/test_sensor2/value");
|
capture("/api/temperaturesensor/test_tempsensor2/value");
|
||||||
|
|
||||||
// analogsensor
|
// analogsensor
|
||||||
capture("/api/analogsensor");
|
capture("/api/analogsensor");
|
||||||
capture("/api/analogsensor/info");
|
capture("/api/analogsensor/info");
|
||||||
capture("/api/analogsensor/test_analog1");
|
capture("/api/analogsensor/test_analogsensor1");
|
||||||
capture("/api/analogsensor/test_analog1/offset");
|
capture("/api/analogsensor/test_analogsensor1/offset");
|
||||||
|
|
||||||
// these tests should all fail...
|
// these tests should all fail...
|
||||||
capture("/api/boiler2");
|
capture("/api/boiler2");
|
||||||
@@ -391,10 +391,10 @@ void create_tests() {
|
|||||||
// temperaturesensor
|
// temperaturesensor
|
||||||
capture("/api/temperaturesensor/test_sensor20");
|
capture("/api/temperaturesensor/test_sensor20");
|
||||||
capture("/api/temperaturesensor/0B_0C0D_0E0F_XXXX");
|
capture("/api/temperaturesensor/0B_0C0D_0E0F_XXXX");
|
||||||
capture("/api/temperaturesensor/test_sensor2/bad");
|
capture("/api/temperaturesensor/test_tempsensor2/bad");
|
||||||
|
|
||||||
// analogsensor
|
// analogsensor
|
||||||
capture("/api/analogsensor/test_analog1/bad");
|
capture("/api/analogsensor/test_analogsensor1/bad");
|
||||||
capture("/api/analogsensor/test_analog10");
|
capture("/api/analogsensor/test_analog10");
|
||||||
capture("/api/analogsensor/test_analog10/bad2");
|
capture("/api/analogsensor/test_analog10/bad2");
|
||||||
|
|
||||||
|
|||||||
@@ -234,49 +234,51 @@ void test_26() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_27() {
|
void test_27() {
|
||||||
auto expected_response = "[{\"test_sensor1\":12.3,\"test_sensor2\":45.6}]";
|
auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_28() {
|
void test_28() {
|
||||||
auto expected_response = "[{\"test_sensor1\":12.3,\"test_sensor2\":45.6}]";
|
auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/info"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/info"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_29() {
|
void test_29() {
|
||||||
auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_sensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]";
|
auto expected_response =
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2"));
|
"[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]";
|
||||||
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_30() {
|
void test_30() {
|
||||||
auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_sensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]";
|
auto expected_response =
|
||||||
|
"[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_1011"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_1011"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_31() {
|
void test_31() {
|
||||||
auto expected_response = "[{\"api_data\":\"45.6\"}]";
|
auto expected_response = "[{\"api_data\":\"45.6\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2/value"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_32() {
|
void test_32() {
|
||||||
auto expected_response = "[{\"test_analog1\":0,\"test_analog2\":1}]";
|
auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_33() {
|
void test_33() {
|
||||||
auto expected_response = "[{\"test_analog1\":0,\"test_analog2\":1}]";
|
auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/info"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/info"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_34() {
|
void test_34() {
|
||||||
auto expected_response =
|
auto expected_response =
|
||||||
"[{\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\",\"value\":0,\"writeable\":false,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]";
|
"[{\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\",\"value\":0,\"writeable\":false,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_35() {
|
void test_35() {
|
||||||
auto expected_response = "[{\"api_data\":\"0\"}]";
|
auto expected_response = "[{\"api_data\":\"0\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1/offset"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/offset"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_36() {
|
void test_36() {
|
||||||
@@ -345,13 +347,13 @@ void test_48() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_49() {
|
void test_49() {
|
||||||
auto expected_response = "[{\"message\":\"no bad in test_sensor2\"}]";
|
auto expected_response = "[{\"message\":\"no bad in test_tempsensor2\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2/bad"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/bad"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_50() {
|
void test_50() {
|
||||||
auto expected_response = "[{\"message\":\"no bad in test_analog1\"}]";
|
auto expected_response = "[{\"message\":\"no bad in test_analogsensor1\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1/bad"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/bad"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_51() {
|
void test_51() {
|
||||||
|
|||||||
Reference in New Issue
Block a user