diff --git a/.github/workflows/sonar_check.yml b/.github/workflows/sonar_check.yml index c0ea0e1bb..b638df864 100644 --- a/.github/workflows/sonar_check.yml +++ b/.github/workflows/sonar_check.yml @@ -9,9 +9,10 @@ jobs: build: name: Build runs-on: ubuntu-latest + if: github.repository_owner == 'emsesp' + # if: github.repository == 'emsesp/EMS-ESP32' env: # https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/ - # SONAR_SCANNER_VERSION: 4.6.1.2450 SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index cae9e2969..8be0d7d92 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -12,5 +12,6 @@ - controller data in web-ui only for IVT [#522](https://github.com/emsesp/EMS-ESP32/issues/522) - rename hidden `climate` to a more explaining name [#523](https://github.com/emsesp/EMS-ESP32/issues/523) +- Minor changes to the Customizations web page [#527](https://github.com/emsesp/EMS-ESP32/pull/527) ## **BREAKING CHANGES:** diff --git a/interface/package-lock.json b/interface/package-lock.json index 1f1e0d019..ec5446a98 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -13,9 +13,9 @@ "@msgpack/msgpack": "^2.7.2", "@mui/icons-material": "^5.8.0", "@mui/material": "^5.8.1", - "@table-library/react-table-library": "^3.1.2", + "@table-library/react-table-library": "^3.1.4", "@types/lodash": "^4.14.182", - "@types/node": "^17.0.35", + "@types/node": "^17.0.36", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.5", "@types/react-router-dom": "^5.3.3", @@ -3689,16 +3689,15 @@ } }, "node_modules/@table-library/react-table-library": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-3.1.2.tgz", - "integrity": "sha512-zfjIvcEP114KFh5WyZzbPQHtaxiSr0LHHgm+A8El6YG/SQ7D+i88RUdwiBu3uuKXTY7OV+5v/GESEiioZo150w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-3.1.4.tgz", + "integrity": "sha512-6JgQJLWgkMkdVzB/gGU/7BuMRGhQkkbg150YXiq+2U2ICyV773JPW5oxE5QaQJZUsDGlcS1FuaK/UFJVLZWs4A==", "dependencies": { "clsx": "1.1.1", "react-virtualized-auto-sizer": "1.0.6", - "react-window": "1.8.6" + "react-window": "1.8.7" }, "peerDependencies": { - "@emotion/react": ">= 11", "react": ">=16.8.0", "react-dom": ">=16.8.0" } @@ -3715,22 +3714,6 @@ "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0" } }, - "node_modules/@table-library/react-table-library/node_modules/react-window": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz", - "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - }, - "engines": { - "node": ">8.0.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -3930,9 +3913,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", - "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -15026,6 +15009,22 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-window": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz", + "integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -20452,13 +20451,13 @@ } }, "@table-library/react-table-library": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-3.1.2.tgz", - "integrity": "sha512-zfjIvcEP114KFh5WyZzbPQHtaxiSr0LHHgm+A8El6YG/SQ7D+i88RUdwiBu3uuKXTY7OV+5v/GESEiioZo150w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-3.1.4.tgz", + "integrity": "sha512-6JgQJLWgkMkdVzB/gGU/7BuMRGhQkkbg150YXiq+2U2ICyV773JPW5oxE5QaQJZUsDGlcS1FuaK/UFJVLZWs4A==", "requires": { "clsx": "1.1.1", "react-virtualized-auto-sizer": "1.0.6", - "react-window": "1.8.6" + "react-window": "1.8.7" }, "dependencies": { "react-virtualized-auto-sizer": { @@ -20466,15 +20465,6 @@ "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.6.tgz", "integrity": "sha512-7tQ0BmZqfVF6YYEWcIGuoR3OdYe8I/ZFbNclFlGOC3pMqunkYF/oL30NCjSGl9sMEb17AnzixDz98Kqc3N76HQ==", "requires": {} - }, - "react-window": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz", - "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==", - "requires": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - } } } }, @@ -20671,9 +20661,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/node": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", - "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "@types/parse-json": { "version": "4.0.0", @@ -28628,6 +28618,15 @@ "prop-types": "^15.6.2" } }, + "react-window": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz", + "integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==", + "requires": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", diff --git a/interface/package.json b/interface/package.json index b4bf9d9c3..bfee5c32d 100644 --- a/interface/package.json +++ b/interface/package.json @@ -9,9 +9,9 @@ "@msgpack/msgpack": "^2.7.2", "@mui/icons-material": "^5.8.0", "@mui/material": "^5.8.1", - "@table-library/react-table-library": "^3.1.2", + "@table-library/react-table-library": "^3.1.4", "@types/lodash": "^4.14.182", - "@types/node": "^17.0.35", + "@types/node": "^17.0.36", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.5", "@types/react-router-dom": "^5.3.3", diff --git a/interface/src/project/DashboardData.tsx b/interface/src/project/DashboardData.tsx index 5b4bd827d..5d8ae278c 100644 --- a/interface/src/project/DashboardData.tsx +++ b/interface/src/project/DashboardData.tsx @@ -384,7 +384,10 @@ const DashboardData: FC = () => { const handleDownloadCsv = () => { const columns = [ { accessor: (dv: any) => dv.id.slice(2), name: 'Entity' }, - { accessor: (dv: any) => (typeof dv.v === 'number') ? new Intl.NumberFormat().format(dv.v) : dv.v, name: 'Value' }, + { + accessor: (dv: any) => (typeof dv.v === 'number' ? new Intl.NumberFormat().format(dv.v) : dv.v), + name: 'Value' + }, { accessor: (dv: any) => DeviceValueUOM_s[dv.u], name: 'UoM' } ]; downloadAsCsv( @@ -449,7 +452,7 @@ const DashboardData: FC = () => { } }; - const isCmdOnly = (dv: DeviceValue) => dv.v === undefined && dv.c; + const isCmdOnly = (dv: DeviceValue) => dv.v === '' && dv.c; function formatValue(value: any, uom: number) { if (value === undefined) { diff --git a/interface/src/project/OptionIcon.tsx b/interface/src/project/OptionIcon.tsx new file mode 100644 index 000000000..71be44969 --- /dev/null +++ b/interface/src/project/OptionIcon.tsx @@ -0,0 +1,39 @@ +import { FC } from 'react'; +import { SvgIconProps } from '@mui/material'; + +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; + +import StarIcon from '@mui/icons-material/Star'; +import StarOutlineIcon from '@mui/icons-material/StarOutline'; + +import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; + +import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; +import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; + +type OptionType = 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite'; + +const OPTION_ICONS: { [type in OptionType]: [React.ComponentType, React.ComponentType] } = { + readonly: [EditOffOutlinedIcon, EditOutlinedIcon], + web_exclude: [VisibilityOffOutlinedIcon, VisibilityOutlinedIcon], + api_mqtt_exclude: [CommentsDisabledOutlinedIcon, InsertCommentOutlinedIcon], + favorite: [StarIcon, StarOutlineIcon] +}; + +interface OptionIconProps { + type: OptionType; + isSet: boolean; +} + +const OptionIcon: FC = ({ type, isSet }) => { + const Icon = OPTION_ICONS[type][isSet ? 0 : 1]; + return isSet ? ( + + ) : ( + + ); +}; + +export default OptionIcon; diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index a5cac3aae..f1527a6dc 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -25,10 +25,12 @@ import { useSnackbar } from 'notistack'; import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Cancel'; -import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; -import StarIcon from '@mui/icons-material/Star'; -import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; -import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; + +// import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; +// import StarIcon from '@mui/icons-material/Star'; +// import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; +// import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; + import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore'; import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined'; import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined'; @@ -36,6 +38,8 @@ import UnfoldMoreOutlinedIcon from '@mui/icons-material/UnfoldMoreOutlined'; import SearchIcon from '@mui/icons-material/Search'; import FilterListIcon from '@mui/icons-material/FilterList'; +import OptionIcon from './OptionIcon'; + import { ButtonRow, FormLoader, ValidatedTextField, SectionContent } from '../components'; import * as EMSESP from './api'; @@ -47,7 +51,7 @@ import { DeviceShort, Devices, DeviceEntity, DeviceEntityMask } from './types'; const SettingsCustomization: FC = () => { const { enqueueSnackbar } = useSnackbar(); - const [deviceEntities, setDeviceEntities] = useState([{ id: '', v: 0, s: '', m: 0, w: false }]); + const [deviceEntities, setDeviceEntities] = useState([{ id: '', v: 0, n: '', m: 0, w: false }]); const [devices, setDevices] = useState(); const [errorMessage, setErrorMessage] = useState(); const [selectedDevice, setSelectedDevice] = useState(0); @@ -199,6 +203,15 @@ const SettingsCustomization: FC = () => { return value; } + function formatName(de: DeviceEntity) { + if (de.n === undefined || de.n === de.id) { + return de.id; + } else if (de.n === '') { + return 'Command: ' + de.id; + } + return de.n + ' (' + de.id + ')'; + } + const getMaskNumber = (newMask: string[]) => { var new_mask = 0; for (let entry of newMask) { @@ -272,7 +285,7 @@ const SettingsCustomization: FC = () => { if (deviceEntities && selectedDevice) { const masked_entities = deviceEntities .filter((de) => de.m !== de.om) - .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.s); + .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.id); if (masked_entities.length > 60) { enqueueSnackbar('Selected entities exceeded limit of 60. Please Save in batches', { variant: 'warning' }); @@ -303,8 +316,18 @@ const SettingsCustomization: FC = () => { return ( <> - - Select a device and customize each of its entities using the options. + + Select a device and customize each of its entities using the options: + + + =mark as a favorite   + + =disable write action   + + =exclude from MQTT and API outputs   + + =hide from Web Dashboard + { }} /> - - : - + + + : + + { }} > - - - + - - - + - - - + - - - + - - : - - - + - + @@ -458,29 +475,44 @@ const SettingsCustomization: FC = () => { 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(['']); }} > - - + + - + - - + + - - + + - - {de.id} ({de.s}) - + {formatName(de)} {formatValue(de.v)} ))} diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index 9c1cfecef..c0f115a16 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -132,9 +132,9 @@ export interface DeviceData { } export interface DeviceEntity { - id: string; // name - v: any; // value, in any format - s: string; // shortname + id: string; // shortname + v?: any; // value, in any format, optional + n?: string; // fullname, optional m: number; // mask om?: number; // original mask before edits w: boolean; // writeable diff --git a/mock-api/package-lock.json b/mock-api/package-lock.json index e7dd05f0a..5620b6cf0 100644 --- a/mock-api/package-lock.json +++ b/mock-api/package-lock.json @@ -14,7 +14,7 @@ "express": "^4.18.1", "express-sse": "^0.5.3", "nodemon": "^2.0.16", - "ws": "^8.6.0" + "ws": "^8.7.0" } }, "node_modules/@msgpack/msgpack": { @@ -1811,9 +1811,9 @@ } }, "node_modules/ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "engines": { "node": ">=10.0.0" }, @@ -3169,9 +3169,9 @@ } }, "ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "requires": {} }, "xdg-basedir": { diff --git a/mock-api/package.json b/mock-api/package.json index bdc44b1c3..b41cb5039 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -16,6 +16,6 @@ "express": "^4.18.1", "express-sse": "^0.5.3", "nodemon": "^2.0.16", - "ws": "^8.6.0" + "ws": "^8.7.0" } } diff --git a/mock-api/server.js b/mock-api/server.js index d6bc9fefc..0802ddcce 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -337,6 +337,7 @@ settings = { pbutton_gpio: 0, board_profile: 'S32', bool_format: 1, + bool_dashboard: 1, enum_format: 1, } @@ -473,7 +474,7 @@ const emsesp_devicedata_1 = { const emsesp_devicedata_2 = { label: 'Boiler: Nefit GBx72/Trendline/Cerapur/Greenstar Si/27i', data: [ - { v: 0, u: 0, id: '08reset', c: 'reset', l: ['-', 'maintenance', 'error'] }, + { v: '', u: 0, id: '08reset', c: 'reset', l: ['-', 'maintenance', 'error'] }, { v: 'false', u: 0, id: '08heating active' }, { v: 'false', u: 0, id: '04tapwater active' }, { v: 5, u: 1, id: '04selected flow temperature', c: 'selflowtemp' }, @@ -575,145 +576,161 @@ const emsesp_devicedata_4 = { ], } +// CUSTOMIZATION + const emsesp_deviceentities_1 = [ { v: '(0)', - id: 'error code', - s: 'errorcode', + n: 'error code', + id: 'errorcode', m: 0, w: false, }, { v: '14:54:39 06/06/2021', - id: 'date/time', - s: 'datetime', + n: 'date/time', + id: 'datetime', + m: 0, + w: false, + }, + { + v: 'test data', + n: 'test', + id: 'test', + m: 0, + w: false, + }, + { + v: 'roomTemp', + id: 'hc1/HA climate config creation', m: 0, w: false, }, { v: 18.2, - id: 'hc1 selected room temperature', - s: 'hc1/seltemp', + n: 'hc1 selected room temperature', + id: 'hc1/seltemp', m: 0, w: true, }, { v: 22.6, - id: 'hc1 current room temperature', - s: 'hc1/curtemp', + n: 'hc1 current room temperature', + id: 'hc1/curtemp', m: 0, w: false, }, { v: 'auto', - id: 'hc1 mode', - s: 'hc1/mode', + n: 'hc1 mode', + id: 'hc1/mode', m: 0, w: true, }, ] const emsesp_deviceentities_2 = [ - { v: false, id: 'heating active', s: 'heatingactive', m: 0, w: false }, - { v: false, id: 'tapwater active', s: 'tapwateractive', m: 0, w: false }, - { v: 5, id: 'selected flow temperature', s: 'selflowtemp', m: 0, w: true }, - { v: 0, id: 'burner selected max power', s: 'selburnpow', m: 0, w: true }, - { v: 0, id: 'heating pump modulation', s: 'heatingpumpmod', m: 0, w: false }, - { id: 'heating pump 2 modulation', s: 'heatingpump2mod', m: 0, w: false }, - { id: 'outside temperature', s: 'outdoortemp', m: 0, w: false }, - { v: 53, id: 'current flow temperature', s: 'curflowtemp', m: 0, w: false }, - { v: 51.8, id: 'return temperature', s: 'rettemp', m: 0, w: false }, - { id: 'mixing switch temperature', s: 'switchtemp', m: 0, w: false }, - { v: 1.3, id: 'system pressure', s: 'syspress', m: 0, w: false }, - { v: 54.6, id: 'actual boiler temperature', s: 'boiltemp', m: 0, w: false }, - { id: 'exhaust temperature', s: 'exhausttemp', m: 0, w: false }, - { v: false, id: 'gas', s: 'burngas', m: 0, w: false }, - { v: false, id: 'gas stage 2', s: 'burngas2', m: 0, w: false }, - { v: 0, id: 'flame current', s: 'flamecurr', m: 0, w: false }, - { v: false, id: 'heating pump', s: 'heatingpump', m: 0, w: false }, - { v: false, id: 'fan', s: 'fanwork', m: 0, w: false }, - { v: false, id: 'ignition', s: 'ignwork', m: 0, w: false }, - { v: false, id: 'oil preheating', s: 'oilpreheat', m: 0, w: false }, - { v: true, id: 'heating activated', s: 'heatingactivated', m: 0, w: false }, - { v: 80, id: 'heating temperature', s: 'heatingtemp', m: 0, w: false }, - { v: 70, id: 'burner pump max power', s: 'pumpmodmax', m: 0, w: false }, - { v: 30, id: 'burner pump min power', s: 'pumpmodmin', m: 0, w: false }, - { v: 1, id: 'pump delay', s: 'pumpdelay', m: 0, w: false }, - { v: 10, id: 'burner min period', s: 'burnminperiod', m: 0, w: false }, - { v: 0, id: 'burner min power', s: 'burnminpower', m: 0, w: false }, - { v: 50, id: 'burner max power', s: 'burnmaxpower', m: 0, w: false }, - { v: -6, id: 'hysteresis on temperature', s: 'boilhyston', m: 0, w: false }, - { v: 6, id: 'hysteresis off temperature', s: 'boilhystoff', m: 0, w: false }, - { v: 0, id: 'set flow temperature', s: 'setflowtemp', m: 0, w: true }, - { v: 0, id: 'burner set power', s: 'setburnpow', m: 0, w: false }, - { v: 0, id: 'burner current power', s: 'curburnpow', m: 0, w: false }, - { v: 326323, id: 'burner starts', s: 'burnstarts', m: 0, w: false }, - { v: 553437, id: 'total burner operating time', s: 'burnworkmin', m: 0, w: false }, - { v: 451286, id: 'total heat operating time', s: 'heatworkmin', m: 0, w: false }, - { v: 4672175, id: 'total UBA operating time', s: 'ubauptime', m: 0, w: false }, - { v: '1C(210) 06.06.2020 12:07 (0 min)', id: 'last error code', s: 'lastcode', m: 0, w: false }, - { v: '0H', id: 'service code', s: 'servicecode', m: 0, w: false }, - { v: 203, id: 'service code number', s: 'servicecodenumber', m: 0, w: false }, - { v: 'H00', id: 'maintenance message', s: 'maintenancemessage', m: 0, w: false }, - { v: 'manual', id: 'maintenance scheduled', s: 'maintenance', m: 0, w: false }, - { v: 6000, id: 'time to next maintenance', s: 'maintenancetime', m: 0, w: false }, - { v: '01.01.2012', id: 'next maintenance date', s: 'maintenancedate', m: 0, w: false }, - { v: true, id: 'dhw turn on/off', s: 'wwtapactivated', m: 0, w: false }, - { v: 62, id: 'dhw set temperature', s: 'wwsettemp', m: 0, w: false }, - { v: 60, id: 'dhw selected temperature', s: 'wwseltemp', m: 0, w: true }, - { id: 'dhw selected lower temperature', s: 'wwseltemplow', m: 2 }, - { id: 'dhw selected temperature for off', s: 'wwseltempoff', m: 2 }, - { id: 'dhw single charge temperature', s: 'wwseltempsingle', m: 2 }, - { v: 'flow', id: 'dhw type', s: 'wwtype', m: 0, w: false }, - { v: 'hot', id: 'dhw comfort', s: 'wwcomfort', m: 0, w: false }, - { v: 40, id: 'dhw flow temperature offset', s: 'wwflowtempoffset', m: 0, w: false }, - { v: 100, id: 'dhw max power', s: 'wwmaxpower', m: 0, w: false }, - { v: false, id: 'dhw circulation pump available', s: 'wwcircpump', m: 0, w: false }, - { v: '3-way valve', id: 'dhw charging type', s: 'wwchargetype', m: 0, w: false }, - { v: -5, id: 'dhw hysteresis on temperature', s: 'wwhyston', m: 0, w: false }, - { v: 0, id: 'dhw hysteresis off temperature', s: 'wwhystoff', m: 0, w: false }, - { v: 70, id: 'dhw disinfection temperature', s: 'wwdisinfectiontemp', m: 0, w: false }, - { v: 'off', id: 'dhw circulation pump mode', s: 'wwcircmode', m: 0, w: false }, - { v: false, id: 'dhw circulation active', s: 'wwcirc', m: 0, w: false }, - { v: 46.4, id: 'dhw current intern temperature', s: 'wwcurtemp', m: 0, w: false }, - { id: 'dhw current extern temperature', s: 'wwcurtemp2', m: 2 }, - { v: 0, id: 'dhw current tap water flow', s: 'wwcurflow', m: 0, w: false }, - { v: 46.3, id: 'dhw storage intern temperature', s: 'wwstoragetemp1', m: 0, w: false }, - { id: 'dhw storage extern temperature', s: 'wwstoragetemp2', m: 2 }, - { v: true, id: 'dhw activated', s: 'wwactivated', m: 0, w: false }, - { v: false, id: 'dhw one time charging', s: 'wwonetime', m: 0, w: false }, - { v: false, id: 'dhw disinfecting', s: 'wwdisinfecting', m: 0, w: false }, - { v: false, id: 'dhw charging', s: 'wwcharging', m: 0, w: false }, - { v: false, id: 'dhw recharging', s: 'wwrecharging', m: 0, w: false }, - { v: true, id: 'dhw temperature ok', s: 'wwtempok', m: 0, w: false }, - { v: false, id: 'dhw active', s: 'wwactive', m: 0, w: false }, - { v: true, id: 'dhw 3way valve active', s: 'ww3wayvalve', m: 0, w: false }, - { v: 0, id: 'dhw set pump power', s: 'wwsetpumppower', m: 0, w: true }, - { id: 'dhw mixer temperature', s: 'wwmixertemp', m: 2 }, - { id: 'dhw cylinder middle temperature (TS3)', s: 'wwcylmiddletemp', m: 2 }, - { v: 288768, id: 'dhw starts', s: 'wwstarts', m: 0, w: false }, - { v: 102151, id: 'dhw active time', s: 'wwworkm', m: 0, w: false }, + { u: 0, n: '', id: 'reset', m: 8, w: false }, + { v: false, n: 'heating active', id: 'heatingactive', m: 8, w: false }, + { v: false, n: 'tapwater active', id: 'tapwateractive', m: 4, w: false }, + { v: 5, n: 'selected flow temperature', id: 'selflowtemp', m: 4, w: true }, + { v: 0, n: 'burner selected max power', id: 'selburnpow', m: 14, w: true }, + { v: 0, n: 'heating pump modulation', id: 'heatingpumpmod', m: 0, w: false }, + { n: 'heating pump 2 modulation', id: 'heatingpump2mod', m: 0, w: false }, + { n: 'outside temperature', id: 'outdoortemp', m: 0, w: false }, + { v: 53, n: 'current flow temperature', id: 'curflowtemp', m: 0, w: false }, + { v: 51.8, n: 'return temperature', id: 'rettemp', m: 0, w: false }, + { n: 'mixing switch temperature', id: 'switchtemp', m: 0, w: false }, + { v: 1.3, n: 'system pressure', id: 'syspress', m: 0, w: false }, + { v: 54.6, n: 'actual boiler temperature', id: 'boiltemp', m: 0, w: false }, + { n: 'exhaust temperature', id: 'exhausttemp', m: 0, w: false }, + { v: false, n: 'gas', id: 'burngas', m: 0, w: false }, + { v: false, n: 'gas stage 2', id: 'burngas2', m: 0, w: false }, + { v: 0, n: 'flame current', id: 'flamecurr', m: 0, w: false }, + { v: false, n: 'heating pump', id: 'heatingpump', m: 0, w: false }, + { v: false, n: 'fan', id: 'fanwork', m: 0, w: false }, + { v: false, n: 'ignition', id: 'ignwork', m: 0, w: false }, + { v: false, n: 'oil preheating', id: 'oilpreheat', m: 0, w: false }, + { v: true, n: 'heating activated', id: 'heatingactivated', m: 0, w: false }, + { v: 80, n: 'heating temperature', id: 'heatingtemp', m: 0, w: false }, + { v: 70, n: 'burner pump max power', id: 'pumpmodmax', m: 0, w: false }, + { v: 30, n: 'burner pump min power', id: 'pumpmodmin', m: 0, w: false }, + { v: 1, n: 'pump delay', id: 'pumpdelay', m: 0, w: false }, + { v: 10, n: 'burner min period', id: 'burnminperiod', m: 0, w: false }, + { v: 0, n: 'burner min power', id: 'burnminpower', m: 0, w: false }, + { v: 50, n: 'burner max power', id: 'burnmaxpower', m: 0, w: false }, + { v: -6, n: 'hysteresis on temperature', id: 'boilhyston', m: 0, w: false }, + { v: 6, n: 'hysteresis off temperature', id: 'boilhystoff', m: 0, w: false }, + { v: 0, n: 'set flow temperature', id: 'setflowtemp', m: 0, w: true }, + { v: 0, n: 'burner set power', id: 'setburnpow', m: 0, w: false }, + { v: 0, n: 'burner current power', id: 'curburnpow', m: 0, w: false }, + { v: 326323, n: 'burner starts', id: 'burnstarts', m: 0, w: false }, + { v: 553437, n: 'total burner operating time', id: 'burnworkmin', m: 0, w: false }, + { v: 451286, n: 'total heat operating time', id: 'heatworkmin', m: 0, w: false }, + { v: 4672175, n: 'total UBA operating time', id: 'ubauptime', m: 0, w: false }, + { v: '1C(210) 06.06.2020 12:07 (0 min)', n: 'last error code', id: 'lastcode', m: 0, w: false }, + { v: '0H', n: 'service code', id: 'servicecode', m: 0, w: false }, + { v: 203, n: 'service code number', id: 'servicecodenumber', m: 0, w: false }, + { v: 'H00', n: 'maintenance message', id: 'maintenancemessage', m: 0, w: false }, + { v: 'manual', n: 'maintenance scheduled', id: 'maintenance', m: 0, w: false }, + { v: 6000, n: 'time to next maintenance', id: 'maintenancetime', m: 0, w: false }, + { v: '01.01.2012', n: 'next maintenance date', id: 'maintenancedate', m: 0, w: false }, + { v: true, n: 'dhw turn on/off', id: 'wwtapactivated', m: 0, w: false }, + { v: 62, n: 'dhw set temperature', id: 'wwsettemp', m: 0, w: false }, + { v: 60, n: 'dhw selected temperature', id: 'wwseltemp', m: 0, w: true }, + { n: 'dhw selected lower temperature', id: 'wwseltemplow', m: 2 }, + { n: 'dhw selected temperature for off', id: 'wwseltempoff', m: 2 }, + { n: 'dhw single charge temperature', id: 'wwseltempsingle', m: 2 }, + { v: 'flow', n: 'dhw type', id: 'wwtype', m: 0, w: false }, + { v: 'hot', n: 'dhw comfort', id: 'wwcomfort', m: 0, w: false }, + { v: 40, n: 'dhw flow temperature offset', id: 'wwflowtempoffset', m: 0, w: false }, + { v: 100, n: 'dhw max power', id: 'wwmaxpower', m: 0, w: false }, + { v: false, n: 'dhw circulation pump available', id: 'wwcircpump', m: 0, w: false }, + { v: '3-way valve', n: 'dhw charging type', id: 'wwchargetype', m: 0, w: false }, + { v: -5, n: 'dhw hysteresis on temperature', id: 'wwhyston', m: 0, w: false }, + { v: 0, n: 'dhw hysteresis off temperature', id: 'wwhystoff', m: 0, w: false }, + { v: 70, n: 'dhw disinfection temperature', id: 'wwdisinfectiontemp', m: 0, w: false }, + { v: 'off', n: 'dhw circulation pump mode', id: 'wwcircmode', m: 0, w: false }, + { v: false, n: 'dhw circulation active', id: 'wwcirc', m: 0, w: false }, + { v: 46.4, n: 'dhw current intern temperature', id: 'wwcurtemp', m: 0, w: false }, + { n: 'dhw current extern temperature', id: 'wwcurtemp2', m: 2 }, + { v: 0, n: 'dhw current tap water flow', id: 'wwcurflow', m: 0, w: false }, + { v: 46.3, n: 'dhw storage intern temperature', id: 'wwstoragetemp1', m: 0, w: false }, + { n: 'dhw storage extern temperature', id: 'wwstoragetemp2', m: 2 }, + { v: true, n: 'dhw activated', id: 'wwactivated', m: 0, w: false }, + { v: false, n: 'dhw one time charging', id: 'wwonetime', m: 0, w: false }, + { v: false, n: 'dhw disinfecting', id: 'wwdisinfecting', m: 0, w: false }, + { v: false, n: 'dhw charging', id: 'wwcharging', m: 0, w: false }, + { v: false, n: 'dhw recharging', id: 'wwrecharging', m: 0, w: false }, + { v: true, n: 'dhw temperature ok', id: 'wwtempok', m: 0, w: false }, + { v: false, n: 'dhw active', id: 'wwactive', m: 0, w: false }, + { v: true, n: 'dhw 3way valve active', id: 'ww3wayvalve', m: 0, w: false }, + { v: 0, n: 'dhw set pump power', id: 'wwsetpumppower', m: 0, w: true }, + { n: 'dhw mixer temperature', id: 'wwmixertemp', m: 2 }, + { n: 'dhw cylinder middle temperature (TS3)', id: 'wwcylmiddletemp', m: 2 }, + { v: 288768, n: 'dhw starts', id: 'wwstarts', m: 0, w: false }, + { v: 102151, n: 'dhw active time', id: 'wwworkm', m: 0, w: false }, ] const emsesp_deviceentities_4 = [ { v: 16, - id: 'hc2 selected room temperature', - s: 'hc2/seltemp', + n: 'hc2 selected room temperature', + id: 'hc2/seltemp', m: 8, w: true, }, { v: 18.5, - id: 'hc2 current room temperature', - s: 'hc2/curtemp', + n: 'hc2 current room temperature', + id: 'hc2/curtemp', m: 2, w: false, }, { v: 'off', - id: 'hc2 mode', - s: 'hc2/mode', + n: 'hc2 mode', + id: 'hc2/mode', m: 2, w: true, }, @@ -912,13 +929,13 @@ rest_server.post(EMSESP_DEVICEENTITIES_ENDPOINT, (req, res) => { }) function updateMask(entity, de, dd) { - const name = entity.slice(2) + const shortname = entity.slice(2) const new_mask = parseInt(entity.slice(0, 2), 16) - objIndex = de.findIndex((obj) => obj.s == name) + objIndex = de.findIndex((obj) => obj.id == shortname) if (objIndex !== -1) { de[objIndex].m = new_mask - const fullname = de[objIndex].id + const fullname = de[objIndex].n objIndex = dd.data.findIndex((obj) => obj.id.slice(2) == fullname) if (objIndex !== -1) { // see if the mask has changed @@ -930,7 +947,7 @@ function updateMask(entity, de, dd) { } } } else { - console.log("can't locate record for id " + id) + console.log("can't locate record for name " + shortname) } } diff --git a/pio_local.ini_example b/pio_local.ini_example index 9d7c4cd8d..9c2db40f8 100644 --- a/pio_local.ini_example +++ b/pio_local.ini_example @@ -32,5 +32,5 @@ build_type = debug monitor_filters = esp32_exception_decoder debug_tool = esp-prog debug_init_break = tbreak setup -build_flags = ${factory_settings.build_flags} ${common.debug_flags} -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1 +build_flags = ${factory_settings.build_flags} -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1 extra_scripts = pre:scripts/build_interface.py diff --git a/src/devices/controller.cpp b/src/devices/controller.cpp index a60912629..e8e7af228 100644 --- a/src/devices/controller.cpp +++ b/src/devices/controller.cpp @@ -37,7 +37,7 @@ void Controller::process_dateTime(std::shared_ptr telegram) { return; } char newdatetime[sizeof(dateTime_)]; - // publich as dd.mm.yyyy hh:mmF + // publish as dd.mm.yyyy hh:mmF snprintf(newdatetime, sizeof(dateTime_), "%02d.%02d.%04d %02d:%02d", @@ -49,5 +49,4 @@ void Controller::process_dateTime(std::shared_ptr telegram) { has_update(dateTime_, newdatetime, sizeof(dateTime_)); } - } // namespace emsesp \ No newline at end of file diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 29d2759a2..92561391a 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -716,11 +716,9 @@ void EMSdevice::generate_values_web(JsonObject & output) { auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string - // add name, prefixing the tag if it exists. This is the id used for the table sorting + // 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 + read_flash_string(dv.full_name); - } else if (dv.tag < DeviceValueTAG::TAG_HC1) { - obj["id"] = mask + tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name); } else { obj["id"] = mask + tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name); } @@ -775,7 +773,7 @@ void EMSdevice::generate_values_web(JsonObject & output) { // as generate_values_web() but stripped down to only show all entities and their state // this is used only for WebCustomizationService::device_entities() -void EMSdevice::generate_values_web_all(JsonArray & output) { +void EMSdevice::generate_values_web_customization(JsonArray & output) { for (const auto & dv : devicevalues_) { // also show commands and entities that have an empty full name JsonObject obj = output.createNestedObject(); @@ -831,29 +829,29 @@ void EMSdevice::generate_values_web_all(JsonArray & output) { obj["v"] = (divider > 0) ? time_value / divider : time_value * factor; // sometimes we need to divide by 60 } } - } else { - // must always have v for sorting to work in web - obj["v"] = ""; } - // add name, prefixing the tag if it exists as the id (key for table sorting) - if (dv.full_name) { - if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { - obj["id"] = dv.full_name; - } else { - char name[50]; - snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str()); - obj["id"] = name; - } + // id holds the shortname and must always have a value for the WebUI table to work + if (dv.tag >= DeviceValueTAG::TAG_HC1) { + obj["id"] = tag_to_string(dv.tag) + "/" + read_flash_string(dv.short_name); } else { - obj["id"] = ""; + obj["id"] = read_flash_string(dv.short_name); } - // shortname - if (dv.tag >= DeviceValueTAG::TAG_HC1) { - obj["s"] = tag_to_string(dv.tag) + "/" + read_flash_string(dv.short_name); + // n is the fullname, and can be optional + // don't add the fullname if its a command + if (dv.type != DeviceValueType::CMD) { + if (dv.full_name) { + if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { + obj["n"] = dv.full_name; + } else { + char name[50]; + snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str()); + obj["n"] = name; + } + } } else { - obj["s"] = dv.short_name; + obj["n"] = ""; } obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble diff --git a/src/emsdevice.h b/src/emsdevice.h index 567cfd8f8..603e353c8 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -200,7 +200,7 @@ class EMSdevice { enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE }; bool generate_values(JsonObject & output, const uint8_t tag_filter, const bool nested, const uint8_t output_target); void generate_values_web(JsonObject & output); - void generate_values_web_all(JsonArray & output); + void generate_values_web_customization(JsonArray & output); void register_device_value(uint8_t tag, void * value_p, diff --git a/src/locale_EN.h b/src/locale_EN.h index c6f0eb096..72ea00698 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -616,7 +616,7 @@ MAKE_PSTR_LIST(wwDailyHeating, F("wwdailyheating"), F("daily heating")) MAKE_PSTR_LIST(wwDailyHeatTime, F("wwdailyheattime"), F("daily heating time")) MAKE_PSTR_LIST(wwWhenModeOff, F("wwwhenmodeoff"), F("when thermostat mode off")) // thermostat hc -MAKE_PSTR_LIST(climate, F("ha_climate_config_creation")) // no full-name, hidden, only for creation +MAKE_PSTR_LIST(climate, F("HA climate config creation")) // no full-name, hidden, only for creation MAKE_PSTR_LIST(selRoomTemp, F("seltemp"), F("selected room temperature")) MAKE_PSTR_LIST(roomTemp, F("currtemp"), F("current room temperature")) MAKE_PSTR_LIST(mode, F("mode"), F("mode")) diff --git a/src/test/test.cpp b/src/test/test.cpp index 3d320bb47..2b77622f1 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -418,7 +418,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // emsdevice->generate_values_web(root); JsonArray output = doc.to(); - emsdevice->generate_values_web_all(output); + emsdevice->generate_values_web_customization(output); Serial.print(COLOR_BRIGHT_MAGENTA); serializeJson(doc, Serial); diff --git a/src/version.h b/src/version.h index 7ab149645..46af4d0ea 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.4.1b0" +#define EMSESP_APP_VERSION "3.4.1b1" diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 2634030fd..9c9f16f20 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -196,7 +196,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request, J if (emsdevice->unique_id() == json["id"]) { #ifndef EMSESP_STANDALONE JsonArray output = response->getRoot(); - emsdevice->generate_values_web_all(output); + emsdevice->generate_values_web_customization(output); #endif response->setLength(); request->send(response);