Merge branch 'dev' into dev

This commit is contained in:
pswid
2022-10-11 14:10:30 +02:00
committed by GitHub
110 changed files with 3715 additions and 4216 deletions

View File

@@ -4,13 +4,15 @@
## Added
- Translations in Web UI and all device entity names to German. [#22](https://github.com/emsesp/EMS-ESP32/issues/22)
- Translations in Web UI and all device entity names (DE, NL, SE, PL, NO, ...) [#22](https://github.com/emsesp/EMS-ESP32/issues/22)
- Add support for Lolin C3 mini [#620](https://github.com/emsesp/EMS-ESP32/pull/620)
- Add Greenstar 30Ri boiler
- Add support for ESP32-S2 [#667](https://github.com/emsesp/EMS-ESP32/pull/667)
- Add devices: Greenstar 30Ri boiler, Junkers FW500 thermostat, Buderus BC30 controller
- Add program memory info
- Add mqtt queue and connection infos
- Add min/max setting to customizations
- Adapt min/max if ems-value is not in this range
- Add heatpump settings for inputs and limits
- Add heat pump settings for inputs and limits
## Fixed
@@ -20,8 +22,9 @@
- Discovery in HomeAssistant don't work with custom base topic. [#596](https://github.com/emsesp/EMS-ESP32/issues/596) Base topic containing `/` are changed to `_`
- RF room temperature sensor are shown as thermostat
- render mqtt float json values with trailing zero
- removed flash strings
## **BREAKING CHANGES:**
- When upgrading from 3.4.x you may need to erase the flash on the ESP32 before uploading the firmware. Make sure you make a backup of the settings and customizations via the WebUI (System->Upload/Download)

View File

@@ -1,5 +1,5 @@
{
"adapter": "react",
"baseLocale": "en",
"$schema": "https://unpkg.com/typesafe-i18n@5.13.0/schema/typesafe-i18n.json"
"$schema": "https://unpkg.com/typesafe-i18n@5.14.0/schema/typesafe-i18n.json"
}

View File

@@ -12,15 +12,15 @@
"@emotion/styled": "^11.10.4",
"@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.10.6",
"@mui/material": "^5.10.7",
"@table-library/react-table-library": "4.0.18",
"@types/lodash": "^4.14.185",
"@types/node": "^18.7.23",
"@mui/material": "^5.10.8",
"@table-library/react-table-library": "4.0.23",
"@types/lodash": "^4.14.186",
"@types/node": "^18.8.3",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5",
"axios": "^0.27.2",
"axios": "^1.1.2",
"http-proxy-middleware": "^2.0.6",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
@@ -31,10 +31,10 @@
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.2",
"react-icons": "^4.4.0",
"react-router-dom": "^6.4.1",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"sockette": "^2.0.6",
"typesafe-i18n": "^5.13.1",
"typesafe-i18n": "^5.14.0",
"typescript": "^4.8.4"
},
"devDependencies": {
@@ -2316,9 +2316,9 @@
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz",
"integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==",
"version": "0.10.7",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
"integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -3103,9 +3103,9 @@
}
},
"node_modules/@mui/base": {
"version": "5.0.0-alpha.99",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.99.tgz",
"integrity": "sha512-D04H6O1c0Jv561yI0SVbpa8MpqpW3G43CwJxV2o6ALfI0DMJ45w07dGafmDchb6aCWTRTdggd3rjgmuzyNwPiQ==",
"version": "5.0.0-alpha.100",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.100.tgz",
"integrity": "sha512-bSoJEKCENtmJrJDECHUe9PiqztIUACuSskyqw9ypqE7Dz3WxL3e8puFsWBkUsz+WOCjXh4B4Xljn88Ucxxv5HA==",
"dependencies": {
"@babel/runtime": "^7.19.0",
"@emotion/is-prop-valid": "^1.2.0",
@@ -3135,9 +3135,9 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.7.tgz",
"integrity": "sha512-3N0UYVy3MbrHzM3j6f7fIUCZ+bQ1/sSZq143tLxwSssW3Z4AqE83brpr5flEY1Lx+Aowv/cPyQMmZxzRlFCGqw==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.8.tgz",
"integrity": "sha512-V5D7OInO4P9PdT/JACg7fwjbOORm3GklaMVgdGomjyxiyetgRND5CC9r35e1LK/DqHdoyDuhbFzdfrqWtpmEIw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui"
@@ -3169,14 +3169,14 @@
}
},
"node_modules/@mui/material": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.7.tgz",
"integrity": "sha512-o1jcQGii+q7ORrXhBiMmGzFDaboc1qTgOOC3zDW+NR9ryVzWzL7qEeqoORbgDB5zk9OBsXCjB91fUH/ls5xMwg==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.8.tgz",
"integrity": "sha512-sF/Ka0IJjGXV52zoT4xAWEqXVRjNYbIjATo9L4Q5oQC5iJpGrKJFY16uNtWWB0+vp/nayAuPGZHrxtV+t3ecdQ==",
"dependencies": {
"@babel/runtime": "^7.19.0",
"@mui/base": "5.0.0-alpha.99",
"@mui/core-downloads-tracker": "^5.10.7",
"@mui/system": "^5.10.7",
"@mui/base": "5.0.0-alpha.100",
"@mui/core-downloads-tracker": "^5.10.8",
"@mui/system": "^5.10.8",
"@mui/types": "^7.2.0",
"@mui/utils": "^5.10.6",
"@types/react-transition-group": "^4.4.5",
@@ -3239,9 +3239,9 @@
}
},
"node_modules/@mui/styled-engine": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.7.tgz",
"integrity": "sha512-CCrtW+vvCKEm6pOE/QcutQ+ORC/iE6D1ghscN4l7LE2JXPvTXO/z0yu8Wxug1JEDlWm4r1Qa0PzJe1P9bjKzNA==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.8.tgz",
"integrity": "sha512-w+y8WI18EJV6zM/q41ug19cE70JTeO6sWFsQ7tgePQFpy6ToCVPh0YLrtqxUZXSoMStW5FMw0t9fHTFAqPbngw==",
"dependencies": {
"@babel/runtime": "^7.19.0",
"@emotion/cache": "^11.10.3",
@@ -3270,13 +3270,13 @@
}
},
"node_modules/@mui/system": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.7.tgz",
"integrity": "sha512-kwyhjjKGsgtBRFl6vSqidDZcNKU5S1juTgm4Xi2fyWxaEbIQb9Sh9y0iVP2bNCJzgDr0alLaENOZOEaDWHISAQ==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.8.tgz",
"integrity": "sha512-hRQ354zcrYP/KHqK8FheICSvE9raQaUgQaV+A3oD4JETaFUCVI9Ytt+RcQYgTqx02xlCXIjl8LK1rPjTneySqw==",
"dependencies": {
"@babel/runtime": "^7.19.0",
"@mui/private-theming": "^5.10.6",
"@mui/styled-engine": "^5.10.7",
"@mui/styled-engine": "^5.10.8",
"@mui/types": "^7.2.0",
"@mui/utils": "^5.10.6",
"clsx": "^1.2.1",
@@ -3470,9 +3470,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.1.tgz",
"integrity": "sha512-eBV5rvW4dRFOU1eajN7FmYxjAIVz/mRHgUE9En9mBn6m3mulK3WTR5C3iQhL9MZ14rWAq+xOlEaCkDiW0/heOg==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",
"integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==",
"engines": {
"node": ">=14"
}
@@ -3557,9 +3557,9 @@
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg=="
},
"node_modules/@sinclair/typebox": {
"version": "0.24.43",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.43.tgz",
"integrity": "sha512-1orQTvtazZmsPeBroJjysvsOQCYV2yjWlebkSY38pl5vr2tdLjEJ+LoxITlGNZaH2RE19WlAwQMkH/7C14wLfw=="
"version": "0.24.44",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.44.tgz",
"integrity": "sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg=="
},
"node_modules/@sinonjs/commons": {
"version": "1.8.3",
@@ -3796,9 +3796,9 @@
}
},
"node_modules/@table-library/react-table-library": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.18.tgz",
"integrity": "sha512-3GX5712YuWPFeUilr8UG22SCNR9BylD5Y4xPF+pHhb1QM7WXw6SLgpeGq6UDTQGOtmKtKtwKGWXdkmvVDg8pkA==",
"version": "4.0.23",
"resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.23.tgz",
"integrity": "sha512-o2L/fqhwQNxsNbbm3LIiyZzEwaTslhG1tY9ArkYdS0xJyRhJxcOLfbJ3+dcnOOn+aIJpmPmQH+gr7RYJC0P8uw==",
"dependencies": {
"clsx": "1.1.1",
"react-virtualized-auto-sizer": "1.0.6",
@@ -4019,9 +4019,9 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
"node_modules/@types/lodash": {
"version": "4.14.185",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.185.tgz",
"integrity": "sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA=="
"version": "4.14.186",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw=="
},
"node_modules/@types/mime": {
"version": "3.0.1",
@@ -4029,9 +4029,9 @@
"integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
},
"node_modules/@types/node": {
"version": "18.7.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.23.tgz",
"integrity": "sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg=="
"version": "18.8.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
"integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -5008,12 +5008,13 @@
}
},
"node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.2.tgz",
"integrity": "sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
@@ -5561,9 +5562,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001412",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz",
"integrity": "sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA==",
"version": "1.0.30001414",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz",
"integrity": "sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg==",
"funding": [
{
"type": "opencollective",
@@ -6755,9 +6756,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.266",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.266.tgz",
"integrity": "sha512-saJTYECxUSv7eSpnXw0XIEvUkP9x4s/x2mm3TVX7k4rIFS6f5TjBih1B5h437WzIhHQjid+d8ouQzPQskMervQ=="
"version": "1.4.270",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.270.tgz",
"integrity": "sha512-KNhIzgLiJmDDC444dj9vEOpZEgsV96ult9Iff98Vanumn+ShJHd5se8aX6KeVxdc0YQeqdrezBZv89rleDbvSg=="
},
"node_modules/emittery": {
"version": "0.8.1",
@@ -11430,9 +11431,9 @@
}
},
"node_modules/js-sdsl": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
"integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw=="
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
"integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
@@ -12853,9 +12854,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
"version": "8.4.17",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
"integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
"funding": [
{
"type": "opencollective",
@@ -14147,6 +14148,11 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -14545,11 +14551,11 @@
}
},
"node_modules/react-router": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.1.tgz",
"integrity": "sha512-OJASKp5AykDWFewgWUim1vlLr7yfD4vO/h+bSgcP/ix8Md+LMHuAjovA74MQfsfhQJGGN1nHRhwS5qQQbbBt3A==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz",
"integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==",
"dependencies": {
"@remix-run/router": "1.0.1"
"@remix-run/router": "1.0.2"
},
"engines": {
"node": ">=14"
@@ -14559,12 +14565,12 @@
}
},
"node_modules/react-router-dom": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.1.tgz",
"integrity": "sha512-MY7NJCrGNVJtGp8ODMOBHu20UaIkmwD2V3YsAOUQoCXFk7Ppdwf55RdcGyrSj+ycSL9Uiwrb3gTLYSnzcRoXww==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz",
"integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==",
"dependencies": {
"@remix-run/router": "1.0.1",
"react-router": "6.4.1"
"@remix-run/router": "1.0.2",
"react-router": "6.4.2"
},
"engines": {
"node": ">=14"
@@ -16420,9 +16426,9 @@
}
},
"node_modules/typesafe-i18n": {
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.13.1.tgz",
"integrity": "sha512-9Cgikxcj+2LAWQGZ8pOQAf14P+aOOcOel/G1kSAXAObA4Htj8R+qgCx9DqUnxjRfugWzM/19ZqGss7dl8Dw7Gg==",
"version": "5.14.0",
"resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.14.0.tgz",
"integrity": "sha512-ZNHysUvZZhmUuMjBvDGtUI8vT3g//4ay5fFOk2sJCsjx4ztippW1Hrhrq59nJ9mV/Q0u4OX80Gyorq8L3rwNLw==",
"bin": {
"typesafe-i18n": "cli/typesafe-i18n.mjs"
},
@@ -19062,9 +19068,9 @@
}
},
"@humanwhocodes/config-array": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz",
"integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==",
"version": "0.10.7",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
"integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -19643,9 +19649,9 @@
"integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ=="
},
"@mui/base": {
"version": "5.0.0-alpha.99",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.99.tgz",
"integrity": "sha512-D04H6O1c0Jv561yI0SVbpa8MpqpW3G43CwJxV2o6ALfI0DMJ45w07dGafmDchb6aCWTRTdggd3rjgmuzyNwPiQ==",
"version": "5.0.0-alpha.100",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.100.tgz",
"integrity": "sha512-bSoJEKCENtmJrJDECHUe9PiqztIUACuSskyqw9ypqE7Dz3WxL3e8puFsWBkUsz+WOCjXh4B4Xljn88Ucxxv5HA==",
"requires": {
"@babel/runtime": "^7.19.0",
"@emotion/is-prop-valid": "^1.2.0",
@@ -19658,9 +19664,9 @@
}
},
"@mui/core-downloads-tracker": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.7.tgz",
"integrity": "sha512-3N0UYVy3MbrHzM3j6f7fIUCZ+bQ1/sSZq143tLxwSssW3Z4AqE83brpr5flEY1Lx+Aowv/cPyQMmZxzRlFCGqw=="
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.8.tgz",
"integrity": "sha512-V5D7OInO4P9PdT/JACg7fwjbOORm3GklaMVgdGomjyxiyetgRND5CC9r35e1LK/DqHdoyDuhbFzdfrqWtpmEIw=="
},
"@mui/icons-material": {
"version": "5.10.6",
@@ -19671,14 +19677,14 @@
}
},
"@mui/material": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.7.tgz",
"integrity": "sha512-o1jcQGii+q7ORrXhBiMmGzFDaboc1qTgOOC3zDW+NR9ryVzWzL7qEeqoORbgDB5zk9OBsXCjB91fUH/ls5xMwg==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.8.tgz",
"integrity": "sha512-sF/Ka0IJjGXV52zoT4xAWEqXVRjNYbIjATo9L4Q5oQC5iJpGrKJFY16uNtWWB0+vp/nayAuPGZHrxtV+t3ecdQ==",
"requires": {
"@babel/runtime": "^7.19.0",
"@mui/base": "5.0.0-alpha.99",
"@mui/core-downloads-tracker": "^5.10.7",
"@mui/system": "^5.10.7",
"@mui/base": "5.0.0-alpha.100",
"@mui/core-downloads-tracker": "^5.10.8",
"@mui/system": "^5.10.8",
"@mui/types": "^7.2.0",
"@mui/utils": "^5.10.6",
"@types/react-transition-group": "^4.4.5",
@@ -19700,9 +19706,9 @@
}
},
"@mui/styled-engine": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.7.tgz",
"integrity": "sha512-CCrtW+vvCKEm6pOE/QcutQ+ORC/iE6D1ghscN4l7LE2JXPvTXO/z0yu8Wxug1JEDlWm4r1Qa0PzJe1P9bjKzNA==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.8.tgz",
"integrity": "sha512-w+y8WI18EJV6zM/q41ug19cE70JTeO6sWFsQ7tgePQFpy6ToCVPh0YLrtqxUZXSoMStW5FMw0t9fHTFAqPbngw==",
"requires": {
"@babel/runtime": "^7.19.0",
"@emotion/cache": "^11.10.3",
@@ -19711,13 +19717,13 @@
}
},
"@mui/system": {
"version": "5.10.7",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.7.tgz",
"integrity": "sha512-kwyhjjKGsgtBRFl6vSqidDZcNKU5S1juTgm4Xi2fyWxaEbIQb9Sh9y0iVP2bNCJzgDr0alLaENOZOEaDWHISAQ==",
"version": "5.10.8",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.8.tgz",
"integrity": "sha512-hRQ354zcrYP/KHqK8FheICSvE9raQaUgQaV+A3oD4JETaFUCVI9Ytt+RcQYgTqx02xlCXIjl8LK1rPjTneySqw==",
"requires": {
"@babel/runtime": "^7.19.0",
"@mui/private-theming": "^5.10.6",
"@mui/styled-engine": "^5.10.7",
"@mui/styled-engine": "^5.10.8",
"@mui/types": "^7.2.0",
"@mui/utils": "^5.10.6",
"clsx": "^1.2.1",
@@ -19819,9 +19825,9 @@
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
},
"@remix-run/router": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.1.tgz",
"integrity": "sha512-eBV5rvW4dRFOU1eajN7FmYxjAIVz/mRHgUE9En9mBn6m3mulK3WTR5C3iQhL9MZ14rWAq+xOlEaCkDiW0/heOg=="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",
"integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ=="
},
"@rollup/plugin-babel": {
"version": "5.3.1",
@@ -19877,9 +19883,9 @@
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg=="
},
"@sinclair/typebox": {
"version": "0.24.43",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.43.tgz",
"integrity": "sha512-1orQTvtazZmsPeBroJjysvsOQCYV2yjWlebkSY38pl5vr2tdLjEJ+LoxITlGNZaH2RE19WlAwQMkH/7C14wLfw=="
"version": "0.24.44",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.44.tgz",
"integrity": "sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg=="
},
"@sinonjs/commons": {
"version": "1.8.3",
@@ -20018,9 +20024,9 @@
}
},
"@table-library/react-table-library": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.18.tgz",
"integrity": "sha512-3GX5712YuWPFeUilr8UG22SCNR9BylD5Y4xPF+pHhb1QM7WXw6SLgpeGq6UDTQGOtmKtKtwKGWXdkmvVDg8pkA==",
"version": "4.0.23",
"resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.23.tgz",
"integrity": "sha512-o2L/fqhwQNxsNbbm3LIiyZzEwaTslhG1tY9ArkYdS0xJyRhJxcOLfbJ3+dcnOOn+aIJpmPmQH+gr7RYJC0P8uw==",
"requires": {
"clsx": "1.1.1",
"react-virtualized-auto-sizer": "1.0.6",
@@ -20223,9 +20229,9 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
"@types/lodash": {
"version": "4.14.185",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.185.tgz",
"integrity": "sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA=="
"version": "4.14.186",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw=="
},
"@types/mime": {
"version": "3.0.1",
@@ -20233,9 +20239,9 @@
"integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
},
"@types/node": {
"version": "18.7.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.23.tgz",
"integrity": "sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg=="
"version": "18.8.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
"integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"@types/parse-json": {
"version": "4.0.0",
@@ -20973,12 +20979,13 @@
"integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w=="
},
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.2.tgz",
"integrity": "sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"axobject-query": {
@@ -21399,9 +21406,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001412",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz",
"integrity": "sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA=="
"version": "1.0.30001414",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz",
"integrity": "sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg=="
},
"case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
@@ -22255,9 +22262,9 @@
}
},
"electron-to-chromium": {
"version": "1.4.266",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.266.tgz",
"integrity": "sha512-saJTYECxUSv7eSpnXw0XIEvUkP9x4s/x2mm3TVX7k4rIFS6f5TjBih1B5h437WzIhHQjid+d8ouQzPQskMervQ=="
"version": "1.4.270",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.270.tgz",
"integrity": "sha512-KNhIzgLiJmDDC444dj9vEOpZEgsV96ult9Iff98Vanumn+ShJHd5se8aX6KeVxdc0YQeqdrezBZv89rleDbvSg=="
},
"emittery": {
"version": "0.8.1",
@@ -25614,9 +25621,9 @@
}
},
"js-sdsl": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
"integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw=="
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
"integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q=="
},
"js-tokens": {
"version": "4.0.0",
@@ -26649,9 +26656,9 @@
}
},
"postcss": {
"version": "8.4.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
"version": "8.4.17",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
"integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@@ -27407,6 +27414,11 @@
}
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -27692,20 +27704,20 @@
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
},
"react-router": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.1.tgz",
"integrity": "sha512-OJASKp5AykDWFewgWUim1vlLr7yfD4vO/h+bSgcP/ix8Md+LMHuAjovA74MQfsfhQJGGN1nHRhwS5qQQbbBt3A==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz",
"integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==",
"requires": {
"@remix-run/router": "1.0.1"
"@remix-run/router": "1.0.2"
}
},
"react-router-dom": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.1.tgz",
"integrity": "sha512-MY7NJCrGNVJtGp8ODMOBHu20UaIkmwD2V3YsAOUQoCXFk7Ppdwf55RdcGyrSj+ycSL9Uiwrb3gTLYSnzcRoXww==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz",
"integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==",
"requires": {
"@remix-run/router": "1.0.1",
"react-router": "6.4.1"
"@remix-run/router": "1.0.2",
"react-router": "6.4.2"
}
},
"react-scripts": {
@@ -29096,9 +29108,9 @@
}
},
"typesafe-i18n": {
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.13.1.tgz",
"integrity": "sha512-9Cgikxcj+2LAWQGZ8pOQAf14P+aOOcOel/G1kSAXAObA4Htj8R+qgCx9DqUnxjRfugWzM/19ZqGss7dl8Dw7Gg==",
"version": "5.14.0",
"resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.14.0.tgz",
"integrity": "sha512-ZNHysUvZZhmUuMjBvDGtUI8vT3g//4ay5fFOk2sJCsjx4ztippW1Hrhrq59nJ9mV/Q0u4OX80Gyorq8L3rwNLw==",
"requires": {}
},
"typescript": {

View File

@@ -8,15 +8,15 @@
"@emotion/styled": "^11.10.4",
"@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.10.6",
"@mui/material": "^5.10.7",
"@table-library/react-table-library": "4.0.18",
"@types/lodash": "^4.14.185",
"@types/node": "^18.7.23",
"@mui/material": "^5.10.8",
"@table-library/react-table-library": "4.0.23",
"@types/lodash": "^4.14.186",
"@types/node": "^18.8.3",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5",
"axios": "^0.27.2",
"axios": "^1.1.2",
"http-proxy-middleware": "^2.0.6",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
@@ -27,10 +27,10 @@
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.2",
"react-icons": "^4.4.0",
"react-router-dom": "^6.4.1",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"sockette": "^2.0.6",
"typesafe-i18n": "^5.13.1",
"typesafe-i18n": "^5.14.0",
"typescript": "^4.8.4"
},
"scripts": {

View File

@@ -9,8 +9,6 @@ import * as AuthenticationApi from './api/authentication';
import { PROJECT_NAME } from './api/env';
import { AuthenticationContext } from './contexts/authentication';
import { AxiosError } from 'axios';
import { extractErrorMessage, onEnterCallback, updateValue } from './utils';
import { SignInRequest } from './types';
import { ValidatedTextField } from './components';
@@ -24,6 +22,8 @@ import { ReactComponent as NLflag } from './i18n/NL.svg';
import { ReactComponent as DEflag } from './i18n/DE.svg';
import { ReactComponent as GBflag } from './i18n/GB.svg';
import { ReactComponent as SEflag } from './i18n/SE.svg';
import { ReactComponent as PLflag } from './i18n/PL.svg';
import { ReactComponent as NOflag } from './i18n/NO.svg';
const SignIn: FC = () => {
const authenticationContext = useContext(AuthenticationContext);
@@ -56,8 +56,8 @@ const SignIn: FC = () => {
try {
const { data: loginResponse } = await AuthenticationApi.signIn(signInRequest);
authenticationContext.signIn(loginResponse.access_token);
} catch (error: unknown) {
if (error instanceof AxiosError) {
} catch (error) {
if (error.response) {
if (error.response?.status === 401) {
enqueueSnackbar(LL.INVALID_LOGIN(), { variant: 'warning' });
}
@@ -125,6 +125,14 @@ const SignIn: FC = () => {
<SEflag style={{ width: 24 }} />
&nbsp;SE
</Button>
<Button size="small" variant={locale === 'pl' ? 'contained' : 'outlined'} onClick={() => selectLocale('pl')}>
<PLflag style={{ width: 24 }} />
&nbsp;PL
</Button>
<Button size="small" variant={locale === 'no' ? 'contained' : 'outlined'} onClick={() => selectLocale('no')}>
<NOflag style={{ width: 24 }} />
&nbsp;NO
</Button>
</Box>
<ValidatedTextField

View File

@@ -1,4 +1,4 @@
import axios, { AxiosPromise, CancelToken } from 'axios';
import axios, { AxiosPromise, CancelToken, AxiosProgressEvent } from 'axios';
import { decode } from '@msgpack/msgpack';
@@ -89,7 +89,7 @@ function calculateEventSourceRoot(endpointPath: string) {
export interface FileUploadConfig {
cancelToken?: CancelToken;
onUploadProgress?: (progressEvent: ProgressEvent) => void;
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
}
export const startUploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise<void> => {

View File

@@ -93,7 +93,7 @@ const LayoutAuthMenu: FC = () => {
<PLflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;PL
</MenuItem>
<MenuItem key="no" value="no" disabled>
<MenuItem key="no" value="no">
<NOflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;NO
</MenuItem>

View File

@@ -1,6 +1,8 @@
import { FC, Fragment } from 'react';
import { useDropzone, DropzoneState } from 'react-dropzone';
import { AxiosProgressEvent } from 'axios';
import { Box, Button, LinearProgress, Theme, Typography, useTheme } from '@mui/material';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
@@ -8,8 +10,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
import { useI18nContext } from '../../i18n/i18n-react';
const progressPercentage = (progress: ProgressEvent) => Math.round((progress.loaded * 100) / progress.total);
const getBorderColor = (theme: Theme, props: DropzoneState) => {
if (props.isDragAccept) {
return theme.palette.success.main;
@@ -27,7 +27,7 @@ export interface SingleUploadProps {
onDrop: (acceptedFiles: File[]) => void;
onCancel: () => void;
uploading: boolean;
progress?: ProgressEvent;
progress?: AxiosProgressEvent;
}
const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, progress }) => {
@@ -47,8 +47,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
const progressText = () => {
if (uploading) {
if (progress?.lengthComputable) {
return LL.UPLOADING() + `: ${progressPercentage(progress)}%`;
if (progress?.total) {
return LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%';
}
return LL.UPLOADING() + `\u2026`;
}
@@ -80,8 +80,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
<Fragment>
<Box width="100%" p={2}>
<LinearProgress
variant={!progress || progress.lengthComputable ? 'determinate' : 'indeterminate'}
value={!progress ? 0 : progress.lengthComputable ? progressPercentage(progress) : 0}
variant={!progress || progress.total ? 'determinate' : 'indeterminate'}
value={!progress ? 0 : progress.total ? Math.round((progress.loaded * 100) / progress.total) : 0}
/>
</Box>
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import axios, { AxiosPromise, CancelTokenSource } from 'axios';
import axios, { AxiosPromise, CancelTokenSource, AxiosProgressEvent } from 'axios';
import { useSnackbar } from 'notistack';
import { extractErrorMessage } from '../../utils';
@@ -16,7 +16,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
const { enqueueSnackbar } = useSnackbar();
const [uploading, setUploading] = useState<boolean>(false);
const [uploadProgress, setUploadProgress] = useState<ProgressEvent>();
const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
const resetUploadingStates = () => {
@@ -47,7 +47,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
});
resetUploadingStates();
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
} catch (error: unknown) {
} catch (error) {
if (axios.isCancel(error)) {
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.ABORTED(), { variant: 'warning' });
} else {

View File

@@ -28,7 +28,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
const decodedMe = AuthenticationApi.decodeMeJWT(accessToken);
setMe(decodedMe);
enqueueSnackbar(LL.LOGGED_IN({ name: decodedMe.username }), { variant: 'success' });
} catch (error: unknown) {
} catch (error) {
setMe(undefined);
throw new Error('Failed to parse JWT');
}
@@ -54,7 +54,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
await AuthenticationApi.verifyAuthorization();
setMe(AuthenticationApi.decodeMeJWT(accessToken));
setInitialized(true);
} catch (error: unknown) {
} catch (error) {
setMe(undefined);
setInitialized(true);
}

View File

@@ -16,7 +16,7 @@ const FeaturesLoader: FC<RequiredChildrenProps> = (props) => {
try {
const response = await FeaturesApi.readFeatures();
setFeatures(response.data);
} catch (error: unknown) {
} catch (error) {
setErrorMessage(extractErrorMessage(error, 'Failed to fetch application details.'));
}
}, []);

View File

@@ -5,6 +5,7 @@ import DeviceHubIcon from '@mui/icons-material/DeviceHub';
import RefreshIcon from '@mui/icons-material/Refresh';
import ReportIcon from '@mui/icons-material/Report';
import SpeakerNotesOffIcon from '@mui/icons-material/SpeakerNotesOff';
import AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion';
import { ButtonRow, FormLoader, SectionContent } from '../../components';
import { MqttStatus, MqttDisconnectReason } from '../../types';
@@ -31,6 +32,12 @@ export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, theme: Theme) =
return theme.palette.error.main;
};
export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatus, theme: Theme) => {
if (mqtt_queued <= 1) return theme.palette.success.main;
return theme.palette.warning.main;
};
const MqttStatusForm: FC = () => {
const { loadData, data, errorMessage } = useRest<MqttStatus>({ read: MqttApi.readMqttStatus });
@@ -38,14 +45,14 @@ const MqttStatusForm: FC = () => {
const theme = useTheme();
const mqttStatus = ({ enabled, connected }: MqttStatus) => {
const mqttStatus = ({ enabled, connected, connect_count }: MqttStatus) => {
if (!enabled) {
return LL.NOT_ENABLED();
}
if (connected) {
return LL.CONNECTED();
return LL.CONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
}
return LL.DISCONNECTED();
return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
};
const disconnectReason = ({ disconnect_reason }: MqttStatus) => {
@@ -77,36 +84,44 @@ const MqttStatusForm: FC = () => {
}
const renderConnectionStatus = () => {
if (data.connected) {
return (
<>
<ListItem>
<ListItemAvatar>
<Avatar>#</Avatar>
</ListItemAvatar>
<ListItemText primary="Client ID" secondary={data.client_id} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar sx={{ bgcolor: mqttPublishHighlight(data, theme) }}>
<SpeakerNotesOffIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={'MQTT Publish ' + LL.ERRORS()} secondary={data.mqtt_fails} />
</ListItem>
</>
);
}
return (
<>
{!data.connected && (
<>
<ListItem>
<ListItemAvatar>
<Avatar>
<ReportIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.DISCONNECT_REASON()} secondary={disconnectReason(data)} />
</ListItem>
<Divider variant="inset" component="li" />
</>
)}
<ListItem>
<ListItemAvatar>
<Avatar>
<ReportIcon />
<Avatar>#</Avatar>
</ListItemAvatar>
<ListItemText primary="Client ID" secondary={data.client_id} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar sx={{ bgcolor: mqttQueueHighlight(data, theme) }}>
<AutoAwesomeMotionIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.DISCONNECT_REASON()} secondary={disconnectReason(data)} />
<ListItemText primary="MQTT Queue" secondary={data.mqtt_queued} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar sx={{ bgcolor: mqttPublishHighlight(data, theme) }}>
<SpeakerNotesOffIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={'MQTT ' + LL.ERRORS()} secondary={data.mqtt_fails} />
</ListItem>
<Divider variant="inset" component="li" />
</>

View File

@@ -1,8 +1,6 @@
import { useEffect, FC, useState, useCallback, useRef } from 'react';
import { useSnackbar } from 'notistack';
import { AxiosError } from 'axios';
import { Button } from '@mui/material';
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
@@ -57,8 +55,8 @@ const WiFiNetworkScanner: FC = () => {
newNetworkList.networks.sort(compareNetworks);
setNetworkList(newNetworkList);
}
} catch (error: unknown) {
if (error instanceof AxiosError) {
} catch (error) {
if (error.response) {
finishedWithError(LL.PROBLEM_LOADING() + ' ' + error.response?.data.message);
} else {
finishedWithError(LL.PROBLEM_LOADING());
@@ -73,8 +71,8 @@ const WiFiNetworkScanner: FC = () => {
try {
await NetworkApi.scanNetworks();
setTimeout(pollNetworkList, POLLING_FREQUENCY);
} catch (error: unknown) {
if (error instanceof AxiosError) {
} catch (error) {
if (error.response) {
finishedWithError(LL.PROBLEM_LOADING() + ' ' + error.response?.data.message);
} else {
finishedWithError(LL.PROBLEM_LOADING());

View File

@@ -91,7 +91,7 @@ const NTPStatusForm: FC = () => {
enqueueSnackbar(LL.TIME_SET(), { variant: 'success' });
setSettingTime(false);
loadData();
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setProcessing(false);

View File

@@ -37,7 +37,7 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
const getToken = useCallback(async () => {
try {
setToken((await SecurityApi.generateToken(username)).data);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
}
}, [username, enqueueSnackbar, LL]);

View File

@@ -50,7 +50,7 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
} else {
saveFile(response.data, 'settings');
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};
@@ -63,7 +63,7 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
} else {
saveFile(response.data, 'customizations');
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};

View File

@@ -21,7 +21,7 @@ const RestartMonitor: FC = () => {
try {
await SystemApi.readSystemStatus(POLL_TIMEOUT);
document.location.href = '/fileUpdated';
} catch (error: unknown) {
} catch (error) {
if (new Date().getTime() < timeoutAt.current) {
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
} else {

View File

@@ -111,7 +111,7 @@ const SystemLog: FC = () => {
if (response.status !== 200) {
enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
}
}
@@ -163,7 +163,7 @@ const SystemLog: FC = () => {
const fetchLog = useCallback(async () => {
try {
setLogEntries((await SystemApi.readLogEntries()).data);
} catch (error: unknown) {
} catch (error) {
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}, [LL]);

View File

@@ -86,7 +86,7 @@ const SystemStatusForm: FC = () => {
try {
await SystemApi.restart();
enqueueSnackbar(LL.APPLICATION_RESTARTING(), { variant: 'info' });
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
} finally {
setConfirmRestart(false);
@@ -180,7 +180,7 @@ const SystemStatusForm: FC = () => {
try {
await SystemApi.factoryReset();
enqueueSnackbar(LL.SYSTEM_FACTORY_TEXT(), { variant: 'info' });
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setConfirmFactoryReset(false);

File diff suppressed because it is too large Load Diff

View File

@@ -1,270 +1,269 @@
import type { BaseTranslation } from '../i18n-types';
const no: BaseTranslation = {
LANGUAGE: 'Language',
RETRY: 'Retry',
LOADING: 'Loading',
IS_REQUIRED: 'is required',
SIGN_IN: 'Sign In',
SIGN_OUT: 'Sign Out',
USERNAME: 'Username',
PASSWORD: 'Password',
LANGUAGE: 'Språk',
RETRY: 'Forsøk igjen',
LOADING: 'Laster',
IS_REQUIRED: 'er nødvendig',
SIGN_IN: 'Logg inn',
SIGN_OUT: 'Logg ut',
USERNAME: 'Brukernavn',
PASSWORD: 'Passord',
DASHBOARD: 'Dashboard',
SETTINGS: 'Settings',
SAVED: 'saved',
HELP: 'Help',
LOGGED_IN: 'Logged in as {name}',
PLEASE_SIGNIN: 'Please sign in to continue',
UPLOAD_SUCCESSFUL: 'Upload successful',
DOWNLOAD_SUCCESSFUL: 'Download successful',
INVALID_LOGIN: 'Invalid login details',
NETWORK: 'Network',
SECURITY: 'Security',
ONOFF_CAP: 'ON/OFF',
ONOFF: 'on/off',
SETTINGS: 'Innstillinger',
SAVED: 'lagret',
HELP: 'Hjelp',
LOGGED_IN: 'Logget in som {name}',
PLEASE_SIGNIN: 'Venligst logge inn for å fortsetta',
UPLOAD_SUCCESSFUL: 'Opplasting lykkes',
DOWNLOAD_SUCCESSFUL: 'Nedlasting lykkes',
INVALID_LOGIN: 'Ugyldig innlogging',
NETWORK: 'Nettverk',
SECURITY: 'Sikkerhet',
ONOFF_CAP: 'PÅ/AV',
ONOFF: 'på/av',
TYPE: 'Type',
DESCRIPTION: 'Description',
ENTITIES: 'Entities',
REFRESH: 'Refresh',
EXPORT: 'Export',
DEVICE_DETAILS: 'Device Details',
BRAND: 'Brand',
ENTITY_NAME: 'Entity Name',
VALUE: 'Value',
SHOW_FAV: 'only show favorites',
DEVICE_SENSOR_DATA: 'Device and Sensor Data',
DEVICES_SENSORS: 'Devices & Sensors',
ATTACHED_SENSORS: 'Attached EMS-ESP Sensors',
RUN_COMMAND: 'Call Command',
CHANGE_VALUE: 'Change Value',
CANCEL: 'Cancel',
RESET: 'Reset',
DESCRIPTION: 'Beskrivelse',
ENTITIES: 'Ojekter',
REFRESH: 'Oppdater',
EXPORT: 'Eksport',
DEVICE_DETAILS: 'Enhetsdetaljer',
BRAND: 'Fabrikat',
ENTITY_NAME: 'Objektsnavn',
VALUE: 'Verdi',
SHOW_FAV: ' Vis kun favoritter',
DEVICE_SENSOR_DATA: 'Enheter og Sensordata',
DEVICES_SENSORS: 'Enheter og Sensorer',
ATTACHED_SENSORS: 'Tilkoblede EMS-ESP Sensorer',
RUN_COMMAND: 'Kjør kommando',
CHANGE_VALUE: 'Endre Verdi',
CANCEL: 'Avbryt',
RESET: 'Nullstill',
SEND: 'Send',
SAVE: 'Save',
REMOVE: 'Remove',
PROBLEM_UPDATING: 'Problem updating',
PROBLEM_LOADING: 'Problem loading',
ACCESS_DENIED: 'Access Denied',
SAVE: 'Lagre',
REMOVE: 'Fjern',
PROBLEM_UPDATING: 'Problem med oppdatering',
PROBLEM_LOADING: 'Problem med opplasting',
ACCESS_DENIED: 'Tilgang nektet',
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',
TEMP_SENSORS: 'Temperature Sensors',
WRITE_COMMAND: 'Write command {cmd}',
ANALOG_SENSORS: 'Analoge Sensorer',
UPDATED: 'Oppdatert',
UPDATE: 'Oppdater',
REMOVED: 'Slettet',
DELETION: 'Sletting',
OFFSET: 'Kompensering',
FACTOR: 'Faktor',
FREQ: 'Frekvens',
STARTVALUE: 'Startverdi',
WARN_GPIO: 'Advarsel: vær forsiktig ved aktivering av GPIO!',
EDIT: 'Endre',
TEMP_SENSOR: 'Temperatursensor',
TEMP_SENSORS: 'Temperaturesensorer',
WRITE_COMMAND: 'Skriv kommando {cmd}',
EMS_BUS_WARNING:
'EMS bus disconnected. If this warning still persists after a few seconds please check settings and board profile',
EMS_BUS_SCANNING: 'Scanning for EMS devices...',
CONNECTED: 'Connected',
TX_ISSUES: 'Tx issues - try a different Tx Mode',
DISCONNECTED: 'Disconnected',
EMS_SCAN: 'Are you sure you want to initiate a full device scan of the EMS bus?',
EMS_BUS_STATUS: 'EMS Bus Status',
ACTIVE_DEVICES: 'Active Devices & Sensors',
DEVICE: 'Device',
SUCCESS: 'SUCCESS',
FAIL: 'FAIL',
QUALITY: 'QUALITY',
SCAN_DEVICES: 'Scan for new devices',
EMS_BUS_STATUS_TITLE: 'EMS Bus & Activity Status',
SCAN: 'Scan',
'EMS bussen koblet ned. Hvis denne advarselen fortsetter etter noen f¨sekunder sjekk instillinger og prosessorkort',
EMS_BUS_SCANNING: 'Søker etter EMS enheter...',
CONNECTED: 'Tilkoblet',
TX_ISSUES: 'Tx problemer - prøv en annen Tx Modus',
DISCONNECTED: 'Frakoblet',
EMS_SCAN: 'Er du sikker på du vil starte full søking av EMS bussen?',
EMS_BUS_STATUS: 'EMS Buss Status',
ACTIVE_DEVICES: 'Aktive Enheter og Sensorer',
DEVICE: 'Enhet',
SUCCESS: 'VELLYKKET',
FAIL: 'MISLYKKET',
QUALITY: 'KVALITET',
SCAN_DEVICES: 'Søk etter nye enheter',
EMS_BUS_STATUS_TITLE: 'EMS Buss & Aktivitet Status',
SCAN: 'Søk',
STATUS_NAMES: [
'EMS Telegrams Received (Rx)',
'EMS Reads (Tx)',
'EMS Writes (Tx)',
'Temperature Sensor Reads',
'Analog Sensor Reads',
'MQTT Publishes',
'API Calls',
'Syslog Messages'
'EMS Telegrammer Mottatt (Rx)',
'EMS Lest (Tx)',
'EMS Skrevet (Tx)',
'Temperatur Sensor Lest',
'Analog Sensor Lest',
'MQTT Publiseringer',
'API Anrop',
'Syslog Meldinger'
],
NUM_DEVICES: '{num} Device{{s}}',
NUM_TEMP_SENSORS: '{num} Temperature Sensor{{s}}',
NUM_ANALOG_SENSORS: '{num} Analog Sensor{{s}}',
NUM_DAYS: '{num} Day{{s}}',
NUM_SECONDS: '{num} Second{{s}}',
NUM_HOURS: '{num} Hour{{s}}',
NUM_MINUTES: '{num} Minute{{s}}',
APPLICATION_SETTINGS: 'Application Settings',
CUSTOMIZATION: 'Customization',
APPLICATION_RESTARTING: 'EMS-ESP is restarting',
NUM_DEVICES: '{num} Enhet{{er}}',
NUM_TEMP_SENSORS: '{num} Temperatursensor{{er}}',
NUM_ANALOG_SENSORS: '{num} Analogsensor{{er}}',
NUM_DAYS: '{num} Dag{{er}}',
NUM_SECONDS: '{num} Sekund{{er}}',
NUM_HOURS: '{num} Time{{r}}',
NUM_MINUTES: '{num} Minutt{{er}}',
APPLICATION_SETTINGS: 'Innstillinger',
CUSTOMIZATION: 'Tilpasninger',
APPLICATION_RESTARTING: 'EMS-ESP restarter',
BOARD_PROFILE_TEXT:
'Select a pre-configured interface board profile from the list below or choose Custom to configure your own hardware settings',
BOARD_PROFILE: 'Board Profile',
BUTTON: 'Button',
TEMPERATURE: 'Temperature',
DISABLED: 'disabled',
GENERAL_OPTIONS: 'General Options',
LANGUAGE_ENTITIES: 'Language (for device entities)',
HIDE_LED: 'Hide LED',
ENABLE_TELNET: 'Enable Telnet Console',
ENABLE_ANALOG: 'Enable Analog Sensors',
CONVERT_FAHRENHEIT: 'Convert temperature values to Fahrenheit',
BYPASS_TOKEN: 'Bypass Access Token authorization on API calls',
READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)',
UNDERCLOCK_CPU: 'Underclock CPU speed',
ENABLE_SHOWER_TIMER: 'Enable Shower Timer',
ENABLE_SHOWER_ALERT: 'Enable Shower Alert',
TRIGGER_TIME: 'Trigger Time',
COLD_SHOT_DURATION: 'Cold Shot Duration',
FORMATTING_OPTIONS: 'Formatting Options',
BOOLEAN_FORMAT_DASHBOARD: 'Boolean Format Dashboard',
BOOLEAN_FORMAT_API: 'Boolean Format API/MQTT',
'Velg en pre-konfigurert prosessor profil fra listen under eller velg Tilpasset for å konfigurere dine egne innstillinger',
BOARD_PROFILE: 'Prosessor Profil',
BUTTON: 'Knapp',
TEMPERATURE: 'Temperatur',
DISABLED: 'avslått',
GENERAL_OPTIONS: 'Generelle Innstillinger',
LANGUAGE_ENTITIES: 'Språk (for objekter)',
HIDE_LED: 'Skjul LED',
ENABLE_TELNET: 'Aktiver Telnet',
ENABLE_ANALOG: 'Aktiver Analoge Sensorer',
CONVERT_FAHRENHEIT: 'Konverter temperatur til Fahrenheit',
BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall',
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
TRIGGER_TIME: 'Aktiveringstid',
COLD_SHOT_DURATION: 'Tid på kaldt vann',
FORMATTING_OPTIONS: 'Formatteringsalternativs',
BOOLEAN_FORMAT_DASHBOARD: 'Bool Format Dashboard',
BOOLEAN_FORMAT_API: 'Bool Format API/MQTT',
ENUM_FORMAT: 'Enum Format API/MQTT',
INDEX: 'Index',
ENABLE_PARASITE: 'Enable parasite power',
INDEX: 'Indeks',
ENABLE_PARASITE: 'Aktiver parasitt strømforsyning',
LOGGING: 'Logging',
LOG_HEX: 'Log EMS telegrams in hexadecimal',
ENABLE_SYSLOG: 'Enable Syslog',
MARK_INTERVAL: 'Mark Interval',
SECONDS: 'seconds',
MINUTES: 'minutes',
RESTART: 'Restart',
HOURS: 'hours',
RESTART_TEXT: 'EMS-ESP needs to be restarted to apply changed system settings',
RESTART_CONFIRM: 'Are you sure you want to restart EMS-ESP?',
COMMAND: 'Command',
CUSTOMIZATIONS_RESTART: 'All customizations have been removed. Restarting...',
CUSTOMIZATIONS_FULL: 'Selected entities exceeded limit. Please save in batches',
CUSTOMIZATIONS_SAVED: 'Customizations saved',
CUSTOMIZATIONS_HELP_1: 'Select a device and customize the entities options or click to rename',
CUSTOMIZATIONS_HELP_2: 'mark as favorite',
CUSTOMIZATIONS_HELP_3: 'disable write action',
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
SELECT_DEVICE: 'Select a device',
SET_ALL: 'set all',
OPTIONS: 'Options',
NAME: 'Name',
LOG_HEX: 'Logg EMS telegrammer i hexadesimal',
ENABLE_SYSLOG: 'Aktiver Syslog',
MARK_INTERVAL: 'Oppdateringsintervall',
SECONDS: 'sekunder',
MINUTES: 'minutter',
RESTART: 'Omstart',
HOURS: 'timer',
RESTART_TEXT: 'EMS-ESP må omstartes for å iverksette endrede systeminstillinger',
RESTART_CONFIRM: 'Er du sikker på at du vil omstarte EMS-ESP?',
COMMAND: 'Kommando',
CUSTOMIZATIONS_RESTART: 'Alle tilpasninger har blitt slettet. Restarter...',
CUSTOMIZATIONS_FULL: 'Antall valgte objekter for høyt. Largre i mindre antall om gangen',
CUSTOMIZATIONS_SAVED: 'Tilpasninger lagret',
CUSTOMIZATIONS_HELP_1: 'Velg en enhet og tilpass underenheter med hjelp av alternativer eller velg å gi nytt navn',
CUSTOMIZATIONS_HELP_2: 'merk som favoritt',
CUSTOMIZATIONS_HELP_3: 'inaktiviser skriving',
CUSTOMIZATIONS_HELP_4: 'ekskludere fra MQTT og API',
CUSTOMIZATIONS_HELP_5: 'gjemme fra Dashboard',
SELECT_DEVICE: 'Velg en enhet',
SET_ALL: 'sett alle',
OPTIONS: 'Alternativ',
NAME: 'Navn',
CUSTOMIZATIONS_RESET:
'Are you sure you want remove all customizations including the custom settings of the Temperature and Analog sensors?',
DEVICE_ENTITIES: 'Device Entities',
USER_CUSTOMIZATION: 'User Customization',
SUPPORT_INFORMATION: 'Support Information',
CLICK_HERE: 'Click Here',
HELP_INFORMATION_1: 'Visit the online wiki to get instructions on how to configure EMS-ESP',
HELP_INFORMATION_2: 'For live community chat join our Discord server',
HELP_INFORMATION_3: 'To request a feature or report a bug',
'Er du sikker på att du vil fjerne tilpassninger inkludert innstillinger for Temperatur og Analoge sensorer?',
DEVICE_ENTITIES: 'Enhets objekter',
USER_CUSTOMIZATION: 'Brukertilpasninger',
SUPPORT_INFORMATION: 'Supportinformasjon',
CLICK_HERE: 'Klikk her',
HELP_INFORMATION_1: 'Besøk wiki for instruksjoner for å konfigurere EMS-ESP',
HELP_INFORMATION_2: 'For community-support besøk vår Discord-server',
HELP_INFORMATION_3: 'For å be om en ny funksjon eller melde feil',
HELP_INFORMATION_4:
'remember to download and attach your system information for a faster response when reporting an issue',
HELP_INFORMATION_5:
'EMS-ESP is a free and open-source project. Please support its future development by giving it a star on Github!',
SUPPORT_INFO: 'Support Info',
UPLOAD: 'Upload',
DOWNLOAD: 'Download',
ABORTED: 'aborted',
FAILED: 'failed',
SUCCESSFUL: 'successful',
'husk å laste ned og legg ved din systeminformasjon for en raskere respons når du rapporterer et problem',
HELP_INFORMATION_5: 'EMS-ESP er gratis og åpen kildekode. Bidra til utviklingen ved å gi oss en stjerne på GitHub!',
SUPPORT_INFO: 'Supportinfo',
UPLOAD: 'Opplasning',
DOWNLOAD: 'Nedlasting',
ABORTED: 'avbrutt',
FAILED: 'feilet',
SUCCESSFUL: 'vellykket',
SYSTEM: 'System',
LOG: 'Log',
LOG: 'Logg',
STATUS: 'Status',
UPLOAD_DOWNLOAD: 'Upload/Download',
SYSTEM_VERSION_RUNNING: 'You are currently running version',
SYSTEM_APPLY_FIRMWARE: 'to apply the new firmware',
CLOSE: 'Close',
USE: 'Use',
FACTORY_RESET: 'Factory Reset',
SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart',
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset the device to its factory defaults?',
VERSION_CHECK: 'Version Check',
THE_LATEST: 'The latest',
VERSION_IS: 'version is',
PLATFORM: 'Device (Platform / SDK)',
UPTIME: 'System Uptime',
CPU_FREQ: 'CPU Frequency',
HEAP: 'Heap (Free / Max Alloc)',
PSRAM: 'PSRAM (Size / Free)',
FLASH: 'Flash Chip (Size / Speed)',
APPSIZE: 'Application (Used / Free)',
FILESYSTEM: 'File System (Used / Free)',
BUFFER_SIZE: 'Buffer Size',
COMPACT: 'Compact',
ENABLE_OTA: 'Enable OTA Updates',
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download the entity customizations',
UPLOAD_DOWNLOAD: 'Opp/Nedlasting',
SYSTEM_VERSION_RUNNING: 'Du benytter versjon',
SYSTEM_APPLY_FIRMWARE: 'for å aktivere ny firmware',
CLOSE: 'Steng',
USE: 'Bruk',
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte',
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
VERSION_CHECK: 'Versjonsjekk',
THE_LATEST: 'Den nyeste',
VERSION_IS: 'versjonen er',
PLATFORM: 'Enhet (Platform / SDK)',
UPTIME: 'System Oppetid',
CPU_FREQ: 'CPU Frekvens',
HEAP: 'Heap (Ledig / Max Allokert)',
PSRAM: 'PSRAM (Størrelse / Ledig)',
FLASH: 'Flash Chip (Størrelse / Hastighet)',
APPSIZE: 'Applikasjon (Brukt / Ledig)',
FILESYSTEM: 'File System (Brukt / Ledig)',
BUFFER_SIZE: 'Buffer Størrelse',
COMPACT: 'Komprimere',
ENABLE_OTA: 'Aktiviser OTA oppdateringer',
DOWNLOAD_CUSTOMIZATION_TEXT: 'Last ned objektstilpasninger',
DOWNLOAD_SETTINGS_TEXT:
'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
UPLOAD_TEXT: 'Upload a new firmware (.bin) file, settings or customizations (.json) file below',
UPLOADING: 'Uploading',
UPLOAD_DROP_TEXT: 'Drop file or click here',
ERROR: 'Unexpected Error, please try again',
TIME_SET: 'Time set',
MANAGE_USERS: 'Manage Users',
IS_ADMIN: 'is Admin',
USER_WARNING: 'You must have at least one admin user configured',
ADD: 'Add',
ACCESS_TOKEN_FOR: 'Access Token for',
'Last ned applikasjonskonfigurasjon. Vær varsom med å dele fila da den inneholder passord og annen sensitiv system informasjon',
UPLOAD_TEXT: 'Last opp en ny firmware (.bin) fil, innstillinger eller tilpassninger (.json) fil nedenfor',
UPLOADING: 'Opplasting',
UPLOAD_DROP_TEXT: 'Slipp fil eller klikk her',
ERROR: 'Ukjent feil, prøv igjen',
TIME_SET: 'Still in tid',
MANAGE_USERS: 'Administrer Brukere',
IS_ADMIN: 'er Admin',
USER_WARNING: 'Du må ha minst en admin bruker konfigurert',
ADD: 'Legg til',
ACCESS_TOKEN_FOR: 'Aksess Token for',
ACCESS_TOKEN_TEXT:
'The token below is used with REST API calls that require authorization. It can be passed either as a Bearer token in the Authorization header or in the access_token URL query parameter.',
GENERATING_TOKEN: 'Generating token',
USER: 'User',
MODIFY: 'Modify',
'Token nedenfor benyttes med REST API-kall som krever autorisering. Den kan sendes med enten som en Bearer token i Authorization-headern eller i access_token URL query-parameter.',
GENERATING_TOKEN: 'Generer token',
USER: 'Bruker',
MODIFY: 'Endre',
SU_TEXT:
'The su (super user) password is used to sign authentication tokens and also enable admin privileges within the Console.',
NOT_ENABLED: 'Not enabled',
ERRORS: 'Errors',
DISCONNECT_REASON: 'Disconnect Reason',
ENABLE_MQTT: 'Enable MQTT',
OPTIONAL: 'Optional',
FORMATTING: 'Formatting',
'su brukeren (super user) passord benyttes for å signere autentiserings token samt å tillate admin privileger i konsoll modus.',
NOT_ENABLED: 'Ikke aktiv',
ERRORS: 'Feil',
DISCONNECT_REASON: 'Årsak til nedkobling',
ENABLE_MQTT: 'Aktiver MQTT',
OPTIONAL: 'Valgfritt',
FORMATTING: 'Formatering',
FORMAT: 'Format',
MQTT_NEST_1: 'Nested in a single topic',
MQTT_NEST_2: 'As individual topics',
MQTT_RESPONSE: 'Publish command output to a `response` topic',
MQTT_PUBLISH_TEXT_1: 'Publish single value topics on change',
MQTT_PUBLISH_TEXT_2: 'Publish to command topics (ioBroker)',
MQTT_PUBLISH_TEXT_3: 'Enable MQTT Discovery (Home Assistant, Domoticz)',
MQTT_PUBLISH_TEXT_4: 'Prefix for the Discovery topics',
MQTT_PUBLISH_INTERVALS: 'Publish Intervals',
MQTT_INT_BOILER: 'Boilers and Heat Pumps',
MQTT_INT_THERMOSTATS: 'Thermostats',
MQTT_INT_SOLAR: 'Solar Modules',
MQTT_INT_MIXER: 'Mixer Modules',
DEFAULT: 'Default',
MQTT_CLEAN_SESSION: 'Set Clean Session',
MQTT_RETAIN_FLAG: 'Always set Retain flag',
INACTIVE: 'Inactive',
ACTIVE: 'Active',
UNKNOWN: 'Unknown',
SET_TIME: 'Set Time',
SET_TIME_TEXT: 'Enter local date and time below to set the time',
LOCAL_TIME: 'Local Time',
UTC_TIME: 'UTC Time',
ENABLE_NTP: 'Enable NTP',
TIME_ZONE: 'Time Zone',
ACCESS_POINT: 'Access Point',
AP_PROVIDE: 'Enable Access Point',
AP_PROVIDE_TEXT_1: 'always',
AP_PROVIDE_TEXT_2: 'when WiFi is disconnected',
AP_PROVIDE_TEXT_3: 'never',
AP_PREFERRED_CHANNEL: 'Preferred Channel',
AP_HIDE_SSID: 'Hide SSID',
NETWORK_SCAN: 'Scan WiFi Networks',
IDLE: 'Idle',
LOST: 'Lost',
SCANNING: 'Scanning',
SCAN_AGAIN: 'Scan again',
NETWORK_SCANNER: 'Network Scanner',
NETWORK_NO_WIFI: 'No WiFi networks found',
NETWORK_BLANK_SSID: 'leave blank to disable WiFi',
POWER: 'Power',
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
NETWORK_LOW_BAND: 'Use Lower WiFi Bandwidth',
NETWORK_USE_DNS: 'Enable mDNS Service',
NETWORK_ENABLE_IPV6: 'Enable IPv6 support',
NETWORK_FIXED_IP: 'Use Fixed IP address',
MQTT_NEST_1: 'Nestet i en topic',
MQTT_NEST_2: 'Som individuelle topics',
MQTT_RESPONSE: 'Publiser kommandoer til en `response` topic',
MQTT_PUBLISH_TEXT_1: 'Publiser singel verdi topics ved endringer',
MQTT_PUBLISH_TEXT_2: 'Publiser til kommando topics (ioBroker)',
MQTT_PUBLISH_TEXT_3: 'Aktiver MQTT Discovery (Home Assistant, Domoticz)',
MQTT_PUBLISH_TEXT_4: 'Prefiks for Discovery topics',
MQTT_PUBLISH_INTERVALS: 'Publiseringsintervall',
MQTT_INT_BOILER: 'Fyr/Varmepumpe',
MQTT_INT_THERMOSTATS: 'Termostat',
MQTT_INT_SOLAR: 'Solpaneler',
MQTT_INT_MIXER: 'Blandeventil',
DEFAULT: 'Standard',
MQTT_CLEAN_SESSION: 'Benytt Clean Session',
MQTT_RETAIN_FLAG: 'Alltid sett Retain flag',
INACTIVE: 'Innaktiv',
ACTIVE: 'Aktiv',
UNKNOWN: 'Ukjent',
SET_TIME: 'Sett Tid',
SET_TIME_TEXT: 'Skriv inn dato og klokke nedenfor',
LOCAL_TIME: 'Lokaltid',
UTC_TIME: 'UTC Tid',
ENABLE_NTP: 'Aktiver NTP',
TIME_ZONE: 'Tidssone',
ACCESS_POINT: 'Aksesspunkt',
AP_PROVIDE: 'Aktiver Aksesspunkt',
AP_PROVIDE_TEXT_1: 'alltid',
AP_PROVIDE_TEXT_2: 'når WiFi er utilgjengelig',
AP_PROVIDE_TEXT_3: 'aldri',
AP_PREFERRED_CHANNEL: 'Foretrukket kanal',
AP_HIDE_SSID: 'Skjul SSID',
NETWORK_SCAN: 'Søk etter trådløst nettverk',
IDLE: 'Klar',
LOST: 'Mistet',
SCANNING: 'Søker',
SCAN_AGAIN: 'Søk igjen',
NETWORK_SCANNER: 'Nettverk Scanner',
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk',
POWER: 'Effekt',
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
NETWORK_LOW_BAND: 'Benytt smalere båndbredde på trådløst nettverk',
NETWORK_USE_DNS: 'Aktiviser mDNS Service',
NETWORK_ENABLE_IPV6: 'Aktiviser IPv6 støtte',
NETWORK_FIXED_IP: 'Benytt statisk IP adresse',
ADMIN: 'Admin',
GUEST: 'Guest',
NEW: 'New',
RENAME: 'Rename',
ENTITY: 'Entity'
GUEST: 'Gjest',
NEW: 'Ny',
RENAME: 'Bytt navn',
ENTITY: 'Entitet'
};
export default no;

View File

@@ -96,8 +96,7 @@ const pl: BaseTranslation = {
APPLICATION_SETTINGS: 'Ustawienia aplikacji',
CUSTOMIZATION: 'Personalizacja',
APPLICATION_RESTARTING: 'Trwa ponowne uruchamianie',
BOARD_PROFILE_TEXT:
'Wybierz z listy wstępną konfigurację płytki interfejsu lub stwórz własną konfigurację.',
BOARD_PROFILE_TEXT: 'Wybierz z listy wstępną konfigurację płytki interfejsu lub stwórz własną konfigurację.',
BOARD_PROFILE: 'Profil płytki',
BUTTON: 'Przycisk',
TEMPERATURE: 'Temperatura',

View File

@@ -364,7 +364,7 @@ const DashboardData: FC = () => {
const fetchCoreData = useCallback(async () => {
try {
setCoreData((await EMSESP.readCoreData()).data);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
}, [enqueueSnackbar, LL]);
@@ -385,7 +385,7 @@ const DashboardData: FC = () => {
const unique_id = parseInt(id);
try {
setDeviceData((await EMSESP.readDeviceData({ id: unique_id })).data);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};
@@ -393,7 +393,7 @@ const DashboardData: FC = () => {
const fetchSensorData = async () => {
try {
setSensorData((await EMSESP.readSensorData()).data);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};
@@ -474,7 +474,7 @@ const DashboardData: FC = () => {
enqueueSnackbar(LL.WRITE_COMMAND({ cmd: 'send' }), { variant: 'success' });
}
setDeviceValue(undefined);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
refreshData();
@@ -508,7 +508,7 @@ const DashboardData: FC = () => {
<ValidatedTextField
name="v"
label={deviceValue.id.slice(2)}
value={deviceValue.u ? numberValue(deviceValue.v) : deviceValue.v}
value={typeof deviceValue.v === 'number' ? Math.round(deviceValue.v * 10) / 10 : deviceValue.v}
autoFocus
multiline={deviceValue.u ? false : true}
sx={{ width: '30ch' }}
@@ -566,7 +566,7 @@ const DashboardData: FC = () => {
enqueueSnackbar(LL.TEMP_SENSOR() + ' ' + LL.UPDATED(), { variant: 'success' });
}
setSensor(undefined);
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setSensor(undefined);
@@ -992,7 +992,7 @@ const DashboardData: FC = () => {
} else {
enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.REMOVED(), { variant: 'success' });
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setAnalog(undefined);
@@ -1020,7 +1020,7 @@ const DashboardData: FC = () => {
} else {
enqueueSnackbar(LL.ANALOG_SENSOR() + ' ' + LL.UPDATED(), { variant: 'success' });
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setAnalog(undefined);

View File

@@ -149,7 +149,7 @@ const DashboardStatus: FC = () => {
try {
await EMSESP.scanDevices();
enqueueSnackbar(LL.SCANNING() + '...', { variant: 'info' });
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setConfirmScan(false);

View File

@@ -50,7 +50,7 @@ const HelpInformation: FC = () => {
} else {
saveFile(response.data, endpoint);
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};

View File

@@ -68,7 +68,7 @@ const SettingsApplication: FC = () => {
eth_clock_mode: response.data.eth_clock_mode
});
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setProcessingBoard(false);
@@ -107,7 +107,7 @@ const SettingsApplication: FC = () => {
try {
await EMSESP.restart();
enqueueSnackbar(LL.APPLICATION_RESTARTING(), { variant: 'info' });
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
}
};

View File

@@ -131,7 +131,7 @@ const SettingsCustomization: FC = () => {
const fetchDevices = useCallback(async () => {
try {
setDevices((await EMSESP.readDevices()).data);
} catch (error: unknown) {
} catch (error) {
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}, [LL]);
@@ -144,7 +144,7 @@ const SettingsCustomization: FC = () => {
try {
const new_deviceEntities = (await EMSESP.readDeviceEntities({ id: unique_id })).data;
setInitialMask(new_deviceEntities);
} catch (error: unknown) {
} catch (error) {
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
};
@@ -165,17 +165,21 @@ const SettingsCustomization: FC = () => {
}
function formatName(de: DeviceEntity) {
if (de.n === undefined || de.n === de.id) {
return de.id;
}
if (de.n[0] === '!') {
return LL.COMMAND() + ': ' + de.n.slice(1);
if (de.n === undefined) {
return (
<>
(
<Link target="_blank" href={APIURL + devices?.devices[selectedDevice].t + '/' + de.id}>
{de.id}
</Link>
)
</>
);
}
return (
<>
{de.cn !== undefined && de.cn !== '' ? de.cn : de.n}
{de.n[0] === '!' ? LL.COMMAND() + ': ' + de.n.slice(1) : de.cn !== undefined && de.cn !== '' ? de.cn : de.n}
&nbsp;(
<Link target="_blank" href={APIURL + devices?.devices[selectedDevice].t + '/' + de.id}>
{de.id}
@@ -239,7 +243,7 @@ const SettingsCustomization: FC = () => {
try {
await EMSESP.resetCustomizations();
enqueueSnackbar(LL.CUSTOMIZATIONS_RESTART(), { variant: 'info' });
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setConfirmReset(false);
@@ -250,13 +254,15 @@ const SettingsCustomization: FC = () => {
if (devices && deviceEntities && selectedDevice !== -1) {
const masked_entities = deviceEntities
.filter((de) => de.m !== de.o_m || de.cn !== de.o_cn || de.ma !== de.o_ma || de.mi !== de.o_mi)
.map((new_de) =>
new_de.m.toString(16).padStart(2, '0') +
new_de.id +
((new_de.cn || new_de.mi || new_de.ma) ? '|' : '') +
(new_de.cn ? new_de.cn : '') +
(new_de.mi ? '>' + new_de.mi : '') +
(new_de.ma ? '<' + new_de.ma : ''));
.map(
(new_de) =>
new_de.m.toString(16).padStart(2, '0') +
new_de.id +
(new_de.cn || new_de.mi || new_de.ma ? '|' : '') +
(new_de.cn ? new_de.cn : '') +
(new_de.mi ? '>' + new_de.mi : '') +
(new_de.ma ? '<' + new_de.ma : '')
);
// check size in bytes to match buffer in CPP, which is 4096
const bytes = new TextEncoder().encode(JSON.stringify(masked_entities)).length;
@@ -275,7 +281,7 @@ const SettingsCustomization: FC = () => {
} else {
enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
}
} catch (error: unknown) {
} catch (error) {
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
}
setInitialMask(deviceEntities);
@@ -322,7 +328,7 @@ const SettingsCustomization: FC = () => {
};
const editEntity = (de: DeviceEntity) => {
if (de.n && de.n[0] === '!') {
if (de.n === undefined || (de.n && de.n[0] === '!')) {
return;
}

View File

@@ -15,6 +15,8 @@ export interface MqttStatus {
client_id: string;
disconnect_reason: MqttDisconnectReason;
mqtt_fails: number;
mqtt_queued: number;
connect_count: number;
}
export interface MqttSettings {

View File

@@ -1,7 +1,5 @@
import { AxiosError } from 'axios';
export const extractErrorMessage = (error: unknown, defaultMessage: string) => {
if (error instanceof AxiosError) {
export const extractErrorMessage = (error: any, defaultMessage: string) => {
if (error.request) {
return defaultMessage + ' (' + error.request.statusText + ')';
} else if (error instanceof Error) {
return defaultMessage + ' (' + error.message + ')';

View File

@@ -26,7 +26,7 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
setErrorMessage(undefined);
try {
setData((await read()).data);
} catch (error: unknown) {
} catch (error) {
const message = extractErrorMessage(error, LL.PROBLEM_LOADING());
enqueueSnackbar(message, { variant: 'error' });
setErrorMessage(message);
@@ -49,7 +49,7 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
} else {
enqueueSnackbar(LL.SETTINGS() + ' ' + LL.SAVED(), { variant: 'success' });
}
} catch (error: unknown) {
} catch (error) {
const message = extractErrorMessage(error, LL.PROBLEM_UPDATING());
enqueueSnackbar(message, { variant: 'error' });
setErrorMessage(message);

View File

@@ -14,6 +14,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"useUnknownInCatchVariables": false,
"jsx": "react-jsx"
},
"include": ["src"]

View File

@@ -49,9 +49,9 @@ void APSettingsService::startAP() {
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
#ifdef ARDUINO_LOLIN_C3_MINI
WiFi.setTxPower(WIFI_POWER_8_5dBm); //https://www.wemos.cc/en/latest/c3/c3_mini.html#about-wifi
#endif
#ifdef ARDUINO_LOLIN_C3_MINI
WiFi.setTxPower(WIFI_POWER_8_5dBm); //https://www.wemos.cc/en/latest/c3/c3_mini.html#about-wifi
#endif
if (!_dnsServer) {
IPAddress apIp = WiFi.softAPIP();
emsesp::EMSESP::logger().info(F("Starting Access Point with captive portal on %s"), apIp.toString().c_str());

View File

@@ -20,7 +20,9 @@ void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
root["client_id"] = _mqttSettingsService->getClientId();
root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason();
root["mqtt_fails"] = emsesp::Mqtt::publish_fails(); // proddy added
root["mqtt_queued"] = emsesp::Mqtt::publish_queued(); // mdvp added
root["mqtt_fails"] = emsesp::Mqtt::publish_fails(); // proddy added
root["connect_count"] = emsesp::Mqtt::connect_count(); // mdvp added
response->setLength();
request->send(response);

View File

@@ -56,7 +56,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
public:
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin();
void begin();
static void ntp_received(struct timeval * tv);
private:
@@ -68,7 +68,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
void WiFiEvent(WiFiEvent_t event);
void configureNTP();
void configureTime(AsyncWebServerRequest * request, JsonVariant & json);
};
#endif

View File

@@ -75,9 +75,9 @@ void NetworkSettingsService::manageSTA() {
});
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network
#ifdef ARDUINO_LOLIN_C3_MINI
WiFi.setTxPower(WIFI_POWER_8_5dBm); //https://www.wemos.cc/en/latest/c3/c3_mini.html#about-wifi
#endif
#ifdef ARDUINO_LOLIN_C3_MINI
WiFi.setTxPower(WIFI_POWER_8_5dBm); //https://www.wemos.cc/en/latest/c3/c3_mini.html#about-wifi
#endif
}
}

View File

@@ -85,7 +85,8 @@ std::string printable_to_string(const Printable & printable);
*
* @since 1.0.0
*/
using flash_string_vector = std::vector<const __FlashStringHelper *>;
using string_vector = std::vector<const char *>;
/**
* Loop function that must be called regularly to detect a 32-bit

View File

@@ -42,30 +42,36 @@ namespace uuid {
namespace console {
void Commands::add_command(const flash_string_vector & name, command_function function) {
add_command(0, 0, name, flash_string_vector{}, function, nullptr);
void Commands::add_command(const string_vector & name, command_function function) {
add_command(0, 0, name, string_vector{}, function, nullptr);
}
void Commands::add_command(const flash_string_vector & name, const flash_string_vector & arguments, command_function function) {
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function) {
add_command(0, 0, name, arguments, function, nullptr);
}
void Commands::add_command(const flash_string_vector & name, const flash_string_vector & arguments, command_function function, argument_completion_function arg_function) {
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function, argument_completion_function arg_function) {
add_command(0, 0, name, arguments, function, arg_function);
}
void Commands::add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, command_function function) {
add_command(context, flags, name, flash_string_vector{}, function, nullptr);
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, command_function function) {
add_command(context, flags, name, string_vector{}, function, nullptr);
}
void Commands::add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, const flash_string_vector & arguments, command_function function) {
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, const string_vector & arguments, command_function function) {
add_command(context, flags, name, arguments, function, nullptr);
}
void Commands::add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, const flash_string_vector & arguments, command_function function, argument_completion_function arg_function) {
void Commands::add_command(unsigned int context,
unsigned int flags,
const string_vector & name,
const string_vector & arguments,
command_function function,
argument_completion_function arg_function) {
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, name, arguments, function, arg_function));
}
// added by proddy
// note we should really iterate and free up the lambda code and any flashstrings
void Commands::remove_all_commands() {
@@ -139,7 +145,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
for (size_t length = 0; all_match && length < shortest_match; length++) {
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
if (read_flash_string(*std::next(first.begin(), length)) != read_flash_string(*std::next(command_it->second->name_.begin(), length))) {
if ((*std::next(first.begin(), length)) != (*std::next(command_it->second->name_.begin(), length))) {
all_match = false;
break;
}
@@ -152,7 +158,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
auto name_it = first.begin();
for (size_t i = 0; i < component_prefix; i++) {
longest_name.push_back(std::move(read_flash_string(*name_it)));
longest_name.push_back(std::move((*name_it)));
name_it++;
}
}
@@ -167,7 +173,8 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
// This relies on the null terminator character limiting the
// length before it becomes longer than any of the strings
if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length) != pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length)
!= pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
all_match = false;
break;
}
@@ -179,7 +186,8 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
}
if (chars_prefix > 0) {
longest_name.push_back(std::move(read_flash_string(first).substr(0, chars_prefix)));
// TODO fix, no more PGM
longest_name.push_back(std::string(first).substr(0, chars_prefix));
return false;
}
}
@@ -243,7 +251,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
}
if (!temp_command_name.empty() && command_line.total_size() <= temp_command_name.size()) {
temp_command = std::make_unique<Command>(0, flash_string_vector{}, flash_string_vector{}, nullptr, nullptr);
temp_command = std::make_unique<Command>(0, string_vector{}, string_vector{}, nullptr, nullptr);
count = 1;
match = commands.partial.end();
result.replacement.trailing_space = whole_components;
@@ -259,10 +267,11 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
auto & matching_command = match->second;
for (auto & name : matching_command->name_) {
result.replacement->push_back(std::move(read_flash_string(name)));
result.replacement->push_back(std::move((name)));
}
if (command_line.total_size() > result.replacement->size() && command_line.total_size() <= matching_command->name_.size() + matching_command->maximum_arguments()) {
if (command_line.total_size() > result.replacement->size()
&& command_line.total_size() <= matching_command->name_.size() + matching_command->maximum_arguments()) {
// Try to auto-complete arguments
std::vector<std::string> arguments{std::next(command_line->cbegin(), result.replacement->size()), command_line->cend()};
@@ -332,7 +341,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
remaining_help.escape_initial_parameters();
for (auto it = std::next(matching_command->arguments_.cbegin(), current_args_count); it != matching_command->arguments_.cend(); it++) {
remaining_help->push_back(std::move(read_flash_string(*it)));
remaining_help->push_back(std::move((*it)));
}
}
@@ -380,7 +389,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
}
for (; flash_name_it != command_it->second->name_.cend(); flash_name_it++) {
std::string name = read_flash_string(*flash_name_it);
std::string name = (*flash_name_it);
// Skip parts of the command name that match the command line
if (line_it != command_line->cend()) {
@@ -403,7 +412,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
continue;
}
help->push_back(std::move(read_flash_string(argument)));
help->push_back(std::move((argument)));
}
result.help.push_back(std::move(help));
@@ -416,7 +425,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
if (commands.exact.count(longest->first) == 1) {
for (auto & name : longest->second->name_) {
result.replacement->push_back(std::move(read_flash_string(name)));
result.replacement->push_back(name);
}
// Add a space because there are sub-commands for a command that has matched exactly
@@ -449,7 +458,7 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
auto line_it = command_line->cbegin();
for (; name_it != command.name_.cend() && line_it != command_line->cend(); name_it++, line_it++) {
std::string name = read_flash_string(*name_it);
std::string name = (*name_it);
size_t found = name.rfind(*line_it, 0);
if (found == std::string::npos) {
@@ -499,12 +508,12 @@ void Commands::for_each_available_command(Shell & shell, apply_function f) const
name.reserve(command_it->second.name_.size());
for (auto flash_name : command_it->second.name_) {
name.push_back(std::move(read_flash_string(flash_name)));
name.push_back(std::move((flash_name)));
}
arguments.reserve(command_it->second.arguments_.size());
for (auto flash_argument : command_it->second.arguments_) {
arguments.push_back(std::move(read_flash_string(flash_argument)));
arguments.push_back(std::move((flash_argument)));
}
f(name, arguments);
@@ -512,7 +521,11 @@ void Commands::for_each_available_command(Shell & shell, apply_function f) const
}
}
Commands::Command::Command(unsigned int flags, const flash_string_vector name, const flash_string_vector arguments, command_function function, argument_completion_function arg_function)
Commands::Command::Command(unsigned int flags,
const string_vector name,
const string_vector arguments,
command_function function,
argument_completion_function arg_function)
: flags_(flags)
, name_(name)
, arguments_(arguments)
@@ -524,7 +537,7 @@ Commands::Command::~Command() {
}
size_t Commands::Command::minimum_arguments() const {
return std::count_if(arguments_.cbegin(), arguments_.cend(), [](const __FlashStringHelper * argument) { return pgm_read_byte(argument) == '<'; });
return std::count_if(arguments_.cbegin(), arguments_.cend(), [](const char * argument) { return pgm_read_byte(argument) == '<'; });
}
} // namespace console

View File

@@ -135,8 +135,8 @@ void Shell::loop_one() {
}
}
void Shell::set_command_str(const __FlashStringHelper * str) {
line_buffer_ = read_flash_string(str);
void Shell::set_command_str(const char * str) {
line_buffer_ = (str);
erase_current_line();
prompt_displayed_ = false;
display_prompt();
@@ -262,30 +262,30 @@ void Shell::loop_normal() {
} else if (esc_ == 1) { // pos1
cursor_ = line_buffer_.length();
} else if (esc_ == 11) { // F1
set_command_str(F("help"));
set_command_str("help");
} else if (esc_ == 12) { // F2
set_command_str(F("show"));
set_command_str("show");
} else if (esc_ == 13) { // F3
set_command_str(F("log notice"));
set_command_str("log notice");
} else if (esc_ == 14) { // F4
set_command_str(F("log info"));
set_command_str("log info");
} else if (esc_ == 15) { // F5
set_command_str(F("log debug"));
set_command_str("log debug");
} else if (esc_ == 17) { // F6
set_command_str(F("watch off"));
set_command_str("watch off");
} else if (esc_ == 18) { // F7
set_command_str(F("watch on"));
set_command_str("watch on");
} else if (esc_ == 19) { // F8
set_command_str(F("watch raw"));
set_command_str("watch raw");
} else if (esc_ == 20) { // F9
set_command_str(F("call system info"));
set_command_str("call system info");
} else if (esc_ == 21) { // F10
set_command_str(F("call system settings"));
set_command_str("call system settings");
} else if (esc_ == 23) { // F11
line_buffer_ = read_flash_string(F("call send \"0B \""));
line_buffer_ = ("call send \"0B \"");
cursor_ = 1;
} else if (esc_ == 24) { // F12
set_command_str(F("log debug; watch raw"));
set_command_str("log debug; watch raw");
}
esc_ = 0;
} else if (c >= '0' && (c <= '9')) { // numbers
@@ -320,7 +320,7 @@ void Shell::loop_normal() {
idle_time_ = uuid::get_uptime_ms();
}
Shell::PasswordData::PasswordData(const __FlashStringHelper * password_prompt, password_function && password_function)
Shell::PasswordData::PasswordData(const char * password_prompt, password_function && password_function)
: password_prompt_(password_prompt)
, password_function_(std::move(password_function)) {
}
@@ -452,7 +452,7 @@ void Shell::loop_blocking() {
}
}
void Shell::enter_password(const __FlashStringHelper * prompt, password_function function) {
void Shell::enter_password(const char * prompt, password_function function) {
if (mode_ == Mode::NORMAL) {
mode_ = Mode::PASSWORD;
mode_data_ = std::make_unique<Shell::PasswordData>(prompt, std::move(function));

View File

@@ -30,8 +30,8 @@ namespace uuid {
namespace console {
static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "shell";
const uuid::log::Logger Shell::logger_{reinterpret_cast<const __FlashStringHelper *>(__pstr__logger_name), uuid::log::Facility::LPR};
static const char __pstr__logger_name[] = "shell";
const uuid::log::Logger Shell::logger_{(__pstr__logger_name), uuid::log::Facility::LPR};
Shell::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
: id_(id)

View File

@@ -335,7 +335,7 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
* execution.
* @since 0.1.0
*/
void enter_password(const __FlashStringHelper * prompt, password_function function);
void enter_password(const char * prompt, password_function function);
/**
* Stop executing anything on this shell for a period of time.
@@ -682,11 +682,11 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
* execution.
* @since 0.1.0
*/
PasswordData(const __FlashStringHelper * password_prompt, password_function && password_function);
PasswordData(const char * password_prompt, password_function && password_function);
~PasswordData() override = default;
const __FlashStringHelper * password_prompt_; /*!< Prompt requesting password input. @since 0.1.0 */
password_function password_function_; /*!< Function to execute after password entry. @since 0.1.0 */
const char * password_prompt_; /*!< Prompt requesting password input. @since 0.1.0 */
password_function password_function_; /*!< Function to execute after password entry. @since 0.1.0 */
};
/**
@@ -761,7 +761,7 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
const std::shared_ptr<const uuid::log::Message> content_; /*!< Log message content. @since 0.1.0 */
};
Shell(const Shell &) = delete;
Shell(const Shell &) = delete;
Shell & operator=(const Shell &) = delete;
/**
@@ -893,7 +893,7 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
* @since 0.1.0
*/
size_t vprintf(const __FlashStringHelper * format, va_list ap);
void set_command_str(const __FlashStringHelper * str);
void set_command_str(const char * str);
static const uuid::log::Logger logger_; /*!< uuid::log::Logger instance for shells. @since 0.1.0 */
static std::set<std::shared_ptr<Shell>> shells_; /*!< Registered running shells to be executed. @since 0.1.0 */
@@ -906,7 +906,7 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum command line length in bytes. @since 0.6.0 */
std::string line_buffer_; /*!< Command line buffer. Limited to maximum_command_line_length() bytes. @since 0.1.0 */
std::string line_old_[MAX_LINES]; /*!< old Command line buffer.*/
uint8_t line_no_ = 0;
uint8_t line_no_ = 0;
size_t maximum_command_line_length_ = MAX_COMMAND_LINE_LENGTH; /*!< Maximum command line length in bytes. @since 0.6.0 */
unsigned char previous_ = 0; /*!< Previous character that was entered on the command line. Used to detect CRLF line endings. @since 0.1.0 */
uint8_t cursor_ = 0; /*!< cursor position from end of line */
@@ -955,9 +955,9 @@ class CommandLine {
~CommandLine() = default;
#ifdef UNIT_TEST
CommandLine(CommandLine &&) = default;
CommandLine & operator=(CommandLine &&) = default;
CommandLine(const CommandLine &) __attribute__((deprecated)) = default;
CommandLine(CommandLine &&) = default;
CommandLine & operator=(CommandLine &&) = default;
CommandLine(const CommandLine &) __attribute__((deprecated)) = default;
CommandLine & operator=(const CommandLine &) __attribute__((deprecated)) = default;
#endif
@@ -1188,7 +1188,7 @@ class Commands {
* executed.
* @since 0.2.0
*/
void add_command(const flash_string_vector & name, command_function function);
void add_command(const string_vector & name, command_function function);
/**
* Add a command with arguments to the list of commands in this
* container.
@@ -1205,7 +1205,7 @@ class Commands {
* executed.
* @since 0.2.0
*/
void add_command(const flash_string_vector & name, const flash_string_vector & arguments, command_function function);
void add_command(const string_vector & name, const string_vector & arguments, command_function function);
/**
* Add a command with arguments and automatic argument completion
* to the list of commands in this container.
@@ -1224,7 +1224,7 @@ class Commands {
* completions for this command.
* @since 0.2.0
*/
void add_command(const flash_string_vector & name, const flash_string_vector & arguments, command_function function, argument_completion_function arg_function);
void add_command(const string_vector & name, const string_vector & arguments, command_function function, argument_completion_function arg_function);
/**
* Add a command with no arguments to the list of commands in this
* container.
@@ -1239,7 +1239,7 @@ class Commands {
* executed.
* @since 0.1.0
*/
void add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, command_function function);
void add_command(unsigned int context, unsigned int flags, const string_vector & name, command_function function);
/**
* Add a command with arguments to the list of commands in this
* container.
@@ -1257,7 +1257,7 @@ class Commands {
* executed.
* @since 0.1.0
*/
void add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, const flash_string_vector & arguments, command_function function);
void add_command(unsigned int context, unsigned int flags, const string_vector & name, const string_vector & arguments, command_function function);
/**
* Add a command with arguments and automatic argument completion
* to the list of commands in this container.
@@ -1279,8 +1279,8 @@ class Commands {
*/
void add_command(unsigned int context,
unsigned int flags,
const flash_string_vector & name,
const flash_string_vector & arguments,
const string_vector & name,
const string_vector & arguments,
command_function function,
argument_completion_function arg_function);
@@ -1344,11 +1344,7 @@ class Commands {
* completions for this command.
* @since 0.1.0
*/
Command(unsigned int flags,
const flash_string_vector name,
const flash_string_vector arguments,
command_function function,
argument_completion_function arg_function);
Command(unsigned int flags, const string_vector name, const string_vector arguments, command_function function, argument_completion_function arg_function);
~Command();
/**
@@ -1372,13 +1368,13 @@ class Commands {
}
unsigned int flags_; /*!< Shell flags that must be set for this command to be available. @since 0.1.0 */
const flash_string_vector name_; /*!< Name of the command as a std::vector of flash strings. @since 0.1.0 */
const flash_string_vector arguments_; /*!< Help text for arguments that the command accepts as a std::vector of flash strings. @since 0.1.0 */
const string_vector name_; /*!< Name of the command as a std::vector of flash strings. @since 0.1.0 */
const string_vector arguments_; /*!< Help text for arguments that the command accepts as a std::vector of flash strings. @since 0.1.0 */
command_function function_; /*!< Function to be used when the command is executed. @since 0.1.0 */
argument_completion_function arg_function_; /*!< Function to be used to perform argument completions for this command. @since 0.1.0 */
private:
Command(const Command &) = delete;
Command(const Command &) = delete;
Command & operator=(const Command &) = delete;
};
@@ -1487,7 +1483,7 @@ class StreamConsole : virtual public Shell {
explicit StreamConsole(Stream & stream);
private:
StreamConsole(const StreamConsole &) = delete;
StreamConsole(const StreamConsole &) = delete;
StreamConsole & operator=(const StreamConsole &) = delete;
/**

View File

@@ -26,17 +26,17 @@ namespace uuid {
namespace log {
static constexpr const char * pstr_level_lowercase_off __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "off";
static constexpr const char * pstr_level_lowercase_emerg __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "emerg";
static constexpr const char * pstr_level_lowercase_crit __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "crit";
static constexpr const char * pstr_level_lowercase_alert __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "alert";
static constexpr const char * pstr_level_lowercase_err __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "err";
static constexpr const char * pstr_level_lowercase_warning __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "warning";
static constexpr const char * pstr_level_lowercase_notice __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "notice";
static constexpr const char * pstr_level_lowercase_info __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "info";
static constexpr const char * pstr_level_lowercase_debug __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "debug";
static constexpr const char * pstr_level_lowercase_trace __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "trace";
static constexpr const char * pstr_level_lowercase_all __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "all";
static constexpr const char * pstr_level_lowercase_off = "off";
static constexpr const char * pstr_level_lowercase_emerg = "emerg";
static constexpr const char * pstr_level_lowercase_crit = "crit";
static constexpr const char * pstr_level_lowercase_alert = "alert";
static constexpr const char * pstr_level_lowercase_err = "err";
static constexpr const char * pstr_level_lowercase_warning = "warning";
static constexpr const char * pstr_level_lowercase_notice = "notice";
static constexpr const char * pstr_level_lowercase_info = "info";
static constexpr const char * pstr_level_lowercase_debug = "debug";
static constexpr const char * pstr_level_lowercase_trace = "trace";
static constexpr const char * pstr_level_lowercase_all = "all";
static const __FlashStringHelper * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_off),

View File

@@ -26,17 +26,17 @@ namespace uuid {
namespace log {
static constexpr const char * pstr_level_uppercase_off __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "OFF";
static constexpr const char * pstr_level_uppercase_emerg __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "EMERG";
static constexpr const char * pstr_level_uppercase_crit __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "CRIT";
static constexpr const char * pstr_level_uppercase_alert __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ALERT";
static constexpr const char * pstr_level_uppercase_err __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ERR";
static constexpr const char * pstr_level_uppercase_warning __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "WARNING";
static constexpr const char * pstr_level_uppercase_notice __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "NOTICE";
static constexpr const char * pstr_level_uppercase_info __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "INFO";
static constexpr const char * pstr_level_uppercase_debug __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "DEBUG";
static constexpr const char * pstr_level_uppercase_trace __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "TRACE";
static constexpr const char * pstr_level_uppercase_all __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ALL";
static constexpr const char * pstr_level_uppercase_off = "OFF";
static constexpr const char * pstr_level_uppercase_emerg = "EMERG";
static constexpr const char * pstr_level_uppercase_crit = "CRIT";
static constexpr const char * pstr_level_uppercase_alert = "ALERT";
static constexpr const char * pstr_level_uppercase_err = "ERR";
static constexpr const char * pstr_level_uppercase_warning = "WARNING";
static constexpr const char * pstr_level_uppercase_notice = "NOTICE";
static constexpr const char * pstr_level_uppercase_info = "INFO";
static constexpr const char * pstr_level_uppercase_debug = "DEBUG";
static constexpr const char * pstr_level_uppercase_trace = "TRACE";
static constexpr const char * pstr_level_uppercase_all = "ALL";
static const __FlashStringHelper * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_off),

View File

@@ -47,7 +47,7 @@ std::string format_timestamp_ms(uint64_t timestamp_ms, unsigned int days_width)
static std::vector<char> text(10 + 1 /* days */ + 2 + 1 /* hours */ + 2 + 1 /* minutes */ + 2 + 1 /* seconds */ + 3 /* milliseconds */ + 1);
snprintf_P(text.data(), text.size(), PSTR("%0*lu+%02u:%02u:%02u.%03u"), std::min(days_width, 10U), days, hours, minutes, seconds, milliseconds);
snprintf(text.data(), text.size(), ("%0*lu+%02u:%02u:%02u.%03u"), std::min(days_width, 10U), days, hours, minutes, seconds, milliseconds);
return text.data();
}

View File

@@ -36,7 +36,7 @@ namespace log {
std::map<Handler *, Level> Logger::handlers_;
Level Logger::level_ = Level::OFF;
Message::Message(uint64_t uptime_ms, Level level, Facility facility, const __FlashStringHelper * name, const std::string && text)
Message::Message(uint64_t uptime_ms, Level level, Facility facility, const char * name, const std::string && text)
: uptime_ms(uptime_ms)
, level(level)
, facility(facility)
@@ -44,7 +44,7 @@ Message::Message(uint64_t uptime_ms, Level level, Facility facility, const __Fla
, text(std::move(text)) {
}
Logger::Logger(const __FlashStringHelper * name, Facility facility)
Logger::Logger(const char * name, Facility facility)
: name_(name)
, facility_(facility){

View File

@@ -231,7 +231,7 @@ struct Message {
* @param[in] text Log message text.
* @since 1.0.0
*/
Message(uint64_t uptime_ms, Level level, Facility facility, const __FlashStringHelper * name, const std::string && text);
Message(uint64_t uptime_ms, Level level, Facility facility, const char * name, const std::string && text);
~Message() = default;
/**
@@ -261,7 +261,7 @@ struct Message {
*
* @since 1.0.0
*/
const __FlashStringHelper * name;
const char * name;
/**
* Formatted log message text.
@@ -331,7 +331,7 @@ class Logger {
*
* @since 1.0.0
*/
Logger(const __FlashStringHelper * name, Facility facility = Facility::LOCAL0);
Logger(const char * name, Facility facility = Facility::LOCAL0);
~Logger() = default;
/**
@@ -626,8 +626,8 @@ class Logger {
static std::map<Handler *, Level> handlers_; /*!< Registered log handlers. @since 1.0.0 */
static Level level_; /*!< Minimum global log level across all handlers. @since 1.0.0 */
const __FlashStringHelper * name_; /*!< Logger name (flash string). @since 1.0.0 */
const Facility facility_; /*!< Default logging facility for messages. @since 1.0.0 */
const char * name_; /*!< Logger name (flash string). @since 1.0.0 */
const Facility facility_; /*!< Default logging facility for messages. @since 1.0.0 */
};
} // namespace log

View File

@@ -82,13 +82,13 @@
#include <uuid/common.h>
#include <uuid/log.h>
static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "syslog";
static const char __pstr__logger_name[] = "syslog";
namespace uuid {
namespace syslog {
uuid::log::Logger SyslogService::logger_{FPSTR(__pstr__logger_name), uuid::log::Facility::SYSLOG};
uuid::log::Logger SyslogService::logger_{__pstr__logger_name, uuid::log::Facility::SYSLOG};
bool SyslogService::QueuedLogMessage::time_good_ = false;
SyslogService::~SyslogService() {
@@ -116,7 +116,7 @@ void SyslogService::remove_queued_messages(uuid::log::Level level) {
}
}
log_message_id_ -= offset;
log_message_id_ -= offset;
log_message_fails_ += offset;
}
@@ -269,8 +269,8 @@ void SyslogService::loop() {
operator<<(std::make_shared<uuid::log::Message>(uuid::get_uptime_ms(),
uuid::log::Level::INFO,
uuid::log::Facility::SYSLOG,
reinterpret_cast<const __FlashStringHelper *>(__pstr__logger_name),
uuid::read_flash_string(F("-- MARK --"))));
(__pstr__logger_name),
(F("-- MARK --"))));
}
}
}
@@ -443,7 +443,7 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
udp_.print('-');
}
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), (message.content_->name));
char id_c_str[15];
snprintf_P(id_c_str, sizeof(id_c_str), PSTR(" %lu: "), message.id_);

View File

@@ -28,329 +28,322 @@ namespace uuid {
namespace telnet {
TelnetStream::TelnetStream(WiFiClient &client)
: client_(client) {
output_buffer_.reserve(BUFFER_SIZE);
TelnetStream::TelnetStream(WiFiClient & client)
: client_(client) {
output_buffer_.reserve(BUFFER_SIZE);
}
void TelnetStream::start() {
raw_write({
IAC, WILL, OPT_ECHO,
IAC, WILL, OPT_BINARY,
IAC, WILL, OPT_SGA,
IAC, DONT, OPT_ECHO,
IAC, DO, OPT_BINARY,
IAC, DO, OPT_SGA
});
raw_write({IAC, WILL, OPT_ECHO, IAC, WILL, OPT_BINARY, IAC, WILL, OPT_SGA, IAC, DONT, OPT_ECHO, IAC, DO, OPT_BINARY, IAC, DO, OPT_SGA});
}
int TelnetStream::available() {
if (peek() == -1) {
return 0;
} else {
return 1;
}
if (peek() == -1) {
return 0;
} else {
return 1;
}
}
int TelnetStream::read() {
if (peek_ != -1) {
int data = peek_;
peek_ = -1;
return data;
}
if (peek_ != -1) {
int data = peek_;
peek_ = -1;
return data;
}
buffer_flush();
buffer_flush();
restart:
int data = raw_read();
int data = raw_read();
if (data == -1) {
return -1;
}
if (data == -1) {
return -1;
}
unsigned char c = data;
unsigned char c = data;
if (sub_negotiation_) {
if (previous_raw_in_ == IAC) {
switch (c) {
case SE:
sub_negotiation_ = false;
previous_raw_in_ = 0;
goto restart;
if (sub_negotiation_) {
if (previous_raw_in_ == IAC) {
switch (c) {
case SE:
sub_negotiation_ = false;
previous_raw_in_ = 0;
goto restart;
case IAC:
previous_raw_in_ = 0;
goto restart;
}
} else {
switch (c) {
case IAC:
previous_raw_in_ = c;
goto restart;
case IAC:
previous_raw_in_ = 0;
goto restart;
}
} else {
switch (c) {
case IAC:
previous_raw_in_ = c;
goto restart;
default:
previous_raw_in_ = 0;
goto restart;
}
}
} else {
if (previous_raw_in_ == IAC) {
switch (c) {
case IP:
// Interrupt (^C)
previous_raw_in_ = 0;
c = '\x03';
break;
default:
previous_raw_in_ = 0;
goto restart;
}
}
} else {
if (previous_raw_in_ == IAC) {
switch (c) {
case IP:
// Interrupt (^C)
previous_raw_in_ = 0;
c = '\x03';
break;
case EC:
// Backspace (^H)
previous_raw_in_ = 0;
c = '\x08';
break;
case EC:
// Backspace (^H)
previous_raw_in_ = 0;
c = '\x08';
break;
case EL:
// Delete line (^U)
previous_raw_in_ = 0;
c = '\x15';
break;
case EL:
// Delete line (^U)
previous_raw_in_ = 0;
c = '\x15';
break;
case IAC:
previous_raw_in_ = 0;
break;
case IAC:
previous_raw_in_ = 0;
break;
case SB:
case WILL:
case WONT:
case DO:
case DONT:
previous_raw_in_ = c;
goto restart;
case SB:
case WILL:
case WONT:
case DO:
case DONT:
previous_raw_in_ = c;
goto restart;
case SE:
case DM:
case BRK:
case AO:
case AYT:
case GA:
case NOP:
default:
previous_raw_in_ = 0;
goto restart;
}
} else if (previous_raw_in_ == SB) {
sub_negotiation_ = true;
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == WILL || previous_raw_in_ == WONT) {
switch (c) {
case OPT_ECHO:
// Don't do these
raw_write({IAC, DONT, c});
break;
case SE:
case DM:
case BRK:
case AO:
case AYT:
case GA:
case NOP:
default:
previous_raw_in_ = 0;
goto restart;
}
} else if (previous_raw_in_ == SB) {
sub_negotiation_ = true;
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == WILL || previous_raw_in_ == WONT) {
switch (c) {
case OPT_ECHO:
// Don't do these
raw_write({IAC, DONT, c});
break;
case OPT_BINARY:
case OPT_SGA:
// Do these
raw_write({IAC, DO, c});
break;
case OPT_BINARY:
case OPT_SGA:
// Do these
raw_write({IAC, DO, c});
break;
default:
// Don't do anything else
raw_write({IAC, DONT, c});
break;
}
default:
// Don't do anything else
raw_write({IAC, DONT, c});
break;
}
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == DO) {
switch (c) {
case OPT_ECHO:
case OPT_BINARY:
case OPT_SGA:
// These are always enabled
break;
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == DO) {
switch (c) {
case OPT_ECHO:
case OPT_BINARY:
case OPT_SGA:
// These are always enabled
break;
default:
// Refuse to do anything else
raw_write({IAC, WONT, c});
break;
}
default:
// Refuse to do anything else
raw_write({IAC, WONT, c});
break;
}
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == DONT) {
switch (c) {
case OPT_ECHO:
case OPT_BINARY:
case OPT_SGA:
// Insist that we do these
raw_write({IAC, WILL, c});
break;
previous_raw_in_ = 0;
goto restart;
} else if (previous_raw_in_ == DONT) {
switch (c) {
case OPT_ECHO:
case OPT_BINARY:
case OPT_SGA:
// Insist that we do these
raw_write({IAC, WILL, c});
break;
default:
// Everything else is always disabled
break;
}
default:
// Everything else is always disabled
break;
}
previous_raw_in_ = 0;
goto restart;
} else {
switch (c) {
case IAC:
previous_raw_in_ = c;
goto restart;
previous_raw_in_ = 0;
goto restart;
} else {
switch (c) {
case IAC:
previous_raw_in_ = c;
goto restart;
default:
previous_raw_in_ = 0;
break;
}
}
}
default:
previous_raw_in_ = 0;
break;
}
}
}
if (previous_in_ == CR) {
if (c == NUL) {
previous_in_ = 0;
goto restart;
}
}
if (previous_in_ == CR) {
if (c == NUL) {
previous_in_ = 0;
goto restart;
}
}
previous_in_ = c;
return c;
previous_in_ = c;
return c;
}
int TelnetStream::peek() {
buffer_flush();
buffer_flush();
// It's too complicated to implement this by calling peek()
// on the original stream, especially if the original stream
// doesn't actually support peeking.
if (peek_ == -1) {
peek_ = read();
}
// It's too complicated to implement this by calling peek()
// on the original stream, especially if the original stream
// doesn't actually support peeking.
if (peek_ == -1) {
peek_ = read();
}
return peek_;
return peek_;
}
size_t TelnetStream::write(uint8_t data) {
if (previous_out_ == CR && data != LF) {
previous_out_ = data;
if (previous_out_ == CR && data != LF) {
previous_out_ = data;
if (raw_write({NUL, data}) != 2) {
return 0;
}
} else {
previous_out_ = data;
}
if (raw_write({NUL, data}) != 2) {
return 0;
}
} else {
previous_out_ = data;
}
if (data == IAC) {
if (raw_write({IAC, IAC}) != 2) {
return 0;
}
} else {
if (raw_write(data) != 1) {
return 0;
}
}
if (data == IAC) {
if (raw_write({IAC, IAC}) != 2) {
return 0;
}
} else {
if (raw_write(data) != 1) {
return 0;
}
}
return 1;
return 1;
}
size_t TelnetStream::write(const uint8_t *buffer, size_t size) {
std::vector<unsigned char> data;
data.reserve(size);
size_t TelnetStream::write(const uint8_t * buffer, size_t size) {
std::vector<unsigned char> data;
data.reserve(size);
while (size-- > 0) {
unsigned char c = *buffer++;
while (size-- > 0) {
unsigned char c = *buffer++;
if (previous_out_ == CR && c != LF) {
data.push_back((unsigned char)NUL);
}
if (previous_out_ == CR && c != LF) {
data.push_back((unsigned char)NUL);
}
if (c == IAC) {
data.push_back((unsigned char)IAC);
}
if (c == IAC) {
data.push_back((unsigned char)IAC);
}
previous_out_ = c;
data.push_back(c);
}
previous_out_ = c;
data.push_back(c);
}
size_t len = raw_write(data);
if (len < size) {
len = 0;
}
return len;
size_t len = raw_write(data);
if (len < size) {
len = 0;
}
return len;
}
void TelnetStream::flush() {
// This is a pure virtual function in Arduino's Stream class, which
// makes no sense because that class is for input and this is an
// output function. Later versions move it to Print as an empty
// virtual function so this is here for backward compatibility.
// This is a pure virtual function in Arduino's Stream class, which
// makes no sense because that class is for input and this is an
// output function. Later versions move it to Print as an empty
// virtual function so this is here for backward compatibility.
}
int TelnetStream::raw_available() {
return client_.available();
return client_.available();
}
int TelnetStream::raw_read() {
return client_.read();
return client_.read();
}
void TelnetStream::buffer_flush() {
if (!output_buffer_.empty()) {
size_t len = client_.write(reinterpret_cast<const unsigned char*>(output_buffer_.data()), output_buffer_.size());
if (len != output_buffer_.size()) {
client_.stop();
}
output_buffer_.clear();
output_buffer_.shrink_to_fit();
}
if (!output_buffer_.empty()) {
size_t len = client_.write(reinterpret_cast<const unsigned char *>(output_buffer_.data()), output_buffer_.size());
if (len != output_buffer_.size()) {
client_.stop();
}
output_buffer_.clear();
output_buffer_.shrink_to_fit();
}
}
size_t TelnetStream::raw_write(unsigned char data) {
output_buffer_.push_back(data);
output_buffer_.push_back(data);
if (output_buffer_.size() >= BUFFER_SIZE) {
buffer_flush();
}
if (output_buffer_.size() >= BUFFER_SIZE) {
buffer_flush();
}
return 1;
return 1;
}
size_t TelnetStream::raw_write(const std::vector<unsigned char> &data) {
return raw_write(reinterpret_cast<const unsigned char*>(data.data()), data.size());
size_t TelnetStream::raw_write(const std::vector<unsigned char> & data) {
return raw_write(reinterpret_cast<const unsigned char *>(data.data()), data.size());
}
size_t TelnetStream::raw_write(const uint8_t *buffer, size_t size) {
size_t offset = 0;
size_t remaining = size;
size_t TelnetStream::raw_write(const uint8_t * buffer, size_t size) {
size_t offset = 0;
size_t remaining = size;
if (!output_buffer_.empty()) {
// Fill the rest of the buffer
size_t block = std::min(remaining, BUFFER_SIZE - output_buffer_.size());
if (!output_buffer_.empty()) {
// Fill the rest of the buffer
size_t block = std::min(remaining, BUFFER_SIZE - output_buffer_.size());
output_buffer_.insert(output_buffer_.end(), buffer, buffer + block);
offset += block;
remaining -= block;
output_buffer_.insert(output_buffer_.end(), buffer, buffer + block);
offset += block;
remaining -= block;
if (output_buffer_.size() >= BUFFER_SIZE) {
buffer_flush();
}
}
if (output_buffer_.size() >= BUFFER_SIZE) {
buffer_flush();
}
}
if (remaining >= BUFFER_SIZE) {
// Output directly if it won't fit in the buffer
size_t len = client_.write(buffer + offset, remaining);
if (len != remaining) {
client_.stop();
return offset + len;
}
} else if (remaining > 0) {
// Put the rest in the buffer
output_buffer_.insert(output_buffer_.end(), buffer + offset, buffer + offset + remaining);
}
if (remaining >= BUFFER_SIZE) {
// Output directly if it won't fit in the buffer
size_t len = client_.write(buffer + offset, remaining);
if (len != remaining) {
client_.stop();
return offset + len;
}
} else if (remaining > 0) {
// Put the rest in the buffer
output_buffer_.insert(output_buffer_.end(), buffer + offset, buffer + offset + remaining);
}
return size;
return size;
}
} // namespace telnet

View File

@@ -54,22 +54,20 @@
#endif
#endif
static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "telnet";
namespace uuid {
namespace telnet {
uuid::log::Logger TelnetService::logger_{FPSTR(__pstr__logger_name), uuid::log::Facility::DAEMON};
uuid::log::Logger TelnetService::logger_{"telnet", uuid::log::Facility::DAEMON};
TelnetService::TelnetService(std::shared_ptr<uuid::console::Commands> commands, unsigned int context, unsigned int flags)
: TelnetService(DEFAULT_PORT, commands, context, flags) {
}
TelnetService::TelnetService(uint16_t port, std::shared_ptr<uuid::console::Commands> commands, unsigned int context, unsigned int flags)
: TelnetService(port, [commands, context, flags](Stream & stream, const IPAddress & addr __attribute__((unused)), uint16_t port __attribute__((unused))) -> std::shared_ptr<uuid::console::Shell> {
return std::make_shared<uuid::console::StreamConsole>(commands, stream, context, flags);
}) {
: TelnetService(port,
[commands, context, flags](Stream & stream, const IPAddress & addr __attribute__((unused)), uint16_t port __attribute__((unused)))
-> std::shared_ptr<uuid::console::Shell> { return std::make_shared<uuid::console::StreamConsole>(commands, stream, context, flags); }) {
}
TelnetService::TelnetService(shell_factory_function shell_factory)
@@ -145,7 +143,9 @@ void TelnetService::loop() {
if (client) {
if (connections_.size() >= maximum_connections_) {
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
logger_.info(F("New connection from [%s]:%u rejected (connection limit reached)"), uuid::printable_to_string(client.remoteIP()).c_str(), client.remotePort());
logger_.info(F("New connection from [%s]:%u rejected (connection limit reached)"),
uuid::printable_to_string(client.remoteIP()).c_str(),
client.remotePort());
#else
logger_.info(F("New connection rejected (connection limit reached)"));
#endif

View File

@@ -21,9 +21,9 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_ESP8266
# include <ESP8266WiFi.h>
#include <ESP8266WiFi.h>
#else
# include <WiFi.h>
#include <WiFi.h>
#endif
#include <WiFiUdp.h>
@@ -51,47 +51,47 @@ namespace telnet {
*
* @since 0.1.0
*/
class TelnetStream: public ::Stream {
public:
/**
class TelnetStream : public ::Stream {
public:
/**
* Create a new telnet stream wrapper.
*
* @param[in] client Client connection.
* @since 0.1.0
*/
explicit TelnetStream(WiFiClient &client);
virtual ~TelnetStream() = default;
explicit TelnetStream(WiFiClient & client);
virtual ~TelnetStream() = default;
/**
/**
* Perform initial negotiation.
*
* @since 0.1.0
*/
void start();
void start();
/**
/**
* Check for available input.
*
* @return The number of bytes available to read.
* @since 0.1.0
*/
int available() override;
/**
int available() override;
/**
* Read one byte from the available input.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.1.0
*/
int read() override;
/**
int read() override;
/**
* Read one byte from the available input without advancing to the
* next one.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.1.0
*/
int peek() override;
/**
int peek() override;
/**
* Write one byte to the output stream.
*
* Disconnect the client if the socket buffer is full.
@@ -100,8 +100,8 @@ public:
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(uint8_t data) override;
/**
size_t write(uint8_t data) override;
/**
* Write an array of bytes to the output stream.
*
* Disconnect the client if the socket buffer is full.
@@ -111,8 +111,8 @@ public:
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(const uint8_t *buffer, size_t size) override;
/**
size_t write(const uint8_t * buffer, size_t size) override;
/**
* Does nothing.
*
* This is a pure virtual function in Arduino's Stream class, which
@@ -122,66 +122,69 @@ public:
*
* @since 0.1.0
*/
void flush() override;
void flush() override;
private:
static constexpr const unsigned char NUL = 0; /*!< No operation. @since 0.1.0 */
static constexpr const unsigned char BEL = 7; /*!< Produces an audible or visible signal. @since 0.1.0 */
static constexpr const unsigned char BS = 8; /*!< Moves the print head one character position towards the left margin. @since 0.1.0 */
static constexpr const unsigned char HT = 9; /*!< Moves the printer to the next horizontal tab stop. @since 0.1.0 */
static constexpr const unsigned char LF = 10; /*!< Line Feed. @since 0.1.0 */
static constexpr const unsigned char VT = 11; /*!< Moves the printer to the next vertical tab stop. @since 0.1.0 */
static constexpr const unsigned char FF = 12; /*!< Moves the printer to the top of the next page, keeping the same horizontal position. @since 0.1.0 */
static constexpr const unsigned char CR = 13; /*!< Carriage Return. @since 0.1.0 */
static constexpr const unsigned char SE = 240; /*!< End of sub-negotiation parameters. @since 0.1.0 */
static constexpr const unsigned char NOP = 241; /*!< No operation. @since 0.1.0 */
static constexpr const unsigned char DM = 242; /*!< The data stream portion of a Synch. @since 0.1.0 */
static constexpr const unsigned char BRK = 243; /*!< NVT character BRK. @since 0.1.0 */
static constexpr const unsigned char IP = 244; /*!< Interrupt Process function. @since 0.1.0 */
static constexpr const unsigned char AO = 245; /*!< Abort Output function. @since 0.1.0 */
static constexpr const unsigned char AYT = 246; /*!< Are You There function. @since 0.1.0 */
static constexpr const unsigned char EC = 247; /*!< Erase Character function. @since 0.1.0 */
static constexpr const unsigned char EL = 248; /*!< Erase Line function. @since 0.1.0 */
static constexpr const unsigned char GA = 249; /*!< Go Ahead signal. @since 0.1.0 */
static constexpr const unsigned char SB = 250; /*!< Sub-negotiation of the indicated option. @since 0.1.0 */
static constexpr const unsigned char WILL = 251; /*!< Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. @since 0.1.0 */
static constexpr const unsigned char WONT = 252; /*!< Indicates the refusal to perform, or continue performing, the indicated option. @since 0.1.0 */
static constexpr const unsigned char DO = 253; /*!< Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. @since 0.1.0 */
static constexpr const unsigned char DONT = 254; /*!< Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. @since 0.1.0 */
static constexpr const unsigned char IAC = 255; /*!< Interpret As Command escape character. @since 0.1.0 */
private:
static constexpr const unsigned char NUL = 0; /*!< No operation. @since 0.1.0 */
static constexpr const unsigned char BEL = 7; /*!< Produces an audible or visible signal. @since 0.1.0 */
static constexpr const unsigned char BS = 8; /*!< Moves the print head one character position towards the left margin. @since 0.1.0 */
static constexpr const unsigned char HT = 9; /*!< Moves the printer to the next horizontal tab stop. @since 0.1.0 */
static constexpr const unsigned char LF = 10; /*!< Line Feed. @since 0.1.0 */
static constexpr const unsigned char VT = 11; /*!< Moves the printer to the next vertical tab stop. @since 0.1.0 */
static constexpr const unsigned char FF = 12; /*!< Moves the printer to the top of the next page, keeping the same horizontal position. @since 0.1.0 */
static constexpr const unsigned char CR = 13; /*!< Carriage Return. @since 0.1.0 */
static constexpr const unsigned char SE = 240; /*!< End of sub-negotiation parameters. @since 0.1.0 */
static constexpr const unsigned char NOP = 241; /*!< No operation. @since 0.1.0 */
static constexpr const unsigned char DM = 242; /*!< The data stream portion of a Synch. @since 0.1.0 */
static constexpr const unsigned char BRK = 243; /*!< NVT character BRK. @since 0.1.0 */
static constexpr const unsigned char IP = 244; /*!< Interrupt Process function. @since 0.1.0 */
static constexpr const unsigned char AO = 245; /*!< Abort Output function. @since 0.1.0 */
static constexpr const unsigned char AYT = 246; /*!< Are You There function. @since 0.1.0 */
static constexpr const unsigned char EC = 247; /*!< Erase Character function. @since 0.1.0 */
static constexpr const unsigned char EL = 248; /*!< Erase Line function. @since 0.1.0 */
static constexpr const unsigned char GA = 249; /*!< Go Ahead signal. @since 0.1.0 */
static constexpr const unsigned char SB = 250; /*!< Sub-negotiation of the indicated option. @since 0.1.0 */
static constexpr const unsigned char WILL =
251; /*!< Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. @since 0.1.0 */
static constexpr const unsigned char WONT = 252; /*!< Indicates the refusal to perform, or continue performing, the indicated option. @since 0.1.0 */
static constexpr const unsigned char DO =
253; /*!< Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. @since 0.1.0 */
static constexpr const unsigned char DONT =
254; /*!< Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. @since 0.1.0 */
static constexpr const unsigned char IAC = 255; /*!< Interpret As Command escape character. @since 0.1.0 */
static constexpr const unsigned char OPT_BINARY = 0; /*!< Binary (8-bit) transmission mode. (RFC 856). @since 0.1.0 */
static constexpr const unsigned char OPT_ECHO = 1; /*!< Remote Echo (RFC 857). @since 0.1.0 */
static constexpr const unsigned char OPT_SGA = 3; /*!< Suppress Go Ahead (RFC 858). @since 0.1.0 */
static constexpr const unsigned char OPT_BINARY = 0; /*!< Binary (8-bit) transmission mode. (RFC 856). @since 0.1.0 */
static constexpr const unsigned char OPT_ECHO = 1; /*!< Remote Echo (RFC 857). @since 0.1.0 */
static constexpr const unsigned char OPT_SGA = 3; /*!< Suppress Go Ahead (RFC 858). @since 0.1.0 */
static constexpr const size_t BUFFER_SIZE = 536; /*!< Output buffer size. @since 0.1.0 */
static constexpr const size_t BUFFER_SIZE = 536; /*!< Output buffer size. @since 0.1.0 */
TelnetStream(const TelnetStream&) = delete;
TelnetStream& operator=(const TelnetStream&) = delete;
TelnetStream(const TelnetStream &) = delete;
TelnetStream & operator=(const TelnetStream &) = delete;
/**
/**
* Directly check for available input.
*
* @return The number of bytes available to read.
* @since 0.1.0
*/
int raw_available();
/**
int raw_available();
/**
* Read one byte directly from the available input.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.1.0
*/
int raw_read();
/**
int raw_read();
/**
* Flush output stream buffer.
*
* Disconnect the client if the socket buffer is full.
*
* @since 0.1.0
*/
void buffer_flush();
/**
void buffer_flush();
/**
* Write one byte directly to the output stream.
*
* Disconnect the client if the socket buffer is full.
@@ -190,8 +193,8 @@ private:
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t raw_write(unsigned char data);
/**
size_t raw_write(unsigned char data);
/**
* Write a vector of bytes directly to the output stream.
*
* Disconnect the client if the socket buffer is full.
@@ -200,8 +203,8 @@ private:
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t raw_write(const std::vector<unsigned char> &data);
/**
size_t raw_write(const std::vector<unsigned char> & data);
/**
* Write an array of bytes directly to the output stream.
*
* Disconnect the client if the socket buffer is full.
@@ -211,15 +214,15 @@ private:
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t raw_write(const uint8_t *buffer, size_t size);
size_t raw_write(const uint8_t * buffer, size_t size);
WiFiClient &client_; /*!< Client connection. @since 0.1.0 */
unsigned char previous_raw_in_ = 0; /*!< Previous raw character that was received. Used to detect commands. @since 0.1.0 */
bool sub_negotiation_ = false; /*!< Sub-negotiation mode. @since 0.1.0 */
unsigned char previous_in_ = 0; /*!< Previous character that was received. Used to detect CR NUL. @since 0.1.0 */
unsigned char previous_out_ = 0; /*!< Previous character that was sent. Used to insert NUL after CR without LF. @since 0.1.0 */
int peek_ = -1; /*!< Previously read data cached by peek(). @since 0.1.0 */
std::vector<char> output_buffer_; /*!< Buffer data to be output until a read function is called. @since 0.1.0 */
WiFiClient & client_; /*!< Client connection. @since 0.1.0 */
unsigned char previous_raw_in_ = 0; /*!< Previous raw character that was received. Used to detect commands. @since 0.1.0 */
bool sub_negotiation_ = false; /*!< Sub-negotiation mode. @since 0.1.0 */
unsigned char previous_in_ = 0; /*!< Previous character that was received. Used to detect CR NUL. @since 0.1.0 */
unsigned char previous_out_ = 0; /*!< Previous character that was sent. Used to insert NUL after CR without LF. @since 0.1.0 */
int peek_ = -1; /*!< Previously read data cached by peek(). @since 0.1.0 */
std::vector<char> output_buffer_; /*!< Buffer data to be output until a read function is called. @since 0.1.0 */
};
/**
@@ -228,13 +231,13 @@ private:
* @since 0.1.0
*/
class TelnetService {
public:
static constexpr size_t MAX_CONNECTIONS = 3; /*!< Maximum number of concurrent open connections. @since 0.1.0 */
static constexpr uint16_t DEFAULT_PORT = 23; /*!< Default TCP port to listen on. @since 0.1.0 */
static constexpr unsigned long DEFAULT_IDLE_TIMEOUT = 600; /*!< Default initial idle timeout (in seconds). @since 0.1.0 */
static constexpr unsigned long DEFAULT_WRITE_TIMEOUT = 0; /*!< Default write timeout (in milliseconds). @ since 0.1.0 */
public:
static constexpr size_t MAX_CONNECTIONS = 3; /*!< Maximum number of concurrent open connections. @since 0.1.0 */
static constexpr uint16_t DEFAULT_PORT = 23; /*!< Default TCP port to listen on. @since 0.1.0 */
static constexpr unsigned long DEFAULT_IDLE_TIMEOUT = 600; /*!< Default initial idle timeout (in seconds). @since 0.1.0 */
static constexpr unsigned long DEFAULT_WRITE_TIMEOUT = 0; /*!< Default write timeout (in milliseconds). @ since 0.1.0 */
/**
/**
* Function to handle the creation of a shell.
*
* @param[in] stream Stream for the telnet connection.
@@ -242,9 +245,9 @@ public:
* @param[in] port Remote port.
* @since 0.1.0
*/
using shell_factory_function = std::function<std::shared_ptr<uuid::console::Shell>(Stream &stream, const IPAddress &addr, uint16_t port)>;
using shell_factory_function = std::function<std::shared_ptr<uuid::console::Shell>(Stream & stream, const IPAddress & addr, uint16_t port)>;
/**
/**
* Create a new telnet service listening on the default port.
*
* @param[in] commands Commands available for execution in shells.
@@ -252,9 +255,9 @@ public:
* @param[in] flags Initial flags for shells.
* @since 0.1.0
*/
TelnetService(std::shared_ptr<uuid::console::Commands> commands, unsigned int context = 0, unsigned int flags = 0);
TelnetService(std::shared_ptr<uuid::console::Commands> commands, unsigned int context = 0, unsigned int flags = 0);
/**
/**
* Create a new telnet service listening on a specific port.
*
* @param[in] port TCP listening port.
@@ -263,74 +266,74 @@ public:
* @param[in] flags Initial flags for shells.
* @since 0.1.0
*/
TelnetService(uint16_t port, std::shared_ptr<uuid::console::Commands> commands, unsigned int context = 0, unsigned int flags = 0);
TelnetService(uint16_t port, std::shared_ptr<uuid::console::Commands> commands, unsigned int context = 0, unsigned int flags = 0);
/**
/**
* Create a new telnet service listening on the default port.
*
* @param[in] shell_factory Function to create a shell for new connections.
* @since 0.1.0
*/
explicit TelnetService(shell_factory_function shell_factory);
explicit TelnetService(shell_factory_function shell_factory);
/**
/**
* Create a new telnet service listening on a specific port.
*
* @param[in] port TCP listening port.
* @param[in] shell_factory Function to create a shell for new connections.
* @since 0.1.0
*/
TelnetService(uint16_t port, shell_factory_function shell_factory);
TelnetService(uint16_t port, shell_factory_function shell_factory);
~TelnetService() = default;
~TelnetService() = default;
/**
/**
* Start listening for connections on the configured port.
*
* @since 0.1.0
*/
void start();
/**
void start();
/**
* Close all connections.
*
* The listening status is not affected.
*
* @since 0.1.0
*/
void close_all();
/**
void close_all();
/**
* Stop listening for connections.
*
* Existing connections are not affected.
*
* @since 0.1.0
*/
void stop();
void stop();
/**
/**
* Get the maximum number of concurrent open connections.
*
* @return The maximum number of concurrent open connections.
* @since 0.1.0
*/
size_t maximum_connections() const;
/**
size_t maximum_connections() const;
/**
* Set the maximum number of concurrent open connections.
*
* Defaults to TelnetService::MAX_CONNECTIONS.
*
* @since 0.1.0
*/
void maximum_connections(size_t count);
void maximum_connections(size_t count);
/**
/**
* Get the initial idle timeout for new connections.
*
* @return The initial idle timeout in seconds (or 0 for disabled).
* @since 0.1.0
*/
unsigned long initial_idle_timeout() const;
/**
unsigned long initial_idle_timeout() const;
/**
* Set the initial idle timeout for new connections.
*
* Defaults to TelnetService::DEFAULT_IDLE_TIMEOUT.
@@ -338,17 +341,17 @@ public:
* @param[in] timeout Idle timeout in seconds (or 0 to disable).
* @since 0.1.0
*/
void initial_idle_timeout(unsigned long timeout);
void initial_idle_timeout(unsigned long timeout);
/**
/**
* Get the default socket write timeout for new connections.
*
* @return The default socket write timeout in seconds (or 0 for
* platform default).
* @since 0.1.0
*/
unsigned long default_write_timeout() const;
/**
unsigned long default_write_timeout() const;
/**
* Set the default socket write timeout for new connections.
*
* Defaults to TelnetService::DEFAULT_WRITE_TIMEOUT (platform
@@ -358,26 +361,26 @@ public:
* platform default).
* @since 0.1.0
*/
void default_write_timeout(unsigned long timeout);
void default_write_timeout(unsigned long timeout);
/**
/**
* Accept new connections.
*
* @since 0.1.0
*/
void loop();
void loop();
private:
/**
private:
/**
* Telnet connection.
*
* Holds the client and stream instance for the lifetime of the shell.
*
* @since 0.1.0
*/
class Connection {
public:
/**
class Connection {
public:
/**
* Create a telnet connection shell.
*
* @param[in] shell_factory Function to create a shell for new connections.
@@ -386,52 +389,52 @@ private:
* @param[in] write_timeout Idle timeout in milliseconds.
* @since 0.1.0
*/
Connection(shell_factory_function &shell_factory, WiFiClient &&client, unsigned long idle_timeout, unsigned long write_timeout);
~Connection() = default;
Connection(shell_factory_function & shell_factory, WiFiClient && client, unsigned long idle_timeout, unsigned long write_timeout);
~Connection() = default;
/**
/**
* Check if the shell is still active.
*
* @return Active status of the shell.
* @since 0.1.0
*/
bool active();
/**
bool active();
/**
* Stop the shell if the client is not connected.
*
* @return Active status of the shell.
* @since 0.1.0
*/
bool loop();
/**
bool loop();
/**
* Stop the shell.
*
* @since 0.1.0
*/
void stop();
void stop();
private:
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
private:
Connection(const Connection &) = delete;
Connection & operator=(const Connection &) = delete;
WiFiClient client_; /*!< Client connection. @since 0.1.0 */
TelnetStream stream_; /*!< Telnet stream for the connection. @since 0.1.0 */
std::shared_ptr<uuid::console::Shell> shell_; /*!< Shell for connection. @since 0.1.0 */
IPAddress addr_; /*!< Remote address of connection. @since 0.1.0 */
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
};
WiFiClient client_; /*!< Client connection. @since 0.1.0 */
TelnetStream stream_; /*!< Telnet stream for the connection. @since 0.1.0 */
std::shared_ptr<uuid::console::Shell> shell_; /*!< Shell for connection. @since 0.1.0 */
IPAddress addr_; /*!< Remote address of connection. @since 0.1.0 */
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
};
TelnetService(const TelnetService&) = delete;
TelnetService& operator=(const TelnetService&) = delete;
TelnetService(const TelnetService &) = delete;
TelnetService & operator=(const TelnetService &) = delete;
static uuid::log::Logger logger_; /*!< uuid::log::Logger instance for telnet services. @since 0.1.0 */
static uuid::log::Logger logger_; /*!< uuid::log::Logger instance for telnet services. @since 0.1.0 */
WiFiServer server_; /*!< TCP server. @since 0.1.0 */
size_t maximum_connections_ = MAX_CONNECTIONS; /*!< Maximum number of concurrent open connections. @since 0.1.0 */
std::list<Connection> connections_; /*!< Open connections. @since 0.1.0 */
shell_factory_function shell_factory_; /*!< Function to create a shell. @since 0.1.0 */
unsigned long initial_idle_timeout_ = DEFAULT_IDLE_TIMEOUT; /*!< Initial idle timeout (in seconds). @since 0.1.0 */
unsigned long write_timeout_ = DEFAULT_WRITE_TIMEOUT; /*!< Write timeout (in milliseconds). @since 0.1.0 */
WiFiServer server_; /*!< TCP server. @since 0.1.0 */
size_t maximum_connections_ = MAX_CONNECTIONS; /*!< Maximum number of concurrent open connections. @since 0.1.0 */
std::list<Connection> connections_; /*!< Open connections. @since 0.1.0 */
shell_factory_function shell_factory_; /*!< Function to create a shell. @since 0.1.0 */
unsigned long initial_idle_timeout_ = DEFAULT_IDLE_TIMEOUT; /*!< Initial idle timeout (in seconds). @since 0.1.0 */
unsigned long write_timeout_ = DEFAULT_WRITE_TIMEOUT; /*!< Write timeout (in milliseconds). @since 0.1.0 */
};
} // namespace telnet

View File

@@ -68,11 +68,10 @@ void ledcWrite(uint8_t chan, uint32_t duty);
#define PROGMEM
#define PGM_P const char *
#define PSTR(s) s
class __FlashStringHelper;
#define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(string_literal))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
#define F(string_literal) (FPSTR((string_literal)))
int snprintf_P(char * str, size_t size, const char * format, ...);
int vsnprintf_P(char * str, size_t size, const char * format, va_list ap);
@@ -97,7 +96,7 @@ class Print {
virtual size_t write(uint8_t c) = 0;
virtual size_t write(const uint8_t * buffer, size_t size) = 0;
size_t print(char c) {
return write((uint8_t)c);
return write((uint8_t)c);
}
size_t print(const char * data) {
return write(reinterpret_cast<const uint8_t *>(data), strlen(data));

View File

@@ -26,12 +26,12 @@ struct AsyncMqttClientMessageProperties {
namespace AsyncMqttClientInternals {
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;
typedef std::function<void(AsyncMqttClientDisconnectReason reason)> OnDisconnectUserCallback;
typedef std::function<void(uint16_t packetId, uint8_t qos)> OnSubscribeUserCallback;
typedef std::function<void(uint16_t packetId)> OnUnsubscribeUserCallback;
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;
typedef std::function<void(AsyncMqttClientDisconnectReason reason)> OnDisconnectUserCallback;
typedef std::function<void(uint16_t packetId, uint8_t qos)> OnSubscribeUserCallback;
typedef std::function<void(uint16_t packetId)> OnUnsubscribeUserCallback;
typedef std::function<void(char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)> OnMessageUserCallback;
typedef std::function<void(uint16_t packetId)> OnPublishUserCallback;
typedef std::function<void(uint16_t packetId)> OnPublishUserCallback;
}; // namespace AsyncMqttClientInternals
class AsyncMqttClient {

View File

@@ -13,120 +13,111 @@
template <class T>
class HttpGetEndpoint {
public:
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
}
public:
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _statefulService(statefulService)
, _bufferSize(bufferSize) {
}
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
}
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _statefulService(statefulService)
, _bufferSize(bufferSize) {
}
protected:
JsonStateReader<T> _stateReader;
StatefulService<T>* _statefulService;
size_t _bufferSize;
protected:
JsonStateReader<T> _stateReader;
StatefulService<T> * _statefulService;
size_t _bufferSize;
void fetchSettings(AsyncWebServerRequest* request) {
}
void fetchSettings(AsyncWebServerRequest * request) {
}
};
template <class T>
class HttpPostEndpoint {
public:
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
_bufferSize(bufferSize) {
}
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
_bufferSize(bufferSize) {
}
protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T>* _statefulService;
size_t _bufferSize;
void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) {
if (!json.is<JsonObject>()) {
return;
public:
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _bufferSize(bufferSize) {
}
JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
if (outcome == StateUpdateResult::ERROR) {
return;
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _bufferSize(bufferSize) {
}
if (outcome == StateUpdateResult::CHANGED) {
protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService;
size_t _bufferSize;
void updateSettings(AsyncWebServerRequest * request, JsonVariant & json) {
if (!json.is<JsonObject>()) {
return;
}
JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
if (outcome == StateUpdateResult::ERROR) {
return;
}
if (outcome == StateUpdateResult::CHANGED) {
}
}
}
};
template <class T>
class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
public:
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
HttpGetEndpoint<T>(stateReader,
statefulService,
server,
servicePath,
securityManager,
authenticationPredicate,
bufferSize),
HttpPostEndpoint<T>(stateReader,
stateUpdater,
statefulService,
server,
servicePath,
securityManager,
authenticationPredicate,
bufferSize) {
}
public:
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize) {
}
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize),
HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
}
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
}
};
#endif

View File

@@ -61,51 +61,51 @@ typedef enum {
} wifi_auth_mode_t;
typedef struct {
uint32_t status; /**< status of scanning APs: 0 — success, 1 - failure */
uint8_t number; /**< number of scan results */
uint8_t scan_id; /**< scan sequence number, used for block scan */
uint32_t status; /**< status of scanning APs: 0 — success, 1 - failure */
uint8_t number; /**< number of scan results */
uint8_t scan_id; /**< scan sequence number, used for block scan */
} wifi_event_sta_scan_done_t;
/** Argument structure for WIFI_EVENT_STA_CONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of connected AP */
uint8_t ssid_len; /**< SSID length of connected AP */
uint8_t bssid[6]; /**< BSSID of connected AP*/
uint8_t channel; /**< channel of connected AP*/
wifi_auth_mode_t authmode;/**< authentication mode used by AP*/
uint8_t ssid[32]; /**< SSID of connected AP */
uint8_t ssid_len; /**< SSID length of connected AP */
uint8_t bssid[6]; /**< BSSID of connected AP*/
uint8_t channel; /**< channel of connected AP*/
wifi_auth_mode_t authmode; /**< authentication mode used by AP*/
} wifi_event_sta_connected_t;
/** Argument structure for WIFI_EVENT_STA_DISCONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of disconnected AP */
uint8_t ssid_len; /**< SSID length of disconnected AP */
uint8_t bssid[6]; /**< BSSID of disconnected AP */
uint8_t reason; /**< reason of disconnection */
uint8_t ssid[32]; /**< SSID of disconnected AP */
uint8_t ssid_len; /**< SSID length of disconnected AP */
uint8_t bssid[6]; /**< BSSID of disconnected AP */
uint8_t reason; /**< reason of disconnection */
} wifi_event_sta_disconnected_t;
/** Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event */
typedef struct {
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
} wifi_event_sta_authmode_change_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event */
typedef struct {
uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */
uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */
} wifi_event_sta_wps_er_pin_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event */
typedef enum {
WPS_FAIL_REASON_NORMAL = 0, /**< ESP32 WPS normal fail reason */
WPS_FAIL_REASON_RECV_M2D, /**< ESP32 WPS receive M2D frame */
WPS_FAIL_REASON_NORMAL = 0, /**< ESP32 WPS normal fail reason */
WPS_FAIL_REASON_RECV_M2D, /**< ESP32 WPS receive M2D frame */
WPS_FAIL_REASON_MAX
} wifi_event_sta_wps_fail_reason_t;
typedef union {
wifi_event_sta_scan_done_t wifi_scan_done;
wifi_event_sta_authmode_change_t wifi_sta_authmode_change;
wifi_event_sta_connected_t wifi_sta_connected;
wifi_event_sta_disconnected_t wifi_sta_disconnected;
wifi_event_sta_scan_done_t wifi_scan_done;
wifi_event_sta_authmode_change_t wifi_sta_authmode_change;
wifi_event_sta_connected_t wifi_sta_connected;
wifi_event_sta_disconnected_t wifi_sta_disconnected;
} arduino_event_info_t;
typedef struct {

View File

@@ -4,139 +4,136 @@
#include "../../src/emsesp_stub.h" // proddy added
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) :
_httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this),
_fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE),
_jwtHandler(FACTORY_JWT_SECRET) {
addUpdateHandler([&](const String& originId) { configureJWTHandler(); }, false);
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
: _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
, _jwtHandler(FACTORY_JWT_SECRET) {
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
}
void SecuritySettingsService::begin() {
_fsPersistence.readFromFS();
configureJWTHandler();
_fsPersistence.readFromFS();
configureJWTHandler();
}
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) {
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader) {
String value = authorizationHeader->value();
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
return authenticateJWT(value);
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
AsyncWebHeader * authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader) {
String value = authorizationHeader->value();
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
return authenticateJWT(value);
}
} else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) {
AsyncWebParameter * tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER);
String value = tokenParamater->value();
return authenticateJWT(value);
}
} else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) {
AsyncWebParameter* tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER);
String value = tokenParamater->value();
return authenticateJWT(value);
}
return Authentication();
return Authentication();
}
void SecuritySettingsService::configureJWTHandler() {
_jwtHandler.setSecret(_state.jwtSecret);
_jwtHandler.setSecret(_state.jwtSecret);
}
Authentication SecuritySettingsService::authenticateJWT(String& jwt) {
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
_jwtHandler.parseJWT(jwt, payloadDocument);
if (payloadDocument.is<JsonObject>()) {
JsonObject parsedPayload = payloadDocument.as<JsonObject>();
String username = parsedPayload["username"];
Authentication SecuritySettingsService::authenticateJWT(String & jwt) {
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
_jwtHandler.parseJWT(jwt, payloadDocument);
if (payloadDocument.is<JsonObject>()) {
JsonObject parsedPayload = payloadDocument.as<JsonObject>();
String username = parsedPayload["username"];
for (User _user : _state.users) {
if (_user.username == username && validatePayload(parsedPayload, &_user)) {
return Authentication(_user);
}
}
}
return Authentication();
}
Authentication SecuritySettingsService::authenticate(const String & username, const String & password) {
for (User _user : _state.users) {
if (_user.username == username && validatePayload(parsedPayload, &_user)) {
return Authentication(_user);
}
if (_user.username == username && _user.password == password) {
return Authentication(_user);
}
}
}
return Authentication();
return Authentication();
}
Authentication SecuritySettingsService::authenticate(const String& username, const String& password) {
for (User _user : _state.users) {
if (_user.username == username && _user.password == password) {
return Authentication(_user);
}
}
return Authentication();
inline void populateJWTPayload(JsonObject & payload, User * user) {
payload["username"] = user->username;
payload["admin"] = user->admin;
}
inline void populateJWTPayload(JsonObject& payload, User* user) {
payload["username"] = user->username;
payload["admin"] = user->admin;
boolean SecuritySettingsService::validatePayload(JsonObject & parsedPayload, User * user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user);
return payload == parsedPayload;
}
boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user);
return payload == parsedPayload;
}
String SecuritySettingsService::generateJWT(User* user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user);
return _jwtHandler.buildJWT(payload);
String SecuritySettingsService::generateJWT(User * user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user);
return _jwtHandler.buildJWT(payload);
}
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
return [this, predicate](AsyncWebServerRequest* request) {
Authentication authentication = authenticateRequest(request);
return predicate(authentication);
};
return [this, predicate](AsyncWebServerRequest * request) {
Authentication authentication = authenticateRequest(request);
return predicate(authentication);
};
}
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest* request) {
Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) {
request->send(401);
return;
}
onRequest(request);
};
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest * request) {
Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) {
request->send(401);
return;
}
onRequest(request);
};
}
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest* request, JsonVariant& json) {
Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) {
request->send(401);
return;
}
onRequest(request, json);
};
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) {
Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) {
request->send(401);
return;
}
onRequest(request, json);
};
}
#else
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecurityManager() {
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
: SecurityManager() {
}
SecuritySettingsService::~SecuritySettingsService() {
}
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
return [this, predicate](AsyncWebServerRequest* request) { return true; };
return [this, predicate](AsyncWebServerRequest * request) { return true; };
}
// Return the admin user on all request - disabling security features
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) {
return Authentication(ADMIN_USER);
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
return Authentication(ADMIN_USER);
}
// Return the function unwrapped
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) {
return onRequest;
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return onRequest;
}
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) {
return onRequest;
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
return onRequest;
}
#endif

View File

@@ -16,10 +16,10 @@
#endif
enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required
CHANGED = 0, // The update changed the state and propagation should take place if required
CHANGED_RESTART, // The update changed the state and the service should be restarted
UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place
UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place
};
template <typename T>

View File

@@ -240,6 +240,8 @@ const mqtt_status = {
client_id: 'ems-esp',
disconnect_reason: 0,
mqtt_fails: 0,
mqtt_queued: 1,
connect_count: 2,
}
// SYSTEM
@@ -592,20 +594,15 @@ const emsesp_deviceentities_1 = [
m: 0,
w: false,
},
// {
// v: 'test data',
// n: 'test',
// id: 'test',
// m: 0,
// w: false,
// },
// {
// v: 18.2,
// n: 'hc1 selected room temperature',
// id: 'hc1/seltemp',
// m: 0,
// w: true,
// },
{
v: 18.2,
n: 'hc1 selected room temperature',
id: 'hc1/seltemp',
m: 0,
mi: 5,
ma: 52,
w: true,
},
{
v: 22.6,
n: 'hc1 current room temperature',

View File

@@ -92,3 +92,14 @@ board_upload.flash_size = 4MB
board_build.partitions = esp32_partition_4M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:lolin_s2_mini]
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_s2_mini
platform = espressif32
board_upload.flash_size = 4MB
board_build.partitions = esp32_partition_4M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}

View File

@@ -18,27 +18,29 @@ def bin_copy(source, target, env):
bag[var] = m.group(1)
app_version = bag.get('app_version')
platform = "ESP32"
platform = "ESP32"
chip_target = env.get('PIOENV').upper()
# this breaks the CI so removed
# flash_size = env["PIOENV"].split('_')[1]
# print(env.Dump())
# my_flags = env.ParseFlags(env['BUILD_FLAGS'])
# defines = {k: v for (k, v) in my_flags.get("CPPDEFINES")}
# print(my_flags)
# print((my_flags.get("CPPDEFINES"))
# print(my_flags.get("CPPDEFINES")
# alternatively take platform from the pio target
# platform = str(target[0]).split(os.path.sep)[2]
chip_target = env.get('PIOENV').upper()
print("app version: "+app_version)
print("platform: "+platform)
# print("flash size: "+flash_size)
print("app version: " + app_version)
print("platform: " + platform)
print("chip_target: " + chip_target)
# convert . to _ so Windows doesn't complain
variant = "EMS-ESP-" + app_version.replace(".", "_") + "-" + platform
# variant = "EMS-ESP-" + app_version.replace(".", "_") + "-" + platform + "_" + flash_size
variant = "EMS-ESP-" + chip_target + "-" + app_version.replace(".", "_")
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):

View File

@@ -31,26 +31,25 @@ void AnalogSensor::start() {
}
analogSetAttenuation(ADC_2_5db); // for all channels 1.5V
LOG_INFO(F("Starting Analog sensor service"));
LOG_INFO("Starting Analog sensor service");
// Add API call for /info
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(info),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
F_(info_cmd));
FL_(info_cmd));
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(setvalue),
[&](const char * value, const int8_t id) { return command_setvalue(value, id); },
F("set io value"), // TODO this needs translating
FL_(setiovalue_cmd),
CommandFlag::ADMIN_ONLY);
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(commands),
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
F_(commands_cmd));
FL_(commands_cmd));
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, "analogsensor/#", nullptr); // use empty function callback
}
@@ -123,12 +122,12 @@ void AnalogSensor::reload() {
for (auto & sensor : sensors_) {
sensor.ha_registered = false; // force HA configs to be re-created
if (sensor.type() == AnalogType::ADC) {
LOG_DEBUG(F("Adding analog ADC sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding analog ADC sensor on GPIO%d", sensor.gpio());
// analogSetPinAttenuation does not work with analogReadMilliVolts
sensor.analog_ = 0; // initialize
sensor.last_reading_ = 0;
} else if (sensor.type() == AnalogType::COUNTER) {
LOG_DEBUG(F("Adding analog I/O Counter sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding analog I/O Counter sensor on GPIO%d", sensor.gpio());
pinMode(sensor.gpio(), INPUT_PULLUP);
#ifndef ARDUINO_LOLIN_C3_MINI
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
@@ -139,7 +138,7 @@ void AnalogSensor::reload() {
sensor.poll_ = digitalRead(sensor.gpio());
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
LOG_DEBUG(F("Adding analog Timer/Rate sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding analog Timer/Rate sensor on GPIO%d", sensor.gpio());
pinMode(sensor.gpio(), INPUT_PULLUP);
sensor.polltime_ = uuid::get_uptime();
sensor.last_polltime_ = uuid::get_uptime();
@@ -148,7 +147,7 @@ void AnalogSensor::reload() {
sensor.set_value(0);
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
LOG_DEBUG(F("Adding analog Read sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding analog Read sensor on GPIO%d", sensor.gpio());
pinMode(sensor.gpio(), INPUT_PULLUP);
sensor.set_value(digitalRead(sensor.gpio())); // initial value
sensor.set_uom(0); // no uom, just for safe measures
@@ -156,7 +155,7 @@ void AnalogSensor::reload() {
sensor.poll_ = digitalRead(sensor.gpio());
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
LOG_DEBUG(F("Adding analog Write sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding analog Write sensor on GPIO%d", sensor.gpio());
pinMode(sensor.gpio(), OUTPUT);
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
if (sensor.offset() > 255) {
@@ -175,7 +174,7 @@ void AnalogSensor::reload() {
sensor.set_uom(0); // no uom, just for safe measures
publish_sensor(sensor);
} else if (sensor.type() >= AnalogType::PWM_0) {
LOG_DEBUG(F("Adding PWM output sensor on GPIO%d"), sensor.gpio());
LOG_DEBUG("Adding PWM output sensor on GPIO%d", sensor.gpio());
uint channel = sensor.type() - AnalogType::PWM_0;
ledcSetup(channel, sensor.factor(), 13);
ledcAttachPin(sensor.gpio(), channel);
@@ -277,7 +276,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset,
found_sensor = true; // found the record
// see if it's marked for deletion
if (type == AnalogType::MARK_DELETED) {
LOG_DEBUG(F("Removing analog sensor GPIO %d"), gpio);
LOG_DEBUG("Removing analog sensor GPIO %d", gpio);
settings.analogCustomizations.remove(AnalogCustomization);
} else {
// update existing record
@@ -286,7 +285,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset,
AnalogCustomization.factor = factor;
AnalogCustomization.uom = uom;
AnalogCustomization.type = type;
LOG_DEBUG(F("Customizing existing analog GPIO %d"), gpio);
LOG_DEBUG("Customizing existing analog GPIO %d", gpio);
}
return StateUpdateResult::CHANGED; // persist the change
}
@@ -312,7 +311,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset,
newSensor.uom = uom;
newSensor.type = type;
settings.analogCustomizations.push_back(newSensor);
LOG_DEBUG(F("Adding new customization for analog sensor GPIO %d"), gpio);
LOG_DEBUG("Adding new customization for analog sensor GPIO %d", gpio);
return StateUpdateResult::CHANGED; // persist the change
},
"local");
@@ -338,9 +337,9 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (Mqtt::publish_single2cmd()) {
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "%s/%s", (F_(analogsensor)), sensor.name().c_str());
} else {
snprintf(topic, sizeof(topic), "%s%s/%s", read_flash_string(F_(analogsensor)).c_str(), "_data", sensor.name().c_str());
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(analogsensor)), "_data", sensor.name().c_str());
}
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as floats
@@ -353,7 +352,7 @@ void AnalogSensor::remove_ha_topic(const uint8_t gpio) const {
return;
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("Removing HA config for analog sensor GPIO %d"), gpio);
LOG_DEBUG("Removing HA config for analog sensor GPIO %d", gpio);
#endif
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%d/config", Mqtt::basename().c_str(), gpio);
@@ -390,7 +389,7 @@ void AnalogSensor::publish_values(const bool force) {
case AnalogType::PWM_0:
case AnalogType::PWM_1:
case AnalogType::PWM_2:
dataSensor["value"] = sensor.value(); // float
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // float
break;
default:
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
@@ -403,7 +402,7 @@ void AnalogSensor::publish_values(const bool force) {
// create HA config
if (Mqtt::ha_enabled() && (!sensor.ha_registered || force)) {
LOG_DEBUG(F("Recreating HA config for analog sensor GPIO %d"), sensor.gpio());
LOG_DEBUG("Recreating HA config for analog sensor GPIO %d", sensor.gpio());
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
@@ -447,7 +446,7 @@ void AnalogSensor::publish_values(const bool force) {
}
}
Mqtt::publish(F("analogsensor_data"), doc.as<JsonObject>());
Mqtt::publish("analogsensor_data", doc.as<JsonObject>());
}
// called from emsesp.cpp, similar to the emsdevice->get_value_info

View File

@@ -25,8 +25,10 @@
#ifndef EMSESP_STANDALONE
#include "driver/adc.h"
#ifndef ARDUINO_LOLIN_S2_MINI
#include <esp_bt.h>
#endif
#endif
#include <uuid/log.h>

View File

@@ -82,7 +82,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
// validate the device, make sure it exists
uint8_t device_type = EMSdevice::device_name_2_device_type(device_s);
if (!device_has_commands(device_type)) {
LOG_DEBUG(F("Command failed: unknown device '%s'"), device_s);
LOG_DEBUG("Command failed: unknown device '%s'", device_s);
return message(CommandRet::ERROR, "unknown device", output);
}
@@ -170,15 +170,15 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
std::string Command::return_code_string(const uint8_t return_code) {
switch (return_code) {
case CommandRet::ERROR:
return read_flash_string(F("Error"));
return ("Error");
case CommandRet::OK:
return read_flash_string(F("OK"));
return ("OK");
case CommandRet::NOT_FOUND:
return read_flash_string(F("Not Found"));
return ("Not Found");
case CommandRet::NOT_ALLOWED:
return read_flash_string(F("Not Authorized"));
return ("Not Authorized");
case CommandRet::FAIL:
return read_flash_string(F("Failed"));
return ("Failed");
default:
break;
}
@@ -238,7 +238,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output) {
uint8_t return_code = CommandRet::OK;
std::string dname = EMSdevice::device_type_2_device_name(device_type);
auto dname = EMSdevice::device_type_2_device_name(device_type);
// see if there is a command registered
auto cf = find_command(device_type, cmd);
@@ -248,7 +248,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) {
if (!cf || !cf->cmdfunction_json_) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Calling %s command '%s' to retrieve attributes"), dname.c_str(), cmd);
LOG_DEBUG("[DEBUG] Calling %s command '%s' to retrieve attributes", dname, cmd);
#endif
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
}
@@ -262,17 +262,19 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
return CommandRet::NOT_ALLOWED; // command not allowed
}
auto description = Helpers::translated_word(cf->description_);
if ((value == nullptr) || (strlen(value) == 0)) {
if (EMSESP::system_.readonly_mode()) {
LOG_INFO(F("[readonly] Calling command '%s/%s' (%s)"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str());
LOG_INFO(("[readonly] Calling command '%s/%s' (%s)"), dname, cmd, description);
} else {
LOG_DEBUG(F("Calling command '%s/%s' (%s)"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str());
LOG_DEBUG(("Calling command '%s/%s' (%s)"), dname, cmd, description);
}
} else {
if (EMSESP::system_.readonly_mode()) {
LOG_INFO(F("[readonly] Calling command '%s/%s' (%s) with value %s"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str(), value);
LOG_INFO(("[readonly] Calling command '%s/%s' (%s) with value %s"), dname, cmd, description, value);
} else {
LOG_DEBUG(F("Calling command '%s/%s' (%s) with value %s"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str(), value);
LOG_DEBUG(("Calling command '%s/%s' (%s) with value %s"), dname, cmd, description, value);
}
}
@@ -294,14 +296,14 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
}
// we didn't find the command and its not an endpoint, report error
LOG_DEBUG(F("Command failed: invalid command '%s'"), cmd);
LOG_DEBUG("Command failed: invalid command '%s'", cmd);
return message(CommandRet::NOT_FOUND, "invalid command", output);
}
// add a command to the list, which does not return json
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
// if the command already exists for that device type don't add it
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
if (find_command(device_type, cmd) != nullptr) {
return;
}
@@ -314,9 +316,9 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, co
}
// add a command to the list, which does return a json object as output
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_json_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
void Command::add(const uint8_t device_type, const char * cmd, const cmd_json_function_p cb, const char * const * description, uint8_t flags) {
// if the command already exists for that device type don't add it
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
if (find_command(device_type, cmd) != nullptr) {
return;
}
@@ -338,7 +340,7 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
}
for (auto & cf : cmdfunctions_) {
if (!strcmp(lowerCmd, Helpers::toLower(read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
if (!strcmp(lowerCmd, cf.cmd_) && (cf.device_type_ == device_type)) {
return &cf;
}
}
@@ -357,15 +359,15 @@ bool Command::list(const uint8_t device_type, JsonObject & output) {
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back(read_flash_string(cf.cmd_));
sorted_cmds.push_back((cf.cmd_));
}
}
sorted_cmds.sort();
for (const auto & cl : sorted_cmds) {
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
output[cl] = cf.description_;
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
output[cl] = Helpers::translated_word(cf.description_);
}
}
}
@@ -376,7 +378,7 @@ bool Command::list(const uint8_t device_type, JsonObject & output) {
// output list of all commands to console for a specific DeviceType
void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbose) {
if (cmdfunctions_.empty()) {
shell.println(F("No commands available"));
shell.println("No commands available");
return;
}
@@ -384,7 +386,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back(read_flash_string(cf.cmd_));
sorted_cmds.push_back((cf.cmd_));
}
}
sorted_cmds.sort();
@@ -404,7 +406,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
for (const auto & cl : sorted_cmds) {
// find and print the description
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
uint8_t i = cl.length();
shell.print(" ");
if (cf.has_flags(MQTT_SUB_FLAG_HC)) {
@@ -424,7 +426,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
shell.print(EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW));
shell.print(' ');
}
shell.print(read_flash_string(cf.description_).c_str());
shell.print(Helpers::translated_word(cf.description_));
if (!cf.has_flags(CommandFlag::ADMIN_ONLY)) {
shell.print(' ');
shell.print(COLOR_BRIGHT_RED);
@@ -474,19 +476,19 @@ bool Command::device_has_commands(const uint8_t device_type) {
// list sensors and EMS devices
void Command::show_devices(uuid::console::Shell & shell) {
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM));
if (EMSESP::dallassensor_.have_sensors()) {
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR));
}
if (EMSESP::analogsensor_.have_sensors()) {
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR).c_str());
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
}
for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_class.first) && (device_has_commands(device_class.first))) {
shell.printf("%s ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
shell.printf("%s ", EMSdevice::device_type_2_device_name(device_class.first));
break; // we only want to show one (not multiple of the same device types)
}
}
@@ -497,12 +499,12 @@ void Command::show_devices(uuid::console::Shell & shell) {
// output list of all commands to console
// calls show with verbose mode set
void Command::show_all(uuid::console::Shell & shell) {
shell.println(F("Available commands (*=do not need authorization): "));
shell.println(("Available commands (*=do not need authorization): "));
// show system first
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM));
shell.print(COLOR_RESET);
show(shell, EMSdevice::DeviceType::SYSTEM, true);
@@ -510,14 +512,14 @@ void Command::show_all(uuid::console::Shell & shell) {
if (EMSESP::dallassensor_.have_sensors()) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR));
shell.print(COLOR_RESET);
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
}
if (EMSESP::analogsensor_.have_sensors()) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR).c_str());
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
shell.print(COLOR_RESET);
show(shell, EMSdevice::DeviceType::ANALOGSENSOR, true);
}
@@ -527,7 +529,7 @@ void Command::show_all(uuid::console::Shell & shell) {
if (Command::device_has_commands(device_class.first)) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(device_class.first));
shell.print(COLOR_RESET);
show(shell, device_class.first, true);
}

View File

@@ -54,19 +54,19 @@ using cmd_json_function_p = std::function<bool(const char * data, const int8_t i
class Command {
public:
struct CmdFunction {
uint8_t device_type_; // DeviceType::
uint8_t flags_; // mqtt flags for command subscriptions
const __FlashStringHelper * cmd_;
const cmd_function_p cmdfunction_;
const cmd_json_function_p cmdfunction_json_;
const __FlashStringHelper * description_;
uint8_t device_type_; // DeviceType::
uint8_t flags_; // mqtt flags for command subscriptions
const char * cmd_;
const cmd_function_p cmdfunction_;
const cmd_json_function_p cmdfunction_json_;
const char * const * description_;
CmdFunction(const uint8_t device_type,
const uint8_t flags,
const __FlashStringHelper * cmd,
const cmd_function_p cmdfunction,
const cmd_json_function_p cmdfunction_json,
const __FlashStringHelper * description)
CmdFunction(const uint8_t device_type,
const uint8_t flags,
const char * cmd,
const cmd_function_p cmdfunction,
const cmd_json_function_p cmdfunction_json,
const char * const * description)
: device_type_(device_type)
, flags_(flags)
, cmd_(cmd)
@@ -93,24 +93,22 @@ class Command {
return cmdfunctions_;
}
#define add_
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output);
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value);
// with normal call back function taking a value and id
static void add(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_function_p cb,
const __FlashStringHelper * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void add(const uint8_t device_type,
const char * cmd,
const cmd_function_p cb,
const char * const * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
// callback function taking value, id and a json object for its output
static void add(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_json_function_p cb,
const __FlashStringHelper * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void add(const uint8_t device_type,
const char * cmd,
const cmd_json_function_p cb,
const char * const * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void show_all(uuid::console::Shell & shell);
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);

View File

@@ -31,29 +31,23 @@ using uuid::log::Level;
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
// flash strings
using uuid::flash_string_vector;
using uuid::read_flash_string;
using uuid::string_vector;
using string_vector = std::vector<const char *>;
#ifdef FPSTR
#undef FPSTR
#endif
#define FJSON(x) x
// #define FJSON(x) F(x)
// clang-format off
#define MAKE_STR(string_name, string_literal) static constexpr const char * __str__##string_name = string_literal;
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F_(string_name) FPSTR(__pstr__##string_name)
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
#define FPSTR(pstr_pointer) pstr_pointer
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
#define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name)
#define F_(string_name) (__pstr__##string_name)
#define FL_(list_name) (__pstr__L_##list_name)
#define MAKE_PSTR_LIST(list_name, ...) static const __FlashStringHelper * const __pstr__L_##list_name[] PROGMEM = {__VA_ARGS__, nullptr};
#define MAKE_PSTR_ENUM(enum_name, ...) static const __FlashStringHelper * const * __pstr__L_##enum_name[] PROGMEM = {__VA_ARGS__, nullptr};
#define MAKE_PSTR_LIST(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
#define MAKE_PSTR_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
// clang-format on

View File

@@ -45,26 +45,26 @@ EMSESPShell::EMSESPShell()
}
void EMSESPShell::started() {
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session opened on console %s"), console_name().c_str());
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, ("User session opened on console %s"), console_name().c_str());
}
void EMSESPShell::stopped() {
if (has_flags(CommandFlags::ADMIN)) {
logger().log(LogLevel::DEBUG, LogFacility::AUTH, F("su session closed on console %s"), console_name().c_str());
logger().log(LogLevel::DEBUG, LogFacility::AUTH, ("su session closed on console %s"), console_name().c_str());
}
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, ("User session closed on console %s"), console_name().c_str());
}
// show welcome banner
// this is one of the first functions called when the shell is started
void EMSESPShell::display_banner() {
println();
printfln(F("┌──────────────────────────────────────┐"));
printfln(F("│ %sEMS-ESP version %-10s%s │"), COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
printfln(F("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │"), COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
printfln(F("│ │"));
printfln(F("│ type %shelp%s to show available commands │"), COLOR_UNDERLINE, COLOR_RESET);
printfln(F("└──────────────────────────────────────┘"));
printfln("┌──────────────────────────────────────┐");
printfln("│ %sEMS-ESP version %-10s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
printfln("│ │");
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
printfln("└──────────────────────────────────────┘");
println();
// set console name
@@ -100,9 +100,9 @@ void EMSESPShell::add_console_commands() {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show)},
string_vector{F_(show)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
shell.printfln("%s%sEMS-ESP version %s%s", COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
shell.println();
EMSESP::show_device_values(shell);
EMSESP::show_sensor_values(shell);
@@ -110,36 +110,36 @@ void EMSESPShell::add_console_commands() {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show), F_(devices)},
string_vector{F_(show), F_(devices)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_devices(shell); });
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show), F_(ems)},
string_vector{F_(show), F_(ems)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_ems(shell); });
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show), F_(values)},
string_vector{F_(show), F_(values)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_device_values(shell); });
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show), F_(mqtt)},
string_vector{F_(show), F_(mqtt)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show), F_(commands)},
string_vector{F_(show), F_(commands)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Command::show_all(shell); });
commands->add_command(
ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(bus_id)},
flash_string_vector{F_(deviceid_mandatory)},
string_vector{F_(set), F_(bus_id)},
string_vector{F_(deviceid_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
if ((device_id == 0x0B) || (device_id == 0x0D) || (device_id == 0x0A) || (device_id == 0x0F) || (device_id == 0x12)) {
@@ -151,24 +151,24 @@ void EMSESPShell::add_console_commands() {
},
"local");
} else {
shell.println(F("Must be 0B, 0D, 0A, 0F or 12"));
shell.println("Must be 0B, 0D, 0A, 0F or 12");
}
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{
read_flash_string(F("0B")),
read_flash_string(F("0D")),
read_flash_string(F("0A")),
read_flash_string(F("0F")),
read_flash_string(F("12")),
("0B"),
("0D"),
("0A"),
("0F"),
("12"),
};
});
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(tx_mode)},
flash_string_vector{F_(n_mandatory)},
string_vector{F_(set), F_(tx_mode)},
string_vector{F_(n_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
// save the tx_mode
@@ -183,13 +183,13 @@ void EMSESPShell::add_console_commands() {
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(scan), F_(devices)},
flash_string_vector{F_(deep_optional)},
string_vector{F_(scan), F_(devices)},
string_vector{F_(deep_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) {
EMSESP::scan_devices();
} else {
shell.printfln(F("Performing a deep scan..."));
shell.printfln("Performing a deep scan...");
EMSESP::clear_all_devices();
std::vector<uint8_t> Device_Ids;
@@ -222,10 +222,10 @@ void EMSESPShell::add_console_commands() {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(set)},
string_vector{F_(set)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
shell.printfln(F("Language: %s"), settings.locale.c_str());
shell.printfln("Language: %s", settings.locale.c_str());
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
@@ -234,13 +234,13 @@ void EMSESPShell::add_console_commands() {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(read)},
flash_string_vector{F_(deviceid_mandatory), F_(typeid_mandatory), F_(offset_optional), F_(length_optional)},
string_vector{F_(read)},
string_vector{F_(deviceid_mandatory), F_(typeid_mandatory), F_(offset_optional), F_(length_optional)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
if (!EMSESP::valid_device(device_id)) {
shell.printfln(F("Invalid deviceID"));
shell.printfln("Invalid deviceID");
return;
}
@@ -261,31 +261,32 @@ void EMSESPShell::add_console_commands() {
#ifndef EMSESP_STANDALONE
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(set), F_(timeout)},
flash_string_vector{F_(n_mandatory)},
string_vector{F_(set), F_(timeout)},
string_vector{F_(n_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint16_t value = Helpers::atoint(arguments.front().c_str());
telnet_.initial_idle_timeout(value * 60);
shell.printfln(F("Telnet timeout set to %d minutes"), value);
shell.printfln("Telnet timeout set to %d minutes", value);
});
#endif
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(watch)},
flash_string_vector{F_(watch_format_optional), F_(watchid_optional)},
string_vector{F_(watch)},
string_vector{F_(watch_format_optional), F_(watchid_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint16_t watch_id = WATCH_ID_NONE;
// only use english commands, not the translations
if (!arguments.empty()) {
// get raw/pretty
if (arguments[0] == read_flash_string(F_(raw))) {
if (arguments[0] == (F_(raw))) {
EMSESP::watch(EMSESP::WATCH_RAW); // raw
} else if (arguments[0] == Helpers::translated_word(FL_(on), true) || arguments[0] == read_flash_string(FL_(on)[0])) {
} else if (arguments[0] == (FL_(on)[0])) {
EMSESP::watch(EMSESP::WATCH_ON); // on
} else if (arguments[0] == Helpers::translated_word(FL_(off), true) || arguments[0] == read_flash_string(FL_(off)[0])) {
} else if (arguments[0] == (FL_(off)[0])) {
EMSESP::watch(EMSESP::WATCH_OFF); // off
} else if (arguments[0] == Helpers::translated_word(FL_(unknown), true) || arguments[0] == read_flash_string(FL_(unknown)[0])) {
} else if (arguments[0] == (FL_(unknown)[0])) {
EMSESP::watch(EMSESP::WATCH_UNKNOWN); // unknown
watch_id = WATCH_ID_NONE;
} else {
@@ -305,43 +306,43 @@ void EMSESPShell::add_console_commands() {
EMSESP::watch_id(watch_id);
} else {
shell.printfln(F("Invalid: use watch raw|on|off|unknown|id [id]"));
shell.printfln("Invalid: use watch raw|on|off|unknown|id [id]");
return;
}
uint8_t watch = EMSESP::watch();
if (watch == EMSESP::WATCH_OFF) {
shell.printfln(F("Watching telegrams is off"));
shell.printfln("Watching telegrams is off");
return;
}
// if logging is off, the watch won't show anything, show force it back to NOTICE
if (shell.log_level() < Level::NOTICE) {
shell.log_level(Level::NOTICE);
shell.printfln(F("Setting log level to Notice"));
shell.printfln("Setting log level to Notice");
}
if (watch == EMSESP::WATCH_ON) {
shell.printfln(F("Watching incoming telegrams, displayed in decoded format"));
shell.printfln("Watching incoming telegrams, displayed in decoded format");
} else if (watch == EMSESP::WATCH_RAW) {
shell.printfln(F("Watching incoming telegrams, displayed as raw bytes")); // WATCH_RAW
shell.printfln("Watching incoming telegrams, displayed as raw bytes"); // WATCH_RAW
} else {
shell.printfln(F("Watching unknown telegrams")); // WATCH_UNKNOWN
shell.printfln("Watching unknown telegrams"); // WATCH_UNKNOWN
}
watch_id = EMSESP::watch_id();
if (watch_id > 0x80) {
shell.printfln(F("Filtering only telegrams that match a telegram type of 0x%02X"), watch_id);
shell.printfln("Filtering only telegrams that match a telegram type of 0x%02X", watch_id);
} else if (watch_id != WATCH_ID_NONE) {
shell.printfln(F("Filtering only telegrams that match a deviceID or telegram type of 0x%02X"), watch_id);
shell.printfln("Filtering only telegrams that match a deviceID or telegram type of 0x%02X", watch_id);
}
});
commands->add_command(
ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(call)},
flash_string_vector{F_(device_type_optional), F_(cmd_optional), F_(data_optional), F_(id_optional)},
string_vector{F_(call)},
string_vector{F_(device_type_optional), F_(cmd_optional), F_(data_optional), F_(id_optional)},
[&](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) {
Command::show_all(shell); // list options
@@ -351,7 +352,7 @@ void EMSESPShell::add_console_commands() {
// validate the device_type
uint8_t device_type = EMSdevice::device_name_2_device_type(arguments[0].c_str());
if (!Command::device_has_commands(device_type)) {
shell.print(F("Invalid device. Available devices are: "));
shell.print("Invalid device. Available devices are: ");
Command::show_devices(shell);
return;
}
@@ -359,7 +360,7 @@ void EMSESPShell::add_console_commands() {
// validate that a command is present
if (arguments.size() < 2) {
shell.print(F("Missing command. Available commands are: "));
shell.print("Missing command. Available commands are: ");
Command::show(shell, device_type, false); // non-verbose mode
return;
}
@@ -399,11 +400,11 @@ void EMSESPShell::add_console_commands() {
}
if (return_code == CommandRet::NOT_FOUND) {
shell.println(F("Unknown command"));
shell.print(F("Available commands are: "));
shell.println("Unknown command");
shell.print("Available commands are: ");
Command::show(shell, device_type, false); // non-verbose mode
} else if (return_code != CommandRet::OK) {
shell.printfln(F("Bad syntax (error code %d)"), return_code);
shell.printfln(("Bad syntax (error code %d)"), return_code);
}
},
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
@@ -424,7 +425,7 @@ void EMSESPShell::add_console_commands() {
if (Command::device_has_commands(device_type)) {
for (const auto & cf : Command::commands()) {
if (cf.device_type_ == device_type) {
command_list.emplace_back(read_flash_string(cf.cmd_));
command_list.emplace_back((cf.cmd_));
}
}
return command_list;
@@ -448,8 +449,8 @@ void Console::load_standard_commands(unsigned int context) {
// create commands test and t
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F("test")},
flash_string_vector{F_(name_optional), F_(data_optional)},
string_vector{("test")},
string_vector{F_(name_optional), F_(data_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) {
Test::run_test(shell, "default");
@@ -460,7 +461,7 @@ void Console::load_standard_commands(unsigned int context) {
}
});
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("t")}, [](Shell & shell, const std::vector<std::string> & arguments) {
EMSESPShell::commands->add_command(context, CommandFlags::USER, string_vector{("t")}, [](Shell & shell, const std::vector<std::string> & arguments) {
Test::run_test(shell, "default");
});
#endif
@@ -468,8 +469,8 @@ void Console::load_standard_commands(unsigned int context) {
#if defined(EMSESP_DEBUG)
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(debug)},
flash_string_vector{F_(name_optional)},
string_vector{F_(debug)},
string_vector{F_(name_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) {
Test::debug(shell, "default");
@@ -482,8 +483,8 @@ void Console::load_standard_commands(unsigned int context) {
EMSESPShell::commands->add_command(
context,
CommandFlags::USER,
flash_string_vector{F_(log)},
flash_string_vector{F_(log_level_optional)},
string_vector{F_(log)},
string_vector{F_(log_level_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (!arguments.empty()) {
uuid::log::Level level;
@@ -494,7 +495,7 @@ void Console::load_standard_commands(unsigned int context) {
return;
}
} else {
shell.print(F("levels: "));
shell.print("levels: ");
std::vector<std::string> v = uuid::log::levels_lowercase();
size_t i = v.size();
while (i--) {
@@ -511,22 +512,22 @@ void Console::load_standard_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(help)},
string_vector{F_(help)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.print_all_available_commands();
});
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(exit)},
string_vector{F_(exit)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.stop(); });
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(su)},
string_vector{F_(su)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
auto become_admin = [](Shell & shell) {
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("su session opened on console"));
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, ("su session opened on console"));
shell.add_flags(CommandFlags::ADMIN);
};
@@ -542,8 +543,8 @@ void Console::load_standard_commands(unsigned int context) {
become_admin(shell);
} else {
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid su password on console"));
shell.println(F("su: incorrect password"));
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, ("Invalid su password on console"));
shell.println("su: incorrect password");
});
}
});
@@ -557,21 +558,21 @@ void Console::load_standard_commands(unsigned int context) {
void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(restart)},
string_vector{F_(restart)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.system_restart();
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(wifi), F_(reconnect)},
string_vector{F_(wifi), F_(reconnect)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.wifi_reconnect();
});
EMSESPShell::commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(format)},
string_vector{F_(format)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
if (completed) {
@@ -579,7 +580,7 @@ void Console::load_system_commands(unsigned int context) {
if (securitySettings.jwtSecret.equals(password.c_str())) {
EMSESP::system_.format(shell);
} else {
shell.println(F("incorrect password"));
shell.println("incorrect password");
}
});
}
@@ -588,7 +589,7 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(passwd)},
string_vector{F_(passwd)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
@@ -602,9 +603,9 @@ void Console::load_system_commands(unsigned int context) {
return StateUpdateResult::CHANGED;
},
"local");
shell.println(F("su password updated"));
shell.println("su password updated");
} else {
shell.println(F("Passwords do not match"));
shell.println("Passwords do not match");
}
}
});
@@ -614,7 +615,7 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(show), F_(system)},
string_vector{F_(show), F_(system)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_system(shell);
shell.println();
@@ -622,8 +623,8 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(hostname)},
flash_string_vector{F_(name_mandatory)},
string_vector{F_(set), F_(hostname)},
string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
shell.println("The network connection will be reset...");
Shell::loop_all();
@@ -638,8 +639,8 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
flash_string_vector{F_(name_mandatory)},
string_vector{F_(set), F_(wifi), F_(ssid)},
string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
networkSettings.ssid = arguments.front().c_str();
@@ -651,8 +652,8 @@ void Console::load_system_commands(unsigned int context) {
// added by mvdp
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F("mqtt"), F("subscribe")},
flash_string_vector{F("<topic>")},
string_vector{("mqtt"), ("subscribe")},
string_vector{("<topic>")},
[](Shell & shell, const std::vector<std::string> & arguments) {
Mqtt::subscribe(arguments.front());
shell.println("subscribing");
@@ -660,7 +661,7 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(wifi), F_(password)},
string_vector{F_(set), F_(wifi), F_(password)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
@@ -675,7 +676,7 @@ void Console::load_system_commands(unsigned int context) {
});
shell.println("Use `wifi reconnect` to save and apply the new settings");
} else {
shell.println(F("Passwords do not match"));
shell.println("Passwords do not match");
}
}
});
@@ -685,13 +686,13 @@ void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(board_profile)},
flash_string_vector{F_(name_mandatory)},
string_vector{F_(set), F_(board_profile)},
string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
std::string board_profile = Helpers::toUpper(arguments.front());
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, OLIMEXPOE, C3MINI, CUSTOM)"));
shell.println(("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, OLIMEXPOE, C3MINI, CUSTOM)"));
return;
}
EMSESP::webSettingsService.update(
@@ -714,7 +715,7 @@ void Console::load_system_commands(unsigned int context) {
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(show), F_(users)},
string_vector{F_(show), F_(users)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_users(shell);
});
@@ -730,14 +731,14 @@ std::string EMSESPShell::prompt_suffix() {
}
void EMSESPShell::end_of_transmission() {
invoke_command(read_flash_string(F_(exit)));
invoke_command((F_(exit)));
}
EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, bool local)
: uuid::console::Shell(commands, ShellContext::MAIN, local ? (CommandFlags::USER | CommandFlags::LOCAL) : CommandFlags::USER)
, uuid::console::StreamConsole(stream)
, EMSESPShell()
, name_(read_flash_string(F("Serial")))
, name_("Serial")
, pty_(SIZE_MAX)
, addr_()
, port_(0) {
@@ -763,14 +764,14 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, const IPAddress & addr
snprintf(text.data(), text.size(), "pty%u", (uint16_t)pty_);
name_ = text.data();
#ifndef EMSESP_STANDALONE
logger().info(F("Allocated console %s for connection from [%s]:%u"), name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
logger().info("Allocated console %s for connection from [%s]:%u", name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
#endif
}
EMSESPStreamConsole::~EMSESPStreamConsole() {
if (pty_ != SIZE_MAX) {
#ifndef EMSESP_STANDALONE
logger().info(F("Shutdown console %s for connection from [%s]:%u"), name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
logger().info("Shutdown console %s for connection from [%s]:%u", name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
#endif
ptys_[pty_] = false;
ptys_.shrink_to_fit();

View File

@@ -42,7 +42,7 @@ void DallasSensor::start() {
#ifndef EMSESP_STANDALONE
bus_.begin(dallas_gpio_);
LOG_INFO(F("Starting Dallas sensor service"));
LOG_INFO("Starting Dallas sensor service");
#endif
// Add API calls
@@ -50,12 +50,12 @@ void DallasSensor::start() {
EMSdevice::DeviceType::DALLASSENSOR,
F_(info),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
F_(info_cmd));
FL_(info_cmd));
Command::add(
EMSdevice::DeviceType::DALLASSENSOR,
F_(commands),
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
F_(commands_cmd));
FL_(commands_cmd));
Mqtt::subscribe(EMSdevice::DeviceType::DALLASSENSOR, "dallasssensor/#", nullptr); // use empty function callback
}
@@ -84,7 +84,7 @@ void DallasSensor::loop() {
if (state_ == State::IDLE) {
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
#ifdef EMSESP_DEBUG_SENSOR
LOG_DEBUG(F("[DEBUG] Read sensor temperature"));
LOG_DEBUG("[DEBUG] Read sensor temperature");
#endif
if (bus_.reset() || parasite_) {
YIELD;
@@ -99,7 +99,7 @@ void DallasSensor::loop() {
if (++scanretry_ > SCAN_MAX) { // every 30 sec
scanretry_ = 0;
#ifdef EMSESP_DEBUG_SENSOR
LOG_ERROR(F("Bus reset failed"));
LOG_ERROR("Bus reset failed");
#endif
for (auto & sensor : sensors_) {
sensor.temperature_c = EMS_VALUE_SHORT_NOTSET;
@@ -112,13 +112,13 @@ void DallasSensor::loop() {
} else if (state_ == State::READING) {
if (temperature_convert_complete() && (time_now - last_activity_ > CONVERSION_MS)) {
#ifdef EMSESP_DEBUG_SENSOR
LOG_DEBUG(F("Scanning for sensors"));
LOG_DEBUG("Scanning for sensors");
#endif
bus_.reset_search();
state_ = State::SCANNING;
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
#ifdef EMSESP_DEBUG_SENSOR
LOG_WARNING(F("Dallas sensor read timeout"));
LOG_WARNING("Dallas sensor read timeout");
#endif
state_ = State::IDLE;
sensorfails_++;
@@ -126,7 +126,7 @@ void DallasSensor::loop() {
} else if (state_ == State::SCANNING) {
if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
#ifdef EMSESP_DEBUG_SENSOR
LOG_ERROR(F("Dallas sensor scan timeout"));
LOG_ERROR("Dallas sensor scan timeout");
#endif
state_ = State::IDLE;
sensorfails_++;
@@ -181,12 +181,12 @@ void DallasSensor::loop() {
default:
sensorfails_++;
LOG_ERROR(F("Unknown dallas sensor %s"), Sensor(addr).id().c_str());
LOG_ERROR("Unknown dallas sensor %s", Sensor(addr).id().c_str());
break;
}
} else {
sensorfails_++;
LOG_ERROR(F("Invalid dallas sensor %s"), Sensor(addr).id().c_str());
LOG_ERROR("Invalid dallas sensor %s", Sensor(addr).id().c_str());
}
} else {
if (!parasite_) {
@@ -204,7 +204,7 @@ void DallasSensor::loop() {
scancnt_ = 0;
} else if (scancnt_ == SCAN_START + 1) { // startup
firstscan_ = sensors_.size();
// LOG_DEBUG(F("Adding %d dallas sensor(s) from first scan"), firstscan_);
// LOG_DEBUG(("Adding %d dallas sensor(s) from first scan"), firstscan_);
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
scancnt_ = SCAN_START;
sensors_.clear(); // restart scaning and clear to get correct numbering
@@ -230,7 +230,7 @@ bool DallasSensor::temperature_convert_complete() {
int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
#ifndef EMSESP_STANDALONE
if (!bus_.reset()) {
LOG_ERROR(F("Bus reset failed before reading scratchpad from %s"), Sensor(addr).id().c_str());
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id().c_str());
return EMS_VALUE_SHORT_NOTSET;
}
YIELD;
@@ -242,13 +242,13 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
YIELD;
if (!bus_.reset()) {
LOG_ERROR(F("Bus reset failed after reading scratchpad from %s"), Sensor(addr).id().c_str());
LOG_ERROR("Bus reset failed after reading scratchpad from %s", Sensor(addr).id().c_str());
return EMS_VALUE_SHORT_NOTSET;
}
YIELD;
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
LOG_WARNING(F("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s"),
LOG_WARNING("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s",
scratchpad[0],
scratchpad[1],
scratchpad[2],
@@ -315,7 +315,7 @@ bool DallasSensor::update(const std::string & id, const std::string & name, int1
SensorCustomization.name = name;
SensorCustomization.offset = offset;
found = true;
LOG_DEBUG(F("Customizing existing sensor ID %s"), id.c_str());
LOG_DEBUG("Customizing existing sensor ID %s", id.c_str());
break;
}
}
@@ -325,7 +325,7 @@ bool DallasSensor::update(const std::string & id, const std::string & name, int1
newSensor.name = name;
newSensor.offset = offset;
settings.sensorCustomizations.push_back(newSensor);
LOG_DEBUG(F("Adding new customization for sensor ID %s"), id.c_str());
LOG_DEBUG("Adding new customization for sensor ID %s", id.c_str());
}
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
return StateUpdateResult::CHANGED;
@@ -360,14 +360,15 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
}
for (const auto & sensor : sensors_) {
char val[10];
if (id == -1) { // show number and id
JsonObject dataSensor = output.createNestedObject(sensor.name());
dataSensor["id"] = sensor.id();
if (Helpers::hasValue(sensor.temperature_c)) {
dataSensor["temp"] = Helpers::transformNumFloat((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
} else if (Helpers::hasValue(sensor.temperature_c)) {
output[sensor.name()] = Helpers::transformNumFloat((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
}
@@ -392,14 +393,17 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
if (strcmp(command_s, sensor.name().c_str()) == 0) {
output["id"] = sensor.id();
output["name"] = sensor.name();
char val[10];
if (Helpers::hasValue(sensor.temperature_c)) {
output["value"] = Helpers::transformNumFloat((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
output["value"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
output["type"] = F_(number);
output["min"] = Helpers::transformNumFloat(-55, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
output["max"] = Helpers::transformNumFloat(125, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
output["min"] = serialized(Helpers::render_value(val, -55, 0, EMSESP::system_.fahrenheit() ? 2 : 0));
output["max"] = serialized(Helpers::render_value(val, 125, 0, EMSESP::system_.fahrenheit() ? 2 : 0));
output["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
output["writeable"] = false;
// if we're filtering on an attribute, go find it
if (attribute_s) {
if (output.containsKey(attribute_s)) {
@@ -426,9 +430,9 @@ void DallasSensor::publish_sensor(const Sensor & sensor) {
if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (Mqtt::publish_single2cmd()) {
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(dallassensor)).c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "%s/%s", (F_(dallassensor)), sensor.name().c_str());
} else {
snprintf(topic, sizeof(topic), "%s%s/%s", read_flash_string(F_(dallassensor)).c_str(), "_data", sensor.name().c_str());
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(dallassensor)), "_data", sensor.name().c_str());
}
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
@@ -441,7 +445,7 @@ void DallasSensor::remove_ha_topic(const std::string & id) {
return;
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("Removing HA config for temperature sensor ID %s"), id.c_str());
LOG_DEBUG("Removing HA config for temperature sensor ID %s", id.c_str());
#endif
// use '_' as HA doesn't like '-' in the topic name
std::string sensorid = id;
@@ -469,24 +473,25 @@ void DallasSensor::publish_values(const bool force) {
for (auto & sensor : sensors_) {
bool has_value = Helpers::hasValue(sensor.temperature_c);
char val[10];
if (Mqtt::is_nested()) {
JsonObject dataSensor = doc.createNestedObject(sensor.id());
dataSensor["name"] = sensor.name();
if (has_value) {
dataSensor["temp"] = Helpers::transformNumFloat((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
} else if (has_value) {
doc[sensor.name()] = Helpers::transformNumFloat((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
doc[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
// create the HA MQTT config
// to e.g. homeassistant/sensor/ems-esp/dallassensor_28-233D-9497-0C03/config
if (Mqtt::ha_enabled()) {
if (!sensor.ha_registered || force) {
LOG_DEBUG(F("Recreating HA config for sensor ID %s"), sensor.id().c_str());
LOG_DEBUG("Recreating HA config for sensor ID %s", sensor.id().c_str());
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
config["dev_cla"] = FJSON("temperature");
config["dev_cla"] = "temperature";
char stat_t[50];
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str());
@@ -530,7 +535,7 @@ void DallasSensor::publish_values(const bool force) {
}
}
Mqtt::publish(F("dallassensor_data"), doc.as<JsonObject>());
Mqtt::publish("dallassensor_data", doc.as<JsonObject>());
}
@@ -574,7 +579,7 @@ bool DallasSensor::Sensor::apply_customization() {
if (!sensors.empty()) {
for (const auto & sensor : sensors) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("Loading customization for dallas sensor %s"), sensor.id.c_str());
LOG_DEBUG("Loading customization for dallas sensor %s", sensor.id.c_str());
#endif
if (id_ == sensor.id) {
set_name(sensor.name);

View File

@@ -24,140 +24,142 @@
*/
// Boilers - 0x08
{ 64, DeviceType::BOILER, F("BK13/BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_EMS},
{ 81, DeviceType::BOILER, F("Cascade CM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_HT3},
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{131, DeviceType::BOILER, F("GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{132, DeviceType::BOILER, F("GC7000F"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{133, DeviceType::BOILER, F("Logano GB125/KB195i/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{154, DeviceType::BOILER, F("Greenstar 30Ri Compact"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{168, DeviceType::BOILER, F("Hybrid Heatpump"), DeviceFlags::EMS_DEVICE_FLAG_HYBRID},
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco/Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{173, DeviceType::BOILER, F("Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{206, DeviceType::BOILER, F("Ecomline Excellent"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{210, DeviceType::BOILER, F("Cascade MC400"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{211, DeviceType::BOILER, F("EasyControl Adapter"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{228, DeviceType::BOILER, F("Alternative Heatsource"), DeviceFlags::EMS_DEVICE_FLAG_AM200},
{234, DeviceType::BOILER, F("Logamax Plus GB122/Condense 2300"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 64, DeviceType::BOILER, "BK13/BK15/Smartline/GB1x2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, "GB125/MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS},
{ 81, DeviceType::BOILER, "Cascade CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 84, DeviceType::BOILER, "Logamax Plus GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 95, DeviceType::BOILER, "Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3", DeviceFlags::EMS_DEVICE_FLAG_HT3},
{115, DeviceType::BOILER, "Topline/GB162", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{122, DeviceType::BOILER, "Proline", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{123, DeviceType::BOILER, "GBx72/Trendline/Cerapur/Greenstar Si/27i", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{131, DeviceType::BOILER, "GB212", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{132, DeviceType::BOILER, "GC7000F", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{133, DeviceType::BOILER, "Logano GB125/KB195i/Logamatic MC110", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{154, DeviceType::BOILER, "Greenstar 30Ri Compact", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{167, DeviceType::BOILER, "Cerapur Aero", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{168, DeviceType::BOILER, "Hybrid Heatpump", DeviceFlags::EMS_DEVICE_FLAG_HYBRID},
{170, DeviceType::BOILER, "Logano GB212", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, "Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco/Geo 5xx", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{173, DeviceType::BOILER, "Geo 5xx", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{195, DeviceType::BOILER, "Condens 5000i/Greenstar 8000", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, "Logamax U122/Cerapur", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{206, DeviceType::BOILER, "Ecomline Excellent", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{228, DeviceType::BOILER, "Alternative Heatsource", DeviceFlags::EMS_DEVICE_FLAG_AM200},
{234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Controllers - 0x09 / 0x10 / 0x50
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 81, DeviceType::CONTROLLER, F("CM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 84, DeviceType::CONTROLLER, F("GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 89, DeviceType::CONTROLLER, F("BC10 GB142"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{125, DeviceType::CONTROLLER, F("BC25"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{152, DeviceType::CONTROLLER, F("Controller"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{168, DeviceType::CONTROLLER, F("Hybrid Heatpump"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{194, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{206, DeviceType::CONTROLLER, F("Ecomline"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{224, DeviceType::CONTROLLER, F("9000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{229, DeviceType::CONTROLLER, F("8700i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{240, DeviceType::CONTROLLER, F("Rego 3000"), DeviceFlags::EMS_DEVICE_FLAG_IVT}, // 0x09
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 68, DeviceType::CONTROLLER, "BC10/RFM20", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 81, DeviceType::CONTROLLER, "CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 84, DeviceType::CONTROLLER, "GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 89, DeviceType::CONTROLLER, "BC10 GB142", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 95, DeviceType::CONTROLLER, "HT3", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{114, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{125, DeviceType::CONTROLLER, "BC25", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{152, DeviceType::CONTROLLER, "Controller", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{168, DeviceType::CONTROLLER, "Hybrid Heatpump", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{169, DeviceType::CONTROLLER, "BC40", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{190, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{194, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{206, DeviceType::CONTROLLER, "Ecomline", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{207, DeviceType::CONTROLLER, "Sense II/CS200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
{209, DeviceType::CONTROLLER, "ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{218, DeviceType::CONTROLLER, "M200/RFM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{220, DeviceType::CONTROLLER, "BC30", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x16
{224, DeviceType::CONTROLLER, "9000i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{229, DeviceType::CONTROLLER, "8700i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{230, DeviceType::CONTROLLER, "BC Base", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{240, DeviceType::CONTROLLER, "Rego 3000", DeviceFlags::EMS_DEVICE_FLAG_IVT}, // 0x09
{241, DeviceType::CONTROLLER, "Condens 5000i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{202, DeviceType::THERMOSTAT, "Logamatic TC100/Moduline Easy", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, "EasyControl CT200", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19-0x1B for hc2-4 / 0x38
{ 65, DeviceType::THERMOSTAT, F("RC10"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N},// 0x17
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
{151, DeviceType::THERMOSTAT, F("RC25"), DeviceFlags::EMS_DEVICE_FLAG_RC25}, // 0x17
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
{172, DeviceType::THERMOSTAT, F("Rego 2000/3000"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{216, DeviceType::THERMOSTAT, F("CRF200S"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{246, DeviceType::THERMOSTAT, F("Comfort+2RF"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{ 65, DeviceType::THERMOSTAT, "RC10", DeviceFlags::EMS_DEVICE_FLAG_RC20_N},// 0x17
{ 67, DeviceType::THERMOSTAT, "RC30", DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
{ 77, DeviceType::THERMOSTAT, "RC20/Moduline 300", DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 78, DeviceType::THERMOSTAT, "Moduline 400", DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
{ 79, DeviceType::THERMOSTAT, "RC10/Moduline 100", DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
{ 80, DeviceType::THERMOSTAT, "Moduline 200", DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
{ 86, DeviceType::THERMOSTAT, "RC35", DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{ 90, DeviceType::THERMOSTAT, "RC10/Moduline 100", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{ 93, DeviceType::THERMOSTAT, "RC20RF", DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
{ 94, DeviceType::THERMOSTAT, "RFM20 Remote", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
{151, DeviceType::THERMOSTAT, "RC25", DeviceFlags::EMS_DEVICE_FLAG_RC25}, // 0x17
{157, DeviceType::THERMOSTAT, "RC200/CW100", DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
{158, DeviceType::THERMOSTAT, "RC300/RC310/Moduline 3000/1010H/CW400/Sense II", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{165, DeviceType::THERMOSTAT, "RC100/Moduline 1000/1010", DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
{172, DeviceType::THERMOSTAT, "Rego 2000/3000", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{216, DeviceType::THERMOSTAT, "CRF200S", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{246, DeviceType::THERMOSTAT, "Comfort+2RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
// Thermostat - Sieger - 0x10 / 0x17
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N}, // 0x10
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{ 66, DeviceType::THERMOSTAT, "ES72/RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
{ 76, DeviceType::THERMOSTAT, "ES73", DeviceFlags::EMS_DEVICE_FLAG_RC30_N}, // 0x10
{113, DeviceType::THERMOSTAT, "ES72/RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
// Thermostat - Junkers - 0x10
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{106, DeviceType::THERMOSTAT, F("FW200"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{109, DeviceType::THERMOSTAT, F("FB10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{110, DeviceType::THERMOSTAT, F("FB100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD},
{191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{192, DeviceType::THERMOSTAT, F("FW120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{105, DeviceType::THERMOSTAT, "FW100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{106, DeviceType::THERMOSTAT, "FW200", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{107, DeviceType::THERMOSTAT, "FR100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{108, DeviceType::THERMOSTAT, "FR110", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{109, DeviceType::THERMOSTAT, "FB10", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{110, DeviceType::THERMOSTAT, "FB100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{111, DeviceType::THERMOSTAT, "FR10", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{116, DeviceType::THERMOSTAT, "FW500", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{147, DeviceType::THERMOSTAT, "FR50", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD},
{191, DeviceType::THERMOSTAT, "FR120", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{192, DeviceType::THERMOSTAT, "FW120", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
// Thermostat remote - 0x38
{200, DeviceType::THERMOSTAT, F("RC100H"), DeviceFlags::EMS_DEVICE_FLAG_RC100H},
{200, DeviceType::THERMOSTAT, "RC100H", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
// Solar Modules - 0x30 (for solar), 0x2A, 0x41 (for ww)
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10},
{101, DeviceType::SOLAR, F("ISM1"), DeviceFlags::EMS_DEVICE_FLAG_ISM},
{103, DeviceType::SOLAR, F("ISM2"), DeviceFlags::EMS_DEVICE_FLAG_ISM},
{162, DeviceType::SOLAR, F("SM50"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{163, DeviceType::SOLAR, F("SM100/MS100"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::SOLAR, F("SM200/MS200"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{ 73, DeviceType::SOLAR, "SM10", DeviceFlags::EMS_DEVICE_FLAG_SM10},
{101, DeviceType::SOLAR, "ISM1", DeviceFlags::EMS_DEVICE_FLAG_ISM},
{103, DeviceType::SOLAR, "ISM2", DeviceFlags::EMS_DEVICE_FLAG_ISM},
{162, DeviceType::SOLAR, "SM50", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{163, DeviceType::SOLAR, "SM100/MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::SOLAR, "SM200/MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
{ 69, DeviceType::MIXER, F("MM10"), DeviceFlags::EMS_DEVICE_FLAG_MM10},
{100, DeviceType::MIXER, F("IPM"), DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::MIXER, F("IPM"), DeviceFlags::EMS_DEVICE_FLAG_IPM},
{159, DeviceType::MIXER, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{160, DeviceType::MIXER, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXER, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{204, DeviceType::MIXER, F("MP100"), DeviceFlags::EMS_DEVICE_FLAG_MP}, // pool
{ 69, DeviceType::MIXER, "MM10", DeviceFlags::EMS_DEVICE_FLAG_MM10},
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{159, DeviceType::MIXER, "MM50", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{160, DeviceType::MIXER, "MM100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXER, "MM200", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{204, DeviceType::MIXER, "MP100", DeviceFlags::EMS_DEVICE_FLAG_MP}, // pool
// Heat Pumps - 0x38?
{252, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{252, DeviceType::HEATPUMP, "HP Module", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Heat Pumps - 0x53
{248, DeviceType::HEATPUMP, F("Hybrid Manager HM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{248, DeviceType::HEATPUMP, "Hybrid Manager HM200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Connect devices - 0x02
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{171, DeviceType::CONNECT, "OpenTherm Converter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{205, DeviceType::CONNECT, "Moduline Easy Connect", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{206, DeviceType::CONNECT, "Easy Connect", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Wireless sensor base - 0x50
{236, DeviceType::CONNECT, F("Wireless sensor base"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{238, DeviceType::CONNECT, F("Wireless sensor base"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{236, DeviceType::CONNECT, "Wireless sensor base", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{238, DeviceType::CONNECT, "Wireless sensor base", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Switches - 0x11
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 71, DeviceType::SWITCH, "WM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// EM10 error contact and analog flowtemp control- 0x12
{ 74, DeviceType::GATEWAY, F("Error Module EM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 74, DeviceType::GATEWAY, "Error Module EM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Gateways - 0x48
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{189, DeviceType::GATEWAY, "KM200/MB LAN 2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Generic - 0x40 or other with no product-id and no version
{0, DeviceType::GENERIC, F("unknown"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
{0, DeviceType::GENERIC, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE}
// clang-format on

View File

@@ -24,16 +24,16 @@ REGISTER_FACTORY(Boiler, EMSdevice::DeviceType::BOILER)
uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE};
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// alternative heatsource special messages
if (device_id == EMSdevice::EMS_DEVICE_ID_AM200) {
register_telegram_type(0x54D, F("AmTemperatures"), false, MAKE_PF_CB(process_amTempMessage));
register_telegram_type(0x54E, F("AmStatus"), false, MAKE_PF_CB(process_amStatusMessage));
register_telegram_type(0x54F, F("AmCommand"), false, MAKE_PF_CB(process_amCommandMessage)); // not broadcasted, but actually not used
register_telegram_type(0x550, F("AmExtra"), false, MAKE_PF_CB(process_amExtraMessage));
register_telegram_type(0x54C, F("AmSettings"), true, MAKE_PF_CB(process_amSettingMessage)); // not broadcasted
register_telegram_type(0x54D, "AmTemperatures", false, MAKE_PF_CB(process_amTempMessage));
register_telegram_type(0x54E, "AmStatus", false, MAKE_PF_CB(process_amStatusMessage));
register_telegram_type(0x54F, "AmCommand", false, MAKE_PF_CB(process_amCommandMessage)); // not broadcasted, but actually not used
register_telegram_type(0x550, "AmExtra", false, MAKE_PF_CB(process_amExtraMessage));
register_telegram_type(0x54C, "AmSettings", true, MAKE_PF_CB(process_amSettingMessage)); // not broadcasted
register_device_value(DeviceValueTAG::TAG_AHS,
&curFlowTemp_,
@@ -113,12 +113,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
if (device_id >= EMSdevice::EMS_DEVICE_ID_BOILER_1) {
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
// Runtime of each heatingsource in 0x06DC, ff
register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage));
register_telegram_type(0x6DC + hs, "CascadeMessage", false, MAKE_PF_CB(process_CascadeMessage));
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
// selBurnpower in D2 and E4
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage));
// register_telegram_type(0xD2, "CascadePowerMessage", false, MAKE_PF_CB(process_CascadePowerMessage));
// individual Flowtemps and powervalues for each heatingsource in E4
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_telegram_type(0xE4, "UBAMonitorFastPlus", false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, FL_(selBurnPow), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_HS1 + hs,
@@ -136,51 +136,51 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// the telegram handlers...
// common for all boilers
register_telegram_type(0xBF, F("ErrorMessage"), false, MAKE_PF_CB(process_ErrorMessage));
register_telegram_type(0x10, F("UBAErrorMessage1"), false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0x11, F("UBAErrorMessage2"), false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0xC2, F("UBAErrorMessage3"), false, MAKE_PF_CB(process_UBAErrorMessage2));
register_telegram_type(0x14, F("UBATotalUptime"), true, MAKE_PF_CB(process_UBATotalUptime));
register_telegram_type(0x15, F("UBAMaintenanceData"), false, MAKE_PF_CB(process_UBAMaintenanceData));
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
register_telegram_type(0xBF, "ErrorMessage", false, MAKE_PF_CB(process_ErrorMessage));
register_telegram_type(0x10, "UBAErrorMessage1", false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0x11, "UBAErrorMessage2", false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0xC2, "UBAErrorMessage3", false, MAKE_PF_CB(process_UBAErrorMessage2));
register_telegram_type(0x14, "UBATotalUptime", true, MAKE_PF_CB(process_UBATotalUptime));
register_telegram_type(0x15, "UBAMaintenanceData", false, MAKE_PF_CB(process_UBAMaintenanceData));
register_telegram_type(0x1C, "UBAMaintenanceStatus", false, MAKE_PF_CB(process_UBAMaintenanceStatus));
// EMS1.0 and maybe EMS+?
register_telegram_type(0x18, F("UBAMonitorFast"), false, MAKE_PF_CB(process_UBAMonitorFast));
register_telegram_type(0x19, F("UBAMonitorSlow"), false, MAKE_PF_CB(process_UBAMonitorSlow));
register_telegram_type(0x1A, F("UBASetPoints"), false, MAKE_PF_CB(process_UBASetPoints));
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
register_telegram_type(0x18, "UBAMonitorFast", false, MAKE_PF_CB(process_UBAMonitorFast));
register_telegram_type(0x19, "UBAMonitorSlow", false, MAKE_PF_CB(process_UBAMonitorSlow));
register_telegram_type(0x1A, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints));
register_telegram_type(0x35, "UBAFlags", false, MAKE_PF_CB(process_UBAFlags));
// only EMS 1.0
register_telegram_type(0x16, F("UBAParameters"), true, MAKE_PF_CB(process_UBAParameters));
register_telegram_type(0x33, F("UBAParameterWW"), true, MAKE_PF_CB(process_UBAParameterWW));
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_UBAMonitorWW));
register_telegram_type(0x16, "UBAParameters", true, MAKE_PF_CB(process_UBAParameters));
register_telegram_type(0x33, "UBAParameterWW", true, MAKE_PF_CB(process_UBAParameterWW));
register_telegram_type(0x34, "UBAMonitorWW", false, MAKE_PF_CB(process_UBAMonitorWW));
// not ems1.0, but HT3
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS) {
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x2A, F("MC110Status"), false, MAKE_PF_CB(process_MC110Status));
register_telegram_type(0x26, "UBASettingsWW", true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x2A, "MC110Status", false, MAKE_PF_CB(process_MC110Status));
}
// only EMS+
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS && model() != EMSdevice::EMS_DEVICE_FLAG_HT3 && model() != EMSdevice::EMS_DEVICE_FLAG_HYBRID) {
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
register_telegram_type(0xE3, F("UBAMonitorSlowPlus2"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
register_telegram_type(0xE9, F("UBAMonitorWWPlus"), false, MAKE_PF_CB(process_UBAMonitorWWPlus));
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
register_telegram_type(0xD1, "UBAOutdoorTemp", false, MAKE_PF_CB(process_UBAOutdoorTemp));
register_telegram_type(0xE3, "UBAMonitorSlowPlus2", false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
register_telegram_type(0xE4, "UBAMonitorFastPlus", false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_telegram_type(0xE5, "UBAMonitorSlowPlus", false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
register_telegram_type(0xE6, "UBAParametersPlus", true, MAKE_PF_CB(process_UBAParametersPlus));
register_telegram_type(0xE9, "UBAMonitorWWPlus", false, MAKE_PF_CB(process_UBAMonitorWWPlus));
register_telegram_type(0xEA, "UBAParameterWWPlus", true, MAKE_PF_CB(process_UBAParameterWWPlus));
}
if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
register_telegram_type(0x48D, F("HpPower"), true, MAKE_PF_CB(process_HpPower));
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
register_telegram_type(0x48A, F("HpPool"), true, MAKE_PF_CB(process_HpPool));
register_telegram_type(0x4A2, F("HpInput"), false, MAKE_PF_CB(process_HpInput));
register_telegram_type(0x486, F("HpInConfig"), false, MAKE_PF_CB(process_HpInConfig));
register_telegram_type(0x492, F("HpHeaterConfig"), false, MAKE_PF_CB(process_HpHeaterConfig));
register_telegram_type(0x494, "UBAEnergySupplied", false, MAKE_PF_CB(process_UBAEnergySupplied));
register_telegram_type(0x495, "UBAInformation", false, MAKE_PF_CB(process_UBAInformation));
register_telegram_type(0x48D, "HpPower", true, MAKE_PF_CB(process_HpPower));
register_telegram_type(0x48F, "HpOutdoor", false, MAKE_PF_CB(process_HpOutdoor));
register_telegram_type(0x48A, "HpPool", true, MAKE_PF_CB(process_HpPool));
register_telegram_type(0x4A2, "HpInput", false, MAKE_PF_CB(process_HpInput));
register_telegram_type(0x486, "HpInConfig", false, MAKE_PF_CB(process_HpInConfig));
register_telegram_type(0x492, "HpHeaterConfig", false, MAKE_PF_CB(process_HpHeaterConfig));
}
/*
@@ -189,7 +189,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
* enable settings here if no thermostat is used in system
*
if (model() == EMSdevice::EMS_DEVICE_FLAG_HYBRID) {
register_telegram_type(0xBB, F("HybridHp"), true, MAKE_PF_CB(process_HybridHp));
register_telegram_type(0xBB, "HybridHp", true, MAKE_PF_CB(process_HybridHp));
}
*/
@@ -2178,11 +2178,11 @@ bool Boiler::set_ww_circulation_mode(const char * value, const int8_t id) {
}
if (v < 7) {
// LOG_INFO(F("Setting dhw circulation mode %dx3min"), v);
// LOG_INFO("Setting dhw circulation mode %dx3min", v);
} else if (v == 7) {
// LOG_INFO(F("Setting dhw circulation mode continuous"));
// LOG_INFO("Setting dhw circulation mode continuous");
} else {
// LOG_WARNING(F("Set dhw circulation mode: Invalid value"));
// LOG_WARNING("Set dhw circulation mode: Invalid value");
return false;
}
@@ -2206,12 +2206,12 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
}
if (num == 1) {
// LOG_INFO(F("Reset boiler maintenance message"));
// LOG_INFO("Reset boiler maintenance message");
write_command(0x05, 0x08, 0xFF, 0x1C);
has_update(&reset_);
return true;
} else if (num == 2) {
// LOG_INFO(F("Reset boiler error message"));
// LOG_INFO("Reset boiler error message");
write_command(0x05, 0x00, 0x5A); // error reset
has_update(&reset_);
return true;
@@ -2227,8 +2227,8 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
std::string s;
if (Helpers::value2string(value, s)) {
if (s == Helpers::translated_word(FL_(reset))) {
// LOG_INFO(F("Reset boiler maintenance message"));
if (s == std::string(Helpers::translated_word(FL_(reset)))) {
// LOG_INFO("Reset boiler maintenance message");
write_command(0x05, 0x08, 0xFF, 0x1C);
return true;
}
@@ -2239,11 +2239,11 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
uint8_t month = (value[3] - '0') * 10 + (value[4] - '0');
uint8_t year = (uint8_t)(Helpers::atoint(&value[6]) - 2000);
if (day > 0 && day < 32 && month > 0 && month < 13) {
LOG_INFO(F("Setting maintenance date to %02d.%02d.%04d"), day, month, year + 2000);
LOG_INFO("Setting maintenance date to %02d.%02d.%04d", day, month, year + 2000);
uint8_t data[5] = {2, (uint8_t)(maintenanceTime_ / 100), day, month, year};
write_command(0x15, 0, data, 5, 0x15);
} else {
LOG_WARNING(F("Setting maintenance: wrong format %d.%d.%d"), day, month, year + 2000);
LOG_WARNING("Setting maintenance: wrong format %d.%d.%d", day, month, year + 2000);
return false;
}
return true;
@@ -2252,7 +2252,7 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
int hrs;
if (Helpers::value2number(value, hrs)) {
if (hrs > 99 && hrs < 25600) {
LOG_INFO(F("Setting maintenance time %d hours"), hrs);
LOG_INFO("Setting maintenance time %d hours", hrs);
uint8_t data[2] = {1, (uint8_t)(hrs / 100)};
write_command(0x15, 0, data, 2, 0x15);
return true;
@@ -2261,12 +2261,12 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
uint8_t num;
if (Helpers::value2enum(value, num, FL_(enum_off_time_date_manual))) {
LOG_INFO(F("Setting maintenance type to %s"), value);
LOG_INFO("Setting maintenance type to %s", value);
write_command(0x15, 0, num, 0x15);
return true;
}
LOG_WARNING(F("Setting maintenance: wrong format"));
LOG_WARNING("Setting maintenance: wrong format");
return false;
}
//maintenance
@@ -2274,13 +2274,13 @@ bool Boiler::set_maintenancetime(const char * value, const int8_t id) {
int hrs;
if (Helpers::value2number(value, hrs)) {
if (hrs > 99 && hrs < 25600) {
LOG_INFO(F("Setting maintenance time %d hours"), hrs);
LOG_INFO("Setting maintenance time %d hours", hrs);
uint8_t data[2] = {1, (uint8_t)(hrs / 100)};
write_command(0x15, 0, data, 2, 0x15);
return true;
}
}
LOG_WARNING(F("Setting maintenance: wrong format"));
LOG_WARNING("Setting maintenance: wrong format");
return false;
}
@@ -2291,17 +2291,17 @@ bool Boiler::set_maintenancedate(const char * value, const int8_t id) {
uint8_t month = (value[3] - '0') * 10 + (value[4] - '0');
uint8_t year = (uint8_t)(Helpers::atoint(&value[6]) - 2000);
if (day > 0 && day < 32 && month > 0 && month < 13) {
LOG_INFO(F("Setting maintenance date to %02d.%02d.%04d"), day, month, year + 2000);
LOG_INFO("Setting maintenance date to %02d.%02d.%04d", day, month, year + 2000);
uint8_t data[5] = {2, (uint8_t)(maintenanceTime_ / 100), day, month, year};
write_command(0x15, 0, data, 5, 0x15);
} else {
LOG_WARNING(F("Setting maintenance: wrong format %d.%d.%d"), day, month, year + 2000);
LOG_WARNING("Setting maintenance: wrong format %d.%d.%d", day, month, year + 2000);
return false;
}
return true;
}
LOG_WARNING(F("Setting maintenance: wrong format"));
LOG_WARNING("Setting maintenance: wrong format");
return false;
}
@@ -2312,7 +2312,7 @@ bool Boiler::set_pool_temp(const char * value, const int8_t id) {
return false;
}
uint8_t v2 = ((v * 2) + 0.5);
// LOG_INFO(F("Setting pool temperature to %d.%d C"), v2 >> 1, (v2 & 0x01) * 5);
// LOG_INFO("Setting pool temperature to %d.%d C", v2 >> 1, (v2 & 0x01) * 5);
write_command(0x48A, 1, v2, 0x48A);
return true;

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Boiler : public EMSdevice {
public:
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
static uuid::log::Logger logger_;

View File

@@ -22,7 +22,7 @@ namespace emsesp {
REGISTER_FACTORY(Connect, EMSdevice::DeviceType::CONNECT);
Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Connect : public EMSdevice {
public:
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
};
} // namespace emsesp

View File

@@ -22,11 +22,11 @@ namespace emsesp {
REGISTER_FACTORY(Controller, EMSdevice::DeviceType::CONTROLLER);
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// IVT broadcasts Thermostat time from controller (0x09) if display is off.
if ((flags & 0x0F) == EMS_DEVICE_FLAG_IVT) {
register_telegram_type(0x06, F("RCTime"), false, MAKE_PF_CB(process_dateTime));
register_telegram_type(0x06, "RCTime", false, MAKE_PF_CB(process_dateTime));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dateTime_, DeviceValueType::STRING, FL_(dateTime), DeviceValueUOM::NONE);
}
}

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Controller : public EMSdevice {
public:
Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
void process_dateTime(std::shared_ptr<const Telegram> telegram);

View File

@@ -22,7 +22,7 @@ namespace emsesp {
REGISTER_FACTORY(Gateway, EMSdevice::DeviceType::GATEWAY);
Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Gateway : public EMSdevice {
public:
Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
};
} // namespace emsesp

View File

@@ -24,7 +24,7 @@ REGISTER_FACTORY(Generic, EMSdevice::DeviceType::GENERIC);
uuid::log::Logger Generic::logger_{F_(generic), uuid::log::Facility::CONSOLE};
Generic::Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Generic::Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Generic : public EMSdevice {
public:
Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
static uuid::log::Logger logger_;

View File

@@ -22,11 +22,11 @@ namespace emsesp {
REGISTER_FACTORY(Heatpump, EMSdevice::DeviceType::HEATPUMP);
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
register_telegram_type(0x042B, F("HP1"), false, MAKE_PF_CB(process_HPMonitor1));
register_telegram_type(0x047B, F("HP2"), false, MAKE_PF_CB(process_HPMonitor2));
register_telegram_type(0x042B, "HP1", false, MAKE_PF_CB(process_HPMonitor1));
register_telegram_type(0x047B, "HP2", false, MAKE_PF_CB(process_HPMonitor2));
// device values
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &airHumidity_, DeviceValueType::UINT, FL_(airHumidity), DeviceValueUOM::PERCENT);

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Heatpump : public EMSdevice {
public:
Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
uint8_t airHumidity_;

View File

@@ -24,11 +24,11 @@ REGISTER_FACTORY(Mixer, EMSdevice::DeviceType::MIXER);
uuid::log::Logger Mixer::logger_{F_(mixer), uuid::log::Facility::CONSOLE};
Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// Pool module
if (flags == EMSdevice::EMS_DEVICE_FLAG_MP) {
register_telegram_type(0x5BA, F("HpPoolStatus"), true, MAKE_PF_CB(process_HpPoolStatus));
register_telegram_type(0x5BA, "HpPoolStatus", true, MAKE_PF_CB(process_HpPoolStatus));
type_ = Type::MP;
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&poolTemp_,
@@ -43,8 +43,8 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
// EMS+
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device_id >= 0x20 && device_id <= 0x27) {
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, F("MMPLUSStetMessage_HC"), true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSStetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
type_ = Type::HC;
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
@@ -53,9 +53,9 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
} else if (device_id >= 0x28 && device_id <= 0x29) {
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
register_telegram_type(device_id - 0x28 + 0x0313, F("MMPLUSConfigMessage_WWC"), true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC));
// register_telegram_type(device_id - 0x28 + 0x033B, F("MMPLUSSetMessage_WWC"), true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
register_telegram_type(device_id - 0x28 + 0x0331, "MMPLUSStatusMessage_WWC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
register_telegram_type(device_id - 0x28 + 0x0313, "MMPLUSConfigMessage_WWC", true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC));
// register_telegram_type(device_id - 0x28 + 0x033B, "MMPLUSSetMessage_WWC", true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
type_ = Type::WWC;
hc_ = device_id - 0x28 + 1;
uint8_t tag = DeviceValueTAG::TAG_WWC1 + hc_ - 1;
@@ -85,9 +85,9 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
// EMS 1.0
if (flags == EMSdevice::EMS_DEVICE_FLAG_MM10) {
register_telegram_type(0x00AA, F("MMConfigMessage"), true, MAKE_PF_CB(process_MMConfigMessage));
register_telegram_type(0x00AB, F("MMStatusMessage"), false, MAKE_PF_CB(process_MMStatusMessage));
register_telegram_type(0x00AC, F("MMSetMessage"), false, MAKE_PF_CB(process_MMSetMessage));
register_telegram_type(0x00AA, "MMConfigMessage", true, MAKE_PF_CB(process_MMConfigMessage));
register_telegram_type(0x00AB, "MMStatusMessage", false, MAKE_PF_CB(process_MMStatusMessage));
register_telegram_type(0x00AC, "MMSetMessage", false, MAKE_PF_CB(process_MMSetMessage));
type_ = Type::HC;
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
@@ -110,10 +110,10 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
// HT3
if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
if (device_id >= 0x40) { // special DHW pos 10
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_IPMMonitorWW));
register_telegram_type(0x1E, F("HydrTemp"), false, MAKE_PF_CB(process_IPMHydrTemp));
register_telegram_type(0x33, F("UBAParameterWW"), true, MAKE_PF_CB(process_IPMParameterWW));
// register_telegram_type(0x10D, F("wwNTCStatus"), false, MAKE_PF_CB(process_wwNTCStatus));
register_telegram_type(0x34, "UBAMonitorWW", false, MAKE_PF_CB(process_IPMMonitorWW));
register_telegram_type(0x1E, "HydrTemp", false, MAKE_PF_CB(process_IPMHydrTemp));
register_telegram_type(0x33, "UBAParameterWW", true, MAKE_PF_CB(process_IPMParameterWW));
// register_telegram_type(0x10D, "wwNTCStatus", false, MAKE_PF_CB(process_wwNTCStatus));
type_ = Type::WWC;
hc_ = device_id - 0x40 + 1;
uint8_t tag = DeviceValueTAG::TAG_WWC9 + hc_ - 1;
@@ -139,9 +139,9 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
MAKE_CF_CB(set_wwCircPump));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} else {
register_telegram_type(0x010C, F("IPMStatusMessage"), false, MAKE_PF_CB(process_IPMStatusMessage));
register_telegram_type(0x011E, F("IPMTempMessage"), false, MAKE_PF_CB(process_IPMTempMessage));
// register_telegram_type(0x0123, F("IPMSetMessage"), false, MAKE_PF_CB(process_IPMSetMessage));
register_telegram_type(0x010C, "IPMStatusMessage", false, MAKE_PF_CB(process_IPMStatusMessage));
register_telegram_type(0x011E, "IPMTempMessage", false, MAKE_PF_CB(process_IPMTempMessage));
// register_telegram_type(0x0123, "IPMSetMessage", false, MAKE_PF_CB(process_IPMSetMessage));
type_ = Type::HC;
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Mixer : public EMSdevice {
public:
Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
static uuid::log::Logger logger_;

View File

@@ -24,48 +24,48 @@ REGISTER_FACTORY(Solar, EMSdevice::DeviceType::SOLAR);
uuid::log::Logger Solar::logger_{F_(solar), uuid::log::Facility::CONSOLE};
Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
register_telegram_type(0x97, F("SM10Monitor"), false, MAKE_PF_CB(process_SM10Monitor));
register_telegram_type(0x96, F("SM10Config"), true, MAKE_PF_CB(process_SM10Config));
register_telegram_type(0x97, "SM10Monitor", false, MAKE_PF_CB(process_SM10Monitor));
register_telegram_type(0x96, "SM10Config", true, MAKE_PF_CB(process_SM10Config));
EMSESP::send_read_request(0x97, device_id);
}
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
if (device_id == 0x2A) { // SM100 DHW
register_telegram_type(0x07D6, F("SM100wwTemperature"), false, MAKE_PF_CB(process_SM100wwTemperature));
register_telegram_type(0x07AA, F("SM100wwStatus"), false, MAKE_PF_CB(process_SM100wwStatus));
register_telegram_type(0x07AB, F("SM100wwCommand"), false, MAKE_PF_CB(process_SM100wwCommand));
register_telegram_type(0x07A5, F("SM100wwCirc"), true, MAKE_PF_CB(process_SM100wwCirc));
register_telegram_type(0x07A6, F("SM100wwParam"), true, MAKE_PF_CB(process_SM100wwParam));
register_telegram_type(0x07AE, F("SM100wwKeepWarm"), true, MAKE_PF_CB(process_SM100wwKeepWarm));
register_telegram_type(0x07E0, F("SM100wwStatus2"), true, MAKE_PF_CB(process_SM100wwStatus2));
register_telegram_type(0x07D6, "SM100wwTemperature", false, MAKE_PF_CB(process_SM100wwTemperature));
register_telegram_type(0x07AA, "SM100wwStatus", false, MAKE_PF_CB(process_SM100wwStatus));
register_telegram_type(0x07AB, "SM100wwCommand", false, MAKE_PF_CB(process_SM100wwCommand));
register_telegram_type(0x07A5, "SM100wwCirc", true, MAKE_PF_CB(process_SM100wwCirc));
register_telegram_type(0x07A6, "SM100wwParam", true, MAKE_PF_CB(process_SM100wwParam));
register_telegram_type(0x07AE, "SM100wwKeepWarm", true, MAKE_PF_CB(process_SM100wwKeepWarm));
register_telegram_type(0x07E0, "SM100wwStatus2", true, MAKE_PF_CB(process_SM100wwStatus2));
} else {
// F9 is not a telegram type, it's a flag for configure
// register_telegram_type(0xF9, F("ParamCfg"), false, MAKE_PF_CB(process_SM100ParamCfg));
register_telegram_type(0x0358, F("SM100SystemConfig"), true, MAKE_PF_CB(process_SM100SystemConfig));
register_telegram_type(0x035A, F("SM100CircuitConfig"), true, MAKE_PF_CB(process_SM100CircuitConfig));
register_telegram_type(0x035D, F("SM100Circuit2Config"), true, MAKE_PF_CB(process_SM100Circuit2Config));
register_telegram_type(0x0362, F("SM100Monitor"), false, MAKE_PF_CB(process_SM100Monitor));
register_telegram_type(0x0363, F("SM100Monitor2"), false, MAKE_PF_CB(process_SM100Monitor2));
register_telegram_type(0x0366, F("SM100Config"), false, MAKE_PF_CB(process_SM100Config));
register_telegram_type(0x0364, F("SM100Status"), false, MAKE_PF_CB(process_SM100Status));
register_telegram_type(0x036A, F("SM100Status2"), false, MAKE_PF_CB(process_SM100Status2));
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, MAKE_PF_CB(process_SM100CollectorConfig));
register_telegram_type(0x038E, F("SM100Energy"), true, MAKE_PF_CB(process_SM100Energy));
register_telegram_type(0x0391, F("SM100Time"), true, MAKE_PF_CB(process_SM100Time));
register_telegram_type(0x035F, F("SM100Config1"), true, MAKE_PF_CB(process_SM100Config1));
register_telegram_type(0x035C, F("SM100HeatAssist"), true, MAKE_PF_CB(process_SM100HeatAssist));
register_telegram_type(0x0361, F("SM100Differential"), true, MAKE_PF_CB(process_SM100Differential));
// register_telegram_type(0xF9, "ParamCfg", false, MAKE_PF_CB(process_SM100ParamCfg));
register_telegram_type(0x0358, "SM100SystemConfig", true, MAKE_PF_CB(process_SM100SystemConfig));
register_telegram_type(0x035A, "SM100CircuitConfig", true, MAKE_PF_CB(process_SM100CircuitConfig));
register_telegram_type(0x035D, "SM100Circuit2Config", true, MAKE_PF_CB(process_SM100Circuit2Config));
register_telegram_type(0x0362, "SM100Monitor", false, MAKE_PF_CB(process_SM100Monitor));
register_telegram_type(0x0363, "SM100Monitor2", false, MAKE_PF_CB(process_SM100Monitor2));
register_telegram_type(0x0366, "SM100Config", false, MAKE_PF_CB(process_SM100Config));
register_telegram_type(0x0364, "SM100Status", false, MAKE_PF_CB(process_SM100Status));
register_telegram_type(0x036A, "SM100Status2", false, MAKE_PF_CB(process_SM100Status2));
register_telegram_type(0x0380, "SM100CollectorConfig", true, MAKE_PF_CB(process_SM100CollectorConfig));
register_telegram_type(0x038E, "SM100Energy", true, MAKE_PF_CB(process_SM100Energy));
register_telegram_type(0x0391, "SM100Time", true, MAKE_PF_CB(process_SM100Time));
register_telegram_type(0x035F, "SM100Config1", true, MAKE_PF_CB(process_SM100Config1));
register_telegram_type(0x035C, "SM100HeatAssist", true, MAKE_PF_CB(process_SM100HeatAssist));
register_telegram_type(0x0361, "SM100Differential", true, MAKE_PF_CB(process_SM100Differential));
}
}
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, MAKE_PF_CB(process_ISM1StatusMessage));
register_telegram_type(0x0101, F("ISM1Set"), true, MAKE_PF_CB(process_ISM1Set));
register_telegram_type(0x0104, F("ISM2StatusMessage"), false, MAKE_PF_CB(process_ISM2StatusMessage));
register_telegram_type(0x0103, "ISM1StatusMessage", true, MAKE_PF_CB(process_ISM1StatusMessage));
register_telegram_type(0x0101, "ISM1Set", true, MAKE_PF_CB(process_ISM1Set));
register_telegram_type(0x0104, "ISM2StatusMessage", false, MAKE_PF_CB(process_ISM2StatusMessage));
}
// device values...
@@ -664,7 +664,7 @@ void Solar::process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(max, 13);
telegram->read_value(cur, 17);
// LOG_DEBUG(F("SM100ParamCfg param=0x%04X, offset=%d, min=%d, default=%d, max=%d, current=%d"), t_id, of, min, def, max, cur));
// LOG_DEBUG("SM100ParamCfg param=0x%04X, offset=%d, min=%d, default=%d, max=%d, current=%d", t_id, of, min, def, max, cur));
}
/*

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Solar : public EMSdevice {
public:
Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
static uuid::log::Logger logger_;

View File

@@ -22,12 +22,12 @@ namespace emsesp {
REGISTER_FACTORY(Switch, EMSdevice::DeviceType::SWITCH);
Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// WM10 module, device_id 0x11
register_telegram_type(0x9C, F("WM10MonitorMessage"), false, MAKE_PF_CB(process_WM10MonitorMessage));
register_telegram_type(0x9D, F("WM10SetMessage"), false, MAKE_PF_CB(process_WM10SetMessage));
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
register_telegram_type(0x9C, "WM10MonitorMessage", false, MAKE_PF_CB(process_WM10MonitorMessage));
register_telegram_type(0x9D, "WM10SetMessage", false, MAKE_PF_CB(process_WM10SetMessage));
register_telegram_type(0x1E, "WM10TempMessage", false, MAKE_PF_CB(process_WM10TempMessage));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &activated_, DeviceValueType::BOOL, FL_(activated), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Switch : public EMSdevice {
public:
Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
void process_WM10SetMessage(std::shared_ptr<const Telegram> telegram);

View File

@@ -24,37 +24,37 @@ REGISTER_FACTORY(Thermostat, EMSdevice::DeviceType::THERMOSTAT);
uuid::log::Logger Thermostat::logger_{F_(thermostat), uuid::log::Facility::CONSOLE};
Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
uint8_t model = this->model();
// RF remote sensor seen at 0x40, maybe this is also for different hc with id 0x40 - 0x47? emsesp.cpp maps only 0x40
if (device_id >= 0x40 && device_id <= 0x47) {
register_telegram_type(0x0435, F("RFTemp"), false, MAKE_PF_CB(process_RemoteTemp));
register_telegram_type(0x0435, "RFTemp", false, MAKE_PF_CB(process_RemoteTemp));
return;
}
// remote thermostats with humidity: RC100H remote
if (device_id >= 0x38 && device_id <= 0x3F) {
register_telegram_type(0x042B, F("RemoteTemp"), false, MAKE_PF_CB(process_RemoteTemp));
register_telegram_type(0x047B, F("RemoteHumidity"), false, MAKE_PF_CB(process_RemoteHumidity));
register_telegram_type(0x0273, F("RemoteCorrection"), true, MAKE_PF_CB(process_RemoteCorrection));
register_telegram_type(0x042B, "RemoteTemp", false, MAKE_PF_CB(process_RemoteTemp));
register_telegram_type(0x047B, "RemoteHumidity", false, MAKE_PF_CB(process_RemoteHumidity));
register_telegram_type(0x0273, "RemoteCorrection", true, MAKE_PF_CB(process_RemoteCorrection));
register_device_values(); // register device values for common values (not heating circuit)
return; // no values to add
}
// common telegram handlers
register_telegram_type(EMS_TYPE_RCOutdoorTemp, F("RCOutdoorTemp"), false, MAKE_PF_CB(process_RCOutdoorTemp));
register_telegram_type(EMS_TYPE_RCTime, F("RCTime"), false, MAKE_PF_CB(process_RCTime));
register_telegram_type(0xA2, F("RCError"), false, MAKE_PF_CB(process_RCError));
register_telegram_type(0x12, F("RCErrorMessage"), false, MAKE_PF_CB(process_RCErrorMessage));
register_telegram_type(0x13, F("RCErrorMessage2"), false, MAKE_PF_CB(process_RCErrorMessage));
register_telegram_type(EMS_TYPE_RCOutdoorTemp, "RCOutdoorTemp", false, MAKE_PF_CB(process_RCOutdoorTemp));
register_telegram_type(EMS_TYPE_RCTime, "RCTime", false, MAKE_PF_CB(process_RCTime));
register_telegram_type(0xA2, "RCError", false, MAKE_PF_CB(process_RCError));
register_telegram_type(0x12, "RCErrorMessage", false, MAKE_PF_CB(process_RCErrorMessage));
register_telegram_type(0x13, "RCErrorMessage2", false, MAKE_PF_CB(process_RCErrorMessage));
// RC10
if (model == EMSdevice::EMS_DEVICE_FLAG_RC10) {
monitor_typeids = {0xB1};
set_typeids = {0xB0};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC10Monitor"), false, MAKE_PF_CB(process_RC10Monitor));
register_telegram_type(set_typeids[i], F("RC10Set"), false, MAKE_PF_CB(process_RC10Set));
register_telegram_type(monitor_typeids[i], "RC10Monitor", false, MAKE_PF_CB(process_RC10Monitor));
register_telegram_type(set_typeids[i], "RC10Set", false, MAKE_PF_CB(process_RC10Set));
}
// RC35
@@ -64,15 +64,15 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
timer_typeids = {0x3F, 0x49, 0x53, 0x5D};
timer2_typeids = {0x42, 0x4C, 0x56, 0x60};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC35Monitor"), false, MAKE_PF_CB(process_RC35Monitor));
register_telegram_type(set_typeids[i], F("RC35Set"), false, MAKE_PF_CB(process_RC35Set));
register_telegram_type(timer_typeids[i], F("RC35Timer"), false, MAKE_PF_CB(process_RC35Timer));
register_telegram_type(timer2_typeids[i], F("RC35Timer2"), false, MAKE_PF_CB(process_RC35Timer));
register_telegram_type(monitor_typeids[i], "RC35Monitor", false, MAKE_PF_CB(process_RC35Monitor));
register_telegram_type(set_typeids[i], "RC35Set", false, MAKE_PF_CB(process_RC35Set));
register_telegram_type(timer_typeids[i], "RC35Timer", false, MAKE_PF_CB(process_RC35Timer));
register_telegram_type(timer2_typeids[i], "RC35Timer2", false, MAKE_PF_CB(process_RC35Timer));
}
register_telegram_type(EMS_TYPE_IBASettings, F("IBASettings"), true, MAKE_PF_CB(process_IBASettings));
register_telegram_type(EMS_TYPE_wwSettings, F("WWSettings"), true, MAKE_PF_CB(process_RC35wwSettings));
register_telegram_type(0x38, F("WWTimer"), true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(0x39, F("WWCircTimer"), true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(EMS_TYPE_IBASettings, "IBASettings", true, MAKE_PF_CB(process_IBASettings));
register_telegram_type(EMS_TYPE_wwSettings, "WWSettings", true, MAKE_PF_CB(process_RC35wwSettings));
register_telegram_type(0x38, "WWTimer", true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(0x39, "WWCircTimer", true, MAKE_PF_CB(process_RC35wwTimer));
// RC20
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20) {
@@ -81,22 +81,22 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
curve_typeids = {0x90};
timer_typeids = {0x8F};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC20Monitor"), false, MAKE_PF_CB(process_RC20Monitor));
register_telegram_type(set_typeids[i], F("RC20Set"), false, MAKE_PF_CB(process_RC20Set));
register_telegram_type(curve_typeids[i], F("RC20Temp"), false, MAKE_PF_CB(process_RC20Temp));
register_telegram_type(timer_typeids[i], F("RC20Timer"), false, MAKE_PF_CB(process_RC20Timer));
register_telegram_type(monitor_typeids[i], "RC20Monitor", false, MAKE_PF_CB(process_RC20Monitor));
register_telegram_type(set_typeids[i], "RC20Set", false, MAKE_PF_CB(process_RC20Set));
register_telegram_type(curve_typeids[i], "RC20Temp", false, MAKE_PF_CB(process_RC20Temp));
register_telegram_type(timer_typeids[i], "RC20Timer", false, MAKE_PF_CB(process_RC20Timer));
}
// remote thermostat uses only 0xAF
register_telegram_type(0xAF, F("RC20Remote"), false, MAKE_PF_CB(process_RC20Remote));
register_telegram_type(0xAF, "RC20Remote", false, MAKE_PF_CB(process_RC20Remote));
// RC20 newer
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC20_N) || (model == EMSdevice::EMS_DEVICE_FLAG_RC25)) {
monitor_typeids = {0xAE};
set_typeids = {0xAD};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC20Monitor"), false, MAKE_PF_CB(process_RC20Monitor_2));
register_telegram_type(set_typeids[i], F("RC20Set"), false, MAKE_PF_CB(process_RC20Set_2));
register_telegram_type(monitor_typeids[i], "RC20Monitor", false, MAKE_PF_CB(process_RC20Monitor_2));
register_telegram_type(set_typeids[i], "RC20Set", false, MAKE_PF_CB(process_RC20Set_2));
}
register_telegram_type(0xAF, F("RC20Remote"), false, MAKE_PF_CB(process_RC20Remote));
register_telegram_type(0xAF, "RC20Remote", false, MAKE_PF_CB(process_RC20Remote));
// RC30
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC30) {
monitor_typeids = {0x41};
@@ -104,27 +104,27 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
curve_typeids = {0x40};
timer_typeids = {0x3F};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC30Monitor"), false, MAKE_PF_CB(process_RC30Monitor));
register_telegram_type(set_typeids[i], F("RC30Set"), false, MAKE_PF_CB(process_RC30Set));
register_telegram_type(curve_typeids[i], F("RC30Temp"), false, MAKE_PF_CB(process_RC30Temp));
register_telegram_type(timer_typeids[i], F("RC30Timer"), false, MAKE_PF_CB(process_RC35Timer));
register_telegram_type(monitor_typeids[i], "RC30Monitor", false, MAKE_PF_CB(process_RC30Monitor));
register_telegram_type(set_typeids[i], "RC30Set", false, MAKE_PF_CB(process_RC30Set));
register_telegram_type(curve_typeids[i], "RC30Temp", false, MAKE_PF_CB(process_RC30Temp));
register_telegram_type(timer_typeids[i], "RC30Timer", false, MAKE_PF_CB(process_RC35Timer));
}
register_telegram_type(EMS_TYPE_RC30wwSettings, F("RC30WWSettings"), true, MAKE_PF_CB(process_RC30wwSettings));
register_telegram_type(0x38, F("WWTimer"), true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(0x39, F("WWCircTimer"), true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(EMS_TYPE_RC30wwSettings, "RC30WWSettings", true, MAKE_PF_CB(process_RC30wwSettings));
register_telegram_type(0x38, "WWTimer", true, MAKE_PF_CB(process_RC35wwTimer));
register_telegram_type(0x39, "WWCircTimer", true, MAKE_PF_CB(process_RC35wwTimer));
// EASY
} else if (model == EMSdevice::EMS_DEVICE_FLAG_EASY) {
monitor_typeids = {0x0A};
set_typeids = {};
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, MAKE_PF_CB(process_EasyMonitor));
register_telegram_type(monitor_typeids[0], "EasyMonitor", true, MAKE_PF_CB(process_EasyMonitor));
// CRF
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
set_typeids = {};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("CRFMonitor"), false, MAKE_PF_CB(process_CRFMonitor));
register_telegram_type(monitor_typeids[i], "CRFMonitor", false, MAKE_PF_CB(process_CRFMonitor));
}
// RC300/RC100
@@ -136,46 +136,46 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
curve_typeids = {0x029B, 0x029C, 0x029D, 0x029E, 0x029F, 0x02A0, 0x02A1, 0x02A2};
summer2_typeids = {0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, 0x0478};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC300Monitor"), false, MAKE_PF_CB(process_RC300Monitor));
register_telegram_type(set_typeids[i], F("RC300Set"), false, MAKE_PF_CB(process_RC300Set));
register_telegram_type(summer_typeids[i], F("RC300Summer"), false, MAKE_PF_CB(process_RC300Summer));
register_telegram_type(curve_typeids[i], F("RC300Curves"), false, MAKE_PF_CB(process_RC300Curve));
register_telegram_type(summer2_typeids[i], F("RC300Summer2"), false, MAKE_PF_CB(process_RC300Summer2));
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor));
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set));
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer));
register_telegram_type(curve_typeids[i], "RC300Curves", false, MAKE_PF_CB(process_RC300Curve));
register_telegram_type(summer2_typeids[i], "RC300Summer2", false, MAKE_PF_CB(process_RC300Summer2));
}
for (uint8_t i = 0; i < set2_typeids.size(); i++) {
register_telegram_type(set2_typeids[i], F("RC300Set2"), false, MAKE_PF_CB(process_RC300Set2));
register_telegram_type(set2_typeids[i], "RC300Set2", false, MAKE_PF_CB(process_RC300Set2));
}
register_telegram_type(0x2F5, F("RC300WWmode"), true, MAKE_PF_CB(process_RC300WWmode));
register_telegram_type(0x31B, F("RC300WWtemp"), true, MAKE_PF_CB(process_RC300WWtemp));
register_telegram_type(0x31D, F("RC300WWmode2"), false, MAKE_PF_CB(process_RC300WWmode2));
register_telegram_type(0x31E, F("RC300WWmode2"), false, MAKE_PF_CB(process_RC300WWmode2));
register_telegram_type(0x23A, F("RC300OutdoorTemp"), true, MAKE_PF_CB(process_RC300OutdoorTemp));
register_telegram_type(0x267, F("RC300Floordry"), false, MAKE_PF_CB(process_RC300Floordry));
register_telegram_type(0x240, F("RC300Settings"), true, MAKE_PF_CB(process_RC300Settings));
register_telegram_type(0x2F5, "RC300WWmode", true, MAKE_PF_CB(process_RC300WWmode));
register_telegram_type(0x31B, "RC300WWtemp", true, MAKE_PF_CB(process_RC300WWtemp));
register_telegram_type(0x31D, "RC300WWmode2", false, MAKE_PF_CB(process_RC300WWmode2));
register_telegram_type(0x31E, "RC300WWmode2", false, MAKE_PF_CB(process_RC300WWmode2));
register_telegram_type(0x23A, "RC300OutdoorTemp", true, MAKE_PF_CB(process_RC300OutdoorTemp));
register_telegram_type(0x267, "RC300Floordry", false, MAKE_PF_CB(process_RC300Floordry));
register_telegram_type(0x240, "RC300Settings", true, MAKE_PF_CB(process_RC300Settings));
// JUNKERS/HT3
} else if (model == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
monitor_typeids = {0x016F, 0x0170, 0x0171, 0x0172};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("JunkersMonitor"), false, MAKE_PF_CB(process_JunkersMonitor));
register_telegram_type(monitor_typeids[i], "JunkersMonitor", false, MAKE_PF_CB(process_JunkersMonitor));
}
if (has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
// FR120, FR100
set_typeids = {0x0179, 0x017A, 0x017B, 0x017C};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(set_typeids[i], F("JunkersSet"), false, MAKE_PF_CB(process_JunkersSet2));
register_telegram_type(set_typeids[i], "JunkersSet", false, MAKE_PF_CB(process_JunkersSet2));
}
} else {
set_typeids = {0x0165, 0x0166, 0x0167, 0x0168};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(set_typeids[i], F("JunkersSet"), false, MAKE_PF_CB(process_JunkersSet));
register_telegram_type(set_typeids[i], "JunkersSet", false, MAKE_PF_CB(process_JunkersSet));
}
}
register_telegram_type(0xBB, F("HybridSettings"), true, MAKE_PF_CB(process_JunkersHybridSettings));
register_telegram_type(0x23, F("JunkersSetMixer"), true, MAKE_PF_CB(process_JunkersSetMixer));
register_telegram_type(0x123, F("JunkersRemote"), false, MAKE_PF_CB(process_JunkersRemoteMonitor));
register_telegram_type(0x1D3, F("JunkersDhw"), true, MAKE_PF_CB(process_JunkersWW));
register_telegram_type(0xBB, "HybridSettings", true, MAKE_PF_CB(process_JunkersHybridSettings));
register_telegram_type(0x23, "JunkersSetMixer", true, MAKE_PF_CB(process_JunkersSetMixer));
register_telegram_type(0x123, "JunkersRemote", false, MAKE_PF_CB(process_JunkersRemoteMonitor));
register_telegram_type(0x1D3, "JunkersDhw", true, MAKE_PF_CB(process_JunkersWW));
}
// register device values for common values (not heating circuit)
@@ -522,7 +522,7 @@ void Thermostat::process_RC10Set(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) {
return;
}
has_update(telegram, ibaClockOffset_, 0);
has_update(telegram, ibaCalIntTemperature_, 0);
has_update(telegram, backlight_, 1);
has_update(telegram, wwMode_, 2);
has_update(telegram, hc->nighttemp, 3);
@@ -593,8 +593,7 @@ void Thermostat::process_RC20Timer(std::shared_ptr<const Telegram> telegram) {
uint8_t time = telegram->message_data[1];
// we use EN settings for the day abbreviation
std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day][0]);
// std::string sday = Helpers::translated_word(FL_(enum_dayOfWeek)[day]);
std::string sday = (FL_(enum_dayOfWeek)[day][0]);
if (day == 7) {
snprintf(data, sizeof(data), "%02d not_set", no);
@@ -680,13 +679,19 @@ void Thermostat::process_JunkersSet(std::shared_ptr<const Telegram> telegram) {
return;
}
has_update(telegram, hc->daytemp, 17); // is * 2
has_update(telegram, hc->nighttemp, 16); // is * 2
has_update(telegram, hc->nofrosttemp, 15); // is * 2
has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
has_update(telegram, hc->roomsensor, 9); // 1-intern, 2-extern, 3-autoselect the lower value
has_update(telegram, hc->daytemp, 17); // is * 2
has_update(telegram, hc->nighttemp, 16); // is * 2
has_update(telegram, hc->nofrosttemp, 15); // is * 2
has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
has_update(telegram, hc->daytemp, 17); // is * 2
has_update(telegram, hc->nighttemp, 16); // is * 2
has_update(telegram, hc->nofrosttemp, 15); // is * 2
has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
has_enumupdate(telegram, hc->roomsensor, 9, 1); // 1-intern, 2-extern, 3-autoselect the lower value
}
// type 0x0179, ff
@@ -795,20 +800,11 @@ void Thermostat::process_RC35wwTimer(std::shared_ptr<const Telegram> telegram) {
char data[sizeof(wwSwitchTime_)];
// we use EN settings for the day abbreviation
std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day][0]);
// std::string sday = Helpers::translated_word(FL_(enum_dayOfWeek)[day]);
std::string sday = (FL_(enum_dayOfWeek)[day][0]);
if (day == 7) {
snprintf(data, sizeof(data), "%02d not_set", no);
} else {
snprintf(data,
sizeof(data),
"%02d %s %02d:%02d %s",
no,
sday.c_str(),
time / 6,
10 * (time % 6),
// on ? (Helpers::translated_word(FL_(on))).c_str() : (Helpers::translated_word(FL_(off))).c_str());
on ? "on" : "off");
snprintf(data, sizeof(data), "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), on ? "on" : "off");
}
if (telegram->type_id == 0x38) {
has_update(wwSwitchTime_, data, sizeof(wwSwitchTime_));
@@ -1102,6 +1098,7 @@ void Thermostat::process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegr
// 0x240 RC300 parameter
void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, ibaCalIntTemperature_, 7);
has_update(telegram, ibaDamping_, 8);
has_enumupdate(telegram, ibaBuildingType_, 9, 1); // 1=light, 2=medium, 3=heavy
has_update(telegram, ibaMinExtTemperature_, 10);
@@ -1273,8 +1270,7 @@ void Thermostat::process_RC35Timer(std::shared_ptr<const Telegram> telegram) {
uint8_t time = telegram->message_data[1];
// we use EN settings for the day abbreviation
std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day][0]);
// std::string sday = Helpers::translated_word(FL_(enum_dayOfWeek)[day]);
std::string sday = (FL_(enum_dayOfWeek)[day][0]);
if (day == 7) {
snprintf(data, sizeof(data), "%02d not_set", no);
} else if (model() == EMS_DEVICE_FLAG_RC30) {
@@ -1363,14 +1359,15 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
strftime(newdatetime, sizeof(dateTime_), "%d.%m.%G %H:%M", tm_);
has_update(dateTime_, newdatetime, sizeof(dateTime_));
bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
time_t ttime = mktime(tm_); // thermostat time
bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
bool junkersclock = model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS;
time_t ttime = mktime(tm_); // thermostat time
// correct thermostat clock if we have valid ntp time, and could write the command
if (!ivtclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
if (!ivtclock && !junkersclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
double difference = difftime(now, ttime);
if (difference > 15 || difference < -15) {
set_datetime("ntp", -1); // set from NTP
LOG_INFO(F("thermostat time correction from ntp"));
LOG_INFO("thermostat time correction from ntp");
}
}
#ifndef EMSESP_STANDALONE
@@ -1381,7 +1378,7 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
}
struct timeval newnow = {.tv_sec = ttime};
settimeofday(&newnow, nullptr);
LOG_INFO(F("ems-esp time set from thermostat"));
LOG_INFO("ems-esp time set from thermostat");
}
#endif
}
@@ -1549,7 +1546,7 @@ bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
}
auto t = (int8_t)(ct * 10);
LOG_DEBUG(F("Calibrating internal temperature to %d.%d C"), t / 10, t < 0 ? -t % 10 : t % 10);
LOG_DEBUG("Calibrating internal temperature to %d.%d C", t / 10, t < 0 ? -t % 10 : t % 10);
if (model() == EMS_DEVICE_FLAG_RC10) {
write_command(0xB0, 0, t, 0xB0);
@@ -1557,6 +1554,8 @@ bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
write_command(EMS_TYPE_RC30Settings, 1, t, EMS_TYPE_RC30Settings);
} else if (model() == EMS_DEVICE_FLAG_RC100H) {
write_command(0x273, 0, t, 0x273);
} else if (model() == EMS_DEVICE_FLAG_RC100 || model() == EMS_DEVICE_FLAG_RC300) {
write_command(0x240, 7, t, 0x240);
} else {
write_command(EMS_TYPE_IBASettings, 2, t, EMS_TYPE_IBASettings);
}
@@ -1564,10 +1563,10 @@ bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
return true;
}
// 0xA5 - Set the display settings
// 0xA5 - Set the display settings, RC30_N
bool Thermostat::set_display(const char * value, const int8_t id) {
int ds = 0;
if (!Helpers::value2number(value, ds)) {
uint8_t ds;
if (!Helpers::value2enum(value, ds, FL_(enum_ibaMainDisplay))) {
return false;
}
@@ -1714,7 +1713,7 @@ bool Thermostat::set_roomsensor(const char * value, const int8_t id) {
uint8_t ctrl = 0;
if (model() == EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
if (Helpers::value2enum(value, ctrl, FL_(enum_roomsensor))) {
write_command(set_typeids[hc->hc()], 9, ctrl);
write_command(set_typeids[hc->hc()], 9, ctrl + 1);
return true;
}
}
@@ -2151,7 +2150,7 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
return false;
}
if (!EMSESP::system_.ntp_connected()) {
LOG_WARNING(F("Set date: no valid NTP data, setting from ESP Clock"));
LOG_WARNING("Set date: no valid NTP data, setting from ESP Clock");
}
data[0] = tm_->tm_year - 100; // Bosch counts from 2000
@@ -2162,6 +2161,10 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[5] = tm_->tm_sec;
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
data[6]++; // Junkers use 1-7;
data[7] = 0;
}
} else if (dt.length() == 23) {
data[0] = (dt[7] - '0') * 100 + (dt[8] - '0') * 10 + (dt[9] - '0'); // year
data[1] = (dt[3] - '0') * 10 + (dt[4] - '0'); // month
@@ -2171,16 +2174,19 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[5] = (dt[17] - '0') * 10 + (dt[18] - '0'); // sec
data[6] = (dt[20] - '0'); // day of week, Mo:0
data[7] = (dt[22] - '0') + 2; // DST and flag
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
data[7] = 0;
}
} else {
LOG_WARNING(F("Set date: invalid data, wrong length"));
LOG_WARNING("Set date: invalid data, wrong length");
return false;
}
if (data[1] == 0 || data[1] > 12 || data[2] > 23 || data[3] == 0 || data[3] > 31 || data[4] > 59 || data[5] > 59 || data[6] > 6 || data[7] > 3) {
LOG_WARNING(F("Invalid date/time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d"), data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
LOG_WARNING("Invalid date/time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d", data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
return false;
}
// LOG_INFO(F("Setting date and time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d"), data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
// LOG_INFO("Setting date and time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d", data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
write_command(EMS_TYPE_time, 0, data, 8, EMS_TYPE_time);
return true;
@@ -2203,82 +2209,93 @@ bool Thermostat::set_roominfl_factor(const char * value, const int8_t id) {
return true;
}
// sets the thermostat working mode, where mode is a string
// converts string mode to HeatingCircuit::Mode
// sets the thermostat working mode
bool Thermostat::set_mode(const char * value, const int8_t id) {
if ((value == nullptr) || (strlen(value) >= 20)) {
return false;
}
std::string mode;
if (value[0] >= '0' && value[0] <= '9') {
uint8_t num = value[0] - '0';
switch (model()) {
case EMSdevice::EMS_DEVICE_FLAG_RC10:
mode = Helpers::translated_word(FL_(enum_mode6)[num], true);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC20:
case EMSdevice::EMS_DEVICE_FLAG_RC20_N:
mode = Helpers::translated_word(FL_(enum_mode2)[num], true);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC25:
case EMSdevice::EMS_DEVICE_FLAG_RC30:
case EMSdevice::EMS_DEVICE_FLAG_RC35:
case EMSdevice::EMS_DEVICE_FLAG_RC30_N:
mode = Helpers::translated_word(FL_(enum_mode3)[num], true);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC300:
case EMSdevice::EMS_DEVICE_FLAG_RC100:
mode = Helpers::translated_word(FL_(enum_mode)[num], true);
break;
case EMSdevice::EMS_DEVICE_FLAG_JUNKERS:
mode = Helpers::translated_word(FL_(enum_mode4)[num], true);
break;
case EMSdevice::EMS_DEVICE_FLAG_CRF:
mode = Helpers::translated_word(FL_(enum_mode5)[num], true);
break;
default:
return false;
}
} else if (!Helpers::value2string(value, mode)) {
// first determine which enum we are using
const char * const ** mode_list = nullptr; // points to a translated list of modes
switch (model()) {
case EMSdevice::EMS_DEVICE_FLAG_RC10:
mode_list = FL_(enum_mode6);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC20:
case EMSdevice::EMS_DEVICE_FLAG_RC20_N:
mode_list = FL_(enum_mode2);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC25:
case EMSdevice::EMS_DEVICE_FLAG_RC30:
case EMSdevice::EMS_DEVICE_FLAG_RC35:
case EMSdevice::EMS_DEVICE_FLAG_RC30_N:
mode_list = FL_(enum_mode3);
break;
case EMSdevice::EMS_DEVICE_FLAG_RC300:
case EMSdevice::EMS_DEVICE_FLAG_RC100:
mode_list = FL_(enum_mode);
break;
case EMSdevice::EMS_DEVICE_FLAG_JUNKERS:
mode_list = FL_(enum_mode4);
break;
case EMSdevice::EMS_DEVICE_FLAG_CRF:
mode_list = FL_(enum_mode5);
break;
default:
return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
uint8_t enum_index = 0;
if (Helpers::translated_word(FL_(off), true) == mode) {
return set_mode_n(HeatingCircuit::Mode::OFF, hc_num);
// check for a mode number as a string with a single digit (0..9)
if (value[0] >= '0' && value[0] <= '9') {
enum_index = value[0] - '0';
if (enum_index >= Helpers::count_items(mode_list)) {
return false; // invalid number, not in enum
}
} else {
// check for the mode being a full string name
if (!Helpers::value2enum(value, enum_index, mode_list)) {
return false; // not found
}
}
if (Helpers::translated_word(FL_(manual), true) == mode) {
return set_mode_n(HeatingCircuit::Mode::MANUAL, hc_num);
}
if (Helpers::translated_word(FL_(auto), true) == mode) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; // heating circuit
// compare the english string
auto mode = mode_list[enum_index][0];
if (!strcmp(mode, FL_(auto)[0])) {
return set_mode_n(HeatingCircuit::Mode::AUTO, hc_num);
}
if (Helpers::translated_word(FL_(day), true) == mode) {
if (!strcmp(mode, FL_(off)[0])) {
return set_mode_n(HeatingCircuit::Mode::OFF, hc_num);
}
if (!strcmp(mode, FL_(manual)[0])) {
return set_mode_n(HeatingCircuit::Mode::MANUAL, hc_num);
}
if (!strcmp(mode, FL_(day)[0])) {
return set_mode_n(HeatingCircuit::Mode::DAY, hc_num);
}
if (Helpers::translated_word(FL_(night), true) == mode) {
if (!strcmp(mode, FL_(night)[0])) {
return set_mode_n(HeatingCircuit::Mode::NIGHT, hc_num);
}
if (Helpers::translated_word(FL_(heat), true) == mode) {
if (!strcmp(mode, FL_(heat)[0])) {
return set_mode_n(HeatingCircuit::Mode::HEAT, hc_num);
}
if (Helpers::translated_word(FL_(nofrost), true) == mode) {
if (!strcmp(mode, FL_(nofrost)[0])) {
return set_mode_n(HeatingCircuit::Mode::NOFROST, hc_num);
}
if (Helpers::translated_word(FL_(eco), true) == mode) {
if (!strcmp(mode, FL_(eco)[0])) {
return set_mode_n(HeatingCircuit::Mode::ECO, hc_num);
}
if (Helpers::translated_word(FL_(holiday), true) == mode) {
if (!strcmp(mode, FL_(holiday)[0])) {
return set_mode_n(HeatingCircuit::Mode::HOLIDAY, hc_num);
}
if (Helpers::translated_word(FL_(comfort), true) == mode) {
if (!strcmp(mode, FL_(comfort)[0])) {
return set_mode_n(HeatingCircuit::Mode::COMFORT, hc_num);
}
return false;
return false; // not found
}
// Set the thermostat working mode
@@ -2301,14 +2318,12 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
case HeatingCircuit::Mode::OFF:
set_mode_value = 0;
break;
case HeatingCircuit::Mode::DAY:
case HeatingCircuit::Mode::HEAT:
case HeatingCircuit::Mode::MANUAL:
case HeatingCircuit::Mode::NOFROST:
set_mode_value = 1;
break;
default: // AUTO & ECO
set_mode_value = 2;
break;
@@ -2653,7 +2668,7 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
day = 7;
on = 7;
time = 0x90;
// LOG_INFO(F("switchtime %02d cleared"), no);
// LOG_INFO("switchtime %02d cleared", no);
}
} else {
if (strlen(value) > 1) {
@@ -2670,14 +2685,9 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
if (strlen(value) > 4) {
for (uint8_t i = 0; i < 7; i++) {
// we use EN settings for the day abbreviation
if (!strncmp(&value[3], read_flash_string(FL_(enum_dayOfWeek)[i][0]).c_str(), 2)) {
if (!strncmp(&value[3], (FL_(enum_dayOfWeek)[i][0]), 2)) {
day = i;
}
// auto translated_dow = Helpers::translated_word(FL_(enum_dayOfWeek)[i]);
// if (!strncmp(&value[3], translated_dow.c_str(), translated_dow.length())) {
// day = i;
// }
}
}
if (strlen(value) > 10) {
@@ -2694,7 +2704,7 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
day = 7;
on = 7;
time = 0x90;
// LOG_INFO(F("switchtime %02d cleared"), no);
// LOG_INFO("switchtime %02d cleared", no);
}
}
uint8_t data[2] = {0xE7, 0x90}; // unset switchtime
@@ -2710,21 +2720,19 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
max_on = 1;
}
if (no > 41 || time > 0x90 || ((on < min_on || on > max_on) && on != 7)) {
// LOG_WARNING(F("Setting switchtime: Invalid data: %s"), value);
// LOG_WARNING(F("Setting switchtime: Invalid data: %02d.%1d.0x%02X.%1d"), no, day, time, on);
// LOG_WARNING("Setting switchtime: Invalid data: %s", value);
// LOG_WARNING("Setting switchtime: Invalid data: %02d.%1d.0x%02X.%1d", no, day, time, on);
return false;
}
if (data[0] != 0xE7) {
// we use EN settings for the day abbreviation
std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day][0]);
// std::string sday = Helpers::translated_word(FL_(enum_dayOfWeek)[day]);
std::string sday = (FL_(enum_dayOfWeek)[day][0]);
if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) {
snprintf(out, len, "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), on ? "on" : "off");
} else if ((model() == EMS_DEVICE_FLAG_RC20) || (model() == EMS_DEVICE_FLAG_RC30)) {
snprintf(out, len, "%02d %s %02d:%02d T%d", no, sday.c_str(), time / 6, 10 * (time % 6), on);
} else {
std::string son = read_flash_string(FL_(enum_switchmode)[on][0]);
// std::string son = Helpers::translated_word(FL_(enum_switchmode)[on]);
std::string son = (FL_(enum_switchmode)[on][0]);
snprintf(out, len, "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), son.c_str());
}
} else {
@@ -3382,6 +3390,13 @@ void Thermostat::register_device_values() {
FL_(dateTime),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_datetime));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&ibaCalIntTemperature_,
DeviceValueType::INT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(ibaCalIntTemperature),
DeviceValueUOM::DEGREES_R,
MAKE_CF_CB(set_calinttemp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&floordrystatus_,
DeviceValueType::ENUM,
@@ -3506,12 +3521,6 @@ void Thermostat::register_device_values() {
FL_(ibaLanguage),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_language));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&ibaMainDisplay_,
DeviceValueType::ENUM,
FL_(enum_ibaMainDisplay),
FL_(ibaMainDisplay),
DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &backlight_, DeviceValueType::BOOL, FL_(backlight), DeviceValueUOM::NONE, MAKE_CF_CB(set_backlight));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &brightness_, DeviceValueType::INT, FL_(brightness), DeviceValueUOM::NONE, MAKE_CF_CB(set_brightness), -15, 15);
@@ -3599,13 +3608,15 @@ void Thermostat::register_device_values() {
DeviceValueType::ENUM,
FL_(enum_ibaMainDisplay),
FL_(ibaMainDisplay),
DeviceValueUOM::NONE);
DeviceValueUOM::NONE,
MAKE_CF_CB(set_display));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), FL_(ibaLanguage), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&ibaClockOffset_,
DeviceValueType::INT,
FL_(ibaClockOffset),
DeviceValueUOM::SECONDS); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
DeviceValueUOM::SECONDS,
MAKE_CF_CB(set_clockoffset)); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&ibaCalIntTemperature_,
DeviceValueType::INT,
@@ -3840,8 +3851,9 @@ void Thermostat::register_device_values() {
MAKE_CF_CB(set_wwVacation));
break;
case EMS_DEVICE_FLAG_JUNKERS:
// FR100 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/536
// FW500 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/666
if (has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
// FR100 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/536
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dateTime_, DeviceValueType::STRING, FL_(tpl_datetime), FL_(dateTime), DeviceValueUOM::NONE);
} else {
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
@@ -3950,7 +3962,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 0, 30);
}
register_device_value(tag, &hc->roomTemp, DeviceValueType::SHORT, roomtemp_divider, FL_(roomTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(climate), DeviceValueUOM::NONE);
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(climate), DeviceValueUOM::NONE, nullptr, 5, 30);
switch (model) {
case EMS_DEVICE_FLAG_RC10:
@@ -4135,9 +4147,16 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(
tag, &hc->daytemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
register_device_value(
tag, &hc->nighttemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
tag, &hc->daytemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp), 5, 30);
register_device_value(tag,
&hc->nighttemp,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_DIV2,
FL_(nighttemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_nighttemp),
5,
30);
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp), 30, 90);
register_device_value(tag,
&hc->offsettemp,
@@ -4154,7 +4173,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
DeviceValueNumOp::DV_NUMOP_DIV2,
FL_(holidaytemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_holidaytemp));
MAKE_CF_CB(set_holidaytemp),
5,
30);
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp), 9, 25);
register_device_value(tag, &hc->summermode, DeviceValueType::ENUM, FL_(enum_summer), FL_(summermode), DeviceValueUOM::NONE);
@@ -4183,7 +4204,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
DeviceValueNumOp::DV_NUMOP_DIV2,
FL_(tempautotemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_tempautotemp));
MAKE_CF_CB(set_tempautotemp),
0,
30);
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp), -30, 10);
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp), -20, 10);
register_device_value(tag, &hc->vacreducetemp, DeviceValueType::INT, FL_(vacreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_vacreducetemp), -20, 10);

View File

@@ -25,7 +25,7 @@ namespace emsesp {
class Thermostat : public EMSdevice {
public:
Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
class HeatingCircuit {
public:
HeatingCircuit(const uint8_t hc_num, const uint8_t model)

View File

@@ -44,69 +44,69 @@ bool EMSdevice::has_entities() const {
}
std::string EMSdevice::tag_to_string(uint8_t tag) {
return read_flash_string(DeviceValue::DeviceValueTAG_s[tag]);
return (DeviceValue::DeviceValueTAG_s[tag]);
}
std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
return read_flash_string(DeviceValue::DeviceValueTAG_mqtt[tag]);
return (DeviceValue::DeviceValueTAG_mqtt[tag]);
}
// convert UOM to a string - these don't need translating
std::string EMSdevice::uom_to_string(uint8_t uom) {
if (EMSESP::system_.fahrenheit() && (uom == DeviceValueUOM::DEGREES || uom == DeviceValueUOM::DEGREES_R)) {
return read_flash_string(DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]);
return (DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]);
}
return read_flash_string(DeviceValue::DeviceValueUOM_s[uom]);
return (DeviceValue::DeviceValueUOM_s[uom]);
}
std::string EMSdevice::brand_to_string() const {
switch (brand_) {
case EMSdevice::Brand::BOSCH:
return read_flash_string(F("Bosch"));
return ("Bosch");
case EMSdevice::Brand::JUNKERS:
return read_flash_string(F("Junkers"));
return ("Junkers");
case EMSdevice::Brand::BUDERUS:
return read_flash_string(F("Buderus"));
return ("Buderus");
case EMSdevice::Brand::NEFIT:
return read_flash_string(F("Nefit"));
return ("Nefit");
case EMSdevice::Brand::SIEGER:
return read_flash_string(F("Sieger"));
return ("Sieger");
case EMSdevice::Brand::WORCESTER:
return read_flash_string(F("Worcester"));
return ("Worcester");
case EMSdevice::Brand::IVT:
return read_flash_string(F("IVT"));
return ("IVT");
default:
return read_flash_string(F(""));
return ("");
}
}
// returns the name of the MQTT topic to use for a specific device, without the base
std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) {
const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
switch (device_type) {
case DeviceType::SYSTEM:
return read_flash_string(F_(system));
return (F_(system));
case DeviceType::BOILER:
return read_flash_string(F_(boiler));
return (F_(boiler));
case DeviceType::THERMOSTAT:
return read_flash_string(F_(thermostat));
return (F_(thermostat));
case DeviceType::HEATPUMP:
return read_flash_string(F_(heatpump));
return (F_(heatpump));
case DeviceType::SOLAR:
return read_flash_string(F_(solar));
return (F_(solar));
case DeviceType::CONNECT:
return read_flash_string(F_(connect));
return (F_(connect));
case DeviceType::MIXER:
return read_flash_string(F_(mixer));
return (F_(mixer));
case DeviceType::DALLASSENSOR:
return read_flash_string(F_(dallassensor));
return (F_(dallassensor));
case DeviceType::ANALOGSENSOR:
return read_flash_string(F_(analogsensor));
return (F_(analogsensor));
case DeviceType::CONTROLLER:
return read_flash_string(F_(controller));
return (F_(controller));
case DeviceType::SWITCH:
return read_flash_string(F_(switch));
return (F_(switch));
case DeviceType::GATEWAY:
return read_flash_string(F_(gateway));
return (F_(gateway));
default:
return Helpers::translated_word(FL_(unknown));
}
@@ -193,11 +193,11 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
std::string EMSdevice::to_string() const {
// for devices that haven't been lookup yet, don't show all details
if (product_id_ == 0) {
return name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
return std::string(name_) + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
}
if (brand_ == Brand::NO_BRAND) {
return name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
return std::string(name_) + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
}
return brand_to_string() + " " + name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
@@ -215,7 +215,7 @@ std::string EMSdevice::to_string_short() const {
// for each telegram that has the fetch value set (true) do a read request
void EMSdevice::fetch_values() {
EMSESP::logger().debug(F("Fetching values for deviceID 0x%02X"), device_id());
EMSESP::logger().debug("Fetching values for deviceID 0x%02X", device_id());
for (const auto & tf : telegram_functions_) {
if (tf.fetch_) {
@@ -226,7 +226,7 @@ void EMSdevice::fetch_values() {
// toggle on/off automatic fetch for a telegramID
void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
EMSESP::logger().debug(F("Toggling fetch for deviceID 0x%02X, telegramID 0x%02X to %d"), device_id(), telegram_id, toggle);
EMSESP::logger().debug("Toggling fetch for deviceID 0x%02X, telegramID 0x%02X to %d", device_id(), telegram_id, toggle);
for (auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == telegram_id) {
@@ -259,14 +259,14 @@ 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_) {
std::string fullname = dv.get_fullname();
auto 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()) {
snprintf(key, sizeof(key), "%s.%s", EMSdevice::tag_to_mqtt(dv.tag).c_str(), read_flash_string(dv.short_name).c_str());
snprintf(key, sizeof(key), "%s.%s", EMSdevice::tag_to_mqtt(dv.tag).c_str(), (dv.short_name));
} else {
snprintf(key, sizeof(key), "%s", read_flash_string(dv.short_name).c_str());
snprintf(key, sizeof(key), "%s", (dv.short_name));
}
JsonArray details = output.createNestedArray(key);
@@ -289,7 +289,7 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
}
/*
// colored list of type-ids
shell.printf(F(" This %s will listen to telegram type IDs: "), device_type_name().c_str());
shell.printf(" This %s will listen to telegram type IDs: ", device_type_name().c_str());
for (const auto & tf : telegram_functions_) {
if (tf.received_ && !tf.fetch_) {
shell.printf(COLOR_BRIGHT_GREEN);
@@ -298,34 +298,34 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
} else {
shell.printf(COLOR_BRIGHT_RED);
}
shell.printf(F("0x%02X "), tf.telegram_type_id_);
shell.printf("0x%02X ", tf.telegram_type_id_);
}
shell.printf(COLOR_RESET);
*/
shell.printf(F(" Received telegram type IDs: "));
shell.printf(" Received telegram type IDs: ");
for (const auto & tf : telegram_functions_) {
if (tf.received_ && !tf.fetch_) {
shell.printf(F("0x%02X "), tf.telegram_type_id_);
shell.printf("0x%02X ", tf.telegram_type_id_);
}
}
shell.println();
shell.printf(F(" Fetched telegram type IDs: "));
shell.printf(" Fetched telegram type IDs: ");
for (const auto & tf : telegram_functions_) {
if (tf.fetch_) {
shell.printf(F("0x%02X "), tf.telegram_type_id_);
shell.printf("0x%02X ", tf.telegram_type_id_);
}
}
shell.println();
shell.printf(F(" Pending telegram type IDs: "));
shell.printf(" Pending telegram type IDs: ");
for (const auto & tf : telegram_functions_) {
if (!tf.received_ && !tf.fetch_) {
shell.printf(F("0x%02X "), tf.telegram_type_id_);
shell.printf("0x%02X ", tf.telegram_type_id_);
}
}
shell.println();
shell.printf(F(" Ignored telegram type IDs: "));
shell.printf(" Ignored telegram type IDs: ");
for (auto handlers : handlers_ignored_) {
shell.printf(F("0x%02X "), handlers);
shell.printf("0x%02X ", handlers);
}
shell.println();
}
@@ -379,7 +379,7 @@ void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) const {
}
// register a callback function for a specific telegram type
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const process_function_p f) {
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, const process_function_p f) {
telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, false, f);
}
@@ -390,28 +390,34 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __
// type: one of DeviceValueType
// options: options for enum, which are translated as a list of lists
// options_single: list of names
// numeric_operatpr: to divide or multiply, see DeviceValueNumOps::
// short_name: used in Mqtt as keys
// numeric_operator: to divide or multiply, see DeviceValueNumOps::
// short_name: used in MQTT as keys
// fullname: used in Web and Console unless empty (nullptr) - can be translated
// uom: unit of measure from DeviceValueUOM
// has_cmd: true if this is an associated command
// min: min allowed value
// max: max allowed value
void EMSdevice::add_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * options_single,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
void EMSdevice::add_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * options_single,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
bool has_cmd = (f != nullptr);
auto short_name = name[0];
const class __FlashStringHelper * const * fullname = &name[1]; // translations start at index 1
auto short_name = name[0];
const char * const * fullname;
if (Helpers::count_items(name) == 1) {
fullname = nullptr; // no translations available, use empty to prevent crash
} else {
fullname = &name[1]; // translations start at index 1
}
// initialize the device value depending on it's type
if (type == DeviceValueType::STRING) {
@@ -440,7 +446,7 @@ void EMSdevice::add_device_value(uint8_t tag,
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);
std::string entity = tag < DeviceValueTAG::TAG_HC1 ? (short_name) : tag_to_string(tag) + "/" + (short_name);
for (std::string entity_id : entityCustomization.entity_ids) {
// if there is an appended custom name, strip it to get the true entity name
// and extract the new custom name
@@ -472,7 +478,6 @@ void EMSdevice::add_device_value(uint8_t tag,
// 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);
devicevalues_.back().set_custom_minmax();
// add a new command if it has a function attached
if (!has_cmd) {
@@ -490,108 +495,102 @@ 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 (not the custom fullname)
Command::add(device_type_, short_name, f, Helpers::translated_fword(fullname), flags);
Command::add(device_type_, short_name, f, fullname, flags);
}
// single list of options
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * options_single,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
const char * const * name,
uint8_t uom,
const cmd_function_p f) {
// create a multi-list from the options
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, 0, 0);
};
// single list of options, with no translations, with min and max
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * options_single,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
// create a multi-list from the options
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
};
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f) {
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, 0, 0);
}
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, min, max);
}
// no options, no function
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * name, uint8_t uom, const cmd_function_p f) {
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f) {
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, 0, 0);
};
// no options, with min/max
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, min, max);
};
// function with min and max values
// adds a new command to the command list
// in this function we separate out the short and long names and take any translations
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, min, max);
}
// function with no min and max values (set to 0)
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * name,
uint8_t uom,
const cmd_function_p f) {
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, 0, 0);
}
// no associated command function, or min/max values
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom) {
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom) {
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, nullptr, 0, 0);
}
@@ -610,7 +609,7 @@ bool EMSdevice::is_readonly(const std::string & cmd, const int8_t id) const {
uint8_t tag = id > 0 ? DeviceValueTAG::TAG_HC1 + id - 1 : DeviceValueTAG::TAG_NONE;
for (const auto & dv : devicevalues_) {
// check command name and tag, id -1 is default hc and only checks name
if (dv.has_cmd && read_flash_string(dv.short_name) == cmd && (dv.tag < DeviceValueTAG::TAG_HC1 || dv.tag == tag || id == -1)) {
if (dv.has_cmd && std::string(dv.short_name) == cmd && (dv.tag < DeviceValueTAG::TAG_HC1 || dv.tag == tag || id == -1)) {
return dv.has_state(DeviceValueState::DV_READONLY);
}
}
@@ -638,24 +637,14 @@ void EMSdevice::publish_value(void * value_p) const {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (Mqtt::publish_single2cmd()) {
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
snprintf(topic,
sizeof(topic),
"%s/%s/%s",
device_type_2_device_name(device_type_).c_str(),
tag_to_mqtt(dv.tag).c_str(),
read_flash_string(dv.short_name).c_str());
snprintf(topic, sizeof(topic), "%s/%s/%s", device_type_2_device_name(device_type_), tag_to_mqtt(dv.tag).c_str(), (dv.short_name));
} else {
snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_).c_str(), read_flash_string(dv.short_name).c_str());
snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_), (dv.short_name));
}
} else if (Mqtt::is_nested() && dv.tag >= DeviceValueTAG::TAG_HC1) {
snprintf(topic,
sizeof(topic),
"%s/%s/%s",
Mqtt::tag_to_topic(device_type_, dv.tag).c_str(),
tag_to_mqtt(dv.tag).c_str(),
read_flash_string(dv.short_name).c_str());
snprintf(topic, sizeof(topic), "%s/%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), tag_to_mqtt(dv.tag).c_str(), (dv.short_name));
} else {
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), read_flash_string(dv.short_name).c_str());
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), (dv.short_name));
}
int8_t num_op = dv.numeric_operator;
@@ -673,7 +662,7 @@ void EMSdevice::publish_value(void * value_p) const {
Helpers::render_value(payload, *(uint8_t *)(value_p), 0);
} else {
auto enum_str = Helpers::translated_word(dv.options[*(uint8_t *)(value_p)]);
strlcpy(payload, enum_str.c_str(), sizeof(payload));
strlcpy(payload, enum_str, sizeof(payload));
}
}
break;
@@ -717,7 +706,6 @@ 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];
@@ -725,11 +713,12 @@ std::string EMSdevice::get_value_uom(const char * key) const {
char * key_p = new_key;
for (uint8_t i = 0; i < DeviceValue::tag_count; i++) {
auto tag = read_flash_string(DeviceValue::DeviceValueTAG_s[i]);
if (!tag.empty()) {
std::string key2 = key; // copy char to a std::string
if ((key2.find(tag) != std::string::npos) && (key[tag.length()] == ' ')) {
key_p += tag.length() + 1; // remove the tag
auto tag = (DeviceValue::DeviceValueTAG_s[i]);
if (tag) {
std::string key2 = key; // copy string to a std::string so we can use the find function
uint8_t length = strlen(tag);
if ((key2.find(tag) != std::string::npos) && (key[length] == ' ')) {
key_p += length + 1; // remove the tag
break;
}
}
@@ -800,9 +789,9 @@ void EMSdevice::generate_values_web(JsonObject & output) {
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
obj["v"] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
obj["v"] = dv.numeric_operator ? (*(uint32_t *)(dv.value_p) / dv.numeric_operator) : *(uint32_t *)(dv.value_p);
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else {
obj["v"] = ""; // must have a value for sorting to work
}
@@ -824,7 +813,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
// add the name of the Command function
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
obj["c"] = tag_to_mqtt(dv.tag) + "/" + read_flash_string(dv.short_name);
obj["c"] = tag_to_mqtt(dv.tag) + "/" + (dv.short_name);
} else {
obj["c"] = dv.short_name;
}
@@ -834,7 +823,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
JsonArray l = obj.createNestedArray("l");
for (uint8_t i = 0; i < dv.options_size; i++) {
auto enum_str = Helpers::translated_word(dv.options[i]);
if (!enum_str.empty()) {
if (enum_str) {
l.add(enum_str);
}
}
@@ -860,7 +849,8 @@ void EMSdevice::generate_values_web(JsonObject & output) {
obj["s"] = Helpers::render_value(s, (float)(-1) * dv.numeric_operator, 0);
}
int16_t dv_set_min, dv_set_max;
int16_t dv_set_min;
uint16_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
obj["m"] = Helpers::render_value(s, dv_set_min, 0);
obj["x"] = Helpers::render_value(s, dv_set_max, 0);
@@ -900,61 +890,39 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
// handle Integers and Floats
else {
int8_t num_op = dv.numeric_operator;
bool make_float;
if (num_op == 0) {
// no changes to number
make_float = false;
num_op = 1; // so it gets *1
} else if (num_op < 0) {
// negative numbers, convert to a positive multiplier
make_float = false;
num_op *= -1;
} else {
// has a divider, make it a float
make_float = true;
}
// always convert temperatures to floats with 1 decimal place
if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
make_float = true;
}
if (dv.type == DeviceValueType::INT) {
obj["v"] = make_float ? Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit) : *(int8_t *)(dv.value_p) * num_op;
obj["v"] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::UINT) {
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit) : *(uint8_t *)(dv.value_p) * num_op;
obj["v"] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::SHORT) {
obj["v"] = make_float ? Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit) : *(int16_t *)(dv.value_p) * num_op;
obj["v"] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::USHORT) {
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit) : *(uint16_t *)(dv.value_p) * num_op;
obj["v"] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::ULONG) {
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op) : *(uint32_t *)(dv.value_p) * num_op;
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else if (dv.type == DeviceValueType::TIME) {
// sometimes we need to divide by 60
obj["v"] = (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) ? (uint32_t)Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op)
: *(uint32_t *)(dv.value_p);
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
}
}
}
// 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);
obj["id"] = tag_to_string(dv.tag) + "/" + (dv.short_name);
} else {
obj["id"] = read_flash_string(dv.short_name);
obj["id"] = (dv.short_name);
}
// n is the fullname, and can be optional
// don't add the fullname if its a command
auto fullname = Helpers::translated_word(dv.fullname);
if (dv.type != DeviceValueType::CMD) {
if (!fullname.empty()) {
if (fullname) {
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
obj["n"] = fullname;
} else {
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), fullname.c_str());
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), fullname);
obj["n"] = name;
}
}
@@ -965,8 +933,12 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
obj["cn"] = custom_fullname;
}
} else {
// it's a command
obj["n"] = "!" + fullname; // prefix ! to fullname for commands
obj["n"] = "!" + std::string(fullname); // prefix commands with a !
}
// 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
@@ -986,12 +958,25 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
}
}
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
for (auto & dv : devicevalues_) {
if (dv.tag == tag && dv.short_name == FL_(climate[0])) {
if (dv.min != min || dv.max != max) {
dv.min = min;
dv.max = max;
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
Mqtt::publish_ha_climate_config(dv.tag, false, true); // delete topic (remove = true)
}
return;
}
}
}
// set mask per device entity based on the id which is prefixed with the 2 char hex mask value
// returns true if the entity has a mask set (not 0 the default)
void EMSdevice::setCustomEntity(const std::string & entity_id) {
for (auto & dv : devicevalues_) {
std::string entity_name =
dv.tag < DeviceValueTAG::TAG_HC1 ? read_flash_string(dv.short_name) : tag_to_string(dv.tag) + "/" + read_flash_string(dv.short_name);
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? (dv.short_name) : tag_to_string(dv.tag) + "/" + (dv.short_name);
// extra shortname
std::string shortname;
@@ -1009,7 +994,7 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
uint8_t new_mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); // first character contains mask flags
// if it's a new mask, reconfigure HA
if (Mqtt::ha_enabled() && ((current_mask ^ new_mask) & (DeviceValueState::DV_READONLY >> 4))) {
if (Mqtt::ha_enabled() && (has_custom_name || ((current_mask ^ new_mask) & (DeviceValueState::DV_READONLY >> 4)))) {
// remove ha config on change of dv_readonly flag
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
@@ -1024,7 +1009,17 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
} else {
dv.custom_fullname = "";
}
auto min = dv.min;
auto max = dv.max;
// set the min / max
dv.set_custom_minmax();
if (Mqtt::ha_enabled() && dv.short_name == FL_(seltemp)[0] && (min != dv.min || max != dv.max)) {
set_climate_minmax(dv.tag, dv.min, dv.max);
}
return;
}
}
@@ -1033,9 +1028,8 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
// populate a string vector with entities that have masks set or have a custom name
void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
for (const auto & dv : devicevalues_) {
std::string entity_name =
dv.tag < DeviceValueTAG::TAG_HC1 ? read_flash_string(dv.short_name) : tag_to_string(dv.tag) + "/" + read_flash_string(dv.short_name);
uint8_t mask = dv.state >> 4;
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? (dv.short_name) : tag_to_string(dv.tag) + "/" + (dv.short_name);
uint8_t mask = dv.state >> 4;
if (mask || !dv.custom_fullname.empty()) {
if (dv.custom_fullname.empty()) {
@@ -1073,9 +1067,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
// search device value with this tag
for (auto & dv : devicevalues_) {
if (strcmp(command_s, Helpers::toLower(read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
int8_t num_op = dv.numeric_operator;
if (!strcmp(command_s, dv.short_name) && (tag <= 0 || tag == dv.tag)) {
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
const char * type = "type";
@@ -1096,6 +1088,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
json["circuit"] = tag_to_mqtt(dv.tag);
}
char val[10];
switch (dv.type) {
case DeviceValueType::ENUM: {
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
@@ -1115,35 +1108,35 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
case DeviceValueType::USHORT:
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
json[value] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::UINT:
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
json[value] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::SHORT:
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
json[value] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::INT:
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
json[value] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::ULONG:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
}
json[type] = F_(number);
break;
@@ -1160,12 +1153,12 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
json[value] = Helpers::render_boolean(s, value_b);
}
}
json[type] = F("boolean");
json[type] = ("boolean");
break;
case DeviceValueType::TIME:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
}
json[type] = F_(number);
break;
@@ -1174,7 +1167,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
if (Helpers::hasValue((char *)(dv.value_p))) {
json[value] = (char *)(dv.value_p);
}
json[type] = F("string");
json[type] = ("string");
break;
case DeviceValueType::CMD:
@@ -1192,11 +1185,14 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
break;
}
// set the min and max
int16_t dv_set_min, dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
json["min"] = dv_set_min;
json["max"] = dv_set_max;
// set the min and max only for commands
if (dv.has_cmd) {
int16_t dv_set_min;
uint16_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
json["min"] = dv_set_min;
json["max"] = dv_set_max;
}
}
// add uom if it's not a " " (single space)
@@ -1216,7 +1212,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
// if we're filtering on an attribute, go find it
if (attribute_s) {
#if defined(EMSESP_DEBUG)
EMSESP::logger().debug(F("[DEBUG] Attribute '%s'"), attribute_s);
EMSESP::logger().debug("[DEBUG] Attribute '%s'", attribute_s);
#endif
if (json.containsKey(attribute_s)) {
JsonVariant data = json[attribute_s];
@@ -1291,7 +1287,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
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
strlcpy(name, (dv.short_name), sizeof(name)); // use short name
// if we have a tag, and its different to the last one create a nested object. only for hc, wwc and hs
if (dv.tag != old_tag) {
@@ -1316,6 +1312,9 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
json[name] = value_b;
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
json[name] = value_b ? 1 : 0;
} else {
char s[7];
json[name] = Helpers::render_boolean(s, value_b);
}
}
@@ -1341,64 +1340,21 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
: 0;
int8_t num_op = dv.numeric_operator;
bool make_float;
if (num_op == 0) {
// no changes to number
make_float = false;
num_op = 1; // so it gets *1
} else if (num_op < 0) {
// negative numbers, convert to a positive multiplier
make_float = false;
num_op *= -1;
} else {
// has a divider, make it a float
make_float = true;
}
// always convert temperatures to floats with 1 decimal place
if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
make_float = true;
}
char val[10];
if (dv.type == DeviceValueType::INT) {
if (make_float) {
json[name] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
} else {
json[name] = *(int8_t *)(dv.value_p) * num_op;
}
json[name] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::UINT) {
if (make_float) {
json[name] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
} else {
json[name] = *(uint8_t *)(dv.value_p) * num_op;
}
json[name] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::SHORT) {
if (make_float) {
json[name] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
} else {
json[name] = *(int16_t *)(dv.value_p) * num_op;
}
json[name] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::USHORT) {
if (make_float) {
json[name] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
} else {
json[name] = *(uint16_t *)(dv.value_p) * num_op;
}
json[name] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::ULONG) {
if (make_float) {
json[name] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op, fahrenheit);
} else {
json[name] = *(uint32_t *)(dv.value_p) * num_op;
}
json[name] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
uint32_t time_value;
if (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) {
// sometimes we need to divide by 60
time_value = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
} else {
time_value = *(uint32_t *)(dv.value_p);
uint32_t time_value = *(uint32_t *)(dv.value_p);
if (dv.numeric_operator == DeviceValueNumOp::DV_NUMOP_DIV60) {
time_value /= 60; // sometimes we need to divide by 60
}
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
char time_s[60];
@@ -1406,11 +1362,11 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
sizeof(time_s),
"%d %s %d %s %d %s",
(time_value / 1440),
Helpers::translated_word(FL_(days)).c_str(),
Helpers::translated_word(FL_(days)),
((time_value % 1440) / 60),
Helpers::translated_word(FL_(hours)).c_str(),
Helpers::translated_word(FL_(hours)),
(time_value % 60),
Helpers::translated_word(FL_(minutes)).c_str());
Helpers::translated_word(FL_(minutes)));
json[name] = time_s;
} else {
json[name] = time_value;
@@ -1474,12 +1430,12 @@ void EMSdevice::mqtt_ha_entity_config_create() {
if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
Mqtt::publish_ha_climate_config(dv.tag, true);
Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max);
} else if (*(int8_t *)(dv.value_p) == 0
&& (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || !dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
Mqtt::publish_ha_climate_config(dv.tag, false);
Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max);
}
}
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && (dv.type != DeviceValueType::CMD) && dv.has_state(DeviceValueState::DV_ACTIVE)
@@ -1517,14 +1473,14 @@ bool EMSdevice::has_telegram_id(uint16_t id) const {
std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) const {
// see if it's one of the common ones, like Version
if (telegram->type_id == EMS_TYPE_VERSION) {
return read_flash_string(F("Version"));
return ("Version");
} else if (telegram->type_id == EMS_TYPE_UBADevices) {
return read_flash_string(F("UBADevices"));
return ("UBADevices");
}
for (const auto & tf : telegram_functions_) {
if ((tf.telegram_type_id_ == telegram->type_id) && (telegram->type_id != 0xFF)) {
return read_flash_string(tf.telegram_type_name_);
return (tf.telegram_type_name_);
}
}
@@ -1539,7 +1495,7 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
// if the data block is empty and we have not received data before, assume that this telegram
// is not recognized by the bus master. So remove it from the automatic fetch list
if (telegram->message_length == 0 && telegram->offset == 0 && !tf.received_) {
EMSESP::logger().debug(F("This telegram (%s) is not recognized by the EMS bus"), read_flash_string(tf.telegram_type_name_).c_str());
EMSESP::logger().debug(("This telegram (%s) is not recognized by the EMS bus"), (tf.telegram_type_name_));
tf.fetch_ = false;
return false;
}

View File

@@ -34,7 +34,7 @@ class EMSdevice {
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
// device_type defines which derived class to use, e.g. BOILER, THERMOSTAT etc..
EMSdevice(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
EMSdevice(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: device_type_(device_type)
, device_id_(device_id)
, product_id_(product_id)
@@ -46,11 +46,11 @@ class EMSdevice {
std::string device_type_name() const;
static std::string device_type_2_device_name(const uint8_t device_type);
static uint8_t device_name_2_device_type(const char * topic);
static std::string uom_to_string(uint8_t uom);
static std::string tag_to_string(uint8_t tag);
static std::string tag_to_mqtt(uint8_t tag);
static const char * device_type_2_device_name(const uint8_t device_type);
static uint8_t device_name_2_device_type(const char * topic);
static std::string uom_to_string(uint8_t uom);
static std::string tag_to_string(uint8_t tag);
static std::string tag_to_mqtt(uint8_t tag);
bool has_tag(const uint8_t tag) const;
@@ -104,11 +104,11 @@ class EMSdevice {
return brand_;
}
inline void name(const std::string & name) {
inline void name(const char * name) {
name_ = name;
}
inline std::string name() const {
inline const char * name() const {
return name_;
}
@@ -187,12 +187,13 @@ class EMSdevice {
void list_device_entries(JsonObject & output) const;
void add_handlers_ignored(const uint16_t handler);
void set_climate_minmax(uint8_t tag, int16_t min, uint16_t max);
void setCustomEntity(const std::string & entity_id);
void getCustomEntities(std::vector<std::string> & entity_ids);
using process_function_p = std::function<void(std::shared_ptr<const Telegram>)>;
void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const process_function_p cb);
void register_telegram_type(const uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, const process_function_p cb);
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
std::string get_value_uom(const char * key) const;
@@ -204,93 +205,77 @@ class EMSdevice {
void generate_values_web(JsonObject & output);
void generate_values_web_customization(JsonArray & output);
void add_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * options_single,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void add_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * options_single,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f);
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * name,
uint8_t uom);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f = nullptr);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f = nullptr);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
// single list of options
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * options_single,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f = nullptr);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
const char * const * name,
uint8_t uom,
const cmd_function_p f = nullptr);
// single list of options, with no translations, with min and max
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * options_single,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
const char * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
// no options, optional function f
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * name, uint8_t uom, const cmd_function_p f = nullptr);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
// no options, with min/max
void register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * name,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint16_t max);
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;
@@ -424,27 +409,27 @@ class EMSdevice {
bool has_entities() const;
private:
uint8_t unique_id_;
uint8_t device_type_ = DeviceType::SYSTEM;
uint8_t device_id_ = 0;
uint8_t product_id_ = 0;
char version_[6];
std::string name_; // the long name for the EMS model
uint8_t flags_ = 0;
uint8_t brand_ = Brand::NO_BRAND;
uint8_t unique_id_;
uint8_t device_type_ = DeviceType::SYSTEM;
uint8_t device_id_ = 0;
uint8_t product_id_ = 0;
char version_[6];
const char * name_; // the long name for the EMS model
uint8_t flags_ = 0;
uint8_t brand_ = Brand::NO_BRAND;
bool ha_config_done_ = false;
bool has_update_ = false;
bool ha_config_firstrun_ = true; // this means a first setup of HA is needed after a restart
struct TelegramFunction {
uint16_t telegram_type_id_; // it's type_id
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
bool fetch_; // if this type_id be queried automatically
bool received_;
process_function_p process_function_;
uint16_t telegram_type_id_; // it's type_id
const char * telegram_type_name_; // e.g. RC20Message
bool fetch_; // if this type_id be queried automatically
bool received_;
process_function_p process_function_;
TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, bool received, const process_function_p process_function)
TelegramFunction(uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, bool received, const process_function_p process_function)
: telegram_type_id_(telegram_type_id)
, telegram_type_name_(telegram_type_name)
, fetch_(fetch)

View File

@@ -23,21 +23,21 @@
namespace emsesp {
// constructor
DeviceValue::DeviceValue(uint8_t device_type,
uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * options_single,
int8_t numeric_operator,
const __FlashStringHelper * const short_name,
const __FlashStringHelper * const * fullname,
std::string & custom_fullname,
uint8_t uom,
bool has_cmd,
int16_t min,
uint16_t max,
uint8_t state)
DeviceValue::DeviceValue(uint8_t device_type,
uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * options_single,
int8_t numeric_operator,
const char * const short_name,
const char * const * fullname,
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)
@@ -60,17 +60,21 @@ DeviceValue::DeviceValue(uint8_t device_type,
options_size = Helpers::count_items(options);
}
// set the min/max
set_custom_minmax();
#ifdef EMSESP_STANDALONE
// only added for debugging
Serial.print("registering entity: ");
Serial.print(read_flash_string(short_name).c_str());
Serial.print(COLOR_BRIGHT_RED_BACKGROUND);
Serial.print(" registering entity: ");
Serial.print((short_name));
Serial.print("/");
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(Helpers::translated_word(fullname));
}
Serial.print(" (#options=");
Serial.print(options_size);
@@ -83,23 +87,23 @@ DeviceValue::DeviceValue(uint8_t device_type,
Serial.print(" option");
Serial.print(i + 1);
Serial.print(":");
auto str = Helpers::translated_fword(options[i]);
Serial.print(read_flash_string(str).c_str());
auto str = Helpers::translated_word(options[i]);
Serial.print(str);
i++;
}
} else if (options_single != nullptr) {
Serial.print("option1:!");
Serial.print(read_flash_string(options_single[0]).c_str());
Serial.print((options_single[0]));
Serial.print("!");
}
Serial.println("");
Serial.println(COLOR_RESET);
#endif
}
// mapping of UOM, to match order in DeviceValueUOM enum emsdevice.h
// also maps to DeviceValueUOM in interface/src/project/types.ts for the Web UI
// must be an int of 4 bytes, 32bit aligned
const __FlashStringHelper * DeviceValue::DeviceValueUOM_s[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = {
const char * DeviceValue::DeviceValueUOM_s[] = {
F_(uom_blank),
F_(uom_degrees),
@@ -125,7 +129,7 @@ const __FlashStringHelper * DeviceValue::DeviceValueUOM_s[] __attribute__((__ali
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevice.h
// must be an int of 4 bytes, 32bit aligned
const __FlashStringHelper * const DeviceValue::DeviceValueTAG_s[] PROGMEM = {
const char * const DeviceValue::DeviceValueTAG_s[] = {
F_(tag_none), // ""
F_(tag_heartbeat), // ""
@@ -171,7 +175,7 @@ const __FlashStringHelper * const DeviceValue::DeviceValueTAG_s[] PROGMEM = {
};
// MQTT topics derived from tags
const __FlashStringHelper * const DeviceValue::DeviceValueTAG_mqtt[] PROGMEM = {
const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
F_(tag_none), // ""
F_(heartbeat), // "heartbeat"
@@ -217,7 +221,7 @@ const __FlashStringHelper * const DeviceValue::DeviceValueTAG_mqtt[] PROGMEM = {
};
// count #tags once at compile time
size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(__FlashStringHelper *);
size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char *);
// checks whether the device value has an actual value
// returns true if its valid
@@ -266,7 +270,7 @@ bool DeviceValue::hasValue() const {
// converts to signed int, which means rounding to an whole integer
// returns false if there is no min/max needed
// Types BOOL, ENUM, STRING and CMD are not used
bool DeviceValue::get_min_max(int16_t & dv_set_min, int16_t & dv_set_max) {
bool DeviceValue::get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max) {
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
// if we have individual limits set already, just do the conversion

View File

@@ -25,7 +25,6 @@
#include "helpers.h" // for conversions
#include "default_settings.h" // for enum types
#include <uuid/common.h> // for read_flash_string
namespace emsesp {
@@ -143,41 +142,41 @@ class DeviceValue {
DV_NUMOP_MUL15 = -15
};
uint8_t device_type; // EMSdevice::DeviceType
uint8_t tag; // DeviceValueTAG::*
void * value_p; // pointer to variable of any type
uint8_t type; // DeviceValueType::*
const __FlashStringHelper * const ** options; // options as a flash char array
const __FlashStringHelper * const * options_single; // options are not translated
int8_t numeric_operator;
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
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
uint16_t max; // max range
uint8_t state; // DeviceValueState::*
uint8_t device_type; // EMSdevice::DeviceType
uint8_t tag; // DeviceValueTAG::*
void * value_p; // pointer to variable of any type
uint8_t type; // DeviceValueType::*
const char * const ** options; // options as a flash char array
const char * const * options_single; // options are not translated
int8_t numeric_operator;
uint8_t options_size; // number of options in the char array, calculated
const char * const short_name; // used in MQTT and API
const char * const * fullname; // used in Web and Console, is translated
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
uint16_t max; // max range
uint8_t state; // DeviceValueState::*
DeviceValue(uint8_t device_type,
uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const ** options,
const __FlashStringHelper * const * options_single,
int8_t numeric_operator,
const __FlashStringHelper * const short_name,
const __FlashStringHelper * const * fullname,
std::string & custom_fullname,
uint8_t uom,
bool has_cmd,
int16_t min,
uint16_t max,
uint8_t state);
DeviceValue(uint8_t device_type,
uint8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
const char * const * options_single,
int8_t numeric_operator,
const char * const short_name,
const char * const * fullname,
std::string & custom_fullname,
uint8_t uom,
bool has_cmd,
int16_t min,
uint16_t max,
uint8_t state);
bool hasValue() const;
bool get_min_max(int16_t & dv_set_min, int16_t & dv_set_max);
bool get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max);
void set_custom_minmax();
bool get_custom_min(int16_t & val);
@@ -199,10 +198,10 @@ class DeviceValue {
return state;
}
static const __FlashStringHelper * DeviceValueUOM_s[];
static const __FlashStringHelper * const DeviceValueTAG_s[];
static const __FlashStringHelper * const DeviceValueTAG_mqtt[];
static size_t tag_count; // # tags
static const char * DeviceValueUOM_s[];
static const char * const DeviceValueTAG_s[];
static const char * const DeviceValueTAG_mqtt[];
static size_t tag_count; // # tags
};
}; // namespace emsesp

View File

@@ -190,7 +190,7 @@ void EMSESP::uart_init() {
if (System::is_valid_gpio(rx_gpio) && System::is_valid_gpio(tx_gpio)) {
EMSuart::start(tx_mode, rx_gpio, tx_gpio); // start UART
} else {
LOG_WARNING(F("Invalid UART Rx/Tx GPIOs. Check config."));
LOG_WARNING("Invalid UART Rx/Tx GPIOs. Check config.");
}
txservice_.start(); // sends out request to EMS bus for all devices
@@ -234,42 +234,42 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
// EMS bus information
switch (bus_status()) {
case BUS_STATUS_OFFLINE:
shell.printfln(F("EMS Bus is disconnected."));
shell.printfln("EMS Bus is disconnected.");
break;
case BUS_STATUS_TX_ERRORS:
shell.printfln(F("EMS Bus is connected, but Tx is not stable."));
shell.printfln("EMS Bus is connected, but Tx is not stable.");
break;
default:
shell.printfln(F("EMS Bus is connected."));
shell.printfln("EMS Bus is connected.");
break;
}
shell.println();
if (bus_status() != BUS_STATUS_OFFLINE) {
shell.printfln(F("EMS Bus info:"));
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F(" Tx mode: %d"), settings.tx_mode); });
shell.printfln(F(" Bus protocol: %s"), EMSbus::is_ht3() ? F("HT3") : F("Buderus"));
shell.printfln(F(" #recognized EMS devices: %d"), EMSESP::emsdevices.size());
shell.printfln(F(" #telegrams received: %d"), rxservice_.telegram_count());
shell.printfln(F(" #read requests sent: %d"), txservice_.telegram_read_count());
shell.printfln(F(" #write requests sent: %d"), txservice_.telegram_write_count());
shell.printfln(F(" #incomplete telegrams: %d"), rxservice_.telegram_error_count());
shell.printfln(F(" #read fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_read_fail_count());
shell.printfln(F(" #write fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_write_fail_count());
shell.printfln(F(" Rx line quality: %d%%"), rxservice_.quality());
shell.printfln(F(" Tx line quality: %d%%"), (txservice_.read_quality() + txservice_.read_quality()) / 2);
shell.printfln("EMS Bus info:");
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(" Tx mode: %d", settings.tx_mode); });
shell.printfln(" Bus protocol: %s", EMSbus::is_ht3() ? ("HT3") : ("Buderus"));
shell.printfln(" #recognized EMS devices: %d", EMSESP::emsdevices.size());
shell.printfln(" #telegrams received: %d", rxservice_.telegram_count());
shell.printfln(" #read requests sent: %d", txservice_.telegram_read_count());
shell.printfln(" #write requests sent: %d", txservice_.telegram_write_count());
shell.printfln(" #incomplete telegrams: %d", rxservice_.telegram_error_count());
shell.printfln((" #read fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_read_fail_count());
shell.printfln((" #write fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_write_fail_count());
shell.printfln(" Rx line quality: %d%%", rxservice_.quality());
shell.printfln(" Tx line quality: %d%%", (txservice_.read_quality() + txservice_.read_quality()) / 2);
shell.println();
}
// Rx queue
auto rx_telegrams = rxservice_.queue();
if (rx_telegrams.empty()) {
shell.printfln(F("Rx Queue is empty"));
shell.printfln("Rx Queue is empty");
} else {
shell.printfln(F("Rx Queue (%ld telegram%s):"), rx_telegrams.size(), rx_telegrams.size() == 1 ? "" : "s");
shell.printfln(("Rx Queue (%ld telegram%s):"), rx_telegrams.size(), rx_telegrams.size() == 1 ? "" : "s");
for (const auto & it : rx_telegrams) {
shell.printfln(F(" [%02d] %s"), it.id_, pretty_telegram(it.telegram_).c_str());
shell.printfln(" [%02d] %s", it.id_, pretty_telegram(it.telegram_).c_str());
}
}
@@ -278,20 +278,20 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
// Tx queue
auto tx_telegrams = txservice_.queue();
if (tx_telegrams.empty()) {
shell.printfln(F("Tx Queue is empty"));
shell.printfln("Tx Queue is empty");
} else {
shell.printfln(F("Tx Queue (%ld telegram%s):"), tx_telegrams.size(), tx_telegrams.size() == 1 ? "" : "s");
shell.printfln(("Tx Queue (%ld telegram%s):"), tx_telegrams.size(), tx_telegrams.size() == 1 ? "" : "s");
std::string op;
for (const auto & it : tx_telegrams) {
if ((it.telegram_->operation) == Telegram::Operation::TX_RAW) {
op = read_flash_string(F("RAW "));
op = "RAW ";
} else if ((it.telegram_->operation) == Telegram::Operation::TX_READ) {
op = read_flash_string(F("READ "));
op = "READ ";
} else if ((it.telegram_->operation) == Telegram::Operation::TX_WRITE) {
op = read_flash_string(F("WRITE"));
op = "WRITE";
}
shell.printfln(F(" [%02d%c] %s %s"), it.id_, ((it.retry_) ? '*' : ' '), op.c_str(), pretty_telegram(it.telegram_).c_str());
shell.printfln(" [%02d%c] %s %s", it.id_, ((it.retry_) ? '*' : ' '), op.c_str(), pretty_telegram(it.telegram_).c_str());
}
}
@@ -301,7 +301,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
// show EMS device values to the shell console
void EMSESP::show_device_values(uuid::console::Shell & shell) {
if (emsdevices.empty()) {
shell.printfln(F("No EMS devices detected."));
shell.printfln("No EMS devices detected.");
shell.println();
return;
}
@@ -311,7 +311,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_class.first)) {
// print header
shell.printfln(F("%s: %s (%d)"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str(), emsdevice->count_entities());
shell.printfln(("%s: %s (%d)"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str(), emsdevice->count_entities());
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN); // use max size
JsonObject json = doc.to<JsonObject>();
@@ -359,14 +359,14 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
// show Dallas temperature sensors and Analog sensors
void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
if (dallassensor_.have_sensors()) {
shell.printfln(F("Temperature sensors:"));
shell.printfln("Temperature sensors:");
char s[10];
char s2[10];
uint8_t fahrenheit = EMSESP::system_.fahrenheit() ? 2 : 0;
for (const auto & sensor : dallassensor_.sensors()) {
if (Helpers::hasValue(sensor.temperature_c)) {
shell.printfln(F(" %s: %s%s °%c%s (offset %s, ID: %s)"),
shell.printfln((" %s: %s%s °%c%s (offset %s, ID: %s)"),
sensor.name().c_str(),
COLOR_BRIGHT_GREEN,
Helpers::render_value(s, sensor.temperature_c, 10, fahrenheit),
@@ -375,10 +375,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
Helpers::render_value(s2, sensor.offset(), 10, fahrenheit),
sensor.id().c_str());
} else {
shell.printfln(F(" %s (offset %s, ID: %s)"),
sensor.name().c_str(),
Helpers::render_value(s, sensor.offset(), 10, fahrenheit),
sensor.id().c_str());
shell.printfln((" %s (offset %s, ID: %s)"), sensor.name().c_str(), Helpers::render_value(s, sensor.offset(), 10, fahrenheit), sensor.id().c_str());
}
}
shell.println();
@@ -387,11 +384,11 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
if (analogsensor_.have_sensors()) {
char s[10];
char s2[10];
shell.printfln(F("Analog sensors:"));
shell.printfln("Analog sensors:");
for (const auto & sensor : analogsensor_.sensors()) {
switch (sensor.type()) {
case AnalogSensor::AnalogType::ADC:
shell.printfln(F(" %s: %s%s %s%s (Type: ADC, Factor: %s, Offset: %d)"),
shell.printfln((" %s: %s%s %s%s (Type: ADC, Factor: %s, Offset: %d)"),
sensor.name().c_str(),
COLOR_BRIGHT_GREEN,
Helpers::render_value(s, sensor.value(), 2),
@@ -403,7 +400,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
default:
// case AnalogSensor::AnalogType::DIGITAL_IN:
// case AnalogSensor::AnalogType::COUNTER:
shell.printfln(F(" %s: %s%d%s (Type: %s)"),
shell.printfln((" %s: %s%d%s (Type: %s)"),
sensor.name().c_str(),
COLOR_BRIGHT_GREEN,
(uint16_t)sensor.value(), // as int
@@ -540,7 +537,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
}
if (need_publish) {
if (doc.overflowed()) {
LOG_WARNING(F("MQTT buffer overflow, please use individual topics"));
LOG_WARNING("MQTT buffer overflow, please use individual topics");
}
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
}
@@ -625,9 +622,9 @@ bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const in
// search for recognized device_ids : Me, All, otherwise print hex value
std::string EMSESP::device_tostring(const uint8_t device_id) {
if ((device_id & 0x7F) == rxservice_.ems_bus_id()) {
return read_flash_string(F("Me"));
return "Me";
} else if (device_id == 0x00) {
return read_flash_string(F("All"));
return "All";
} else {
char buffer[5];
return Helpers::hextoa(buffer, device_id);
@@ -672,14 +669,14 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
// check for global/common types like Version & UBADevices
if (telegram->type_id == EMSdevice::EMS_TYPE_VERSION) {
type_name = read_flash_string(F("Version"));
type_name = "Version";
} else if (telegram->type_id == EMSdevice::EMS_TYPE_UBADevices) {
type_name = read_flash_string(F("UBADevices"));
type_name = "UBADevices";
}
// if we don't know the type show
if (type_name.empty()) {
type_name = read_flash_string(F("?"));
type_name = "?";
}
std::string str;
@@ -728,7 +725,7 @@ void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
// if we haven't already detected this device, request it's version details, unless its us (EMS-ESP)
// when the version info is received, it will automagically add the device
if ((device_id != EMSbus::ems_bus_id()) && !(EMSESP::device_exists(device_id))) {
LOG_DEBUG(F("New EMS device detected with ID 0x%02X. Requesting version information."), device_id);
LOG_DEBUG("New EMS device detected with ID 0x%02X. Requesting version information.", device_id);
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
}
}
@@ -788,7 +785,7 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// if watching or reading...
if ((telegram->type_id == read_id_) && (telegram->dest == txservice_.ems_bus_id())) {
LOG_INFO(F("%s"), pretty_telegram(telegram).c_str());
LOG_INFO("%s", pretty_telegram(telegram).c_str());
if (Mqtt::send_response()) {
publish_response(telegram);
}
@@ -800,18 +797,18 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
} else if (watch() == WATCH_ON) {
if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_)
|| ((watch_id_ < 0x80) && ((telegram->src == watch_id_) || (telegram->dest == watch_id_)))) {
LOG_NOTICE(F("%s"), pretty_telegram(telegram).c_str());
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
} else if (!trace_raw_) {
LOG_TRACE(F("%s"), pretty_telegram(telegram).c_str());
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
}
} else if (!trace_raw_) {
LOG_TRACE(F("%s"), pretty_telegram(telegram).c_str());
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
}
// only process broadcast telegrams or ones sent to us on request
// if ((telegram->dest != 0x00) && (telegram->dest != rxservice_.ems_bus_id())) {
if (telegram->operation == Telegram::Operation::RX_READ) {
// LOG_DEBUG(F("read telegram received, not processing"));
// LOG_DEBUG("read telegram received, not processing");
return false;
}
@@ -861,9 +858,9 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
}
if (!found) {
LOG_DEBUG(F("No telegram type handler found for ID 0x%02X (src 0x%02X)"), telegram->type_id, telegram->src);
LOG_DEBUG(("No telegram type handler found for ID 0x%02X (src 0x%02X)"), telegram->type_id, telegram->src);
if (watch() == WATCH_UNKNOWN) {
LOG_NOTICE(F("%s"), pretty_telegram(telegram).c_str());
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
}
if (!wait_km_ && !knowndevice && (telegram->src != EMSbus::ems_bus_id()) && (telegram->message_length > 0)) {
send_read_request(EMSdevice::EMS_TYPE_VERSION, telegram->src);
@@ -887,12 +884,12 @@ bool EMSESP::device_exists(const uint8_t device_id) {
// for each associated EMS device go and get its system information
void EMSESP::show_devices(uuid::console::Shell & shell) {
if (emsdevices.empty()) {
shell.printfln(F("No EMS devices detected. Try using 'scan devices' from the ems menu."));
shell.printfln("No EMS devices detected. Try using 'scan devices' from the ems menu.");
shell.println();
return;
}
shell.printfln(F("These EMS devices are currently active:"));
shell.printfln("These EMS devices are currently active:");
shell.println();
// count the number of thermostats
@@ -908,7 +905,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_class.first)) {
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
shell.printf("%s: %s", emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
shell.println();
emsdevice->show_telegram_handlers(shell);
@@ -935,7 +932,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
if (product_id == 0) { // update only with valid product_id
return true;
}
LOG_DEBUG(F("Updating details for already active deviceID 0x%02X"), device_id);
LOG_DEBUG("Updating details for already active deviceID 0x%02X", device_id);
emsdevice->product_id(product_id);
emsdevice->version(version);
// only set brand if it doesn't already exist
@@ -945,7 +942,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
// find the name and flags in our database
for (const auto & device : device_library_) {
if (device.product_id == product_id && device.device_type == emsdevice->device_type()) {
emsdevice->name(std::move(read_flash_string(device.name)));
emsdevice->name(device.name);
emsdevice->add_flags(device.flags);
}
}
@@ -976,14 +973,13 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
// if we don't recognize the productID report it and add as a generic device
if (device_p == nullptr) {
LOG_NOTICE(F("Unrecognized EMS device (deviceID 0x%02X, productID %d). Please report on GitHub."), device_id, product_id);
std::string name("unknown");
LOG_NOTICE(("Unrecognized EMS device (deviceID 0x%02X, productID %d). Please report on GitHub."), device_id, product_id);
emsdevices.push_back(
EMSFactory::add(DeviceType::GENERIC, device_id, product_id, version, name, DeviceFlags::EMS_DEVICE_FLAG_NONE, EMSdevice::Brand::NO_BRAND));
EMSFactory::add(DeviceType::GENERIC, device_id, product_id, version, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE, EMSdevice::Brand::NO_BRAND));
return false; // not found
}
auto name = read_flash_string(device_p->name);
auto name = device_p->name;
auto device_type = device_p->device_type;
auto flags = device_p->flags;
@@ -1022,14 +1018,14 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
name = "generic boiler";
device_type = DeviceType::BOILER;
flags = DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP;
LOG_WARNING(F("Unknown EMS boiler. Using generic profile. Please report on GitHub."));
LOG_WARNING("Unknown EMS boiler. Using generic profile. Please report on GitHub.");
} else {
LOG_WARNING(F("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub."), device_id);
LOG_WARNING(("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub."), device_id);
return false;
}
}
LOG_DEBUG(F("Adding new device %s (deviceID 0x%02X, productID %d, version %s)"), name.c_str(), device_id, product_id, version);
LOG_DEBUG(("Adding new device %s (deviceID 0x%02X, productID %d, version %s)"), name, device_id, product_id, version);
emsdevices.push_back(EMSFactory::add(device_type, device_id, product_id, version, name, flags, brand));
// assign a unique ID. Note that this is not actual unique after a restart as it's dependent on the order that devices are found
@@ -1043,7 +1039,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
fetch_device_values(device_id); // go and fetch its data
// Print to LOG showing we've added a new device
LOG_INFO(F("Recognized new %s with deviceID 0x%02X"), EMSdevice::device_type_2_device_name(device_type).c_str(), device_id);
LOG_INFO("Recognized new %s with deviceID 0x%02X", EMSdevice::device_type_2_device_name(device_type), device_id);
// add command commands for all devices, except for connect, controller and gateway
if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) {
@@ -1056,10 +1052,10 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
[device_type](const char * value, const int8_t id, JsonObject & output) {
return command_info(device_type, output, id, EMSdevice::OUTPUT_TARGET::API_VERBOSE);
},
F_(info_cmd));
FL_(info_cmd));
Command::add(
device_type,
F("values"),
("values"),
[device_type](const char * value, const int8_t id, JsonObject & output) {
return command_info(device_type, output, id, EMSdevice::OUTPUT_TARGET::API_SHORTNAMES); // HIDDEN command showing short names, used in e.g. /api/boiler
},
@@ -1069,15 +1065,16 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
device_type,
F_(commands),
[device_type](const char * value, const int8_t id, JsonObject & output) { return command_commands(device_type, output, id); },
F_(commands_cmd));
FL_(commands_cmd));
Command::add(
device_type,
F_(entities),
[device_type](const char * value, const int8_t id, JsonObject & output) { return command_entities(device_type, output, id); },
F_(entities_cmd));
FL_(entities_cmd));
// MQTT subscribe to the device e.g. "ems-esp/boiler/#"
Mqtt::subscribe(device_type, EMSdevice::device_type_2_device_name(device_type) + "/#", nullptr);
auto topic = std::string(EMSdevice::device_type_2_device_name(device_type)) + "/#";
Mqtt::subscribe(device_type, topic, nullptr);
return true;
}
@@ -1178,7 +1175,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
#ifdef EMSESP_UART_DEBUG
// get_uptime is only updated once per loop, does not give the right time
LOG_TRACE(F("[UART_DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
LOG_TRACE("[UART_DEBUG] Echo after %d ms: %s", ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif
// add to RxQueue for log/watch
rxservice_.add(data, length);
@@ -1194,14 +1191,14 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// if we're waiting on a Write operation, we want a single byte 1 or 4
if ((tx_state == Telegram::Operation::TX_WRITE) && (length == 1)) {
if (first_value == TxService::TX_WRITE_SUCCESS) {
LOG_DEBUG(F("Last Tx write successful"));
LOG_DEBUG("Last Tx write successful");
txservice_.increment_telegram_write_count(); // last tx/write was confirmed ok
txservice_.send_poll(); // close the bus
publish_id_ = txservice_.post_send_query(); // follow up with any post-read if set
txservice_.reset_retry_count();
tx_successful = true;
} else if (first_value == TxService::TX_WRITE_FAIL) {
LOG_ERROR(F("Last Tx write rejected by host"));
LOG_ERROR("Last Tx write rejected by host");
txservice_.send_poll(); // close the bus
txservice_.reset_retry_count();
}
@@ -1210,7 +1207,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
uint8_t src = data[0];
uint8_t dest = data[1];
if (txservice_.is_last_tx(src, dest)) {
LOG_DEBUG(F("Last Tx read successful"));
LOG_DEBUG("Last Tx read successful");
txservice_.increment_telegram_read_count();
txservice_.send_poll(); // close the bus
txservice_.reset_retry_count();
@@ -1252,11 +1249,11 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
#ifdef EMSESP_UART_DEBUG
char s[4];
if (first_value & 0x80) {
LOG_TRACE(F("[UART_DEBUG] next Poll %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
LOG_TRACE("[UART_DEBUG] next Poll %s after %d ms", Helpers::hextoa(s, first_value), ::millis() - rx_time_);
// time measurement starts here, use millis because get_uptime is only updated once per loop
rx_time_ = ::millis();
} else {
LOG_TRACE(F("[UART_DEBUG] Poll ack %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
LOG_TRACE("[UART_DEBUG] Poll ack %s after %d ms", Helpers::hextoa(s, first_value), ::millis() - rx_time_);
}
#endif
// check for poll to us, if so send top message from Tx queue immediately and quit
@@ -1268,7 +1265,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
return;
} else {
#ifdef EMSESP_UART_DEBUG
LOG_TRACE(F("[UART_DEBUG] Reply after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
LOG_TRACE("[UART_DEBUG] Reply after %d ms: %s", ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); // check if there is a message for the roomcontroller
@@ -1293,14 +1290,14 @@ void EMSESP::start() {
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
#ifdef EMSESP_DEBUG
LOG_NOTICE(F("System is running in Debug mode"));
LOG_NOTICE("System is running in Debug mode");
#endif
LOG_INFO(F("Last system reset reason Core0: %s, Core1: %s"), system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
// do any system upgrades
if (system_.check_upgrade()) {
LOG_INFO(F("System needs a restart to apply new settings. Please wait."));
LOG_INFO("System needs a restart to apply new settings. Please wait.");
system_.system_restart();
};
@@ -1316,7 +1313,7 @@ void EMSESP::start() {
// start all the EMS-ESP services
mqtt_.start(); // mqtt init
system_.start(); // starts commands, led, adc, button, network, syslog & uart
LOG_INFO(F("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message
LOG_INFO(("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message
shower_.start(); // initialize shower timer and shower alert
dallassensor_.start(); // Dallas external sensors
@@ -1327,7 +1324,7 @@ void EMSESP::start() {
device_library_ = {
#include "device_library.h"
};
LOG_INFO(F("Loaded EMS device library (%d records)"), device_library_.size());
LOG_INFO(("Loaded EMS device library (%d records)"), device_library_.size());
#if defined(EMSESP_STANDALONE)
Mqtt::on_connect(); // simulate an MQTT connection
@@ -1344,6 +1341,7 @@ void EMSESP::scheduled_fetch_values() {
last_fetch_ = uuid::get_uptime();
no = 1;
}
if (txservice_.tx_queue_empty()) {
uint8_t i = 0;
for (const auto & emsdevice : emsdevices) {

View File

@@ -251,10 +251,10 @@ class EMSESP {
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
struct Device_record {
uint8_t product_id;
EMSdevice::DeviceType device_type;
const __FlashStringHelper * name;
uint8_t flags;
uint8_t product_id;
EMSdevice::DeviceType device_type;
const char * name;
uint8_t flags;
};
static std::vector<Device_record> device_library_;

View File

@@ -55,12 +55,12 @@ class EMSFactory {
}
// Construct derived class returning an unique ptr
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand)
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
-> std::unique_ptr<EMSdevice> {
return std::unique_ptr<EMSdevice>(EMSFactory::makeRaw(device_type, device_id, product_id, version, name, flags, brand));
}
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand) const
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const
-> EMSdevice * = 0;
private:
@@ -72,7 +72,7 @@ class EMSFactory {
// Construct derived class returning a raw pointer
// find which EMS device it is and use that class
static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand)
static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
-> EMSdevice * {
auto it = EMSFactory::getRegister().find(device_type);
if (it != EMSFactory::getRegister().end()) {
@@ -90,7 +90,7 @@ class ConcreteEMSFactory : EMSFactory {
EMSFactory::registerFactory(device_type, this);
}
auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand) const
auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const
-> EMSdevice * {
return new DerivedClass(device_type, device_id, product_id, version, name, flags, brand);
}

View File

@@ -179,9 +179,9 @@ char * Helpers::render_boolean(char * result, const bool value, const bool dashb
uint8_t bool_format_ = dashboard ? EMSESP::system_.bool_dashboard() : EMSESP::system_.bool_format();
if (bool_format_ == BOOL_FORMAT_ONOFF_STR) {
strlcpy(result, value ? translated_word(FL_(on)).c_str() : translated_word(FL_(off)).c_str(), 5);
strlcpy(result, value ? translated_word(FL_(on)) : translated_word(FL_(off)), 5);
} else if (bool_format_ == BOOL_FORMAT_ONOFF_STR_CAP) {
strlcpy(result, value ? translated_word(FL_(ON)).c_str() : translated_word(FL_(OFF)).c_str(), 5);
strlcpy(result, value ? translated_word(FL_(ON)) : translated_word(FL_(OFF)), 5);
} else if ((bool_format_ == BOOL_FORMAT_10) || (bool_format_ == BOOL_FORMAT_10_STR)) {
strlcpy(result, value ? "1" : "0", 2);
} else {
@@ -593,12 +593,12 @@ bool Helpers::value2bool(const char * value, bool & value_b) {
std::string bool_str = toLower(value); // convert to lower case
if ((bool_str == Helpers::translated_word(FL_(on))) || (bool_str == "on") || (bool_str == "1") || (bool_str == "true")) {
if ((bool_str == std::string(Helpers::translated_word(FL_(on)))) || (bool_str == "on") || (bool_str == "1") || (bool_str == "true")) {
value_b = true;
return true; // is a bool
}
if ((bool_str == Helpers::translated_word(FL_(off))) || (bool_str == "off") || (bool_str == "0") || (bool_str == "false")) {
if ((bool_str == std::string(Helpers::translated_word(FL_(off)))) || (bool_str == "off") || (bool_str == "0") || (bool_str == "false")) {
value_b = false;
return true; // is a bool
}
@@ -608,15 +608,15 @@ bool Helpers::value2bool(const char * value, bool & value_b) {
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
// this for a list of lists, when using translated strings
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const __FlashStringHelper * const ** strs) {
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const ** strs) {
if ((value == nullptr) || (strlen(value) == 0)) {
return false;
}
std::string str = toLower(value);
for (value_ui = 0; strs[value_ui]; value_ui++) {
std::string str1 = toLower(Helpers::translated_word(strs[value_ui]));
std::string str2 = toLower(read_flash_string(strs[value_ui][0])); // also check for default language
std::string str1 = toLower(std::string(Helpers::translated_word(strs[value_ui])));
std::string str2 = toLower((strs[value_ui][0])); // also check for default language
if ((str1 != "")
&& ((str2 == "off" && str == "false") || (str2 == "on" && str == "true") || (str == str1) || (str == str2)
|| (value[0] == ('0' + value_ui) && value[1] == '\0'))) {
@@ -627,19 +627,25 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const __FlashSt
return false;
}
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const __FlashStringHelper * const * strs) {
// finds the string (value) of a list vector (strs)
// returns true if found, and sets the value_ui to the index, else false
// also allow true/false for on/off
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const * strs) {
if ((value == nullptr) || (strlen(value) == 0)) {
return false;
}
std::string str = toLower(value);
std::string s_on = Helpers::translated_word(FL_(on));
std::string s_off = Helpers::translated_word(FL_(off));
// stops when a nullptr is found, which is the end delimeter of a MAKE_PSTR_LIST()
// could use count_items() to avoid buffer over-run but this works
for (value_ui = 0; strs[value_ui]; value_ui++) {
std::string enum_str = toLower(read_flash_string(strs[value_ui]));
std::string enum_str = toLower((strs[value_ui]));
if ((enum_str != "")
&& ((enum_str == "off" && (str == Helpers::translated_word(FL_(off)) || str == "false"))
|| (enum_str == "on" && (str == Helpers::translated_word(FL_(on)) || str == "true")) || (str == enum_str)
&& ((enum_str == "off" && (str == s_off || str == "false")) || (enum_str == "on" && (str == s_on || str == "true")) || (str == enum_str)
|| (value[0] == ('0' + value_ui) && value[1] == '\0'))) {
return true;
}
@@ -655,6 +661,10 @@ std::string Helpers::toLower(std::string const & s) {
return lc;
}
std::string Helpers::toLower(const char * s) {
return toLower(std::string(s));
}
std::string Helpers::toUpper(std::string const & s) {
std::string lc = s;
std::transform(lc.begin(), lc.end(), lc.begin(), [](unsigned char c) { return std::toupper(c); });
@@ -676,7 +686,7 @@ void Helpers::replace_char(char * str, char find, char replace) {
// count number of items in a list
// the end of a list has a nullptr
uint8_t Helpers::count_items(const __FlashStringHelper * const * list) {
uint8_t Helpers::count_items(const char * const * list) {
uint8_t list_size = 0;
if (list != nullptr) {
while (list[list_size]) {
@@ -688,7 +698,7 @@ uint8_t Helpers::count_items(const __FlashStringHelper * const * list) {
// count number of items in a list of lists
// the end of a list has a nullptr
uint8_t Helpers::count_items(const __FlashStringHelper * const ** list) {
uint8_t Helpers::count_items(const char * const ** list) {
uint8_t list_size = 0;
if (list != nullptr) {
while (list[list_size]) {
@@ -698,27 +708,17 @@ uint8_t Helpers::count_items(const __FlashStringHelper * const ** list) {
return list_size;
}
// return translated string as a std::string, optionally converting to lowercase (for console commands)
// takes a FL(...)
std::string Helpers::translated_word(const __FlashStringHelper * const * strings, bool to_lower) {
// returns char pointer to translated description or fullname
const char * Helpers::translated_word(const char * const * strings) {
uint8_t language_index = EMSESP::system_.language_index();
uint8_t index = 0;
// see how many translations we have for this entity. if there is no translation for this, revert to EN
if (Helpers::count_items(strings) >= language_index + 1 && !read_flash_string(strings[language_index]).empty()) {
index = language_index;
if (!strings) {
return ""; // no translations
}
return to_lower ? toLower(read_flash_string(strings[index])) : read_flash_string(strings[index]);
}
// return translated string
// takes a F(...)
const __FlashStringHelper * Helpers::translated_fword(const __FlashStringHelper * const * strings) {
uint8_t language_index = EMSESP::system_.language_index();
uint8_t index = 0;
// see how many translations we have for this entity. if there is no translation for this, revert to EN
if (Helpers::count_items(strings) >= language_index + 1 && !read_flash_string(strings[language_index]).empty()) {
if (Helpers::count_items(strings) >= language_index + 1 && strlen(strings[language_index])) {
index = language_index;
}
return strings[index];

View File

@@ -54,7 +54,9 @@ class Helpers {
static std::string toLower(std::string const & s);
static std::string toUpper(std::string const & s);
static void replace_char(char * str, char find, char replace);
static std::string toLower(const char * s);
static void replace_char(char * str, char find, char replace);
static bool hasValue(const uint8_t & value, const uint8_t isBool = 0);
static bool hasValue(const int8_t & value);
@@ -67,16 +69,15 @@ class Helpers {
static bool value2float(const char * value, float & value_f);
static bool value2bool(const char * value, bool & value_b);
static bool value2string(const char * value, std::string & value_s);
static bool value2enum(const char * value, uint8_t & value_ui, const __FlashStringHelper * const ** strs);
static bool value2enum(const char * value, uint8_t & value_ui, const __FlashStringHelper * const * strs);
static bool value2enum(const char * value, uint8_t & value_ui, const char * const ** strs);
static bool value2enum(const char * value, uint8_t & value_ui, const char * const * strs);
static bool value2temperature(const char * value, float & value_f, bool relative = false);
static bool value2temperature(const char * value, int & value_i, const bool relative = false, const int min = -2147483648, const int max = 2147483647);
static uint8_t count_items(const __FlashStringHelper * const ** list);
static uint8_t count_items(const __FlashStringHelper * const * list);
static uint8_t count_items(const char * const ** list);
static uint8_t count_items(const char * const * list);
static std::string translated_word(const __FlashStringHelper * const * strings, bool to_lower = false);
static const __FlashStringHelper * translated_fword(const __FlashStringHelper * const * strings);
static const char * translated_word(const char * const * strings);
#ifdef EMSESP_STANDALONE
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);

View File

@@ -20,6 +20,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
// clang-format off
/*
* THIS FILE CONTAINS STANDARD STRING LITERALS THAT DON'T NEED LANGUAGE TRANSLATIONS
*/
@@ -144,55 +146,55 @@ MAKE_PSTR(password_prompt, "Password: ")
MAKE_PSTR(unset, "<unset>")
// more common names that don't need translations
MAKE_PSTR_LIST(1x3min, F("1x3min"))
MAKE_PSTR_LIST(2x3min, F("2x3min"))
MAKE_PSTR_LIST(3x3min, F("3x3min"))
MAKE_PSTR_LIST(4x3min, F("4x3min"))
MAKE_PSTR_LIST(5x3min, F("5x3min"))
MAKE_PSTR_LIST(6x3min, F("6x3min"))
MAKE_PSTR_LIST(auto, F("auto"))
MAKE_PSTR_LIST(na, F("n/a"))
MAKE_PSTR_LIST(rc3x, F("rc3x"))
MAKE_PSTR_LIST(rc20, F("rc20"))
MAKE_PSTR_LIST(fb10, F("fb10"))
MAKE_PSTR_LIST(fb100, F("fb100"))
MAKE_PSTR_LIST(dash, F("-"))
MAKE_PSTR_LIST(error, F("error"))
MAKE_PSTR_LIST(BLANK, F(""))
MAKE_PSTR_LIST(pwm, F("pwm"))
MAKE_PSTR_LIST(pwm_invers, F("pwm inverse"))
MAKE_PSTR_LIST(mpc, F("mpc"))
MAKE_PSTR_LIST(tempauto, F("temp auto"))
MAKE_PSTR_LIST(bypass, F("bypass"))
MAKE_PSTR_LIST(mixer, F("mixer"))
MAKE_PSTR_LIST(monovalent, F("monovalent"))
MAKE_PSTR_LIST(bivalent, F("bivalent"))
MAKE_PSTR_LIST(n_o, F("n_o"))
MAKE_PSTR_LIST(n_c, F("n_c"))
MAKE_PSTR_LIST(prog1, F("prog 1"))
MAKE_PSTR_LIST(prog2, F("prog 2"))
MAKE_PSTR_LIST(proga, F("prog a"))
MAKE_PSTR_LIST(progb, F("prog b"))
MAKE_PSTR_LIST(progc, F("prog c"))
MAKE_PSTR_LIST(progd, F("prog d"))
MAKE_PSTR_LIST(proge, F("prog e"))
MAKE_PSTR_LIST(progf, F("prog f"))
MAKE_PSTR_LIST(rc35, F("RC35"))
MAKE_PSTR_LIST(0kW, F("0 kW"))
MAKE_PSTR_LIST(2kW, F("2 kW"))
MAKE_PSTR_LIST(3kW, F("3 kW"))
MAKE_PSTR_LIST(4kW, F("4 kW"))
MAKE_PSTR_LIST(6kW, F("6 kW"))
MAKE_PSTR_LIST(9kW, F("9 kW"))
MAKE_PSTR_LIST(1x3min, "1x3min")
MAKE_PSTR_LIST(2x3min, "2x3min")
MAKE_PSTR_LIST(3x3min, "3x3min")
MAKE_PSTR_LIST(4x3min, "4x3min")
MAKE_PSTR_LIST(5x3min, "5x3min")
MAKE_PSTR_LIST(6x3min, "6x3min")
MAKE_PSTR_LIST(auto, "auto")
MAKE_PSTR_LIST(na, "n/a")
MAKE_PSTR_LIST(rc3x, "rc3x")
MAKE_PSTR_LIST(rc20, "rc20")
MAKE_PSTR_LIST(fb10, "fb10")
MAKE_PSTR_LIST(fb100, "fb100")
MAKE_PSTR_LIST(dash, "-")
MAKE_PSTR_LIST(error, "error")
MAKE_PSTR_LIST(BLANK, "")
MAKE_PSTR_LIST(pwm, "pwm")
MAKE_PSTR_LIST(pwm_invers, "pwm inverse")
MAKE_PSTR_LIST(mpc, "mpc")
MAKE_PSTR_LIST(tempauto, "temp auto")
MAKE_PSTR_LIST(bypass, "bypass")
MAKE_PSTR_LIST(mixer, "mixer")
MAKE_PSTR_LIST(monovalent, "monovalent")
MAKE_PSTR_LIST(bivalent, "bivalent")
MAKE_PSTR_LIST(n_o, "n_o")
MAKE_PSTR_LIST(n_c, "n_c")
MAKE_PSTR_LIST(prog1, "prog 1")
MAKE_PSTR_LIST(prog2, "prog 2")
MAKE_PSTR_LIST(proga, "prog a")
MAKE_PSTR_LIST(progb, "prog b")
MAKE_PSTR_LIST(progc, "prog c")
MAKE_PSTR_LIST(progd, "prog d")
MAKE_PSTR_LIST(proge, "prog e")
MAKE_PSTR_LIST(progf, "prog f")
MAKE_PSTR_LIST(rc35, "RC35")
MAKE_PSTR_LIST(0kW, "0 kW")
MAKE_PSTR_LIST(2kW, "2 kW")
MAKE_PSTR_LIST(3kW, "3 kW")
MAKE_PSTR_LIST(4kW, "4 kW")
MAKE_PSTR_LIST(6kW, "6 kW")
MAKE_PSTR_LIST(9kW, "9 kW")
// templates - this are not translated and will be saved under optons_single
MAKE_PSTR_LIST(tpl_datetime, F("Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >"))
MAKE_PSTR_LIST(tpl_switchtime, F("Format: <nn> [ not_set | day hh:mm on|off ]"))
MAKE_PSTR_LIST(tpl_switchtime1, F("Format: <nn> [ not_set | day hh:mm Tn ]"))
MAKE_PSTR_LIST(tpl_holidays, F("Format: < dd.mm.yyyy-dd.mm.yyyy >"))
MAKE_PSTR_LIST(tpl_date, F("Format: < dd.mm.yyyy >"))
MAKE_PSTR_LIST(tpl_input, F("Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]"))
MAKE_PSTR_LIST(tpl_input4, F("Format: <inv>[<comp><aux><cool><heat><dhw><pv>]"))
// templates - this are not translated and will be saved under options_single
MAKE_PSTR_LIST(tpl_datetime, "Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
MAKE_PSTR_LIST(tpl_switchtime, "Format: <nn> [ not_set | day hh:mm on|off ]")
MAKE_PSTR_LIST(tpl_switchtime1, "Format: <nn> [ not_set | day hh:mm Tn ]")
MAKE_PSTR_LIST(tpl_holidays, "Format: < dd.mm.yyyy-dd.mm.yyyy >")
MAKE_PSTR_LIST(tpl_date, "Format: < dd.mm.yyyy >")
MAKE_PSTR_LIST(tpl_input, "Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
MAKE_PSTR_LIST(tpl_input4, "Format: <inv>[<comp><aux><cool><heat><dhw><pv>]")
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
// These don't need translating, it will mess up HA and the API
@@ -217,11 +219,6 @@ MAKE_PSTR(uom_sqm, "sqm")
MAKE_PSTR(uom_m3, "m3")
MAKE_PSTR(uom_l, "l")
// commands
MAKE_PSTR(info_cmd, "lists all values")
MAKE_PSTR(commands_cmd, "lists all commands")
MAKE_PSTR(entities_cmd, "lists all entities")
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
// use empty string if want to suppress showing tags
// mqtt tags must not have spaces
@@ -273,16 +270,17 @@ MAKE_PSTR(response, "response")
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
MAKE_PSTR(tag_device_data_ww_mqtt, "")
MAKE_PSTR_LIST(climate, F("HA climate config creation"))
// Home Assistant - this is special and has no translations
MAKE_PSTR_LIST(climate, "HA climate config creation")
// syslog
MAKE_PSTR_LIST(list_syslog_level, F("off"), F("emerg"), F("alert"), F("crit"), F("error"), F("warn"), F("notice"), F("info"), F("debug"), F("trace"), F("all"))
MAKE_PSTR_LIST(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
// sensors
MAKE_PSTR_LIST(list_sensortype, F("none"), F("digital in"), F("counter"), F("adc"), F("timer"), F("rate"), F("digital out"), F("pwm 0"), F("pwm 1"), F("pwm 2"))
MAKE_PSTR_LIST(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
// watch
MAKE_PSTR_LIST(list_watch, F("off"), F("on"), F("raw"), F("unknown"))
MAKE_PSTR_LIST(list_watch, "off", "on", "raw", "unknown")
/*
* The rest below are Enums and generated from translations lists
@@ -302,16 +300,7 @@ MAKE_PSTR_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
MAKE_PSTR_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
// thermostat lists
MAKE_PSTR_ENUM(enum_ibaMainDisplay,
FL_(internal_temperature),
FL_(internal_setpoint),
FL_(external_temperature),
FL_(burner_temperature),
FL_(ww_temperature),
FL_(functioning_mode),
FL_(time),
FL_(date),
FL_(smoke_temperature))
MAKE_PSTR_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
MAKE_PSTR_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
MAKE_PSTR_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
MAKE_PSTR_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
@@ -379,3 +368,5 @@ MAKE_PSTR_ENUM(enum_bufConfig, FL_(monovalent), FL_(bivalent))
MAKE_PSTR_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
#pragma GCC diagnostic pop
// clang-format on

File diff suppressed because it is too large Load Diff

View File

@@ -186,26 +186,26 @@ void Mqtt::loop() {
// print MQTT log and other stuff to console
void Mqtt::show_mqtt(uuid::console::Shell & shell) {
shell.printfln(F("MQTT is %s"), connected() ? read_flash_string(F_(connected)).c_str() : read_flash_string(F_(disconnected)).c_str());
shell.printfln("MQTT is %s", connected() ? F_(connected) : F_(disconnected));
shell.printfln(F("MQTT publish errors: %lu"), mqtt_publish_fails_);
shell.printfln("MQTT publish errors: %lu", mqtt_publish_fails_);
shell.println();
// show subscriptions
shell.printfln(F("MQTT topic subscriptions:"));
shell.printfln("MQTT topic subscriptions:");
for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
shell.printfln(F(" %s/%s"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str());
shell.printfln(" %s/%s", mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str());
}
shell.println();
// show queues
if (mqtt_messages_.empty()) {
shell.printfln(F("MQTT queue is empty"));
shell.printfln("MQTT queue is empty");
shell.println();
return;
}
shell.printfln(F("MQTT queue (%d/%d messages):"), mqtt_messages_.size(), MAX_MQTT_MESSAGES);
shell.printfln(("MQTT queue (%d/%d messages):"), mqtt_messages_.size(), MAX_MQTT_MESSAGES);
for (const auto & message : mqtt_messages_) {
auto content = message.content_;
@@ -222,12 +222,12 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
// Publish messages
if (message.retry_count_ == 0) {
if (message.packet_id_ == 0) {
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s"), message.id_, topic, content->payload.c_str());
shell.printfln((" [%02d] (Pub) topic=%s payload=%s"), message.id_, topic, content->payload.c_str());
} else {
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s (pid %d)"), message.id_, topic, content->payload.c_str(), message.packet_id_);
shell.printfln((" [%02d] (Pub) topic=%s payload=%s (pid %d)"), message.id_, topic, content->payload.c_str(), message.packet_id_);
}
} else {
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)"),
shell.printfln((" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)"),
message.id_,
topic,
content->payload.c_str(),
@@ -236,7 +236,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
}
} else {
// Subscribe messages
shell.printfln(F(" [%02d] (Sub) topic=%s"), message.id_, topic);
shell.printfln((" [%02d] (Sub) topic=%s"), message.id_, topic);
}
}
shell.println();
@@ -264,16 +264,16 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons
#if defined(EMSESP_DEBUG)
if (len) {
LOG_DEBUG(F("Received topic `%s` => payload `%s` (length %d)"), topic, message, len);
LOG_DEBUG(("Received topic `%s` => payload `%s` (length %d)"), topic, message, len);
} else {
LOG_DEBUG(F("Received topic `%s`"), topic);
LOG_DEBUG("Received topic `%s`", topic);
}
#endif
// remove HA topics if we don't use discovery
if (strncmp(topic, discovery_prefix().c_str(), discovery_prefix().size()) == 0) {
if (!ha_enabled_ && len) { // don't ping pong the empty message
queue_publish_message(topic, "", true);
LOG_DEBUG(F("Remove topic %s"), topic);
LOG_DEBUG("Remove topic %s", topic);
}
return;
}
@@ -285,7 +285,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons
snprintf(full_topic, sizeof(full_topic), "%s/%s", mqtt_base_.c_str(), mf.topic_.c_str());
if ((!strcmp(topic, full_topic)) && (mf.mqtt_subfunction_)) {
if (!(mf.mqtt_subfunction_)(message)) {
LOG_ERROR(F("error: invalid payload %s for this topic %s"), message, topic);
LOG_ERROR("error: invalid payload %s for this topic %s", message, topic);
if (send_response_) {
Mqtt::publish(F_(response), "error: invalid data");
}
@@ -341,10 +341,10 @@ void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t devic
return;
}
// shell.print(F(" Subscribed MQTT topics: "));
// shell.print(" Subscribed MQTT topics: ");
// for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
// if (mqtt_subfunction.device_type_ == device_type) {
// shell.printf(F("%s "), mqtt_subfunction.topic_.c_str());
// shell.printf("%s ", mqtt_subfunction.topic_.c_str());
// }
// }
shell.println();
@@ -358,7 +358,7 @@ void Mqtt::on_publish(uint16_t packetId) const {
// find the MQTT message in the queue and remove it
if (mqtt_messages_.empty()) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] No message stored for ACK pid %d"), packetId);
LOG_DEBUG("[DEBUG] No message stored for ACK pid %d", packetId);
#endif
return;
}
@@ -368,18 +368,18 @@ void Mqtt::on_publish(uint16_t packetId) const {
// if the last published failed, don't bother checking it. wait for the next retry
if (mqtt_message.packet_id_ == 0) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] ACK for failed message pid 0"));
LOG_DEBUG("[DEBUG] ACK for failed message pid 0");
#endif
return;
}
if (mqtt_message.packet_id_ != packetId) {
LOG_ERROR(F("Mismatch, expecting PID %d, got %d"), mqtt_message.packet_id_, packetId);
LOG_ERROR("Mismatch, expecting PID %d, got %d", mqtt_message.packet_id_, packetId);
mqtt_publish_fails_++; // increment error count
}
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] ACK pid %d"), packetId);
LOG_DEBUG("[DEBUG] ACK pid %d", packetId);
#endif
mqtt_messages_.pop_front(); // always remove from queue, regardless if there was a successful ACK
@@ -450,17 +450,17 @@ void Mqtt::start() {
}
connecting_ = false;
if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
LOG_WARNING(F("MQTT disconnected: TCP"));
LOG_WARNING("MQTT disconnected: TCP");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
LOG_WARNING(F("MQTT disconnected: Identifier Rejected"));
LOG_WARNING("MQTT disconnected: Identifier Rejected");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_SERVER_UNAVAILABLE) {
LOG_WARNING(F("MQTT disconnected: Server unavailable"));
LOG_WARNING("MQTT disconnected: Server unavailable");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_MALFORMED_CREDENTIALS) {
LOG_WARNING(F("MQTT disconnected: Malformed credentials"));
LOG_WARNING("MQTT disconnected: Malformed credentials");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED) {
LOG_WARNING(F("MQTT disconnected: Not authorized"));
LOG_WARNING("MQTT disconnected: Not authorized");
} else {
LOG_WARNING(F("MQTT disconnected: code %d"), reason);
LOG_WARNING("MQTT disconnected: code %d", reason);
}
});
@@ -535,7 +535,7 @@ void Mqtt::on_connect() {
return;
}
LOG_INFO(F("MQTT connected"));
LOG_INFO("MQTT connected");
connecting_ = true;
connectcount_++;
@@ -546,15 +546,15 @@ void Mqtt::on_connect() {
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
// first time to connect
if (connectcount_ == 1) {
doc["event"] = FJSON("start");
doc["event"] = "start";
} else {
doc["event"] = FJSON("reconnect");
doc["event"] = "reconnect";
}
doc["version"] = EMSESP_APP_VERSION;
#ifndef EMSESP_STANDALONE
if (WiFi.status() == WL_CONNECTED) {
doc["connection"] = F("WiFi");
doc["connection"] = ("WiFi");
doc["hostname"] = WiFi.getHostname();
doc["SSID"] = WiFi.SSID();
doc["BSSID"] = WiFi.BSSIDstr();
@@ -567,7 +567,7 @@ void Mqtt::on_connect() {
doc["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
}
} else if (EMSESP::system_.ethernet_connected()) {
doc["connection"] = F("Ethernet");
doc["connection"] = ("Ethernet");
doc["hostname"] = ETH.getHostname();
doc["MAC"] = ETH.macAddress();
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
@@ -600,7 +600,7 @@ void Mqtt::on_connect() {
// re-subscribe to all custom registered MQTT topics
resubscribe();
publish_retain(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on
publish_retain("status", "online", true); // say we're alive to the Last Will topic, with retain on
mqtt_publish_fails_ = 0; // reset fail count to 0
@@ -609,7 +609,7 @@ void Mqtt::on_connect() {
LOG_INFO("Queue size: %d", mqtt_messages_.size());
for (const auto & message : mqtt_messages_) {
auto content = message.content_;
LOG_INFO(F(" [%02d] (%d) topic=%s payload=%s"), message.id_, content->operation, content->topic.c_str(), content->payload.c_str());
LOG_INFO((" [%02d] (%d) topic=%s payload=%s"), message.id_, content->operation, content->topic.c_str(), content->payload.c_str());
}
*/
}
@@ -620,21 +620,21 @@ void Mqtt::on_connect() {
void Mqtt::ha_status() {
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
doc["uniq_id"] = FJSON("ems-esp-system");
doc["uniq_id"] = "ems-esp-system";
doc["~"] = mqtt_base_; // default ems-esp
// doc["avty_t"] = FJSON("~/status"); // commented out, as it causes errors in HA sometimes
// doc["json_attr_t"] = FJSON("~/heartbeat"); // store also as HA attributes
doc["stat_t"] = FJSON("~/heartbeat");
doc["object_id"] = FJSON("ems_esp_status");
doc["name"] = FJSON("EMS-ESP status");
// doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes
// doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes
doc["stat_t"] = "~/heartbeat";
doc["object_id"] = "ems_esp_status";
doc["name"] = "EMS-ESP status";
doc["ic"] = F_(icondevice);
doc["val_tpl"] = FJSON("{{value_json['bus_status']}}");
doc["val_tpl"] = "{{value_json['bus_status']}}";
JsonObject dev = doc.createNestedObject("dev");
dev["name"] = F_(EMSESP); // "EMS-ESP"
dev["name"] = "EMS-ESP";
dev["sw"] = "v" + std::string(EMSESP_APP_VERSION);
dev["mf"] = FJSON("proddy");
dev["mdl"] = F_(EMSESP); // "EMS-ESP"
dev["mf"] = "proddy";
dev["mdl"] = "EMS-ESP";
JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp");
@@ -644,19 +644,20 @@ void Mqtt::ha_status() {
// create the sensors - must match the MQTT payload keys
if (!EMSESP::system_.ethernet_connected()) {
publish_system_ha_sensor_config(DeviceValueType::INT, F("WiFi RSSI"), F("rssi"), DeviceValueUOM::DBM);
publish_system_ha_sensor_config(DeviceValueType::INT, F("WiFi strength"), F("wifistrength"), DeviceValueUOM::PERCENT);
publish_system_ha_sensor_config(DeviceValueType::INT, ("WiFi RSSI"), ("rssi"), DeviceValueUOM::DBM);
publish_system_ha_sensor_config(DeviceValueType::INT, ("WiFi strength"), ("wifistrength"), DeviceValueUOM::PERCENT);
}
publish_system_ha_sensor_config(DeviceValueType::INT, F("Uptime"), F("uptime"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Uptime (sec)"), F("uptime_sec"), DeviceValueUOM::SECONDS);
publish_system_ha_sensor_config(DeviceValueType::BOOL, F("NTP status"), F("ntp_status"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Free memory"), F("freemem"), DeviceValueUOM::KB);
publish_system_ha_sensor_config(DeviceValueType::INT, F("MQTT fails"), F("mqttfails"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Rx received"), F("rxreceived"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Rx fails"), F("rxfails"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx reads"), F("txreads"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx writes"), F("txwrites"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx fails"), F("txfails"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Uptime"), ("uptime"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Uptime (sec)"), ("uptime_sec"), DeviceValueUOM::SECONDS);
publish_system_ha_sensor_config(DeviceValueType::BOOL, ("NTP status"), ("ntp_status"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Free memory"), ("freemem"), DeviceValueUOM::KB);
publish_system_ha_sensor_config(DeviceValueType::INT, ("MQTT fails"), ("mqttfails"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Rx received"), ("rxreceived"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Rx fails"), ("rxfails"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Tx reads"), ("txreads"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Tx writes"), ("txwrites"), DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, ("Tx fails"), ("txfails"), DeviceValueUOM::NONE);
}
// add sub or pub task to the queue.
@@ -686,7 +687,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
// if the queue is full, make room but removing the last one
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
mqtt_messages_.pop_front();
LOG_WARNING(F("Queue overflow, removing one message"));
LOG_WARNING("Queue overflow, removing one message");
mqtt_publish_fails_++;
}
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
@@ -718,17 +719,17 @@ void Mqtt::publish(const std::string & topic, const std::string & payload) {
}
// MQTT Publish, using a user's retain flag - except for char * strings
void Mqtt::publish(const __FlashStringHelper * topic, const char * payload) {
queue_publish_message(read_flash_string(topic), payload, mqtt_retain_);
void Mqtt::publish(const char * topic, const char * payload) {
queue_publish_message((topic), payload, mqtt_retain_);
}
// MQTT Publish, using a specific retain flag, topic is a flash string
void Mqtt::publish(const __FlashStringHelper * topic, const std::string & payload) {
queue_publish_message(read_flash_string(topic), payload, mqtt_retain_);
void Mqtt::publish(const char * topic, const std::string & payload) {
queue_publish_message((topic), payload, mqtt_retain_);
}
void Mqtt::publish(const __FlashStringHelper * topic, const JsonObject & payload) {
publish(read_flash_string(topic), payload);
void Mqtt::publish(const char * topic, const JsonObject & payload) {
publish_retain(topic, payload, mqtt_retain_);
}
// publish json doc, only if its not empty
@@ -737,12 +738,16 @@ void Mqtt::publish(const std::string & topic, const JsonObject & payload) {
}
// MQTT Publish, using a specific retain flag, topic is a flash string, forcing retain flag
void Mqtt::publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain) {
queue_publish_message(read_flash_string(topic), payload, retain);
void Mqtt::publish_retain(const char * topic, const std::string & payload, bool retain) {
queue_publish_message((topic), payload, retain);
}
// publish json doc, only if its not empty, using the retain flag
void Mqtt::publish_retain(const std::string & topic, const JsonObject & payload, bool retain) {
publish_retain(topic.c_str(), payload, retain);
}
void Mqtt::publish_retain(const char * topic, const JsonObject & payload, bool retain) {
if (enabled() && payload.size()) {
std::string payload_text;
serializeJson(payload, payload_text); // convert json to string
@@ -750,30 +755,22 @@ void Mqtt::publish_retain(const std::string & topic, const JsonObject & payload,
}
}
void Mqtt::publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain) {
publish_retain(read_flash_string(topic), payload, retain);
}
void Mqtt::publish_ha(const __FlashStringHelper * topic, const JsonObject & payload) {
publish_ha(read_flash_string(topic), payload);
}
// publish empty payload to remove the topic
void Mqtt::publish_ha(const std::string & topic) {
void Mqtt::publish_ha(const char * topic) {
if (!enabled()) {
return;
}
std::string fulltopic = Mqtt::discovery_prefix() + topic;
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Publishing empty HA topic=%s"), fulltopic.c_str());
LOG_DEBUG("[DEBUG] Publishing empty HA topic=%s", fulltopic.c_str());
#endif
queue_publish_message(fulltopic, "", true); // publish with retain to remove from broker
}
// publish a Home Assistant config topic and payload, with retain flag off.
void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) {
void Mqtt::publish_ha(const char * topic, const JsonObject & payload) {
if (!enabled()) {
return;
}
@@ -784,9 +781,9 @@ void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) {
std::string fulltopic = Mqtt::discovery_prefix() + topic;
#if defined(EMSESP_STANDALONE)
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
LOG_DEBUG("Publishing HA topic=%s, payload=%s", fulltopic.c_str(), payload_text.c_str());
#elif defined(EMSESP_DEBUG)
LOG_DEBUG(F("[debug] Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
LOG_DEBUG("[debug] Publishing HA topic=%s, payload=%s", fulltopic.c_str(), payload_text.c_str());
#endif
// queue messages if the MQTT connection is not yet established. to ensure we don't miss messages
@@ -815,7 +812,7 @@ void Mqtt::process_queue() {
// it will have a real packet ID
if (mqtt_message.packet_id_ > 0) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Waiting for QOS-ACK"));
LOG_DEBUG("[DEBUG] Waiting for QOS-ACK");
#endif
// if we don't get the ack within 10 minutes, republish with new packet_id
if (uuid::get_uptime_sec() - last_publish_queue_ < 600) {
@@ -826,13 +823,13 @@ void Mqtt::process_queue() {
// if we're subscribing...
if (message->operation == Operation::SUBSCRIBE) {
LOG_DEBUG(F("Subscribing to topic '%s'"), topic);
LOG_DEBUG("Subscribing to topic '%s'", topic);
uint16_t packet_id = mqttClient_->subscribe(topic, mqtt_qos_);
if (!packet_id) {
if (++mqtt_messages_.front().retry_count_ < MQTT_PUBLISH_MAX_RETRY) {
return;
}
LOG_ERROR(F("Error subscribing to topic '%s'"), topic);
LOG_ERROR("Error subscribing to topic '%s'", topic);
mqtt_publish_fails_++; // increment failure counter
}
@@ -843,13 +840,13 @@ void Mqtt::process_queue() {
// if we're unsubscribing...
if (message->operation == Operation::UNSUBSCRIBE) {
LOG_DEBUG(F("Subscribing to topic '%s'"), topic);
LOG_DEBUG("Subscribing to topic '%s'", topic);
uint16_t packet_id = mqttClient_->unsubscribe(topic);
if (!packet_id) {
if (++mqtt_messages_.front().retry_count_ < MQTT_PUBLISH_MAX_RETRY) {
return;
}
LOG_ERROR(F("Error unsubscribing to topic '%s'"), topic);
LOG_ERROR("Error unsubscribing to topic '%s'", topic);
mqtt_publish_fails_++; // increment failure counter
}
@@ -860,7 +857,7 @@ void Mqtt::process_queue() {
// else try and publish it
uint16_t packet_id = mqttClient_->publish(topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
LOG_DEBUG(F("Publishing topic %s (#%02d, retain=%d, retry=%d, size=%d, pid=%d)"),
LOG_DEBUG(("Publishing topic %s (#%02d, retain=%d, retry=%d, size=%d, pid=%d)"),
topic,
mqtt_message.id_,
message->retain,
@@ -871,14 +868,14 @@ void Mqtt::process_queue() {
if (packet_id == 0) {
// it failed. if we retried n times, give up. remove from queue
if (mqtt_message.retry_count_ == (MQTT_PUBLISH_MAX_RETRY - 1)) {
LOG_ERROR(F("Failed to publish to %s after %d attempts"), topic, mqtt_message.retry_count_ + 1);
LOG_ERROR("Failed to publish to %s after %d attempts", topic, mqtt_message.retry_count_ + 1);
mqtt_publish_fails_++; // increment failure counter
mqtt_messages_.pop_front(); // delete
return;
} else {
// update the record
mqtt_messages_.front().retry_count_++;
LOG_DEBUG(F("Failed to publish to %s. Trying again, #%d"), topic, mqtt_message.retry_count_ + 1);
LOG_DEBUG("Failed to publish to %s. Trying again, #%d", topic, mqtt_message.retry_count_ + 1);
return; // leave on queue for next time so it gets republished
}
}
@@ -888,7 +885,7 @@ void Mqtt::process_queue() {
if (mqtt_qos_ != 0) {
mqtt_messages_.front().packet_id_ = packet_id;
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Setting packetID for ACK to %d"), packet_id);
LOG_DEBUG("[DEBUG] Setting packetID for ACK to %d", packet_id);
#endif
return;
}
@@ -902,23 +899,24 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model,
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> dev_json;
// always create the ids
JsonArray ids = dev_json.createNestedArray("ids");
char ha_device[40];
std::string device_type_name = EMSdevice::device_type_2_device_name(dv.device_type);
snprintf(ha_device, sizeof(ha_device), "ems-esp-%s", device_type_name.c_str());
JsonArray ids = dev_json.createNestedArray("ids");
char ha_device[40];
auto device_type_name = EMSdevice::device_type_2_device_name(dv.device_type);
snprintf(ha_device, sizeof(ha_device), "ems-esp-%s", device_type_name);
ids.add(ha_device);
if (create_device_config) {
device_type_name[0] = toupper(device_type_name[0]); // capitalize
dev_json["name"] = "EMS-ESP " + device_type_name;
auto cap_name = strdup(device_type_name);
cap_name[0] = toupper(cap_name[0]); // capitalize
dev_json["name"] = std::string("EMS-ESP ") + cap_name;
dev_json["mf"] = brand;
dev_json["mdl"] = model;
dev_json["via_device"] = "ems-esp";
}
// calculate the min and max
int16_t dv_set_min;
int16_t dv_set_max;
int16_t dv_set_min;
uint16_t dv_set_max;
(void)dv.get_min_max(dv_set_min, dv_set_max);
// determine if we're creating the command topics which we use special HA configs
@@ -927,8 +925,8 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model,
publish_ha_sensor_config(dv.type,
dv.tag,
dv.get_fullname(),
dv.fullname[0],
dv.get_fullname().c_str(),
(dv.fullname ? dv.fullname[0] : nullptr), // EN name
dv.device_type,
dv.short_name,
dv.uom,
@@ -942,50 +940,47 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model,
}
// publish HA sensor for System using the heartbeat tag
void Mqtt::publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelper * name, const __FlashStringHelper * entity, const uint8_t uom) {
void Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom) {
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
JsonObject dev_json = doc.createNestedObject("dev");
JsonArray ids = dev_json.createNestedArray("ids");
ids.add("ems-esp");
auto fullname = read_flash_string(name);
publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, fullname, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json);
publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json);
}
// MQTT discovery configs
// entity must match the key/value pair in the *_data topic
// 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 std::string & fullname, // fullname, already translated
const __FlashStringHelper * const en_name,
const uint8_t device_type, // EMSdevice::DeviceType
const __FlashStringHelper * const entity, // same as shortname
const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE)
const bool remove, // true if we want to remove this topic
const bool has_cmd,
const __FlashStringHelper * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const JsonObject & dev_json) {
void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
uint8_t tag, // EMSdevice::DeviceValueTAG
const char * const fullname, // fullname, already translated
const char * const en_name,
const uint8_t device_type, // EMSdevice::DeviceType
const char * const entity, // same as shortname
const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE)
const bool remove, // true if we want to remove this topic
const bool has_cmd,
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const JsonObject & dev_json) {
// ignore if name (fullname) is empty
if (fullname.empty() || en_name == nullptr) {
if (fullname == nullptr || en_name == nullptr) {
return;
}
// create the device name
char device_name[50];
strlcpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
auto device_name = EMSdevice::device_type_2_device_name(device_type);
// create entity by add the hc/wwc tag if present, separating with a .
char new_entity[50];
if (tag >= DeviceValueTAG::TAG_HC1) {
snprintf(new_entity, sizeof(new_entity), "%s.%s", EMSdevice::tag_to_string(tag).c_str(), read_flash_string(entity).c_str());
snprintf(new_entity, sizeof(new_entity), "%s.%s", EMSdevice::tag_to_string(tag).c_str(), (entity));
} else {
snprintf(new_entity, sizeof(new_entity), "%s", read_flash_string(entity).c_str());
snprintf(new_entity, sizeof(new_entity), "%s", (entity));
}
// build unique identifier which will be used in the topic, replacing all . with _ as not to break HA
@@ -1037,7 +1032,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
// if we're asking to remove this topic, send an empty payload and exit
// https://github.com/emsesp/EMS-ESP32/issues/196
if (remove) {
LOG_DEBUG(F("Removing HA config for %s"), uniq);
LOG_DEBUG("Removing HA config for %s", uniq);
publish_ha(topic);
return;
}
@@ -1107,7 +1102,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
// friendly name = <tag> <name>
char ha_name[70];
char * F_name = strdup(fullname.c_str());
char * F_name = strdup(fullname);
F_name[0] = toupper(F_name[0]); // capitalize first letter
if (have_tag) {
snprintf(ha_name, sizeof(ha_name), "%s %s", EMSdevice::tag_to_string(tag).c_str(), F_name);
@@ -1120,14 +1115,12 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
// entity id is generated from the name, see https://www.home-assistant.io/docs/mqtt/discovery/#use-object_id-to-influence-the-entity-id
// so we override it to make it unique using entity_id
// See https://github.com/emsesp/EMS-ESP32/issues/596
// keep it compatible to v3.4, use english fullname, no prefix (basename prefix commmented out)
// keep it compatible to v3.4, use english fullname, no prefix (basename prefix commented out)
char object_id[130];
if (have_tag) {
// snprintf(object_id, sizeof(object_id), "%s_%s_%s_%s", mqtt_basename_, device_name, EMSdevice::tag_to_string(tag).c_str(), read_flash_string(en_name).c_str());
snprintf(object_id, sizeof(object_id), "%s_%s_%s", device_name, EMSdevice::tag_to_string(tag).c_str(), read_flash_string(en_name).c_str());
snprintf(object_id, sizeof(object_id), "%s_%s_%s", device_name, EMSdevice::tag_to_string(tag).c_str(), en_name);
} else {
// snprintf(object_id, sizeof(object_id), "%s_%s_%s", mqtt_basename_, device_name, read_flash_string(en_name).c_str());
snprintf(object_id, sizeof(object_id), "%s_%s", device_name, read_flash_string(en_name).c_str());
snprintf(object_id, sizeof(object_id), "%s_%s", device_name, en_name);
}
doc["object_id"] = object_id;
@@ -1137,7 +1130,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
if (is_nested()) {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", new_entity);
} else {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", read_flash_string(entity).c_str());
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity);
}
doc["val_tpl"] = val_tpl;
@@ -1147,9 +1140,9 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
// and has no unit of measure or icon
if (type == DeviceValueType::BOOL) {
char result[10];
doc[F("payload_on")] = Helpers::render_boolean(result, true);
doc[F("payload_off")] = Helpers::render_boolean(result, false);
doc[sc_ha] = F_(measurement);
doc[("payload_on")] = Helpers::render_boolean(result, true);
doc[("payload_off")] = Helpers::render_boolean(result, false);
doc[sc_ha] = F_(measurement);
} else {
// always set the uom
if (uom != DeviceValueUOM::NONE) {
@@ -1169,11 +1162,11 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
case DeviceValueUOM::DEGREES:
case DeviceValueUOM::DEGREES_R:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = F("temperature"); // no icon needed
doc[dc_ha] = "temperature"; // no icon needed
break;
case DeviceValueUOM::PERCENT:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = F("power_factor"); // no icon needed
doc[dc_ha] = "power_factor"; // no icon needed
break;
case DeviceValueUOM::SECONDS:
case DeviceValueUOM::MINUTES:
@@ -1198,11 +1191,11 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
} else {
doc[sc_ha] = F_(measurement);
}
doc[dc_ha] = F("energy"); // no icon needed
doc[dc_ha] = "energy"; // no icon needed
break;
case DeviceValueUOM::KWH:
doc[sc_ha] = F_(total_increasing);
doc[dc_ha] = F("energy"); // no icon needed
doc[dc_ha] = "energy"; // no icon needed
break;
case DeviceValueUOM::UA:
doc[ic_ha] = F_(iconua);
@@ -1210,16 +1203,16 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
break;
case DeviceValueUOM::BAR:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = F("pressure");
doc[dc_ha] = "pressure";
break;
case DeviceValueUOM::W:
case DeviceValueUOM::KW:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = F("power");
doc[dc_ha] = "power";
break;
case DeviceValueUOM::DBM:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = F("signal_strength");
doc[dc_ha] = "signal_strength";
break;
case DeviceValueUOM::NONE:
// for device entities which have numerical values, with no UOM
@@ -1248,7 +1241,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
publish_ha(topic, doc.as<JsonObject>());
}
void Mqtt::publish_ha_climate_config(uint8_t tag, bool has_roomtemp, bool remove) {
void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint16_t max) {
uint8_t hc_num = tag - DeviceValueTAG::TAG_HC1 + 1;
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
@@ -1321,8 +1314,8 @@ void Mqtt::publish_ha_climate_config(uint8_t tag, bool has_roomtemp, bool remove
doc["curr_temp_tpl"] = currtemp_s;
}
doc["min_temp"] = Helpers::render_value(min_s, (uint32_t)5, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
doc["max_temp"] = Helpers::render_value(max_s, (uint32_t)30, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
doc["max_temp"] = Helpers::render_value(max_s, max, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
doc["temp_step"] = "0.5";
// the HA climate component only responds to auto, heat and off
@@ -1348,11 +1341,13 @@ std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
return EMSdevice::tag_to_mqtt(tag);
}
std::string topic = EMSdevice::device_type_2_device_name(device_type);
// if there is a tag add it
if (!EMSdevice::tag_to_mqtt(tag).empty() && ((tag == DeviceValueTAG::TAG_BOILER_DATA_WW) || (!is_nested() && tag >= DeviceValueTAG::TAG_HC1))) {
return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag);
return topic + "_data_" + EMSdevice::tag_to_mqtt(tag);
} else {
return EMSdevice::device_type_2_device_name(device_type) + "_data";
return topic + "_data";
}
}

View File

@@ -79,36 +79,35 @@ class Mqtt {
static void resubscribe();
static void publish(const std::string & topic, const std::string & payload);
static void publish(const __FlashStringHelper * topic, const char * payload);
static void publish(const char * topic, const char * payload);
static void publish(const std::string & topic, const JsonObject & payload);
static void publish(const __FlashStringHelper * topic, const JsonObject & payload);
static void publish(const __FlashStringHelper * topic, const std::string & payload);
static void publish(const char * topic, const JsonObject & payload);
static void publish(const char * topic, const std::string & payload);
static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
static void publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain);
static void publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain);
static void publish_ha(const std::string & topic, const JsonObject & payload);
static void publish_ha(const __FlashStringHelper * topic, const JsonObject & payload);
static void publish_ha(const std::string & topic);
static void publish_retain(const char * topic, const std::string & payload, bool retain);
static void publish_retain(const char * topic, const JsonObject & payload, bool retain);
static void publish_ha(const char * topic, const JsonObject & payload);
static void publish_ha(const char * topic);
static void
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 std::string & fullname,
const __FlashStringHelper * const en_name,
const uint8_t device_type,
const __FlashStringHelper * const entity,
const uint8_t uom,
const bool remove,
const bool has_cmd,
const __FlashStringHelper * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const JsonObject & dev_json);
static void publish_ha_sensor_config(uint8_t type,
uint8_t tag,
const char * const fullname,
const char * const en_name,
const uint8_t device_type,
const char * const entity,
const uint8_t uom,
const bool remove,
const bool has_cmd,
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const JsonObject & dev_json);
static void publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelper * name, const __FlashStringHelper * entity, const uint8_t uom);
static void publish_ha_climate_config(uint8_t tag, bool has_roomtemp, bool remove = false);
static void publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
static void publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint16_t max = 30);
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
static void show_mqtt(uuid::console::Shell & shell);
@@ -167,6 +166,14 @@ class Mqtt {
return mqtt_publish_fails_;
}
static uint32_t publish_queued() {
return mqtt_messages_.size();
}
static uint8_t connect_count() {
return connectcount_;
}
static void reset_mqtt();
static bool is_nested() {
@@ -178,7 +185,7 @@ class Mqtt {
}
static bool publish_single() {
return publish_single_;
return mqtt_enabled_ && publish_single_;
}
static bool publish_single2cmd() {
@@ -190,7 +197,7 @@ class Mqtt {
}
static bool ha_enabled() {
return ha_enabled_;
return mqtt_enabled_ && ha_enabled_;
}
static void ha_enabled(bool ha_enabled) {

View File

@@ -57,7 +57,7 @@ void Shower::loop() {
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
if (!shower_state_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) {
set_shower_state(true);
LOG_DEBUG(F("[Shower] hot water still running, starting shower timer"));
LOG_DEBUG("[Shower] hot water still running, starting shower timer");
}
// check if the shower has been on too long
else if ((time_now - timer_start_) > shower_alert_trigger_) {
@@ -82,8 +82,8 @@ void Shower::loop() {
char s[50];
snprintf(s, 50, "%d minutes and %d seconds", (uint8_t)(duration_ / 60000), (uint8_t)((duration_ / 1000) % 60));
doc["duration"] = s;
Mqtt::publish(F("shower_data"), doc.as<JsonObject>());
LOG_DEBUG(F("[Shower] finished with duration %d"), duration_);
Mqtt::publish("shower_data", doc.as<JsonObject>());
LOG_DEBUG("[Shower] finished with duration %d", duration_);
}
}
@@ -109,7 +109,7 @@ void Shower::loop() {
// turn back on the hot water for the shower
void Shower::shower_alert_stop() {
if (doing_cold_shot_) {
LOG_DEBUG(F("Shower Alert stopped"));
LOG_DEBUG("Shower Alert stopped");
(void)Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true");
doing_cold_shot_ = false;
}
@@ -117,7 +117,7 @@ void Shower::shower_alert_stop() {
// turn off hot water to send a shot of cold
void Shower::shower_alert_start() {
if (shower_alert_) {
LOG_DEBUG(F("Shower Alert started"));
LOG_DEBUG("Shower Alert started");
(void)Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
doing_cold_shot_ = true;
alert_timer_start_ = uuid::get_uptime(); // timer starts now
@@ -144,22 +144,22 @@ void Shower::set_shower_state(bool state, bool force) {
// always publish as a string
char s[7];
Mqtt::publish(F("shower_active"), Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
Mqtt::publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
// send out HA MQTT Discovery config topic
if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) {
ha_configdone_ = true;
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
doc["name"] = FJSON("Shower Active");
doc["uniq_id"] = FJSON("shower_active");
doc["name"] = "Shower Active";
doc["uniq_id"] = "shower_active";
doc["~"] = Mqtt::base();
doc["stat_t"] = FJSON("~/shower_active");
doc["stat_t"] = "~/shower_active";
// always render boolean as strings for HA
char result[10];
doc[F("payload_on")] = Helpers::render_boolean(result, true);
doc[F("payload_off")] = Helpers::render_boolean(result, false);
doc[("payload_on")] = Helpers::render_boolean(result, true);
doc[("payload_off")] = Helpers::render_boolean(result, false);
JsonObject dev = doc.createNestedObject("dev");
JsonArray ids = dev.createNestedArray("ids");

Some files were not shown because too many files have changed in this diff Show More