Add blacklist/remove entities #891

This commit is contained in:
MichaelDvP
2023-01-24 17:26:18 +01:00
parent ede8e7dfce
commit 0e0e9eccf1
15 changed files with 207 additions and 133 deletions

View File

@@ -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

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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],

View File

@@ -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()}&nbsp;&nbsp; <OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}&nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}&nbsp;&nbsp; <OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}&nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp; <OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()} <OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp;
<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)}:&nbsp;{deviceEntity.n} {LL.DEFAULT(1) + ' ' + LL.NAME(1)}:&nbsp;{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>
); );

View File

@@ -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
} }

View File

@@ -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 {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);
} }