mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
refactor dialog to prevent multiple parent renders
This commit is contained in:
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -7,5 +7,20 @@
|
||||
"eslint.workingDirectories": ["interface"],
|
||||
"prettier.prettierPath": "interface/.yarn/sdks/prettier/index.js",
|
||||
"typescript.tsdk": "interface/.yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"files.associations": {
|
||||
"*.tsx": "typescriptreact",
|
||||
"*.tcc": "cpp",
|
||||
"optional": "cpp",
|
||||
"istream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"functional": "cpp",
|
||||
"regex": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"tsconfigRootDir": ".",
|
||||
"project": ["tsconfig.json"]
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint"],
|
||||
"plugins": ["react", "@typescript-eslint", "autofix", "react-hooks"],
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"typescript": {
|
||||
@@ -36,7 +36,6 @@
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"object-shorthand": "error",
|
||||
"no-console": "warn",
|
||||
"@typescript-eslint/consistent-type-definitions": ["off", "type"],
|
||||
@@ -52,6 +51,29 @@
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-implied-eval": "off",
|
||||
"@typescript-eslint/no-misused-promises": "off",
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"react-hooks/exhaustive-deps": "error",
|
||||
"@typescript-eslint/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports"
|
||||
}
|
||||
],
|
||||
// "autofix/no-unused-vars": [
|
||||
// "error",
|
||||
// {
|
||||
// "argsIgnorePattern": "^_",
|
||||
// "ignoreRestSiblings": true,
|
||||
// "destructuredArrayIgnorePattern": "^_"
|
||||
// }
|
||||
// ],
|
||||
"react/self-closing-comp": [
|
||||
"error",
|
||||
{
|
||||
"component": true,
|
||||
"html": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@msgpack/msgpack": "^3.0.0-beta2",
|
||||
"@mui/icons-material": "^5.11.16",
|
||||
"@mui/material": "^5.12.0",
|
||||
"@mui/material": "^5.12.1",
|
||||
"@remix-run/router": "^1.5.0",
|
||||
"@table-library/react-table-library": "4.1.0",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react": "^18.0.37",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@yarnpkg/pnpify": "^4.0.0-rc.42",
|
||||
@@ -51,14 +51,15 @@
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "^2",
|
||||
"@types/styled-components": "^5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"@vitejs/plugin-react-swc": "^3.3.0",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
"eslint-plugin-autofix": "^1.1.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
@@ -68,11 +69,11 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"terser": "^5.16.9",
|
||||
"vite": "^4.2.1",
|
||||
"terser": "^5.17.0",
|
||||
"vite": "^4.2.2",
|
||||
"vite-plugin-minify": "^1.5.2",
|
||||
"vite-plugin-svgr": "^2.4.0",
|
||||
"vite-tsconfig-paths": "^4.1.0"
|
||||
"vite-tsconfig-paths": "^4.2.0"
|
||||
},
|
||||
"packageManager": "yarn@3.4.1"
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ const WiFiNetworkSelector: FC<WiFiNetworkSelectorProps> = ({ networkList }) => {
|
||||
|
||||
const renderNetwork = (network: WiFiNetwork) => {
|
||||
return (
|
||||
<ListItem key={network.bssid} button onClick={() => wifiConnectionContext.selectNetwork(network)}>
|
||||
<ListItem key={network.bssid} onClick={() => wifiConnectionContext.selectNetwork(network)}>
|
||||
<ListItemAvatar>
|
||||
<Avatar>{isNetworkOpen(network) ? <LockOpenIcon /> : <LockIcon />}</Avatar>
|
||||
</ListItemAvatar>
|
||||
|
||||
@@ -311,7 +311,7 @@ const de: Translation = {
|
||||
LEAVE: 'Verlassen',
|
||||
SCHEDULER: 'Planer',
|
||||
SCHEDULER_HELP_1: 'Fügen Sie eigene, geplante Befehle zur Automatisierung hinzu. Vergeben Sie einen Entitätsnamen um die Aktivierung über API/Mqtt zu steuern',
|
||||
SCHEDULER_HELP_2: 'Use 00:00 to trigger on boot', // TODO translate
|
||||
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
|
||||
SCHEDULE: 'Zeitplan',
|
||||
TIME: 'Zeit',
|
||||
TIMER: 'Timer',
|
||||
@@ -320,7 +320,8 @@ const de: Translation = {
|
||||
SCHEDULE_TIMER_2: 'jede Minute',
|
||||
SCHEDULE_TIMER_3: 'jede Stunde',
|
||||
CUSTOM_ENTITIES: 'Individuelle Entitäten',
|
||||
ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus'
|
||||
ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus',
|
||||
WRITEABLE: 'Schreibbar'
|
||||
};
|
||||
|
||||
export default de;
|
||||
|
||||
@@ -320,7 +320,8 @@ const en: Translation = {
|
||||
SCHEDULE_TIMER_2: 'every minute',
|
||||
SCHEDULE_TIMER_3: 'every hour',
|
||||
CUSTOM_ENTITIES: 'Custom Entities',
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus'
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus',
|
||||
WRITEABLE: 'Writeable'
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
||||
@@ -320,7 +320,8 @@ const fr: Translation = {
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
};
|
||||
|
||||
export default fr;
|
||||
|
||||
@@ -320,7 +320,8 @@ const nl: Translation = {
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
};
|
||||
|
||||
export default nl;
|
||||
|
||||
@@ -320,7 +320,8 @@ const no: Translation = {
|
||||
SCHEDULE_TIMER_2: 'hvert minutt',
|
||||
SCHEDULE_TIMER_3: 'hver time',
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
};
|
||||
|
||||
export default no;
|
||||
|
||||
@@ -320,7 +320,8 @@ const pl: BaseTranslation = {
|
||||
SCHEDULE_TIMER_2: 'co minutę',
|
||||
SCHEDULE_TIMER_3: 'co godzinę',
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus',
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
};
|
||||
|
||||
export default pl;
|
||||
|
||||
@@ -320,7 +320,9 @@ const sv: Translation = {
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
|
||||
};
|
||||
|
||||
export default sv;
|
||||
|
||||
@@ -320,7 +320,8 @@ const tr: Translation = {
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus' // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
WRITEABLE: 'Writeable' // TODO translate
|
||||
};
|
||||
|
||||
export default tr;
|
||||
|
||||
@@ -1,71 +1,40 @@
|
||||
import { FC, useState, useEffect, useCallback } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
Box,
|
||||
Grid,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
MenuItem,
|
||||
InputAdornment
|
||||
} from '@mui/material';
|
||||
import { Button, Typography, Box } from '@mui/material';
|
||||
|
||||
import { useTheme } from '@table-library/react-table-library/theme';
|
||||
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import DoneIcon from '@mui/icons-material/Done';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import { ValidatedTextField, ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
|
||||
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
|
||||
|
||||
import { DeviceValueUOM_s, EntityItem } from './types';
|
||||
import { extractErrorMessage, updateValue } from 'utils';
|
||||
import SettingsEntitiesDialog from './SettingsEntitiesDialog';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import { entityItemValidation } from './validators';
|
||||
import { ValidateFieldsError } from 'async-validator';
|
||||
import type { EntityItem } from './types';
|
||||
import { DeviceValueUOM_s } from './types';
|
||||
import { extractErrorMessage } from 'utils';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import * as EMSESP from './api';
|
||||
|
||||
function makeid() {
|
||||
return Math.floor(Math.random() * (Math.floor(200) - 100) + 100);
|
||||
}
|
||||
import { entityItemValidation } from './validators';
|
||||
|
||||
const SettingsEntities: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
const [numChanges, setNumChanges] = useState<number>(0);
|
||||
const blocker = useBlocker(numChanges !== 0);
|
||||
|
||||
const emptyEntity = {
|
||||
id: 0,
|
||||
name: '',
|
||||
device_id: 8,
|
||||
type_id: 0,
|
||||
offset: 0,
|
||||
factor: 1,
|
||||
uom: 0,
|
||||
val_type: 2,
|
||||
deleted: false,
|
||||
o_name: ''
|
||||
};
|
||||
|
||||
const [entities, setEntities] = useState<EntityItem[]>([emptyEntity]);
|
||||
const [entityItem, setEntityItem] = useState<EntityItem>();
|
||||
const [entities, setEntities] = useState<EntityItem[]>([]);
|
||||
const [selectedEntityItem, setSelectedEntityItem] = useState<EntityItem>();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
const [creating, setCreating] = useState<boolean>(false);
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||
|
||||
function hasEntityChanged(ei: EntityItem) {
|
||||
return (
|
||||
@@ -76,21 +45,15 @@ const SettingsEntities: FC = () => {
|
||||
ei.offset !== ei.o_offset ||
|
||||
ei.uom !== ei.o_uom ||
|
||||
ei.factor !== ei.o_factor ||
|
||||
ei.val_type !== ei.o_val_type ||
|
||||
ei.value_type !== ei.o_value_type ||
|
||||
ei.writeable !== ei.o_writeable ||
|
||||
ei.deleted !== ei.o_deleted
|
||||
);
|
||||
}
|
||||
|
||||
const getNumChanges = () => {
|
||||
if (!entities) {
|
||||
return 0;
|
||||
}
|
||||
return entities.filter((ei) => hasEntityChanged(ei)).length;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setNumChanges(getNumChanges());
|
||||
});
|
||||
setNumChanges(entities ? entities.filter((ei) => hasEntityChanged(ei)).length : 0);
|
||||
}, [entities]);
|
||||
|
||||
const entity_theme = useTheme({
|
||||
Table: `
|
||||
@@ -156,8 +119,9 @@ const SettingsEntities: FC = () => {
|
||||
o_offset: ei.offset,
|
||||
o_factor: ei.factor,
|
||||
o_uom: ei.uom,
|
||||
o_val_type: ei.val_type,
|
||||
o_value_type: ei.value_type,
|
||||
o_name: ei.name,
|
||||
o_writeable: ei.writeable,
|
||||
o_deleted: ei.deleted
|
||||
}))
|
||||
);
|
||||
@@ -182,19 +146,19 @@ const SettingsEntities: FC = () => {
|
||||
const response = await EMSESP.writeEntities({
|
||||
entities: entities
|
||||
.filter((ei) => !ei.deleted)
|
||||
.map((condensed_ei) => {
|
||||
return {
|
||||
id: condensed_ei.id,
|
||||
name: condensed_ei.name,
|
||||
device_id: condensed_ei.device_id,
|
||||
type_id: condensed_ei.type_id,
|
||||
offset: condensed_ei.offset,
|
||||
factor: condensed_ei.factor,
|
||||
uom: condensed_ei.uom,
|
||||
val_type: condensed_ei.val_type
|
||||
};
|
||||
})
|
||||
.map((condensed_ei) => ({
|
||||
id: condensed_ei.id,
|
||||
name: condensed_ei.name,
|
||||
device_id: condensed_ei.device_id,
|
||||
type_id: condensed_ei.type_id,
|
||||
offset: condensed_ei.offset,
|
||||
factor: condensed_ei.factor,
|
||||
uom: condensed_ei.uom,
|
||||
writeable: condensed_ei.writeable,
|
||||
value_type: condensed_ei.value_type
|
||||
}))
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
toast.success(LL.SUCCESS());
|
||||
} else {
|
||||
@@ -207,42 +171,48 @@ const SettingsEntities: FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const editEntityItem = (ei: EntityItem) => {
|
||||
const editEntityItem = useCallback((ei: EntityItem) => {
|
||||
setCreating(false);
|
||||
setEntityItem(ei);
|
||||
setSelectedEntityItem(ei);
|
||||
setDialogOpen(true);
|
||||
}, []);
|
||||
|
||||
const onDialogClose = () => {
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
const onDialogSave = (updatedItem: EntityItem) => {
|
||||
if (creating) {
|
||||
setEntities([...entities.filter((ei) => creating || ei.o_id !== updatedItem.o_id), updatedItem]);
|
||||
} else {
|
||||
setEntities(entities.map((ei) => (ei.id === updatedItem.id ? { ...ei, ...updatedItem } : ei)));
|
||||
}
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
// TODO need callback here too?
|
||||
const addEntityItem = () => {
|
||||
setCreating(true);
|
||||
setEntityItem({
|
||||
id: makeid(),
|
||||
setSelectedEntityItem({
|
||||
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
|
||||
device_id: 11,
|
||||
type_id: 0,
|
||||
offset: 0,
|
||||
factor: 1,
|
||||
val_type: 2,
|
||||
value_type: 2,
|
||||
uom: 0,
|
||||
name: '',
|
||||
writeable: false,
|
||||
deleted: false
|
||||
});
|
||||
};
|
||||
|
||||
const updateEntityItem = () => {
|
||||
console.log('here');
|
||||
if (entityItem) {
|
||||
setEntities([...entities.filter((ei) => creating || ei.o_id !== entityItem.o_id), entityItem]);
|
||||
}
|
||||
setEntityItem(undefined);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
function formatValue(value: any, uom: number) {
|
||||
if (value === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (uom === 0) {
|
||||
return new Intl.NumberFormat().format(value);
|
||||
}
|
||||
return new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom];
|
||||
return new Intl.NumberFormat().format(value) + (uom === 0 ? '' : ' ' + DeviceValueUOM_s[uom]);
|
||||
}
|
||||
|
||||
function showHex(value: number, digit: number) {
|
||||
@@ -287,185 +257,6 @@ const SettingsEntities: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const removeEntityItem = (ei: EntityItem) => {
|
||||
ei.deleted = true;
|
||||
setEntityItem(ei);
|
||||
updateEntityItem();
|
||||
};
|
||||
|
||||
const validateEntityItem = async () => {
|
||||
if (entityItem) {
|
||||
console.log(1);
|
||||
try {
|
||||
setFieldErrors(undefined);
|
||||
console.log(2);
|
||||
await validate(entityItemValidation(entities, entityItem), entityItem);
|
||||
console.log(3);
|
||||
updateEntityItem();
|
||||
} catch (errors: any) {
|
||||
console.log(4);
|
||||
console.log(errors);
|
||||
setFieldErrors(errors);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setEntityItem(undefined);
|
||||
setFieldErrors(undefined);
|
||||
};
|
||||
|
||||
const renderEditEntity = () => {
|
||||
if (entityItem) {
|
||||
return (
|
||||
<Dialog open={!!entityItem} onClose={() => closeDialog()}>
|
||||
<DialogTitle>
|
||||
{creating ? LL.ADD(1) + ' ' + LL.NEW() : LL.EDIT()} {LL.ENTITY()}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box display="flex" flexWrap="wrap" mb={1}>
|
||||
<Box flexGrow={1}></Box>
|
||||
<Box flexWrap="nowrap" whiteSpace="nowrap"></Box>
|
||||
</Box>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="name"
|
||||
label={LL.NAME(0)}
|
||||
value={entityItem.name}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
// onChange={updateValue(setEntityItem)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="device_id"
|
||||
label="Device ID"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
value={entityItem.device_id}
|
||||
onChange={updateValue(setEntityItem)}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">0x</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="type_id"
|
||||
label="Type ID"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
value={entityItem.type_id}
|
||||
onChange={updateValue(setEntityItem)}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">0x</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="offset"
|
||||
label="Offset"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={entityItem.offset}
|
||||
onChange={updateValue(setEntityItem)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="val_type"
|
||||
label="Value Type"
|
||||
value={entityItem.val_type}
|
||||
variant="outlined"
|
||||
onChange={updateValue(setEntityItem)}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>BOOL</MenuItem>
|
||||
<MenuItem value={1}>INT</MenuItem>
|
||||
<MenuItem value={2}>UINT</MenuItem>
|
||||
<MenuItem value={3}>SHORT</MenuItem>
|
||||
<MenuItem value={4}>USHORT</MenuItem>
|
||||
<MenuItem value={5}>ULONG</MenuItem>
|
||||
<MenuItem value={6}>TIME</MenuItem>
|
||||
</ValidatedTextField>
|
||||
</Grid>
|
||||
{entityItem.val_type !== 0 && (
|
||||
<>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="factor"
|
||||
label={LL.FACTOR()}
|
||||
value={entityItem.factor}
|
||||
variant="outlined"
|
||||
onChange={updateValue(setEntityItem)}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="number"
|
||||
inputProps={{ step: '0.001' }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="uom"
|
||||
label={LL.UNIT()}
|
||||
value={entityItem.uom}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
onChange={updateValue(setEntityItem)}
|
||||
select
|
||||
>
|
||||
{DeviceValueUOM_s.map((val, i) => (
|
||||
<MenuItem key={i} value={i}>
|
||||
{val}
|
||||
</MenuItem>
|
||||
))}
|
||||
</ValidatedTextField>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{!creating && (
|
||||
<Box flexGrow={1}>
|
||||
<Button
|
||||
startIcon={<RemoveIcon />}
|
||||
variant="outlined"
|
||||
color="error"
|
||||
onClick={() => removeEntityItem(entityItem)}
|
||||
>
|
||||
{LL.REMOVE()}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
<Button startIcon={<CancelIcon />} variant="outlined" onClick={() => closeDialog()} color="secondary">
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={creating ? <AddIcon /> : <DoneIcon />}
|
||||
variant="outlined"
|
||||
type="submit"
|
||||
onClick={() => validateEntityItem()}
|
||||
color="primary"
|
||||
>
|
||||
{creating ? LL.ADD(0) : LL.UPDATE()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.CUSTOM_ENTITIES()} titleGutter>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
@@ -473,10 +264,21 @@ const SettingsEntities: FC = () => {
|
||||
<Typography variant="body2">{LL.ENTITIES_HELP_1()}</Typography>
|
||||
</Box>
|
||||
{renderEntity()}
|
||||
{renderEditEntity()}
|
||||
|
||||
{selectedEntityItem && (
|
||||
<SettingsEntitiesDialog
|
||||
open={dialogOpen}
|
||||
creating={creating}
|
||||
onClose={onDialogClose}
|
||||
onSave={onDialogSave}
|
||||
selectedEntityItem={selectedEntityItem}
|
||||
validator={entityItemValidation(entities, creating)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1}>
|
||||
{numChanges !== 0 && (
|
||||
{numChanges > 0 && (
|
||||
<ButtonRow>
|
||||
<Button startIcon={<CancelIcon />} variant="outlined" onClick={() => fetchEntities()} color="secondary">
|
||||
{LL.CANCEL()}
|
||||
|
||||
228
interface/src/project/SettingsEntitiesDialog.tsx
Normal file
228
interface/src/project/SettingsEntitiesDialog.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
Grid,
|
||||
Button,
|
||||
Box,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Checkbox,
|
||||
InputAdornment,
|
||||
MenuItem
|
||||
} from '@mui/material';
|
||||
|
||||
import { ValidatedTextField, BlockFormControlLabel } from 'components';
|
||||
|
||||
import DoneIcon from '@mui/icons-material/Done';
|
||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type Schema from 'async-validator';
|
||||
|
||||
import { updateValue } from 'utils';
|
||||
|
||||
import type { EntityItem } from './types';
|
||||
import { DeviceValueUOM_s } from './types';
|
||||
|
||||
type SettingsEntitiesDialogProps = {
|
||||
open: boolean;
|
||||
creating: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (ei: EntityItem) => void;
|
||||
selectedEntityItem: EntityItem;
|
||||
validator: Schema;
|
||||
};
|
||||
|
||||
const SettingsEntitiesDialog = ({
|
||||
open,
|
||||
creating,
|
||||
onClose,
|
||||
onSave,
|
||||
selectedEntityItem,
|
||||
validator
|
||||
}: SettingsEntitiesDialogProps) => {
|
||||
const { LL } = useI18nContext();
|
||||
const [editItem, setEditItem] = useState<EntityItem>(selectedEntityItem);
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
|
||||
const updateFormValue = updateValue(setEditItem);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setFieldErrors(undefined);
|
||||
setEditItem(selectedEntityItem);
|
||||
}
|
||||
}, [open, selectedEntityItem]);
|
||||
|
||||
const close = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
setFieldErrors(undefined);
|
||||
await validate(validator, editItem);
|
||||
onSave(editItem);
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
}
|
||||
};
|
||||
|
||||
const remove = () => {
|
||||
editItem.deleted = true;
|
||||
onSave(editItem);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={close}>
|
||||
<DialogTitle>
|
||||
{creating ? LL.ADD(1) + ' ' + LL.NEW() : LL.EDIT()} {LL.ENTITY()}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box display="flex" flexWrap="wrap" mb={1}>
|
||||
<Box flexWrap="nowrap" whiteSpace="nowrap" />
|
||||
</Box>
|
||||
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={8}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="name"
|
||||
label={LL.NAME(0)}
|
||||
value={editItem.name}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={4} mt={3}>
|
||||
<BlockFormControlLabel
|
||||
control={<Checkbox checked={selectedEntityItem.writeable} onChange={updateFormValue} name="write" />}
|
||||
label={LL.WRITEABLE()}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="device_id"
|
||||
label="Device ID"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
value={editItem.device_id}
|
||||
onChange={updateFormValue}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">0x</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="type_id"
|
||||
label="Type ID"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
value={editItem.type_id}
|
||||
onChange={updateFormValue}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">0x</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
name="offset"
|
||||
label="Offset"
|
||||
margin="normal"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={editItem.offset}
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="val_type"
|
||||
label="Value Type"
|
||||
value={editItem.value_type}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>BOOL</MenuItem>
|
||||
<MenuItem value={1}>INT</MenuItem>
|
||||
<MenuItem value={2}>UINT</MenuItem>
|
||||
<MenuItem value={3}>SHORT</MenuItem>
|
||||
<MenuItem value={4}>USHORT</MenuItem>
|
||||
<MenuItem value={5}>ULONG</MenuItem>
|
||||
<MenuItem value={6}>TIME</MenuItem>
|
||||
</ValidatedTextField>
|
||||
</Grid>
|
||||
{selectedEntityItem.value_type !== 0 && (
|
||||
<>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="factor"
|
||||
label={LL.FACTOR()}
|
||||
value={editItem.factor}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="number"
|
||||
inputProps={{ step: '0.001' }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ValidatedTextField
|
||||
name="uom"
|
||||
label={LL.UNIT()}
|
||||
value={editItem.uom}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
onChange={updateFormValue}
|
||||
select
|
||||
>
|
||||
{DeviceValueUOM_s.map((val, i) => (
|
||||
<MenuItem key={i} value={i}>
|
||||
{val}
|
||||
</MenuItem>
|
||||
))}
|
||||
</ValidatedTextField>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
{!creating && (
|
||||
<Box flexGrow={1}>
|
||||
<Button startIcon={<RemoveIcon />} variant="outlined" color="warning" onClick={remove}>
|
||||
{LL.REMOVE()}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
<Button startIcon={<CancelIcon />} variant="outlined" onClick={close} color="secondary">
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button startIcon={creating ? <AddIcon /> : <DoneIcon />} variant="outlined" onClick={save} color="primary">
|
||||
{creating ? LL.ADD(0) : LL.UPDATE()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsEntitiesDialog;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FC, useState, useEffect, useCallback } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
@@ -43,9 +44,10 @@ import { extractErrorMessage, updateValue } from 'utils';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import { schedulerItemValidation } from './validators';
|
||||
import { ValidateFieldsError } from 'async-validator';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
|
||||
import { ScheduleItem, ScheduleFlag } from './types';
|
||||
import type { ScheduleItem } from './types';
|
||||
import { ScheduleFlag } from './types';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
@@ -105,6 +107,7 @@ const SettingsScheduler: FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO fix
|
||||
const getNumChanges = () => {
|
||||
if (!schedule) {
|
||||
return 0;
|
||||
@@ -184,12 +187,12 @@ const SettingsScheduler: FC = () => {
|
||||
} catch (error) {
|
||||
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
|
||||
}
|
||||
setDow(getDayNames());
|
||||
}, [LL]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSchedule();
|
||||
}, [fetchSchedule]);
|
||||
void fetchSchedule();
|
||||
setDow(getDayNames());
|
||||
}, [getDayNames, fetchSchedule]);
|
||||
|
||||
const getFlagNumber = (newFlag: string[]) => {
|
||||
let new_flag = 0;
|
||||
@@ -234,17 +237,15 @@ const SettingsScheduler: FC = () => {
|
||||
const response = await EMSESP.writeSchedule({
|
||||
schedule: schedule
|
||||
.filter((si) => !si.deleted)
|
||||
.map((condensed_si) => {
|
||||
return {
|
||||
id: condensed_si.id,
|
||||
active: condensed_si.active,
|
||||
flags: condensed_si.flags,
|
||||
time: condensed_si.time,
|
||||
cmd: condensed_si.cmd,
|
||||
value: condensed_si.value,
|
||||
name: condensed_si.name
|
||||
};
|
||||
})
|
||||
.map((condensed_si) => ({
|
||||
id: condensed_si.id,
|
||||
active: condensed_si.active,
|
||||
flags: condensed_si.flags,
|
||||
time: condensed_si.time,
|
||||
cmd: condensed_si.cmd,
|
||||
value: condensed_si.value,
|
||||
name: condensed_si.name
|
||||
}))
|
||||
});
|
||||
if (response.status === 200) {
|
||||
toast.success(LL.SCHEDULE_SAVED());
|
||||
|
||||
@@ -346,8 +346,9 @@ export interface EntityItem {
|
||||
offset: number;
|
||||
factor: number;
|
||||
uom: number;
|
||||
val_type: number;
|
||||
value_type: number;
|
||||
value?: number;
|
||||
writeable: boolean;
|
||||
deleted?: boolean; // optional
|
||||
o_id?: number;
|
||||
o_name?: string;
|
||||
@@ -356,8 +357,9 @@ export interface EntityItem {
|
||||
o_offset?: number;
|
||||
o_factor?: number;
|
||||
o_uom?: number;
|
||||
o_val_type?: number;
|
||||
o_value_type?: number;
|
||||
o_deleted?: boolean;
|
||||
o_writeable?: boolean;
|
||||
}
|
||||
|
||||
export interface Entities {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Schema, { InternalRuleItem } from 'async-validator';
|
||||
import type { InternalRuleItem } from 'async-validator';
|
||||
import Schema from 'async-validator';
|
||||
import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared';
|
||||
import { Settings, ScheduleItem, EntityItem } from './types';
|
||||
import type { Settings, ScheduleItem, EntityItem } from './types';
|
||||
|
||||
export const GPIO_VALIDATOR = {
|
||||
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
||||
@@ -111,17 +112,7 @@ export const schedulerItemValidation = (schedule: ScheduleItem[], scheduleItem:
|
||||
]
|
||||
});
|
||||
|
||||
export const uniqueEntityNameValidator = (entities: EntityItem[], o_name?: string) => ({
|
||||
validator(rule: InternalRuleItem, name: string, callback: (error?: string) => void) {
|
||||
if (name && o_name && o_name !== name && entities.find((ei) => ei.name === name)) {
|
||||
callback('Name already in use');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const entityItemValidation = (entities: EntityItem[], entityItem: EntityItem) =>
|
||||
export const entityItemValidation = (entities: EntityItem[], creating: boolean) =>
|
||||
new Schema({
|
||||
name: [
|
||||
{ required: true, message: 'Name is required' },
|
||||
@@ -129,8 +120,7 @@ export const entityItemValidation = (entities: EntityItem[], entityItem: EntityI
|
||||
type: 'string',
|
||||
pattern: /^[a-zA-Z0-9_\\.]{1,15}$/,
|
||||
message: "Must be <15 characters: alpha numeric, '_' or '.'"
|
||||
},
|
||||
...[uniqueEntityNameValidator(entities, entityItem.o_name)]
|
||||
}
|
||||
],
|
||||
device_id: [{ type: 'hex', required: true, message: 'ID must be a hex value' }]
|
||||
// type_id: [
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import Schema, { InternalRuleItem, ValidateOption } from 'async-validator';
|
||||
import type { InternalRuleItem, ValidateOption } from 'async-validator';
|
||||
import type Schema from 'async-validator';
|
||||
|
||||
export const validate = <T extends object>(
|
||||
validator: Schema,
|
||||
source: Partial<T>,
|
||||
options?: ValidateOption
|
||||
): Promise<T> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
validator.validate(source, options ? options : {}, (errors, fieldErrors) => {
|
||||
): Promise<T> =>
|
||||
new Promise((resolve, reject) => {
|
||||
void validator.validate(source, options ? options : {}, (errors, fieldErrors) => {
|
||||
if (errors) {
|
||||
reject(fieldErrors);
|
||||
} else {
|
||||
@@ -14,7 +15,6 @@ export const validate = <T extends object>(
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// updated to support both IPv4 and IPv6
|
||||
const IP_ADDRESS_REGEXP =
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
// "moduleResolution": "Node",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -703,9 +703,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/base@npm:5.0.0-alpha.125":
|
||||
version: 5.0.0-alpha.125
|
||||
resolution: "@mui/base@npm:5.0.0-alpha.125"
|
||||
"@mui/base@npm:5.0.0-alpha.126":
|
||||
version: 5.0.0-alpha.126
|
||||
resolution: "@mui/base@npm:5.0.0-alpha.126"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.21.0
|
||||
"@emotion/is-prop-valid": ^1.2.0
|
||||
@@ -722,14 +722,14 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 0a87b5141500885c364382375816d23b48799e38c57993b0bbb2dbfce8052a8bdba588b2cd6cee75dc2fc43a873ce3d27a223ef1395c42a0b2e28d59e559b2bf
|
||||
checksum: afb6b99cba541cdb40bb098a78d105721cd6c6db3156fe9923bbb0aea184f6949ab5db79ab95177b4edb011acb35ac34556124b1203fd91ea16f52dc5477da7b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/core-downloads-tracker@npm:^5.12.0":
|
||||
version: 5.12.0
|
||||
resolution: "@mui/core-downloads-tracker@npm:5.12.0"
|
||||
checksum: b0bc0c67be036fc6b965827ffb2ad2134c317237439dcbbe0b90a1807e92f93894e8d5e6650df4833b5a9b88b28d77cb2cd4435d23bbb9ab751c023684012e5f
|
||||
"@mui/core-downloads-tracker@npm:^5.12.1":
|
||||
version: 5.12.1
|
||||
resolution: "@mui/core-downloads-tracker@npm:5.12.1"
|
||||
checksum: 0fc90f840e888e0a671ce43dfaa50018c7f2b82379b3adf3128387c3f29d06cdc235493939698339eb2aab49cf9a167473ed95f6a64eb424ee2d95849dd4aa76
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -749,14 +749,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/material@npm:^5.12.0":
|
||||
version: 5.12.0
|
||||
resolution: "@mui/material@npm:5.12.0"
|
||||
"@mui/material@npm:^5.12.1":
|
||||
version: 5.12.1
|
||||
resolution: "@mui/material@npm:5.12.1"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.21.0
|
||||
"@mui/base": 5.0.0-alpha.125
|
||||
"@mui/core-downloads-tracker": ^5.12.0
|
||||
"@mui/system": ^5.12.0
|
||||
"@mui/base": 5.0.0-alpha.126
|
||||
"@mui/core-downloads-tracker": ^5.12.1
|
||||
"@mui/system": ^5.12.1
|
||||
"@mui/types": ^7.2.4
|
||||
"@mui/utils": ^5.12.0
|
||||
"@types/react-transition-group": ^4.4.5
|
||||
@@ -778,7 +778,7 @@ __metadata:
|
||||
optional: true
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: d8f2e875393dd254d70aafea08e1289d4cc4d085af581cd8fd4cc2882d5e265b8c926322ac64c1e0d18c5f441969abef2611c87346d685ad18fcfbc27e2d8ddf
|
||||
checksum: a4b4becea4da7af787d58b09afe6a3e1c7b1aa1b27f93186f848efd0bcb250e5b39ca51cdbcedc0216db32049665a69a13a2f8480d357f2310ca6efcf8b10a76
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -820,9 +820,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/system@npm:^5.12.0":
|
||||
version: 5.12.0
|
||||
resolution: "@mui/system@npm:5.12.0"
|
||||
"@mui/system@npm:^5.12.1":
|
||||
version: 5.12.1
|
||||
resolution: "@mui/system@npm:5.12.1"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.21.0
|
||||
"@mui/private-theming": ^5.12.0
|
||||
@@ -844,7 +844,7 @@ __metadata:
|
||||
optional: true
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: d53e70f35b8cc19c687ba72fc79a1f4cc20e0dd0335433fc255bc70291f89460b88bdddb7a9f17d11ffa6de3710117cc666c28ea0ac234fd2da13e82ee3c3c34
|
||||
checksum: a9dc1e3503f8c036663d0bc38b9bed71f67833890148a29efba5501f0da1f62d0c5372648c840779bdc74ea6e997783441934fa1df90f00ffef158cb6e6d5ce7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1421,14 +1421,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:^18.0.34":
|
||||
version: 18.0.34
|
||||
resolution: "@types/react@npm:18.0.34"
|
||||
"@types/react@npm:^18.0.37":
|
||||
version: 18.0.37
|
||||
resolution: "@types/react@npm:18.0.37"
|
||||
dependencies:
|
||||
"@types/prop-types": "*"
|
||||
"@types/scheduler": "*"
|
||||
csstype: ^3.0.2
|
||||
checksum: 97e6ea3b5eea0b270c2c36f5cc44699fe21e30ceda4ffc6936ad40ff755bd8b16637f41d8b0bd20d50eb3261b0981e8f4822b2bd5805208532292499f6de340c
|
||||
checksum: 1919fb9fb48d574fafeb196aced7ea1ee345525597afa6ad01049c7ce090a732bc500c4a392deb0a5fb9165d5ae5fe720b795e3023bb5a9b2b2a0fae059b4407
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1473,14 +1473,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:5.58.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:^5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:5.59.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": ^4.4.0
|
||||
"@typescript-eslint/scope-manager": 5.58.0
|
||||
"@typescript-eslint/type-utils": 5.58.0
|
||||
"@typescript-eslint/utils": 5.58.0
|
||||
"@typescript-eslint/scope-manager": 5.59.0
|
||||
"@typescript-eslint/type-utils": 5.59.0
|
||||
"@typescript-eslint/utils": 5.59.0
|
||||
debug: ^4.3.4
|
||||
grapheme-splitter: ^1.0.4
|
||||
ignore: ^5.2.0
|
||||
@@ -1493,43 +1493,43 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: cda31ae6ff09c742f921304ea1a2a0d0823f7e20d7969ba6320ab14df2b3269b93a61eded96a59f01cfd24f28efb91e461e42bb09f493ed013936a899697a868
|
||||
checksum: f3b557fc875f688073835b6d41af4184c08c00b0f4887e62bb110e2935d50a158b026547cde89708bf6463913322e757a07d2de26fc505a3c15a81120d64ccef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/parser@npm:5.58.0"
|
||||
"@typescript-eslint/parser@npm:^5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/parser@npm:5.59.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": 5.58.0
|
||||
"@typescript-eslint/types": 5.58.0
|
||||
"@typescript-eslint/typescript-estree": 5.58.0
|
||||
"@typescript-eslint/scope-manager": 5.59.0
|
||||
"@typescript-eslint/types": 5.59.0
|
||||
"@typescript-eslint/typescript-estree": 5.59.0
|
||||
debug: ^4.3.4
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: fb7a4ce59eb803d29705e0134b6731823d9d7b56dd76a4de4ff07eb09d56cc851ed9988ecacdc2d0cbd929115a02ce564b0bb1b97d8732e05707dbe4332460ae
|
||||
checksum: 5e0f8dfe4eb762bf1d2e8186559d39df653005a8f976101cd7b3739f3e0253d1003b4268976e52c7e42b63bf6ea042b1ed9412f85d06ed8fb0407b56dba19db2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:5.58.0"
|
||||
"@typescript-eslint/scope-manager@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:5.59.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.58.0
|
||||
"@typescript-eslint/visitor-keys": 5.58.0
|
||||
checksum: 66c82609ac6c9cf00e163126619e7c487adc938f02e4567a2c26319916a175b9aee792aa80bd319a20848c834c6e599cd302c9f5b68c64b95d02f024f511ac66
|
||||
"@typescript-eslint/types": 5.59.0
|
||||
"@typescript-eslint/visitor-keys": 5.59.0
|
||||
checksum: b53c9581daf3d6ac2ec5bd660d62c56ea77f71d77261a23bf21bc23a8140b5b7738304ace576b6af6e1d4ffc5170b7b6be7375da488e9e2997984011c509ead8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:5.58.0"
|
||||
"@typescript-eslint/type-utils@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:5.59.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": 5.58.0
|
||||
"@typescript-eslint/utils": 5.58.0
|
||||
"@typescript-eslint/typescript-estree": 5.59.0
|
||||
"@typescript-eslint/utils": 5.59.0
|
||||
debug: ^4.3.4
|
||||
tsutils: ^3.21.0
|
||||
peerDependencies:
|
||||
@@ -1537,23 +1537,23 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 3ca4443f43b8263745afda3ff05517074da77d1dad25867845d386b29b012548b720d12334aca8bf15323a76557099e4ce3025a5a0fa84e070f6a4c1dc36d44e
|
||||
checksum: 8675af740e89ab15e10ef1938530dc14595f45ebaa8b57375e931a4e8b42d71cff8ddfacf71f20a333532a0dbe593eff6d59eb5d43b73c9dd19ad7439a30f443
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/types@npm:5.58.0"
|
||||
checksum: 3e5973909a5c585f5aebf919eec8ac213e9b5089c7357ea832ffa2bd39df70dce0b806d4bcc39a15e309830dfbf7bdf22d9808ab3c466729b8536e9d7e83eccc
|
||||
"@typescript-eslint/types@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/types@npm:5.59.0"
|
||||
checksum: f756843a49b418a23674842d356aaef14e7373e7df80729e64cf23b3fc7c9d9ab4f0a764b41555236af7821cd1d3c0efcc9a3c97b778f0b67b6dbbd9c5e852cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:5.58.0"
|
||||
"@typescript-eslint/typescript-estree@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:5.59.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.58.0
|
||||
"@typescript-eslint/visitor-keys": 5.58.0
|
||||
"@typescript-eslint/types": 5.59.0
|
||||
"@typescript-eslint/visitor-keys": 5.59.0
|
||||
debug: ^4.3.4
|
||||
globby: ^11.1.0
|
||||
is-glob: ^4.0.3
|
||||
@@ -1562,35 +1562,35 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 51c2a92217a1ccc01acf3c5c371b8c4b48b066cb6341441c35b74b6f3e13d21f81e0aed041215f3f4cf73320f5cd6cc3061801c51a3049958ee9a171a6efa196
|
||||
checksum: 2e677677927721d0db286f2f2e0263d5b8ae06072f217fc2fd17c96c347f8cec2201dccaf393c41e6f4b2a7c3e2b7ca6ab8a27283e76c6ec5576f53d1d26a0b6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/utils@npm:5.58.0"
|
||||
"@typescript-eslint/utils@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/utils@npm:5.59.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": ^4.2.0
|
||||
"@types/json-schema": ^7.0.9
|
||||
"@types/semver": ^7.3.12
|
||||
"@typescript-eslint/scope-manager": 5.58.0
|
||||
"@typescript-eslint/types": 5.58.0
|
||||
"@typescript-eslint/typescript-estree": 5.58.0
|
||||
"@typescript-eslint/scope-manager": 5.59.0
|
||||
"@typescript-eslint/types": 5.59.0
|
||||
"@typescript-eslint/typescript-estree": 5.59.0
|
||||
eslint-scope: ^5.1.1
|
||||
semver: ^7.3.7
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
checksum: 71ea338d9b67b59792e9d9a82b723acbee815534044294b169e3727f5394445d95a6200c919f0c28020bc5954df0f7110e9d0a4586e77ebebcd1662c06b30157
|
||||
checksum: 653ea4032b51c8b3bdc386971cb437f59fc20a8df5ca8d11ef6c917e6376df26c73cfd18cbeee8c8818ba4350a8cbd87d0ea9ec5242e35c5d26059a11476bc13
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:5.58.0":
|
||||
version: 5.58.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:5.58.0"
|
||||
"@typescript-eslint/visitor-keys@npm:5.59.0":
|
||||
version: 5.59.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:5.59.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.58.0
|
||||
"@typescript-eslint/types": 5.59.0
|
||||
eslint-visitor-keys: ^3.3.0
|
||||
checksum: e41b0cf8bf766c491fe96e26b4cd20e6af4dbe85ff773a32887b7557ffd199117d8cdc86ceef5ce224d06c5e14d54a8edb679e58185f5a9c6b450615eaac6f30
|
||||
checksum: 184a23424a6bf7ea48f700a71461d3a89270e8af32db6f1fcc5367834818c9e8bc4b57853a15b6a9d44297c064d04d08583815fd8f2019135c0bc197d60a6c0c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1732,18 +1732,18 @@ __metadata:
|
||||
"@emotion/styled": ^11.10.6
|
||||
"@msgpack/msgpack": ^3.0.0-beta2
|
||||
"@mui/icons-material": ^5.11.16
|
||||
"@mui/material": ^5.12.0
|
||||
"@mui/material": ^5.12.1
|
||||
"@remix-run/router": ^1.5.0
|
||||
"@table-library/react-table-library": 4.1.0
|
||||
"@types/lodash-es": ^4.17.7
|
||||
"@types/mime-types": ^2
|
||||
"@types/node": ^18.15.11
|
||||
"@types/react": ^18.0.34
|
||||
"@types/react": ^18.0.37
|
||||
"@types/react-dom": ^18.0.11
|
||||
"@types/react-router-dom": ^5.3.3
|
||||
"@types/styled-components": ^5
|
||||
"@typescript-eslint/eslint-plugin": ^5.58.0
|
||||
"@typescript-eslint/parser": ^5.58.0
|
||||
"@typescript-eslint/eslint-plugin": ^5.59.0
|
||||
"@typescript-eslint/parser": ^5.59.0
|
||||
"@vitejs/plugin-react-swc": ^3.3.0
|
||||
"@yarnpkg/pnpify": ^4.0.0-rc.42
|
||||
async-validator: ^4.2.5
|
||||
@@ -1753,6 +1753,7 @@ __metadata:
|
||||
eslint-config-airbnb-typescript: ^17.0.0
|
||||
eslint-config-prettier: ^8.8.0
|
||||
eslint-import-resolver-typescript: ^3.5.5
|
||||
eslint-plugin-autofix: ^1.1.0
|
||||
eslint-plugin-import: ^2.27.5
|
||||
eslint-plugin-jsx-a11y: ^6.7.1
|
||||
eslint-plugin-prettier: ^4.2.1
|
||||
@@ -1773,13 +1774,13 @@ __metadata:
|
||||
react-toastify: ^9.1.2
|
||||
rollup-plugin-visualizer: ^5.9.0
|
||||
sockette: ^2.0.6
|
||||
terser: ^5.16.9
|
||||
terser: ^5.17.0
|
||||
typesafe-i18n: ^5.24.3
|
||||
typescript: ^5.0.4
|
||||
vite: ^4.2.1
|
||||
vite: ^4.2.2
|
||||
vite-plugin-minify: ^1.5.2
|
||||
vite-plugin-svgr: ^2.4.0
|
||||
vite-tsconfig-paths: ^4.1.0
|
||||
vite-tsconfig-paths: ^4.2.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@@ -3021,6 +3022,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-autofix@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "eslint-plugin-autofix@npm:1.1.0"
|
||||
dependencies:
|
||||
eslint-rule-composer: ^0.3.0
|
||||
espree: ^9.0.0
|
||||
esutils: ^2.0.2
|
||||
lodash: ^4.17.20
|
||||
string-similarity: ^4.0.3
|
||||
peerDependencies:
|
||||
eslint: ">= 5.12.1"
|
||||
checksum: f46b2a9a1e2d99fd2dfdbdd2303c108d5fd773d00abe3c92f7a44a604235d2df28faf74ede9b6802925461815784a57a4909d6e4e2a2c71031cadf8903711c43
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-import@npm:^2.27.5":
|
||||
version: 2.27.5
|
||||
resolution: "eslint-plugin-import@npm:2.27.5"
|
||||
@@ -3121,6 +3137,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-rule-composer@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "eslint-rule-composer@npm:0.3.0"
|
||||
checksum: 1f0c40d209e1503a955101a0dbba37e7fc67c8aaa47a5b9ae0b0fcbae7022c86e52b3df2b1b9ffd658e16cd80f31fff92e7222460a44d8251e61d49e0af79a07
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-scope@npm:^5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "eslint-scope@npm:5.1.1"
|
||||
@@ -3198,7 +3221,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"espree@npm:^9.5.1":
|
||||
"espree@npm:^9.0.0, espree@npm:^9.5.1":
|
||||
version: 9.5.1
|
||||
resolution: "espree@npm:9.5.1"
|
||||
dependencies:
|
||||
@@ -4401,7 +4424,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash@npm:^4.17.15":
|
||||
"lodash@npm:^4.17.15, lodash@npm:^4.17.20":
|
||||
version: 4.17.21
|
||||
resolution: "lodash@npm:4.17.21"
|
||||
checksum: d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c
|
||||
@@ -5883,6 +5906,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-similarity@npm:^4.0.3":
|
||||
version: 4.0.4
|
||||
resolution: "string-similarity@npm:4.0.4"
|
||||
checksum: fce331b818efafa701f692ddc2e170bd3ceaf6e7ca56a445b36b139981effe0884d8edc794a65005e54304da55ba054edfcff16a339bd301c9b94983fbc62047
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
|
||||
version: 4.2.3
|
||||
resolution: "string-width@npm:4.2.3"
|
||||
@@ -6070,9 +6100,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"terser@npm:^5.16.9":
|
||||
version: 5.16.9
|
||||
resolution: "terser@npm:5.16.9"
|
||||
"terser@npm:^5.17.0":
|
||||
version: 5.17.0
|
||||
resolution: "terser@npm:5.17.0"
|
||||
dependencies:
|
||||
"@jridgewell/source-map": ^0.3.2
|
||||
acorn: ^8.5.0
|
||||
@@ -6080,7 +6110,7 @@ __metadata:
|
||||
source-map-support: ~0.5.20
|
||||
bin:
|
||||
terser: bin/terser
|
||||
checksum: eb883b606aa698e314957aa2cf6e70c1dc632d0d2dcda13e7a2cc73569a05034721826c0d6f9b31c6bb08bbc4fc633b6591871814dada71da9d34af9e284dc4f
|
||||
checksum: fb5c81a837fc4083c1471b5cd599505666ef9f007381fb934c39f8fc5e0c034914e32d2de247aeb7f8a9723c1dc411baf5153368cfe8ed1927139ff030516eda
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6365,9 +6395,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-tsconfig-paths@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "vite-tsconfig-paths@npm:4.1.0"
|
||||
"vite-tsconfig-paths@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "vite-tsconfig-paths@npm:4.2.0"
|
||||
dependencies:
|
||||
debug: ^4.1.1
|
||||
globrex: ^0.1.2
|
||||
@@ -6377,13 +6407,13 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
vite:
|
||||
optional: true
|
||||
checksum: 9846dfdd7118067539728f88a3b5f6a109d01392fa83bef6ad0505def2b1ed24579a4955df7db4b3ab60e9a816867a48e8b508f34030ef0d20b773293c91298d
|
||||
checksum: 04bd792bb4f6b4fb57ec8368cff076abffba8d6923af032affb14be43b6e2dfd8b25085947a3204d702a8c8e9d79d3c361373cf98566df682420728857906289
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "vite@npm:4.2.1"
|
||||
"vite@npm:^4.2.2":
|
||||
version: 4.2.2
|
||||
resolution: "vite@npm:4.2.2"
|
||||
dependencies:
|
||||
esbuild: ^0.17.5
|
||||
fsevents: ~2.3.2
|
||||
@@ -6415,7 +6445,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
vite: bin/vite.js
|
||||
checksum: a64e3a1563b9584d1fd1ca45e06ee3c9fa4956320e6d4e9d83bf09fc8e64bb9d3ef62f664b6f740141eee16643690e8a41ffdb3030f4f2e170c57894df1f9a5d
|
||||
checksum: 60e7298c817f0626bcbfdfc8877431421eabab85131c64d69e58f5ea20a66c14c4e0901ed63286b362627a667560d257dd135615b63f844da6052f6248dd7be6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ rest_server.use(express.json());
|
||||
// endpoints
|
||||
const API_ENDPOINT_ROOT = '/api/';
|
||||
const REST_ENDPOINT_ROOT = '/rest/';
|
||||
const EVENTSOURCE_ENDPOINT_ROOT = '/es/';
|
||||
|
||||
// LOG
|
||||
const LOG_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'logSettings';
|
||||
@@ -610,7 +609,8 @@ let emsesp_entities = {
|
||||
offset: 0,
|
||||
factor: 0,
|
||||
uom: 2,
|
||||
val_type: 2
|
||||
value: 1,
|
||||
value_type: 2
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
@@ -620,7 +620,8 @@ let emsesp_entities = {
|
||||
offset: 2,
|
||||
factor: 2,
|
||||
uom: 4,
|
||||
val_type: 5
|
||||
value: 2,
|
||||
value_type: 5
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -33,24 +33,27 @@ void WebEntityService::begin() {
|
||||
EMSESP::logger().info("Starting custom entity service");
|
||||
}
|
||||
|
||||
// this creates the scheduler file, saving it to the FS
|
||||
// and also calls when the Scheduler web page is refreshed
|
||||
// this creates the entity file, saving it to the FS
|
||||
// and also calls when the Entity web page is refreshed
|
||||
void WebEntity::read(WebEntity & webEntity, JsonObject & root) {
|
||||
JsonArray entity = root.createNestedArray("entity");
|
||||
JsonArray entity = root.createNestedArray("entity");
|
||||
uint8_t counter = 0;
|
||||
for (const EntityItem & entityItem : webEntity.entityItems) {
|
||||
JsonObject ei = entity.createNestedObject();
|
||||
ei["device_id"] = Helpers::hextoa(entityItem.device_id, false);
|
||||
ei["type_id"] = Helpers::hextoa(entityItem.type_id, false);
|
||||
ei["offset"] = entityItem.offset;
|
||||
ei["factor"] = entityItem.factor;
|
||||
ei["name"] = entityItem.name;
|
||||
ei["uom"] = entityItem.uom;
|
||||
ei["val_type"] = entityItem.valuetype;
|
||||
JsonObject ei = entity.createNestedObject();
|
||||
ei["id"] = counter++; // id is only used to render the table and must be unique
|
||||
ei["device_id"] = Helpers::hextoa(entityItem.device_id, false);
|
||||
ei["type_id"] = Helpers::hextoa(entityItem.type_id, false);
|
||||
ei["offset"] = entityItem.offset;
|
||||
ei["factor"] = entityItem.factor;
|
||||
ei["name"] = entityItem.name;
|
||||
ei["uom"] = entityItem.uom;
|
||||
ei["value_type"] = entityItem.value_type;
|
||||
ei["writeable"] = entityItem.writeable;
|
||||
EMSESP::webEntityService.render_value(ei, entityItem, true);
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the Schedule web page is updated
|
||||
// call on initialization and also when the Entity web page is updated
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) {
|
||||
for (EntityItem & entityItem : webEntity.entityItems) {
|
||||
@@ -60,36 +63,42 @@ StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) {
|
||||
|
||||
if (root["entity"].is<JsonArray>()) {
|
||||
for (const JsonObject ei : root["entity"].as<JsonArray>()) {
|
||||
auto entityItem = EntityItem();
|
||||
entityItem.device_id = Helpers::hextoint(ei["device_id"]);
|
||||
entityItem.type_id = Helpers::hextoint(ei["type_id"]);
|
||||
entityItem.offset = ei["offset"];
|
||||
entityItem.factor = ei["factor"];
|
||||
entityItem.name = ei["name"].as<std::string>();
|
||||
entityItem.uom = ei["uom"];
|
||||
entityItem.valuetype = ei["val_type"];
|
||||
auto entityItem = EntityItem();
|
||||
entityItem.device_id = Helpers::hextoint(ei["device_id"]); // TODO don't need
|
||||
entityItem.type_id = Helpers::hextoint(ei["type_id"]);
|
||||
entityItem.offset = ei["offset"];
|
||||
entityItem.factor = ei["factor"];
|
||||
entityItem.name = ei["name"].as<std::string>();
|
||||
entityItem.uom = ei["uom"];
|
||||
entityItem.value_type = ei["value_type"];
|
||||
entityItem.writeable = ei["writeable"];
|
||||
|
||||
if (entityItem.valuetype == DeviceValueType::BOOL) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_BOOL;
|
||||
} else if (entityItem.valuetype == DeviceValueType::INT) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_INT;
|
||||
} else if (entityItem.valuetype == DeviceValueType::UINT) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_UINT;
|
||||
} else if (entityItem.valuetype == DeviceValueType::SHORT) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_SHORT;
|
||||
} else if (entityItem.valuetype == DeviceValueType::USHORT) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_USHORT;
|
||||
} else { // if (entityItem.valuetype == DeviceValueType::ULONG || entityItem.valuetype == DeviceValueType::TIME) {
|
||||
entityItem.val = EMS_VALUE_DEFAULT_ULONG;
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
|
||||
} else if (entityItem.value_type == DeviceValueType::INT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_INT;
|
||||
} else if (entityItem.value_type == DeviceValueType::UINT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_UINT;
|
||||
} else if (entityItem.value_type == DeviceValueType::SHORT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_SHORT;
|
||||
} else if (entityItem.value_type == DeviceValueType::USHORT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_USHORT;
|
||||
} else { // if (entityItem.value_type == DeviceValueType::ULONG || entityItem.valuetype == DeviceValueType::TIME) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_ULONG;
|
||||
}
|
||||
|
||||
webEntity.entityItems.push_back(entityItem); // add to list
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::CUSTOM,
|
||||
webEntity.entityItems.back().name.c_str(),
|
||||
[webEntity](const char * value, const int8_t id) { return EMSESP::webEntityService.command_setvalue(value, webEntity.entityItems.back().name); },
|
||||
FL_(entity_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
|
||||
if (entityItem.writeable) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::CUSTOM,
|
||||
webEntity.entityItems.back().name.c_str(),
|
||||
[webEntity](const char * value, const int8_t id) {
|
||||
return EMSESP::webEntityService.command_setvalue(value, webEntity.entityItems.back().name);
|
||||
},
|
||||
FL_(entity_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
@@ -100,7 +109,7 @@ bool WebEntityService::command_setvalue(const char * value, const std::string na
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
for (EntityItem & entityItem : *entityItems) {
|
||||
if (entityItem.name == name) {
|
||||
if (entityItem.valuetype == DeviceValueType::BOOL) {
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
@@ -112,9 +121,9 @@ bool WebEntityService::command_setvalue(const char * value, const std::string na
|
||||
return false;
|
||||
}
|
||||
int v = f / entityItem.factor;
|
||||
if (entityItem.valuetype == DeviceValueType::UINT || entityItem.valuetype == DeviceValueType::INT) {
|
||||
if (entityItem.value_type == DeviceValueType::UINT || entityItem.value_type == DeviceValueType::INT) {
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v, 0);
|
||||
} else if (entityItem.valuetype == DeviceValueType::USHORT || entityItem.valuetype == DeviceValueType::SHORT) {
|
||||
} else if (entityItem.value_type == DeviceValueType::USHORT || entityItem.value_type == DeviceValueType::SHORT) {
|
||||
uint8_t v1[2] = {(uint8_t)(v >> 8), (uint8_t)(v & 0xFF)};
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v1, 2, 0);
|
||||
} else {
|
||||
@@ -122,6 +131,7 @@ bool WebEntityService::command_setvalue(const char * value, const std::string na
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v1, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
publish_single(entityItem);
|
||||
if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
publish();
|
||||
@@ -136,42 +146,42 @@ bool WebEntityService::command_setvalue(const char * value, const std::string na
|
||||
void WebEntityService::render_value(JsonObject & output, EntityItem entity, const bool useVal) {
|
||||
char payload[12];
|
||||
std::string name = useVal ? "value" : entity.name;
|
||||
switch (entity.valuetype) {
|
||||
switch (entity.value_type) {
|
||||
case DeviceValueType::BOOL:
|
||||
if ((uint8_t)entity.val != EMS_VALUE_BOOL_NOTSET) {
|
||||
if ((uint8_t)entity.value != EMS_VALUE_BOOL_NOTSET) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
output[name] = (uint8_t)entity.val ? true : false;
|
||||
output[name] = (uint8_t)entity.value ? true : false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
output[name] = (uint8_t)entity.val ? 1 : 0;
|
||||
output[name] = (uint8_t)entity.value ? 1 : 0;
|
||||
} else {
|
||||
output[name] = Helpers::render_boolean(payload, (uint8_t)entity.val);
|
||||
output[name] = Helpers::render_boolean(payload, (uint8_t)entity.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::INT:
|
||||
if ((int8_t)entity.val != EMS_VALUE_INT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int8_t)entity.val, 2));
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int8_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
if ((uint8_t)entity.val != EMS_VALUE_UINT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint8_t)entity.val, 2));
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint8_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
if ((int16_t)entity.val != EMS_VALUE_SHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int16_t)entity.val, 2));
|
||||
if ((int16_t)entity.value != EMS_VALUE_SHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int16_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::USHORT:
|
||||
if ((uint16_t)entity.val != EMS_VALUE_USHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint16_t)entity.val, 2));
|
||||
if ((uint16_t)entity.value != EMS_VALUE_USHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint16_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
case DeviceValueType::TIME:
|
||||
if (entity.val != EMS_VALUE_ULONG_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * entity.val, 2));
|
||||
if (entity.value != EMS_VALUE_ULONG_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * entity.value, 2));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -215,7 +225,7 @@ bool WebEntityService::get_value_info(JsonObject & output, const char * cmd) {
|
||||
output["name"] = entity.name;
|
||||
output["uom"] = EMSdevice::uom_to_string(entity.uom);
|
||||
output["readable"] = true;
|
||||
output["writeable"] = true;
|
||||
output["writeable"] = entity.writeable;
|
||||
output["visible"] = true;
|
||||
render_value(output, entity, true);
|
||||
if (attribute_s) {
|
||||
@@ -250,8 +260,8 @@ void WebEntityService::publish_single(const EntityItem & entity) {
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", "custom_data", entity.name.c_str());
|
||||
}
|
||||
StaticJsonDocument<256> doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
render_value(output, entity, true);
|
||||
Mqtt::queue_publish(topic, output["value"].as<std::string>());
|
||||
}
|
||||
@@ -326,8 +336,10 @@ uint8_t WebEntityService::count_entities() {
|
||||
if (entityItems->size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
render_value(output, entity);
|
||||
}
|
||||
@@ -337,46 +349,51 @@ uint8_t WebEntityService::count_entities() {
|
||||
// send to dashboard, msgpack don't like serialized, use number
|
||||
void WebEntityService::generate_value_web(JsonObject & output) {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
|
||||
output["label"] = (std::string) "Custom Entities";
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
obj["id"] = "00" + entity.name;
|
||||
obj["u"] = entity.uom;
|
||||
obj["c"] = entity.name;
|
||||
switch (entity.valuetype) {
|
||||
if (entity.writeable) {
|
||||
obj["c"] = entity.name;
|
||||
}
|
||||
|
||||
switch (entity.value_type) {
|
||||
case DeviceValueType::BOOL: {
|
||||
char s[12];
|
||||
obj["v"] = Helpers::render_boolean(s, (uint8_t)entity.val);
|
||||
obj["v"] = Helpers::render_boolean(s, (uint8_t)entity.value);
|
||||
JsonArray l = obj.createNestedArray("l");
|
||||
l.add(Helpers::render_boolean(s, false, true));
|
||||
l.add(Helpers::render_boolean(s, true, true));
|
||||
break;
|
||||
}
|
||||
case DeviceValueType::INT:
|
||||
if ((int8_t)entity.val != EMS_VALUE_INT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int8_t)entity.val, 0);
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int8_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
if ((uint8_t)entity.val != EMS_VALUE_UINT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint8_t)entity.val, 0);
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint8_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
if ((int16_t)entity.val != EMS_VALUE_SHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int16_t)entity.val, 0);
|
||||
if ((int16_t)entity.value != EMS_VALUE_SHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int16_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::USHORT:
|
||||
if ((uint16_t)entity.val != EMS_VALUE_USHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint16_t)entity.val, 0);
|
||||
if ((uint16_t)entity.value != EMS_VALUE_USHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint16_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
case DeviceValueType::TIME:
|
||||
if (entity.val != EMS_VALUE_ULONG_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * entity.val, 0);
|
||||
if (entity.value != EMS_VALUE_ULONG_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * entity.value, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -402,13 +419,13 @@ bool WebEntityService::get_value(std::shared_ptr<const Telegram> telegram) {
|
||||
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
|
||||
for (auto & entity : *entityItems) {
|
||||
if (telegram->type_id == entity.type_id && telegram->src == entity.device_id && telegram->offset <= entity.offset
|
||||
&& (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.valuetype])) {
|
||||
uint32_t val = 0;
|
||||
for (uint8_t i = 0; i < len[entity.valuetype]; i++) {
|
||||
val = (val << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
||||
&& (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) {
|
||||
uint32_t value = 0;
|
||||
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
|
||||
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
||||
}
|
||||
if (val != entity.val) {
|
||||
entity.val = val;
|
||||
if (value != entity.value) {
|
||||
entity.value = value;
|
||||
if (Mqtt::publish_single()) {
|
||||
publish_single(entity);
|
||||
} else if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
|
||||
@@ -27,14 +27,16 @@ namespace emsesp {
|
||||
|
||||
class EntityItem {
|
||||
public:
|
||||
uint8_t id;
|
||||
uint8_t device_id;
|
||||
uint16_t type_id;
|
||||
uint8_t offset;
|
||||
int8_t valuetype;
|
||||
int8_t value_type;
|
||||
uint8_t uom;
|
||||
std::string name;
|
||||
double factor;
|
||||
uint32_t val;
|
||||
bool writeable;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
class WebEntity {
|
||||
@@ -65,7 +67,7 @@ class WebEntityService : public StatefulService<WebEntity> {
|
||||
HttpEndpoint<WebEntity> _httpEndpoint;
|
||||
FSPersistence<WebEntity> _fsPersistence;
|
||||
|
||||
std::list<EntityItem> * entityItems; // pointer to the list of schedule events
|
||||
std::list<EntityItem> * entityItems; // pointer to the list of entity items
|
||||
bool ha_registered_ = false;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user