diff --git a/interface/package-lock.json b/interface/package-lock.json index 39ba6f57f..f6a0e0767 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -15,7 +15,7 @@ "@mui/material": "^5.10.4", "@table-library/react-table-library": "4.0.18", "@types/lodash": "^4.14.184", - "@types/node": "^18.7.15", + "@types/node": "^18.7.16", "@types/react": "^18.0.18", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", @@ -35,7 +35,7 @@ "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.12.0", - "typescript": "^4.8.2" + "typescript": "^4.8.3" }, "devDependencies": { "nodemon": "^2.0.19", @@ -4013,9 +4013,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "node_modules/@types/node": { - "version": "18.7.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", - "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" + "version": "18.7.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz", + "integrity": "sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -16418,9 +16418,9 @@ } }, "node_modules/typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20201,9 +20201,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "@types/node": { - "version": "18.7.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", - "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" + "version": "18.7.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz", + "integrity": "sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==" }, "@types/parse-json": { "version": "4.0.0", @@ -29076,9 +29076,9 @@ "requires": {} }, "typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/interface/package.json b/interface/package.json index c93eef5ae..748fdbc76 100644 --- a/interface/package.json +++ b/interface/package.json @@ -11,7 +11,7 @@ "@mui/material": "^5.10.4", "@table-library/react-table-library": "4.0.18", "@types/lodash": "^4.14.184", - "@types/node": "^18.7.15", + "@types/node": "^18.7.16", "@types/react": "^18.0.18", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", @@ -31,7 +31,7 @@ "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.12.0", - "typescript": "^4.8.2" + "typescript": "^4.8.3" }, "scripts": { "start": "react-app-rewired start", diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index 09df1ae0b..0bfd8e64d 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -45,15 +45,19 @@ const de: Translation = { PROBLEM_UPDATING: 'Problem beim Aktualisieren', PROBLEM_LOADING: 'Problem beim Laden', ACCESS_DENIED: 'Zugriff abgelehnt', - ANALOG_SENSOR: 'Analogsensor{post}', + ANALOG_SENSOR: 'Analogsensor', ANALOG_SENSORS: 'Analogsensoren', + UPDATED: 'DE_Updated', + UPDATE: 'DE_Update', + REMOVED: 'DE_Removed', + DELETION: 'DE_Deletion', OFFSET: 'Addition', FACTOR: 'Faktor', FREQ: 'Frequenz', STARTVALUE: 'Startwert', WARN_GPIO: 'Warnung: Vorsicht bei der korrekten Wahl des GPIO!', EDIT: 'Editiere', - TEMP_SENSOR: 'Temperatursensor{post}', + TEMP_SENSOR: 'Temperatursensor', TEMP_SENSORS: 'Temperatursensoren', WRITE_COMMAND: 'Befehl schreiben {cmd}', EMS_BUS_WARNING: @@ -128,7 +132,7 @@ const de: Translation = { RESTART_TEXT: 'EMS-ESP muss neu gestartet werden, um geänderte Systemeinstellungen zu übernehmen', COMMAND: 'Befehl', CUSTOMIZATIONS_RESTART: 'Alle Anpassungen wurden entfernt. Neustart...', - CUSTOMIZATIONS_FULL: 'Ausgewählte Entitäten haben das Limit von 60 überschritten. Bitte stapelweise speichern', + CUSTOMIZATIONS_FULL: 'Ausgewählte Entitäten haben das Limit überschritten. Bitte stapelweise speichern', CUSTOMIZATIONS_SAVED: 'Anpassungen gespeichert', CUSTOMIZATIONS_HELP_1: 'Wählen Sie ein Gerät aus und passen Sie die Entitäten mithilfe der Optionen an', CUSTOMIZATIONS_HELP_2: 'als Favorit markieren', @@ -253,7 +257,9 @@ const de: Translation = { NETWORK_ENABLE_IPV6: 'Aktiviere IPv6 Unterstützung', NETWORK_FIXED_IP: 'Feste IP Addresse', ADMIN: 'Administrator', - GUEST: 'Gast' + GUEST: 'Gast', + NEW: 'Neu', + RENAME: 'DE_Rename' }; export default de; diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index 158974226..de30e2a58 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -45,15 +45,19 @@ const en: BaseTranslation = { PROBLEM_UPDATING: 'Problem updating', PROBLEM_LOADING: 'Problem loading', ACCESS_DENIED: 'Access Denied', - ANALOG_SENSOR: 'Analog Sensor{post}', + ANALOG_SENSOR: 'Analog Sensor', ANALOG_SENSORS: 'Analog Sensors', + UPDATED: 'Updated', + UPDATE: 'Update', + REMOVED: 'Removed', + DELETION: 'Deletion', OFFSET: 'Offset', FACTOR: 'Factor', FREQ: 'Frequency', STARTVALUE: 'Start value', WARN_GPIO: 'Warning: be careful when assigning a GPIO!', EDIT: 'Edit', - TEMP_SENSOR: 'Temperature Sensor{post}', + TEMP_SENSOR: 'Temperature Sensor', TEMP_SENSORS: 'Temperature Sensors', WRITE_COMMAND: 'Write command {cmd}', EMS_BUS_WARNING: @@ -128,7 +132,7 @@ const en: BaseTranslation = { RESTART_TEXT: 'EMS-ESP needs to be restarted to apply changed system settings', COMMAND: 'Command', CUSTOMIZATIONS_RESTART: 'All customizations have been removed. Restarting...', - CUSTOMIZATIONS_FULL: 'Selected entities exceeded limit of 60. Please Save in batches', + CUSTOMIZATIONS_FULL: 'Selected entities exceeded limit. Please save in batches', CUSTOMIZATIONS_SAVED: 'Customizations saved', CUSTOMIZATIONS_HELP_1: 'Select a device and customize the entities using the options', CUSTOMIZATIONS_HELP_2: 'mark as favorite', @@ -254,7 +258,8 @@ const en: BaseTranslation = { NETWORK_FIXED_IP: 'Use Fixed IP address', ADMIN: 'Admin', GUEST: 'Guest', - NEW: 'New' + NEW: 'New', + RENAME: 'Rename' }; export default en; diff --git a/interface/src/i18n/i18n-types.ts b/interface/src/i18n/i18n-types.ts index 60d3becd2..ddc6adf75 100644 --- a/interface/src/i18n/i18n-types.ts +++ b/interface/src/i18n/i18n-types.ts @@ -193,14 +193,29 @@ type RootTranslation = { */ ACCESS_DENIED: string /** - * Analog Sensor{post} - * @param {unknown} post + * Analog Sensor */ - ANALOG_SENSOR: RequiredParams<'post'> + ANALOG_SENSOR: string /** * Analog Sensors */ ANALOG_SENSORS: string + /** + * Updated + */ + UPDATED: string + /** + * Update + */ + UPDATE: string + /** + * Removed + */ + REMOVED: string + /** + * Deletion + */ + DELETION: string /** * Offset */ @@ -226,10 +241,9 @@ type RootTranslation = { */ EDIT: string /** - * Temperature Sensor{post} - * @param {unknown} post + * Temperature Sensor */ - TEMP_SENSOR: RequiredParams<'post'> + TEMP_SENSOR: string /** * Temperature Sensors */ @@ -521,7 +535,7 @@ type RootTranslation = { */ CUSTOMIZATIONS_RESTART: string /** - * Selected entities exceeded limit of 60. Please Save in batches + * Selected entities exceeded limit. Please save in batches */ CUSTOMIZATIONS_FULL: string /** @@ -1008,6 +1022,10 @@ type RootTranslation = { * New */ NEW: string + /** + * Rename + */ + RENAME: string } export type TranslationFunctions = { @@ -1188,13 +1206,29 @@ export type TranslationFunctions = { */ ACCESS_DENIED: () => LocalizedString /** - * Analog Sensor{post} + * Analog Sensor */ - ANALOG_SENSOR: (arg: { post: unknown }) => LocalizedString + ANALOG_SENSOR: () => LocalizedString /** * Analog Sensors */ ANALOG_SENSORS: () => LocalizedString + /** + * Updated + */ + UPDATED: () => LocalizedString + /** + * Update + */ + UPDATE: () => LocalizedString + /** + * Removed + */ + REMOVED: () => LocalizedString + /** + * Deletion + */ + DELETION: () => LocalizedString /** * Offset */ @@ -1220,9 +1254,9 @@ export type TranslationFunctions = { */ EDIT: () => LocalizedString /** - * Temperature Sensor{post} + * Temperature Sensor */ - TEMP_SENSOR: (arg: { post: unknown }) => LocalizedString + TEMP_SENSOR: () => LocalizedString /** * Temperature Sensors */ @@ -1506,7 +1540,7 @@ export type TranslationFunctions = { */ CUSTOMIZATIONS_RESTART: () => LocalizedString /** - * Selected entities exceeded limit of 60. Please Save in batches + * Selected entities exceeded limit. Please save in batches */ CUSTOMIZATIONS_FULL: () => LocalizedString /** @@ -1993,6 +2027,10 @@ export type TranslationFunctions = { * New */ NEW: () => LocalizedString + /** + * Rename + */ + RENAME: () => LocalizedString } export type Formatters = {} diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index 45f163ab6..3099b9607 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -44,15 +44,19 @@ const nl: BaseTranslation = { PROBLEM_UPDATING: 'Probleem met updaten', PROBLEM_LOADING: 'Probleem met laden', ACCESS_DENIED: 'Toegang geweigerd', - ANALOG_SENSOR: 'Analoge sensor{post}', + ANALOG_SENSOR: 'Analoge sensor', ANALOG_SENSORS: 'Analoge Sensoren', + UPDATED: 'Bijgewerkt', + UPDATE: 'Bijwerken', + REMOVED: 'Verwijderd', + DELETION: 'Verwijder', OFFSET: 'Offset', FACTOR: 'Factor', FREQ: 'Frequentie', STARTVALUE: 'Startwaarde', WARN_GPIO: 'Waarschuwing: let op met het koppelen van de juiste GPIO pin!', EDIT: 'Wijzigen', - TEMP_SENSOR: 'Temperatuur sensor{post}', + TEMP_SENSOR: 'Temperatuur sensor', TEMP_SENSORS: 'Temperatuur Sensoren', WRITE_COMMAND: 'Schrijf commando {cmd}', EMS_BUS_WARNING: @@ -127,7 +131,7 @@ const nl: BaseTranslation = { RESTART_TEXT: 'EMS-ESP dient opnieuw gestart te worden om de wijzingen toe te passen', COMMAND: 'Commando', CUSTOMIZATIONS_RESTART: 'Alle custom profielen worden verwijderd. Herstarten...', - CUSTOMIZATIONS_FULL: 'Meer dan 60 entiteiten geselecteerd. Sla op in delen aub', + CUSTOMIZATIONS_FULL: 'Te veel entiteiten geselecteerd. Sla op in delen aub', CUSTOMIZATIONS_SAVED: 'Custom aanpassingen opgeslagen', CUSTOMIZATIONS_HELP_1: 'Selecteer een apparaat en pas de entiteiten aan door middel van de opties', CUSTOMIZATIONS_HELP_2: 'Markeer as favoriet', @@ -254,7 +258,8 @@ const nl: BaseTranslation = { NETWORK_FIXED_IP: 'Gebruik vast IP addres', ADMIN: 'Admin', GUEST: 'Gast', - NEW: 'Nieuwe' + NEW: 'Nieuwe', + RENAME: 'Hernoem' }; export default nl; diff --git a/interface/src/project/DashboardData.tsx b/interface/src/project/DashboardData.tsx index 465441ea8..e02e38bb2 100644 --- a/interface/src/project/DashboardData.tsx +++ b/interface/src/project/DashboardData.tsx @@ -89,6 +89,7 @@ const DashboardData: FC = () => { active_sensors: 0, analog_enabled: false }); + const [deviceData, setDeviceData] = useState({ label: '', data: [] }); const [sensorData, setSensorData] = useState({ sensors: [], analogs: [] }); const [deviceValue, setDeviceValue] = useState(); @@ -558,11 +559,11 @@ const DashboardData: FC = () => { offset: sensor.o }); if (response.status === 204) { - enqueueSnackbar(LL.TEMP_SENSOR({ post: ' change failed' }), { variant: 'error' }); + enqueueSnackbar(LL.TEMP_SENSOR() + ' ' + LL.UPLOAD_TEXT() + ' ' + LL.FAILED(), { variant: 'error' }); } else if (response.status === 403) { enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); } else { - enqueueSnackbar(LL.TEMP_SENSOR({ post: ' removed' }), { variant: 'success' }); + enqueueSnackbar(LL.TEMP_SENSOR() + ' ' + LL.UPDATED(), { variant: 'success' }); } setSensor(undefined); } catch (error: unknown) { @@ -578,7 +579,9 @@ const DashboardData: FC = () => { if (sensor) { return ( setSensor(undefined)}> - {LL.EDIT()} {LL.TEMP_SENSORS()} + + {LL.EDIT()} {LL.TEMP_SENSORS()} + Sensor ID {sensor.id} @@ -983,11 +986,11 @@ const DashboardData: FC = () => { }); if (response.status === 204) { - enqueueSnackbar(LL.ANALOG_SENSOR({ post: ' deletion failed' }), { variant: 'error' }); + enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.DELETION() + ' ' + LL.FAILED(), { variant: 'error' }); } else if (response.status === 403) { enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); } else { - enqueueSnackbar(LL.ANALOG_SENSOR({ post: ' removed' }), { variant: 'success' }); + enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.REMOVED(), { variant: 'success' }); } } catch (error: unknown) { enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); @@ -1011,11 +1014,11 @@ const DashboardData: FC = () => { }); if (response.status === 204) { - enqueueSnackbar(LL.ANALOG_SENSOR({ post: ' update failed' }), { variant: 'error' }); + enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.UPDATE() + ' ' + LL.FAILED(), { variant: 'error' }); } else if (response.status === 403) { enqueueSnackbar(LL.ACCESS_DENIED(), { variant: 'error' }); } else { - enqueueSnackbar(LL.ANALOG_SENSOR({ post: ' updated' }), { variant: 'success' }); + enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.UPDATED(), { variant: 'success' }); } } catch (error: unknown) { enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); @@ -1030,7 +1033,9 @@ const DashboardData: FC = () => { if (analog) { return ( setAnalog(undefined)}> - {LL.EDIT()} {LL.ANALOG_SENSORS()} + + {LL.EDIT()} {LL.ANALOG_SENSORS()} + @@ -1055,7 +1060,13 @@ const DashboardData: FC = () => { /> - + {AnalogTypeNames.map((val, i) => ( {val} diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index d5b5db0e2..0dd054888 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -19,7 +19,6 @@ import { import { Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; -import { useSort, SortToggleType } from '@table-library/react-table-library/sort'; import { Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { useSnackbar } from 'notistack'; @@ -28,9 +27,6 @@ import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Cancel'; import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore'; -import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined'; -import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined'; -import UnfoldMoreOutlinedIcon from '@mui/icons-material/UnfoldMoreOutlined'; import SearchIcon from '@mui/icons-material/Search'; import FilterListIcon from '@mui/icons-material/FilterList'; @@ -53,17 +49,18 @@ const SettingsCustomization: FC = () => { const { enqueueSnackbar } = useSnackbar(); - const [deviceEntities, setDeviceEntities] = useState([{ id: '', v: 0, n: '', m: 0, w: false }]); + const emptyDeviceEntity = { id: '', v: 0, n: '', cn: '', m: 0, w: false }; + + const [deviceEntities, setDeviceEntities] = useState([emptyDeviceEntity]); const [devices, setDevices] = useState(); const [errorMessage, setErrorMessage] = useState(); const [selectedDevice, setSelectedDevice] = useState(-1); - - const [selectedEntity, setSelectedEntity] = useState(); - const [confirmReset, setConfirmReset] = useState(false); const [selectedFilters, setSelectedFilters] = useState(0); const [search, setSearch] = useState(''); + const [deviceEntity, setDeviceEntity] = useState(); + // eslint-disable-next-line const [masks, setMasks] = useState(() => ['']); @@ -131,32 +128,6 @@ const SettingsCustomization: FC = () => { ` }); - const getSortIcon = (state: any, sortKey: any) => { - if (state.sortKey === sortKey && state.reverse) { - return ; - } - if (state.sortKey === sortKey && !state.reverse) { - return ; - } - return ; - }; - - const entity_sort = useSort( - { nodes: deviceEntities }, - {}, - { - sortIcon: { - iconDefault: , - iconUp: , - iconDown: - }, - sortToggleType: SortToggleType.AlternateWithReset, - sortFns: { - NAME: (array) => array.sort((a, b) => a.id.localeCompare(b.id)) - } - } - ); - const fetchDevices = useCallback(async () => { try { setDevices((await EMSESP.readDevices()).data); @@ -166,13 +137,13 @@ const SettingsCustomization: FC = () => { }, [LL]); const setInitialMask = (data: DeviceEntity[]) => { - setDeviceEntities(data.map((de) => ({ ...de, om: de.m }))); + setDeviceEntities(data.map((de) => ({ ...de, o_m: de.m, o_cn: de.cn }))); }; const fetchDeviceEntities = async (unique_id: number) => { try { - const data = (await EMSESP.readDeviceEntities({ id: unique_id })).data; - setInitialMask(data); + const new_deviceEntities = (await EMSESP.readDeviceEntities({ id: unique_id })).data; + setInitialMask(new_deviceEntities); } catch (error: unknown) { setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING())); } @@ -196,12 +167,16 @@ const SettingsCustomization: FC = () => { function formatName(de: DeviceEntity) { if (de.n === undefined || de.n === de.id) { return de.id; - } else if (de.n === '') { + } + + if (de.n === '') { return LL.COMMAND() + ': ' + de.id; } + return ( <> - {de.n} ( + {de.cn !== undefined && de.cn !== '' ? de.cn : de.n} +  ( {de.id} @@ -274,10 +249,12 @@ const SettingsCustomization: FC = () => { const saveCustomization = async () => { if (devices && deviceEntities && selectedDevice !== -1) { const masked_entities = deviceEntities - .filter((de) => de.m !== de.om) - .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.id); + .filter((de) => de.m !== de.o_m || de.cn !== de.o_cn) + .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.id + (new_de.cn ? '|' + new_de.cn : '')); - if (masked_entities.length > 60) { + // check size in bytes to match buffer in CPP, which is 4096 + const bytes = new TextEncoder().encode(JSON.stringify(masked_entities)).length; + if (bytes > 4000) { enqueueSnackbar(LL.CUSTOMIZATIONS_FULL(), { variant: 'warning' }); return; } @@ -339,16 +316,26 @@ const SettingsCustomization: FC = () => { }; const editEntity = (de: DeviceEntity) => { - if (de.n) { - setSelectedEntity(de); - console.log(de.n); // TODO + if (de.cn === undefined) { + de.cn = ''; } + setDeviceEntity(de); }; const updateEntity = () => { - if (selectedEntity) { - setSelectedEntity(undefined); // TODO + if (deviceEntity) { + setDeviceEntities((prevState) => { + const newState = prevState.map((obj) => { + if (obj.id === deviceEntity.id) { + return { ...obj, cn: deviceEntity.cn }; + } + return obj; + }); + return newState; + }); } + + setDeviceEntity(undefined); }; const renderDeviceData = () => { @@ -445,19 +432,14 @@ const SettingsCustomization: FC = () => { - +
{(tableList: any) => ( <>
{LL.OPTIONS()} - @@ -576,24 +558,24 @@ const SettingsCustomization: FC = () => { ); }; - const renderEditEntity = () => { - if (selectedEntity) { - return ( - setSelectedEntity(undefined)}> - Rename Entity + const renderEditEntity = () => ( + setDeviceEntity(undefined)}> + {deviceEntity && ( + <> + {LL.RENAME() + ' ' + LL.ENTITY_NAME()} - {selectedEntity.n} + {deviceEntity.n} - @@ -602,7 +584,7 @@ const SettingsCustomization: FC = () => { - - ); - } - }; + + )} + + ); return ( diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index de1a6baf4..648d6c908 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -139,7 +139,8 @@ export interface DeviceEntity { n?: string; // fullname, optional cn?: string; // custom fullname, optional m: number; // mask - om?: number; // original mask before edits + o_m?: number; // original mask before edits + o_cn?: string; // original cn before edits w: boolean; // writeable } diff --git a/lib_standalone/FSPersistence.h b/lib_standalone/FSPersistence.h index 10c4b7814..345e9b8ae 100644 --- a/lib_standalone/FSPersistence.h +++ b/lib_standalone/FSPersistence.h @@ -24,6 +24,8 @@ class FSPersistence { } void readFromFS() { + Serial.print("Fake reading file "); + Serial.println(_filePath); applyDefaults(); } diff --git a/mock-api/server.js b/mock-api/server.js index 0a4509489..df672c133 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -442,7 +442,7 @@ const emsesp_devicedata_1 = { { v: '(0)', u: 0, - id: '00error code', + id: '08error code', }, { v: '14:54:39 06/06/2021', @@ -580,8 +580,9 @@ const emsesp_deviceentities_1 = [ { v: '(0)', n: 'error code', + cn: 'my custom error code', id: 'errorcode', - m: 0, + m: 8, w: false, }, { @@ -922,22 +923,55 @@ rest_server.post(EMSESP_DEVICEENTITIES_ENDPOINT, (req, res) => { }) function updateMask(entity, de, dd) { - const shortname = entity.slice(2) - const new_mask = parseInt(entity.slice(0, 2), 16) + new_mask = parseInt(entity.slice(0, 2), 16) + const shortname_with_customname = entity.slice(2) + const shortname = shortname_with_customname.split('|')[0] + const new_custom_name = shortname_with_customname.split('|')[1] + + // find in de + de_objIndex = de.findIndex((obj) => obj.id == shortname) + if (de_objIndex !== -1) { + // find in dd, either looking for fullname or custom name + if (de[de_objIndex].cn) { + fullname = de[de_objIndex].cn + } else { + fullname = de[de_objIndex].n + } + dd_objIndex = dd.data.findIndex((obj) => obj.id.slice(2) == fullname) + if (dd_objIndex !== -1) { + let changed = new Boolean(false) - objIndex = de.findIndex((obj) => obj.id == shortname) - if (objIndex !== -1) { - de[objIndex].m = new_mask - const fullname = de[objIndex].n - objIndex = dd.data.findIndex((obj) => obj.id.slice(2) == fullname) - if (objIndex !== -1) { // see if the mask has changed - const old_mask = parseInt(dd.data[objIndex].id.slice(0, 2), 16) + const old_mask = parseInt(dd.data[dd_objIndex].id.slice(0, 2), 16) if (old_mask !== new_mask) { - const mask_hex = entity.slice(0, 2) - console.log('Updating ' + dd.data[objIndex].id + ' -> ' + mask_hex + fullname) - dd.data[objIndex].id = mask_hex + fullname + console.log('mask has changed') + new_mask = entity.slice(0, 2) + changed = true } + + // see if the custom name has changed + const old_custom_name = dd.data[dd_objIndex].cn + if (old_custom_name !== new_custom_name) { + console.log('name has changed') + changed = true + new_fullname = new_custom_name + } else { + new_fullname = fullname + } + + if (changed) { + console.log('Updating ' + dd.data[dd_objIndex].id + ' -> ' + new_mask + new_fullname) + de[de_objIndex].m = new_mask + de[de_objIndex].cn = new_fullname + dd.data[dd_objIndex].id = new_mask + new_fullname + } + + console.log('dd:') + console.log(dd.data[dd_objIndex]) + console.log('de:') + console.log(de[de_objIndex]) + } else { + console.log('error, not found') } } else { console.log("can't locate record for name " + shortname) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index dfd5b9f9b..fba59676d 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -259,8 +259,8 @@ bool EMSdevice::has_tag(const uint8_t tag) const { // called from the command 'entities' void EMSdevice::list_device_entries(JsonObject & output) const { for (const auto & dv : devicevalues_) { - auto fullname = Helpers::translated_fword(dv.fullname); - if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && fullname) { + std::string fullname = dv.get_fullname(); + if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && !fullname.empty()) { // if we have a tag prefix it char key[50]; if (!EMSdevice::tag_to_mqtt(dv.tag).empty()) { @@ -433,15 +433,51 @@ void EMSdevice::add_device_value(uint8_t tag, // determine state uint8_t state = DeviceValueState::DV_DEFAULT; + // custom fullname + std::string custom_fullname = std::string(""); + // scan through customizations to see if it's on the exclusion list by matching the productID and deviceID EMSESP::webCustomizationService.read([&](WebCustomization & settings) { for (EntityCustomization entityCustomization : settings.entityCustomizations) { if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) { std::string entity = tag < DeviceValueTAG::TAG_HC1 ? read_flash_string(short_name) : tag_to_string(tag) + "/" + read_flash_string(short_name); for (std::string entity_id : entityCustomization.entity_ids) { - if (entity_id.substr(2) == entity) { + // if there is an appended custom name, strip it to get the true entity name + // and extract the new custom name + std::string matched_entity; + auto custom_name_pos = entity_id.find('|'); + bool has_custom_name = (custom_name_pos != std::string::npos); + if (has_custom_name) { + matched_entity = entity_id.substr(2, custom_name_pos - 2); + } else { + matched_entity = entity_id.substr(2); + } + + /* + Serial.print(COLOR_BRIGHT_GREEN); + Serial.print(" entity name="); + Serial.print(entity_id.c_str()); + Serial.print(" ,matched name="); + Serial.print(matched_entity.c_str()); + Serial.print(" ,matched pos="); + Serial.println(custom_name_pos); + Serial.print(COLOR_RESET); + */ + + // we found the device entity + if (matched_entity == entity) { + // get Mask uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); state = mask << 4; // set state high bits to flag, turn off active and ha flags + // see if there is a custom name in the entity string + if (has_custom_name) { + /* + Serial.print(COLOR_BRIGHT_MAGENTA); + Serial.println(entity_id.substr(custom_name_pos + 1).c_str()); + Serial.print(COLOR_RESET); + */ + custom_fullname = entity_id.substr(custom_name_pos + 1); + } break; } } @@ -449,11 +485,6 @@ void EMSdevice::add_device_value(uint8_t tag, } }); - // TODO - const __FlashStringHelper * custom_fullname; - - custom_fullname = nullptr; - // add the device entity devicevalues_.emplace_back( device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state); @@ -475,6 +506,7 @@ void EMSdevice::add_device_value(uint8_t tag, // add the command to our library // cmd is the short_name and the description is the fullname + // TODO this needs adapting to take the custom fullname since its not a FPTR() Command::add(device_type_, short_name, f, Helpers::translated_fword(fullname), flags); } @@ -700,6 +732,8 @@ void EMSdevice::publish_value(void * value_p) const { } // looks up the UOM for a given key from the device value table +// key is the fullname +// TODO really should be using the shortname as the identifier std::string EMSdevice::get_value_uom(const char * key) const { // the key may have a TAG string prefixed at the beginning. If so, remove it char new_key[80]; @@ -719,8 +753,8 @@ std::string EMSdevice::get_value_uom(const char * key) const { // look up key in our device value list for (const auto & dv : devicevalues_) { - auto fullname = Helpers::translated_fword(dv.fullname); - if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && fullname) && (read_flash_string(fullname) == key_p)) { + auto fullname = dv.get_fullname(); + if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && !fullname.empty()) && (fullname == key_p)) { // ignore TIME since "minutes" is already added to the string value if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) { break; @@ -741,13 +775,13 @@ void EMSdevice::generate_values_web(JsonObject & output) { JsonArray data = output.createNestedArray("data"); for (auto & dv : devicevalues_) { - auto fullname = Helpers::translated_fword(dv.fullname); + auto fullname = dv.get_fullname(); // check conditions: // 1. fullname cannot be empty // 2. it must have a valid value, if it is not a command like 'reset' // 3. show favorites first - if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && fullname && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) { + if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && !fullname.empty() && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) { JsonObject obj = data.createNestedObject(); // create the object, we know there is a value uint8_t fahrenheit = 0; @@ -797,9 +831,9 @@ void EMSdevice::generate_values_web(JsonObject & output) { // add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { - obj["id"] = mask + Helpers::translated_word(dv.fullname); + obj["id"] = mask + dv.get_fullname(); } else { - obj["id"] = mask + tag_to_string(dv.tag) + " " + Helpers::translated_word(dv.fullname); + obj["id"] = mask + tag_to_string(dv.tag) + " " + dv.get_fullname(); } // add commands and options @@ -928,14 +962,14 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) { // n is the fullname, and can be optional // don't add the fullname if its a command - auto translated_full_name = Helpers::translated_fword(dv.fullname); + auto fullname = dv.get_fullname(); if (dv.type != DeviceValueType::CMD) { - if (translated_full_name) { + if (!fullname.empty()) { if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { - obj["n"] = read_flash_string(translated_full_name); + obj["n"] = fullname; } else { char name[50]; - snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(translated_full_name).c_str()); + snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), fullname.c_str()); obj["n"] = name; } } @@ -943,6 +977,11 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) { obj["n"] = ""; } + // add the custom name, is optional + if (!dv.custom_fullname.empty()) { + obj["cn"] = dv.custom_fullname; + } + obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble obj["w"] = dv.has_cmd; // if writable } @@ -1018,12 +1057,12 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8 json["name"] = dv.short_name; - auto fullname = Helpers::translated_fword(dv.fullname); - if (fullname != nullptr) { + auto fullname = dv.get_fullname(); + if (!fullname.empty()) { if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { json["fullname"] = fullname; } else { - json["fullname"] = tag_to_string(dv.tag) + " " + read_flash_string(fullname); + json["fullname"] = tag_to_string(dv.tag) + " " + fullname; } } @@ -1202,14 +1241,14 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c dv.remove_state(DeviceValueState::DV_ACTIVE); } - auto fullname = Helpers::translated_fword(dv.fullname); + auto fullname = dv.get_fullname(); // check conditions: // 1. it must have a valid value (state is active) // 2. it must have a visible flag // 3. it must match the given tag filter or have an empty tag // 4. it must not have the exclude flag set or outputs to console - if (dv.has_state(DeviceValueState::DV_ACTIVE) && fullname && (tag_filter == DeviceValueTAG::TAG_NONE || tag_filter == dv.tag) + if (dv.has_state(DeviceValueState::DV_ACTIVE) && !fullname.empty() && (tag_filter == DeviceValueTAG::TAG_NONE || tag_filter == dv.tag) && (output_target == OUTPUT_TARGET::CONSOLE || !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE))) { has_values = true; // flagged if we actually have data @@ -1221,9 +1260,9 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) { if (have_tag) { - snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(fullname).c_str()); // prefix the tag + snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), fullname.c_str()); // prefix the tag } else { - strlcpy(name, read_flash_string(fullname).c_str(), sizeof(name)); // use full name + strlcpy(name, fullname.c_str(), sizeof(name)); // use full name } } else { strlcpy(name, read_flash_string(dv.short_name).c_str(), sizeof(name)); // use short name diff --git a/src/emsdevicevalue.cpp b/src/emsdevicevalue.cpp index 7edbf2dc3..d40a97d5c 100644 --- a/src/emsdevicevalue.cpp +++ b/src/emsdevicevalue.cpp @@ -32,12 +32,12 @@ DeviceValue::DeviceValue(uint8_t device_type, int8_t numeric_operator, const __FlashStringHelper * const short_name, const __FlashStringHelper * const * fullname, - const __FlashStringHelper * const custom_fullname, - uint8_t uom, - bool has_cmd, - int16_t min, - uint16_t max, - uint8_t state) + const std::string & custom_fullname, + uint8_t uom, + bool has_cmd, + int16_t min, + uint16_t max, + uint8_t state) : device_type(device_type) , tag(tag) , value_p(value_p) @@ -65,8 +65,13 @@ DeviceValue::DeviceValue(uint8_t device_type, Serial.print("registering entity: "); Serial.print(read_flash_string(short_name).c_str()); Serial.print("/"); - auto trans_fullname = Helpers::translated_word(fullname); - Serial.print(trans_fullname.c_str()); + if (!custom_fullname.empty()) { + Serial.print(COLOR_BRIGHT_CYAN); + Serial.print(custom_fullname.c_str()); + Serial.print(COLOR_RESET); + } else { + Serial.print(Helpers::translated_word(fullname).c_str()); + } Serial.print(" (#options="); Serial.print(options_size); Serial.print(",numop="); @@ -321,4 +326,13 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, int16_t & dv_set_max) { return false; // nothing changed, not supported } +// returns the translated fullname or the custom fullname (if provided) +// always returns a std::string +std::string DeviceValue::get_fullname() const { + if (custom_fullname.empty()) { + return Helpers::translated_word(fullname); + } + return custom_fullname; +} + } // namespace emsesp diff --git a/src/emsdevicevalue.h b/src/emsdevicevalue.h index 490092fc3..a3419262e 100644 --- a/src/emsdevicevalue.h +++ b/src/emsdevicevalue.h @@ -153,7 +153,7 @@ class DeviceValue { uint8_t options_size; // number of options in the char array, calculated const __FlashStringHelper * const short_name; // used in MQTT and API const __FlashStringHelper * const * fullname; // used in Web and Console, is translated - const __FlashStringHelper * const custom_fullname; // optional, from customization + const std::string custom_fullname; // optional, from customization uint8_t uom; // DeviceValueUOM::* bool has_cmd; // true if there is a Console/MQTT command which matches the short_name int16_t min; // min range @@ -169,7 +169,7 @@ class DeviceValue { int8_t numeric_operator, const __FlashStringHelper * const short_name, const __FlashStringHelper * const * fullname, - const __FlashStringHelper * const custom_fullname, + const std::string & custom_fullname, uint8_t uom, bool has_cmd, int16_t min, @@ -179,6 +179,8 @@ class DeviceValue { bool hasValue() const; bool get_min_max(int16_t & dv_set_min, int16_t & dv_set_max); + std::string get_fullname() const; + // dv state flags void add_state(uint8_t s) { state |= s; diff --git a/src/mqtt.cpp b/src/mqtt.cpp index fd86c4d24..bf2bd1850 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -937,7 +937,7 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, publish_ha_sensor_config(dv.type, dv.tag, - Helpers::translated_fword(dv.fullname), + dv.get_fullname(), dv.device_type, dv.short_name, dv.uom, @@ -958,7 +958,9 @@ void Mqtt::publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelp JsonArray ids = dev_json.createNestedArray("ids"); ids.add("ems-esp"); - publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json); + auto fullname = read_flash_string(name); // TODO make sure it works, it may need a std::move()? + + publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, fullname, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json); } // MQTT discovery configs @@ -966,7 +968,7 @@ void Mqtt::publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelp // note: some extra string copying done here, it looks messy but does help with heap fragmentation issues void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType uint8_t tag, // EMSdevice::DeviceValueTAG - const __FlashStringHelper * const fullname, // fullname, already translated + const std::string & fullname, // fullname, already translated const uint8_t device_type, // EMSdevice::DeviceType const __FlashStringHelper * const entity, // same as shortname const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE) @@ -978,7 +980,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, const int16_t dv_set_max, const JsonObject & dev_json) { // ignore if name (fullname) is empty - if (fullname == nullptr) { + if (fullname.empty()) { return; } @@ -1114,9 +1116,9 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // friendly name = char ha_name[70]; if (have_tag) { - snprintf(ha_name, sizeof(ha_name), "%s %s", EMSdevice::tag_to_string(tag).c_str(), read_flash_string(fullname).c_str()); + snprintf(ha_name, sizeof(ha_name), "%s %s", EMSdevice::tag_to_string(tag).c_str(), fullname.c_str()); } else { - snprintf(ha_name, sizeof(ha_name), "%s", read_flash_string(fullname).c_str()); + snprintf(ha_name, sizeof(ha_name), "%s", fullname.c_str()); } ha_name[0] = toupper(ha_name[0]); // capitalize first letter doc["name"] = ha_name; diff --git a/src/mqtt.h b/src/mqtt.h index 0ab724227..6d107cb89 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -94,7 +94,7 @@ class Mqtt { publish_ha_sensor_config(DeviceValue & dv, const std::string & model, const std::string & brand, const bool remove, const bool create_device_config = false); static void publish_ha_sensor_config(uint8_t type, uint8_t tag, - const __FlashStringHelper * const fullname, + const std::string & fullname, const uint8_t device_type, const __FlashStringHelper * const entity, const uint8_t uom, diff --git a/src/test/test.cpp b/src/test/test.cpp index 81de3686d..6a553d902 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -399,7 +399,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const #if defined(EMSESP_STANDALONE) - DynamicJsonDocument doc(8000); // some absurb high number + DynamicJsonDocument doc(8000); // some absurd high number for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { doc.clear(); diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 5673d285d..a8f5cef9f 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -95,11 +95,27 @@ void WebCustomization::read(WebCustomization & settings, JsonObject & root) { // call on initialization and also when the page is saved via web UI // this loads the data into the internal class StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings) { +#ifdef EMSESP_STANDALONE + // invoke some fake data for testing + // using https://arduinojson.org/v5/assistant/ + const char * json = + "{\"sensors\":[],\"analogs\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom name for heating active\",\"08tapwateractive\"]}]}"; + + StaticJsonDocument<500> doc; + deserializeJson(doc, json); + root = doc.as(); + + Serial.println(COLOR_BRIGHT_MAGENTA); + Serial.print("Using custom file: "); + serializeJson(root, Serial); + Serial.println(COLOR_RESET); +#endif + // Dallas Sensor customization settings.sensorCustomizations.clear(); if (root["sensors"].is()) { for (const JsonObject sensorJson : root["sensors"].as()) { - // create each of the sensor, overwritting any previous settings + // create each of the sensor, overwriting any previous settings auto sensor = SensorCustomization(); sensor.id = sensorJson["id"].as(); sensor.name = sensorJson["name"].as(); @@ -112,7 +128,7 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings.analogCustomizations.clear(); if (root["analogs"].is()) { for (const JsonObject analogJson : root["analogs"].as()) { - // create each of the sensor, overwritting any previous settings + // create each of the sensor, overwriting any previous settings auto sensor = AnalogCustomization(); sensor.gpio = analogJson["gpio"]; sensor.name = analogJson["name"].as(); @@ -160,7 +176,7 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques #endif } -// send back a list of devices used to the customization web page +// send back a list of devices used in the customization web page void WebCustomizationService::devices(AsyncWebServerRequest * request) { auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN); JsonObject root = response->getRoot(); @@ -173,19 +189,7 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) { JsonObject obj = devices.createNestedObject(); obj["i"] = emsdevice->unique_id(); // its unique id obj["s"] = emsdevice->device_type_name() + " (" + emsdevice->name() + ")"; // shortname - - // device type name. We may have one than one (e.g. multiple thermostats) so postfix name with index - // code block not needed - see https://github.com/emsesp/EMS-ESP32/pull/586#issuecomment-1193779668 - /* - uint8_t device_index = EMSESP::device_index(emsdevice->device_type(), emsdevice->unique_id()); - if (device_index) { - char s[10]; - obj["t"] = Helpers::toLower(emsdevice->device_type_name()) + Helpers::smallitoa(s, device_index); - } else { - obj["t"] = Helpers::toLower(emsdevice->device_type_name()); - } - */ - obj["t"] = Helpers::toLower(emsdevice->device_type_name()); + obj["t"] = Helpers::toLower(emsdevice->device_type_name()); } }