mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
Add blacklist/remove entities #891
This commit is contained in:
@@ -32,7 +32,8 @@
|
|||||||
- Add Rego 3000, TR120RF thermostats [#917](https://github.com/emsesp/EMS-ESP32/issues/917)
|
- Add Rego 3000, TR120RF thermostats [#917](https://github.com/emsesp/EMS-ESP32/issues/917)
|
||||||
- Add config for ESP32-S3
|
- Add config for ESP32-S3
|
||||||
- Add heatpump silent mode and other entities [#896](https://github.com/emsesp/EMS-ESP32/issues/896)
|
- Add heatpump silent mode and other entities [#896](https://github.com/emsesp/EMS-ESP32/issues/896)
|
||||||
- Allow reboot to other partition (factory or asymmetric OTA)
|
- Allow reboot to other partition (factory or asymetric OTA)
|
||||||
|
- blacklist entities to remove from memory [#891](https://github.com/emsesp/EMS-ESP32/issues/891)
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const de: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'Schreibaktion deaktivieren',
|
CUSTOMIZATIONS_HELP_3: 'Schreibaktion deaktivieren',
|
||||||
CUSTOMIZATIONS_HELP_4: 'von MQTT und API ausschließen',
|
CUSTOMIZATIONS_HELP_4: 'von MQTT und API ausschließen',
|
||||||
CUSTOMIZATIONS_HELP_5: 'Aus dem Kontrollzentrum ausblenden',
|
CUSTOMIZATIONS_HELP_5: 'Aus dem Kontrollzentrum ausblenden',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'Aus dem Speicher löschen',
|
||||||
SELECT_DEVICE: 'Wählen Sie ein Gerät aus',
|
SELECT_DEVICE: 'Wählen Sie ein Gerät aus',
|
||||||
SET_ALL: 'setzen Sie alle',
|
SET_ALL: 'setzen Sie alle',
|
||||||
OPTIONS: 'Optionen',
|
OPTIONS: 'Optionen',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const en: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'disable write action',
|
CUSTOMIZATIONS_HELP_3: 'disable write action',
|
||||||
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
|
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
|
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Select a device',
|
SELECT_DEVICE: 'Select a device',
|
||||||
SET_ALL: 'set all',
|
SET_ALL: 'set all',
|
||||||
OPTIONS: 'Options',
|
OPTIONS: 'Options',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const fr: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'désactiver l\'action d\'écriture',
|
CUSTOMIZATIONS_HELP_3: 'désactiver l\'action d\'écriture',
|
||||||
CUSTOMIZATIONS_HELP_4: 'exclure de MQTT et de l\'API',
|
CUSTOMIZATIONS_HELP_4: 'exclure de MQTT et de l\'API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'cacher du Tableau de bord',
|
CUSTOMIZATIONS_HELP_5: 'cacher du Tableau de bord',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Sélectionnez un appareil',
|
SELECT_DEVICE: 'Sélectionnez un appareil',
|
||||||
SET_ALL: 'tout régler',
|
SET_ALL: 'tout régler',
|
||||||
OPTIONS: 'Options',
|
OPTIONS: 'Options',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const nl: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit',
|
CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit',
|
||||||
CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API',
|
CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'verberg van het Dashboard',
|
CUSTOMIZATIONS_HELP_5: 'verberg van het Dashboard',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Selecteer een apparaat',
|
SELECT_DEVICE: 'Selecteer een apparaat',
|
||||||
SET_ALL: 'Alles aanzetten',
|
SET_ALL: 'Alles aanzetten',
|
||||||
OPTIONS: 'Opties',
|
OPTIONS: 'Opties',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const no: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'inaktiviser skriving',
|
CUSTOMIZATIONS_HELP_3: 'inaktiviser skriving',
|
||||||
CUSTOMIZATIONS_HELP_4: 'ekskludere fra MQTT og API',
|
CUSTOMIZATIONS_HELP_4: 'ekskludere fra MQTT og API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'gjemme fra Dashboard',
|
CUSTOMIZATIONS_HELP_5: 'gjemme fra Dashboard',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Velg en enhet',
|
SELECT_DEVICE: 'Velg en enhet',
|
||||||
SET_ALL: 'sett alle',
|
SET_ALL: 'sett alle',
|
||||||
OPTIONS: 'Alternativ',
|
OPTIONS: 'Alternativ',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const pl: BaseTranslation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu',
|
CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu',
|
||||||
CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API',
|
CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'ukryj na pulpicie',
|
CUSTOMIZATIONS_HELP_5: 'ukryj na pulpicie',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'wybierz urządzenie',
|
SELECT_DEVICE: 'wybierz urządzenie',
|
||||||
SET_ALL: 'Ustaw wszystko jako',
|
SET_ALL: 'Ustaw wszystko jako',
|
||||||
OPTIONS: 'Opcje',
|
OPTIONS: 'Opcje',
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ const sv: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_3: 'Inaktivera skrivningar',
|
CUSTOMIZATIONS_HELP_3: 'Inaktivera skrivningar',
|
||||||
CUSTOMIZATIONS_HELP_4: 'Exkludera från MQTT & API',
|
CUSTOMIZATIONS_HELP_4: 'Exkludera från MQTT & API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'Göm från Kontrollpanel',
|
CUSTOMIZATIONS_HELP_5: 'Göm från Kontrollpanel',
|
||||||
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Välj en enhet',
|
SELECT_DEVICE: 'Välj en enhet',
|
||||||
SET_ALL: 'ställ in alla',
|
SET_ALL: 'ställ in alla',
|
||||||
OPTIONS: 'Alternativ',
|
OPTIONS: 'Alternativ',
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
|||||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||||
|
|
||||||
type OptionType = 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite';
|
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||||
|
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||||
|
|
||||||
|
type OptionType = 'deleted' | 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite';
|
||||||
|
|
||||||
const OPTION_ICONS: { [type in OptionType]: [React.ComponentType<SvgIconProps>, React.ComponentType<SvgIconProps>] } = {
|
const OPTION_ICONS: { [type in OptionType]: [React.ComponentType<SvgIconProps>, React.ComponentType<SvgIconProps>] } = {
|
||||||
|
deleted: [DeleteForeverIcon, DeleteOutlineIcon],
|
||||||
readonly: [EditOffOutlinedIcon, EditOutlinedIcon],
|
readonly: [EditOffOutlinedIcon, EditOutlinedIcon],
|
||||||
web_exclude: [VisibilityOffOutlinedIcon, VisibilityOutlinedIcon],
|
web_exclude: [VisibilityOffOutlinedIcon, VisibilityOutlinedIcon],
|
||||||
api_mqtt_exclude: [CommentsDisabledOutlinedIcon, InsertCommentOutlinedIcon],
|
api_mqtt_exclude: [CommentsDisabledOutlinedIcon, InsertCommentOutlinedIcon],
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import {
|
|||||||
Link
|
Link
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { MessageBox } from '../components';
|
||||||
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
|
|
||||||
import { Table } from '@table-library/react-table-library/table';
|
import { 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 { Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
import { Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
||||||
@@ -41,11 +44,14 @@ import { extractErrorMessage, updateValue } from '../utils';
|
|||||||
import { DeviceShort, Devices, DeviceEntity, DeviceEntityMask } from './types';
|
import { DeviceShort, Devices, DeviceEntity, DeviceEntityMask } from './types';
|
||||||
|
|
||||||
import { useI18nContext } from '../i18n/i18n-react';
|
import { useI18nContext } from '../i18n/i18n-react';
|
||||||
|
import RestartMonitor from '../framework/system/RestartMonitor';
|
||||||
|
|
||||||
export const APIURL = window.location.origin + '/api/';
|
export const APIURL = window.location.origin + '/api/';
|
||||||
|
|
||||||
const SettingsCustomization: FC = () => {
|
const SettingsCustomization: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
|
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
@@ -66,7 +72,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
|
|
||||||
const entities_theme = useTheme({
|
const entities_theme = useTheme({
|
||||||
Table: `
|
Table: `
|
||||||
--data-table-library_grid-template-columns: 120px repeat(1, minmax(80px, 1fr)) 45px 45px 120px;
|
--data-table-library_grid-template-columns: 150px repeat(1, minmax(80px, 1fr)) 45px 45px 120px;
|
||||||
`,
|
`,
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -210,6 +216,9 @@ const SettingsCustomization: FC = () => {
|
|||||||
if ((m & 8) === 8) {
|
if ((m & 8) === 8) {
|
||||||
new_masks.push('8');
|
new_masks.push('8');
|
||||||
}
|
}
|
||||||
|
if ((m & 128) === 128) {
|
||||||
|
new_masks.push('128');
|
||||||
|
}
|
||||||
return new_masks;
|
return new_masks;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -249,6 +258,15 @@ const SettingsCustomization: FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const restart = async () => {
|
||||||
|
try {
|
||||||
|
await EMSESP.restart();
|
||||||
|
setRestarting(true);
|
||||||
|
} catch (error) {
|
||||||
|
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const saveCustomization = async () => {
|
const saveCustomization = async () => {
|
||||||
if (devices && deviceEntities && selectedDevice !== -1) {
|
if (devices && deviceEntities && selectedDevice !== -1) {
|
||||||
const masked_entities = deviceEntities
|
const masked_entities = deviceEntities
|
||||||
@@ -277,6 +295,8 @@ const SettingsCustomization: FC = () => {
|
|||||||
});
|
});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
enqueueSnackbar(LL.CUSTOMIZATIONS_SAVED(), { variant: 'success' });
|
enqueueSnackbar(LL.CUSTOMIZATIONS_SAVED(), { variant: 'success' });
|
||||||
|
} else if (response.status === 201) {
|
||||||
|
setRestartNeeded(true);
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
|
enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
|
||||||
}
|
}
|
||||||
@@ -300,7 +320,8 @@ const SettingsCustomization: FC = () => {
|
|||||||
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
||||||
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
||||||
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}
|
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}
|
||||||
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
||||||
|
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
@@ -413,6 +434,9 @@ const SettingsCustomization: FC = () => {
|
|||||||
<ToggleButton value="1">
|
<ToggleButton value="1">
|
||||||
<OptionIcon type="web_exclude" isSet={true} />
|
<OptionIcon type="web_exclude" isSet={true} />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
|
<ToggleButton value="128">
|
||||||
|
<OptionIcon type="deleted" isSet={true} />
|
||||||
|
</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -467,53 +491,61 @@ const SettingsCustomization: FC = () => {
|
|||||||
{tableList.map((de: DeviceEntity) => (
|
{tableList.map((de: DeviceEntity) => (
|
||||||
<Row key={de.id} item={de} onClick={() => editEntity(de)}>
|
<Row key={de.id} item={de} onClick={() => editEntity(de)}>
|
||||||
<Cell stiff>
|
<Cell stiff>
|
||||||
<ToggleButtonGroup
|
{!deviceEntity && (
|
||||||
size="small"
|
<ToggleButtonGroup
|
||||||
color="secondary"
|
size="small"
|
||||||
value={getMaskString(de.m)}
|
color="secondary"
|
||||||
onChange={(event, mask) => {
|
value={getMaskString(de.m)}
|
||||||
de.m = getMaskNumber(mask);
|
onChange={(event, mask) => {
|
||||||
if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) {
|
de.m = getMaskNumber(mask);
|
||||||
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;
|
if (de.n === '' && (de.m & DeviceEntityMask.DV_READONLY)) {
|
||||||
}
|
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;
|
||||||
if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) {
|
|
||||||
de.m = de.m & ~DeviceEntityMask.DV_FAVORITE;
|
|
||||||
}
|
|
||||||
setMasks(['']);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleButton value="8" disabled={(de.m & 1) !== 0 || de.n === undefined}>
|
|
||||||
<OptionIcon
|
|
||||||
type="favorite"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_FAVORITE) === DeviceEntityMask.DV_FAVORITE}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
<ToggleButton value="4" disabled={!de.w || (de.m & 3) === 3}>
|
|
||||||
<OptionIcon
|
|
||||||
type="readonly"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_READONLY) === DeviceEntityMask.DV_READONLY}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
<ToggleButton value="2" disabled={de.n === ''}>
|
|
||||||
<OptionIcon
|
|
||||||
type="api_mqtt_exclude"
|
|
||||||
isSet={
|
|
||||||
(de.m & DeviceEntityMask.DV_API_MQTT_EXCLUDE) === DeviceEntityMask.DV_API_MQTT_EXCLUDE
|
|
||||||
}
|
}
|
||||||
/>
|
if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) {
|
||||||
</ToggleButton>
|
de.m = de.m & ~DeviceEntityMask.DV_FAVORITE;
|
||||||
<ToggleButton value="1" disabled={de.n === undefined}>
|
}
|
||||||
<OptionIcon
|
setMasks(['']);
|
||||||
type="web_exclude"
|
}}
|
||||||
isSet={(de.m & DeviceEntityMask.DV_WEB_EXCLUDE) === DeviceEntityMask.DV_WEB_EXCLUDE}
|
>
|
||||||
/>
|
<ToggleButton value="8" disabled={(de.m & 0x81) !== 0 || de.n === undefined}>
|
||||||
</ToggleButton>
|
<OptionIcon
|
||||||
</ToggleButtonGroup>
|
type="favorite"
|
||||||
|
isSet={(de.m & DeviceEntityMask.DV_FAVORITE) === DeviceEntityMask.DV_FAVORITE}
|
||||||
|
/>
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value="4" disabled={!de.w || (de.m & 0x83) >= 3}>
|
||||||
|
<OptionIcon
|
||||||
|
type="readonly"
|
||||||
|
isSet={(de.m & DeviceEntityMask.DV_READONLY) === DeviceEntityMask.DV_READONLY}
|
||||||
|
/>
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value="2" disabled={de.n === '' || (de.m & 0x80) !== 0}>
|
||||||
|
<OptionIcon
|
||||||
|
type="api_mqtt_exclude"
|
||||||
|
isSet={
|
||||||
|
(de.m & DeviceEntityMask.DV_API_MQTT_EXCLUDE) === DeviceEntityMask.DV_API_MQTT_EXCLUDE
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value="1" disabled={de.n === undefined || (de.m & 0x80) !== 0}>
|
||||||
|
<OptionIcon
|
||||||
|
type="web_exclude"
|
||||||
|
isSet={(de.m & DeviceEntityMask.DV_WEB_EXCLUDE) === DeviceEntityMask.DV_WEB_EXCLUDE}
|
||||||
|
/>
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value="128" >
|
||||||
|
<OptionIcon
|
||||||
|
type="deleted"
|
||||||
|
isSet={(de.m & DeviceEntityMask.DV_DELETED) === DeviceEntityMask.DV_DELETED}
|
||||||
|
/>
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
)}
|
||||||
</Cell>
|
</Cell>
|
||||||
<Cell>{formatName(de)}</Cell>
|
<Cell>{!deviceEntity && (formatName(de))}</Cell>
|
||||||
<Cell>{!(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.mi)}</Cell>
|
<Cell>{!deviceEntity && !(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.mi)}</Cell>
|
||||||
<Cell>{!(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.ma)}</Cell>
|
<Cell>{!deviceEntity && !(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.ma)}</Cell>
|
||||||
<Cell>{formatValue(de.v)}</Cell>
|
<Cell>{!deviceEntity && (formatValue(de.v))}</Cell>
|
||||||
</Row>
|
</Row>
|
||||||
))}
|
))}
|
||||||
</Body>
|
</Body>
|
||||||
@@ -552,25 +584,34 @@ const SettingsCustomization: FC = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
{renderDeviceList()}
|
{renderDeviceList()}
|
||||||
{renderDeviceData()}
|
{renderDeviceData()}
|
||||||
<Box display="flex" flexWrap="wrap">
|
{restartNeeded && (
|
||||||
<Box flexGrow={1}>
|
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
|
||||||
|
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
|
||||||
|
{LL.RESTART()}
|
||||||
|
</Button>
|
||||||
|
</MessageBox>
|
||||||
|
)}
|
||||||
|
{!restartNeeded && (
|
||||||
|
<Box display="flex" flexWrap="wrap">
|
||||||
|
<Box flexGrow={1}>
|
||||||
|
<ButtonRow>
|
||||||
|
<Button startIcon={<SaveIcon />} variant="outlined" color="primary" onClick={() => saveCustomization()}>
|
||||||
|
{LL.SAVE()}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button startIcon={<SaveIcon />} variant="outlined" color="primary" onClick={() => saveCustomization()}>
|
<Button
|
||||||
{LL.SAVE()}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="error"
|
||||||
|
onClick={() => setConfirmReset(true)}
|
||||||
|
>
|
||||||
|
{LL.RESET(0)}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Box>
|
</Box>
|
||||||
<ButtonRow>
|
)}
|
||||||
<Button
|
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="error"
|
|
||||||
onClick={() => setConfirmReset(true)}
|
|
||||||
>
|
|
||||||
{LL.RESET(0)}
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</Box>
|
|
||||||
{renderResetDialog()}
|
{renderResetDialog()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -582,48 +623,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
<Dialog open={!!deviceEntity} onClose={() => setDeviceEntity(undefined)}>
|
<Dialog open={!!deviceEntity} onClose={() => setDeviceEntity(undefined)}>
|
||||||
<DialogTitle>{LL.EDIT() + ' ' + LL.ENTITY() + ' "' + de.id + '"'}</DialogTitle>
|
<DialogTitle>{LL.EDIT() + ' ' + LL.ENTITY() + ' "' + de.id + '"'}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<ToggleButtonGroup
|
<Box color="warning.main" mb={2}>
|
||||||
size="small"
|
|
||||||
color="secondary"
|
|
||||||
value={getMaskString(de.m)}
|
|
||||||
onChange={(event, mask) => {
|
|
||||||
de.m = getMaskNumber(mask);
|
|
||||||
if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) {
|
|
||||||
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;
|
|
||||||
}
|
|
||||||
if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) {
|
|
||||||
de.m = de.m & ~DeviceEntityMask.DV_FAVORITE;
|
|
||||||
}
|
|
||||||
setMasks(['']);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleButton value="8" disabled={(de.m & 1) !== 0 || de.n === undefined}>
|
|
||||||
<OptionIcon
|
|
||||||
type="favorite"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_FAVORITE) === DeviceEntityMask.DV_FAVORITE}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
<ToggleButton value="4" disabled={!de.w || (de.m & 3) === 3}>
|
|
||||||
<OptionIcon
|
|
||||||
type="readonly"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_READONLY) === DeviceEntityMask.DV_READONLY}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
<ToggleButton value="2" disabled={de.n === ''}>
|
|
||||||
<OptionIcon
|
|
||||||
type="api_mqtt_exclude"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_API_MQTT_EXCLUDE) === DeviceEntityMask.DV_API_MQTT_EXCLUDE}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
<ToggleButton value="1" disabled={de.n === undefined}>
|
|
||||||
<OptionIcon
|
|
||||||
type="web_exclude"
|
|
||||||
isSet={(de.m & DeviceEntityMask.DV_WEB_EXCLUDE) === DeviceEntityMask.DV_WEB_EXCLUDE}
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
|
||||||
</ToggleButtonGroup>
|
|
||||||
|
|
||||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={2} mb={2}>
|
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{LL.DEFAULT(1) + ' ' + LL.NAME(1)}: {deviceEntity.n}
|
{LL.DEFAULT(1) + ' ' + LL.NAME(1)}: {deviceEntity.n}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -689,7 +689,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.USER_CUSTOMIZATION()} titleGutter>
|
<SectionContent title={LL.USER_CUSTOMIZATION()} titleGutter>
|
||||||
{renderContent()}
|
{restarting ? <RestartMonitor /> : renderContent()}
|
||||||
{renderEditDialog()}
|
{renderEditDialog()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -298,5 +298,6 @@ export enum DeviceEntityMask {
|
|||||||
DV_WEB_EXCLUDE = 1,
|
DV_WEB_EXCLUDE = 1,
|
||||||
DV_API_MQTT_EXCLUDE = 2,
|
DV_API_MQTT_EXCLUDE = 2,
|
||||||
DV_READONLY = 4,
|
DV_READONLY = 4,
|
||||||
DV_FAVORITE = 8
|
DV_FAVORITE = 8,
|
||||||
|
DV_DELETED = 128
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,16 @@ bool EMSdevice::has_entities() const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
bool found = false;
|
||||||
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
|
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||||
|
if (entityCustomization.device_id == device_id() && entityCustomization.entity_ids.size()) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
std::string EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
||||||
@@ -511,14 +520,9 @@ void EMSdevice::add_device_value(uint8_t tag,
|
|||||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||||
// if there is an appended custom name, strip it to get the true entity name
|
// if there is an appended custom name, strip it to get the true entity name
|
||||||
// and extract the new custom name
|
// and extract the new custom name
|
||||||
std::string shortname;
|
|
||||||
auto custom_name_pos = entity_id.find('|');
|
auto custom_name_pos = entity_id.find('|');
|
||||||
bool has_custom_name = (custom_name_pos != std::string::npos);
|
bool has_custom_name = (custom_name_pos != std::string::npos);
|
||||||
if (has_custom_name) {
|
std::string shortname = has_custom_name ? entity_id.substr(2, custom_name_pos - 2) : entity_id.substr(2);
|
||||||
shortname = entity_id.substr(2, custom_name_pos - 2);
|
|
||||||
} else {
|
|
||||||
shortname = entity_id.substr(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we found the device entity
|
// we found the device entity
|
||||||
if (shortname == entity) {
|
if (shortname == entity) {
|
||||||
@@ -1014,6 +1018,22 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
|
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||||
|
if ((entityCustomization.device_id == device_id())) {
|
||||||
|
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||||
|
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||||
|
if (mask & 0x80) {
|
||||||
|
JsonObject obj = output.createNestedObject();
|
||||||
|
obj["id"] = DeviceValue::get_name(entity_id);
|
||||||
|
obj["m"] = mask;
|
||||||
|
obj["w"] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
||||||
@@ -1037,14 +1057,9 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
|
|||||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||||
|
|
||||||
// extra shortname
|
// extra shortname
|
||||||
std::string shortname;
|
|
||||||
auto custom_name_pos = entity_id.find('|');
|
auto custom_name_pos = entity_id.find('|');
|
||||||
bool has_custom_name = (custom_name_pos != std::string::npos);
|
bool has_custom_name = (custom_name_pos != std::string::npos);
|
||||||
if (has_custom_name) {
|
std::string shortname = has_custom_name ? entity_id.substr(2, custom_name_pos - 2) : entity_id.substr(2);
|
||||||
shortname = entity_id.substr(2, custom_name_pos - 2);
|
|
||||||
} else {
|
|
||||||
shortname = entity_id.substr(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity_name == shortname) {
|
if (entity_name == shortname) {
|
||||||
// check the masks
|
// check the masks
|
||||||
@@ -1088,8 +1103,14 @@ void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
|
|||||||
for (const auto & dv : devicevalues_) {
|
for (const auto & dv : devicevalues_) {
|
||||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||||
uint8_t mask = dv.state >> 4;
|
uint8_t mask = dv.state >> 4;
|
||||||
|
bool is_set = false;
|
||||||
if (mask || !dv.custom_fullname.empty()) {
|
for (auto & eid : entity_ids) {
|
||||||
|
if (DeviceValue::get_name(eid) == entity_name) {
|
||||||
|
is_set = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_set && (mask || !dv.custom_fullname.empty())) {
|
||||||
if (dv.custom_fullname.empty()) {
|
if (dv.custom_fullname.empty()) {
|
||||||
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name);
|
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -378,4 +378,12 @@ std::string DeviceValue::get_fullname() const {
|
|||||||
return customname;
|
return customname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DeviceValue::get_name(std::string & entity) {
|
||||||
|
auto pos = entity.find('|');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
return entity.substr(2, pos - 2);
|
||||||
|
}
|
||||||
|
return entity.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class DeviceValue {
|
|||||||
bool get_custom_max(uint16_t & val);
|
bool get_custom_max(uint16_t & val);
|
||||||
std::string get_custom_fullname() const;
|
std::string get_custom_fullname() const;
|
||||||
std::string get_fullname() const;
|
std::string get_fullname() const;
|
||||||
|
static std::string get_name(std::string & entity);
|
||||||
|
|
||||||
// dv state flags
|
// dv state flags
|
||||||
void add_state(uint8_t s) {
|
void add_state(uint8_t s) {
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request, J
|
|||||||
// saves it in the customization service
|
// saves it in the customization service
|
||||||
// and updates the entity list real-time
|
// and updates the entity list real-time
|
||||||
void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||||
|
bool need_reboot = false;
|
||||||
if (json.is<JsonObject>()) {
|
if (json.is<JsonObject>()) {
|
||||||
// find the device using the unique_id
|
// find the device using the unique_id
|
||||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
@@ -245,10 +246,47 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J
|
|||||||
uint8_t device_id = emsdevice->device_id();
|
uint8_t device_id = emsdevice->device_id();
|
||||||
|
|
||||||
// and set the mask and custom names immediately for any listed entities
|
// and set the mask and custom names immediately for any listed entities
|
||||||
JsonArray entity_ids_json = json["entity_ids"];
|
JsonArray entity_ids_json = json["entity_ids"];
|
||||||
|
std::vector<std::string> entity_ids;
|
||||||
for (const JsonVariant id : entity_ids_json) {
|
for (const JsonVariant id : entity_ids_json) {
|
||||||
emsdevice->setCustomEntity(id.as<std::string>());
|
std::string id_s = id.as<std::string>();
|
||||||
|
if (id_s[0] == '8') {
|
||||||
|
entity_ids.push_back(id_s);
|
||||||
|
need_reboot = true;
|
||||||
|
} else {
|
||||||
|
emsdevice->setCustomEntity(id_s);
|
||||||
|
}
|
||||||
|
// emsesp::EMSESP::logger().info(id.as<const char *>());
|
||||||
}
|
}
|
||||||
|
// add deleted entities from file
|
||||||
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
|
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||||
|
if (entityCustomization.device_id == device_id) {
|
||||||
|
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||||
|
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||||
|
std::string name = DeviceValue::get_name(entity_id);
|
||||||
|
if (mask & 0x80) {
|
||||||
|
bool is_set = false;
|
||||||
|
for (const JsonVariant id : entity_ids_json) {
|
||||||
|
std::string id_s = id.as<std::string>();
|
||||||
|
id_s = DeviceValue::get_name(id_s);
|
||||||
|
if (id_s == name) {
|
||||||
|
is_set = true;
|
||||||
|
need_reboot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_set) {
|
||||||
|
entity_ids.push_back(entity_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// get list of entities that have masks set or a custom fullname
|
||||||
|
emsdevice->getCustomEntities(entity_ids);
|
||||||
|
|
||||||
// Save the list to the customization file
|
// Save the list to the customization file
|
||||||
EMSESP::webCustomizationService.update(
|
EMSESP::webCustomizationService.update(
|
||||||
@@ -263,18 +301,11 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entity_ids_json.size()) {
|
|
||||||
return StateUpdateResult::UNCHANGED; // nothing to add
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new entry for this device if there are values
|
// create a new entry for this device if there are values
|
||||||
EntityCustomization new_entry;
|
EntityCustomization new_entry;
|
||||||
new_entry.product_id = product_id;
|
new_entry.product_id = product_id;
|
||||||
new_entry.device_id = device_id;
|
new_entry.device_id = device_id;
|
||||||
|
|
||||||
// get list of entities that have masks set or a custom fullname
|
|
||||||
std::vector<std::string> entity_ids;
|
|
||||||
emsdevice->getCustomEntities(entity_ids);
|
|
||||||
new_entry.entity_ids = entity_ids;
|
new_entry.entity_ids = entity_ids;
|
||||||
|
|
||||||
// add the record and save
|
// add the record and save
|
||||||
@@ -289,7 +320,7 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
AsyncWebServerResponse * response = request->beginResponse(need_reboot ? 201 : 200); // OK
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user