From 8dd18aa24d9a8a2349a223ad66698b2b7d0c9e93 Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 7 Jun 2021 21:19:52 +0200 Subject: [PATCH] Render values in Web natively #70 --- CHANGELOG_LATEST.md | 3 +- interface/package-lock.json | 211 +++--- interface/package.json | 1 + interface/src/project/EMSESPDevicesForm.tsx | 161 ++--- interface/src/project/EMSESPtypes.ts | 55 +- lib/ESPAsyncWebServer/AsyncJson.h | 154 ++--- lib_standalone/AsyncJson.h | 46 ++ lib_standalone/ESPAsyncWebServer.h | 6 +- mock-api/package-lock.json | 14 + mock-api/package.json | 1 + mock-api/server.js | 670 ++++++++++++-------- src/WebDevicesService.cpp | 33 +- src/devices/boiler.cpp | 46 +- src/devices/mixer.cpp | 4 +- src/devices/solar.cpp | 34 +- src/devices/switch.cpp | 2 +- src/devices/thermostat.cpp | 6 +- src/emsdevice.cpp | 147 ++--- src/emsdevice.h | 35 +- src/locale_EN.h | 5 +- src/test/test.cpp | 30 +- src/test/test.h | 4 +- src/version.h | 2 +- 23 files changed, 946 insertions(+), 724 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 74f5eaa9e..04de250b2 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -9,12 +9,13 @@ ## Fixed - HA thermostat mode was not in sync with actual mode [#66](https://github.com/emsesp/EMS-ESP32/issues/66) +- Don't publish rssi if Wifi is disabled and ethernet is being used ## Changed - - `info` command always shows full names in API. For short names query the device or name directly, e.g. `http://ems-esp/api/boiler` - free memory is shown in kilobytes - boiler's warm water entities have ww added to the Home Assistant entity name [#67](https://github.com/emsesp/EMS-ESP32/issues/67) +- improved layout and rendering of device values in the WebUI, also the edit value screen ## Removed diff --git a/interface/package-lock.json b/interface/package-lock.json index 993ae89ab..68b45c481 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", + "@msgpack/msgpack": "^2.7.0", "@types/lodash": "^4.14.168", "@types/node": "^15.0.1", "@types/react": "^17.0.4", @@ -2175,6 +2176,14 @@ "node": ">=8.0.0" } }, + "node_modules/@msgpack/msgpack": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.7.0.tgz", + "integrity": "sha512-mlRYq9FSsOd4m+3wZWatemn3hGFZPWNJ4JQOdrir4rrMK2PyIk26idKBoUWrqF3HJJHl+5GpRU+M0wEruJwecg==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -4578,21 +4587,25 @@ } }, "node_modules/browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dependencies": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" } }, "node_modules/bser": { @@ -4835,9 +4848,13 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001208", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", - "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==" + "version": "1.0.30001235", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", + "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/capture-exit": { "version": "2.0.0", @@ -6613,9 +6630,9 @@ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" }, "node_modules/dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dependencies": { "ip": "^1.1.0", "safe-buffer": "^5.0.1" @@ -6837,9 +6854,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.712", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz", - "integrity": "sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==" + "version": "1.3.749", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz", + "integrity": "sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -13248,9 +13265,9 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, "node_modules/nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==", + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -14860,11 +14877,10 @@ } }, "node_modules/postcss-initial": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz", - "integrity": "sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", "dependencies": { - "lodash.template": "^4.5.0", "postcss": "^7.0.2" } }, @@ -15566,24 +15582,20 @@ } }, "node_modules/postcss-safe-parser/node_modules/postcss": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.9.tgz", - "integrity": "sha512-b+TmuIL4jGtCHtoLi+G/PisuIl9avxs8IZMSmlABRwNz5RLUUACrC+ws81dcomz1nRezm5YPdXiMEzBEKgYn+Q==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", + "integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==", "dependencies": { "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" }, "engines": { "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-safe-parser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-selector-matches": { @@ -16948,9 +16960,9 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "node_modules/resolve-url-loader": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", - "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz", + "integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==", "dependencies": { "adjust-sourcemap-loader": "3.0.0", "camelcase": "5.3.1", @@ -17966,6 +17978,14 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -20710,9 +20730,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dependencies": { "async-limiter": "~1.0.0" } @@ -21489,11 +21509,23 @@ } }, "node_modules/ws": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", - "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "engines": { "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/xdg-basedir": { @@ -23497,6 +23529,11 @@ "react-is": "^16.8.0 || ^17.0.0" } }, + "@msgpack/msgpack": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.7.0.tgz", + "integrity": "sha512-mlRYq9FSsOd4m+3wZWatemn3hGFZPWNJ4JQOdrir4rrMK2PyIk26idKBoUWrqF3HJJHl+5GpRU+M0wEruJwecg==" + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -25598,15 +25635,15 @@ } }, "browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "requires": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" } }, "bser": { @@ -25812,9 +25849,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001208", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", - "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==" + "version": "1.0.30001235", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", + "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==" }, "capture-exit": { "version": "2.0.0", @@ -27268,9 +27305,9 @@ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" }, "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "requires": { "ip": "^1.1.0", "safe-buffer": "^5.0.1" @@ -27477,9 +27514,9 @@ "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" }, "electron-to-chromium": { - "version": "1.3.712", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz", - "integrity": "sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==" + "version": "1.3.749", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz", + "integrity": "sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A==" }, "elliptic": { "version": "6.5.4", @@ -32656,9 +32693,9 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, "nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==" + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" }, "nanomatch": { "version": "1.2.13", @@ -33963,11 +34000,10 @@ } }, "postcss-initial": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz", - "integrity": "sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", "requires": { - "lodash.template": "^4.5.0", "postcss": "^7.0.2" } }, @@ -34569,19 +34605,14 @@ }, "dependencies": { "postcss": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.9.tgz", - "integrity": "sha512-b+TmuIL4jGtCHtoLi+G/PisuIl9avxs8IZMSmlABRwNz5RLUUACrC+ws81dcomz1nRezm5YPdXiMEzBEKgYn+Q==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", + "integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==", "requires": { "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -35699,9 +35730,9 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "resolve-url-loader": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", - "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz", + "integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==", "requires": { "adjust-sourcemap-loader": "3.0.0", "camelcase": "5.3.1", @@ -36573,6 +36604,11 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -39090,9 +39126,9 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "requires": { "async-limiter": "~1.0.0" } @@ -39543,9 +39579,10 @@ } }, "ws": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", - "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==" + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} }, "xdg-basedir": { "version": "4.0.0", diff --git a/interface/package.json b/interface/package.json index 110836b9d..5e34e6b2a 100644 --- a/interface/package.json +++ b/interface/package.json @@ -5,6 +5,7 @@ "dependencies": { "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", + "@msgpack/msgpack": "^2.7.0", "@types/lodash": "^4.14.168", "@types/node": "^15.0.1", "@types/react": "^17.0.4", diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index 394e29ffc..c5495ccdd 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -1,6 +1,11 @@ import React, { Component, Fragment } from 'react'; import { withStyles, Theme, createStyles } from '@material-ui/core/styles'; +import parseMilliseconds from 'parse-ms'; + +import { Decoder } from '@msgpack/msgpack'; +const decoder = new Decoder(); + import { Table, TableBody, @@ -37,7 +42,9 @@ import { EMSESPDevices, EMSESPDeviceData, Device, - DeviceValue + DeviceValue, + DeviceValueUOM, + DeviceValueUOM_s } from './EMSESPtypes'; import ValueForm from './ValueForm'; @@ -85,25 +92,48 @@ interface EMSESPDevicesFormState { processing: boolean; deviceData?: EMSESPDeviceData; selectedDevice?: number; - devicevalue?: DeviceValue; + edit_devicevalue?: DeviceValue; } type EMSESPDevicesFormProps = RestFormProps & AuthenticatedContextProps & WithWidthProps; -function formatTemp(t: string) { - if (t == null) { - return 'n/a'; +export const formatDuration = (duration_min: number) => { + const { days, hours, minutes } = parseMilliseconds(duration_min * 60000); + let formatted = ''; + if (days) { + formatted += pluralize(days, 'day'); } - return t + ' °C'; -} + if (hours) { + formatted += pluralize(hours, 'hour'); + } + if (minutes) { + formatted += pluralize(minutes, 'minute'); + } + return formatted; +}; -function formatUnit(u: string) { - if (u == null) { - return u; +const pluralize = (count: number, noun: string, suffix = 's') => + ` ${count} ${noun}${count !== 1 ? suffix : ''} `; + +function formatValue(value: any, uom: number) { + switch (uom) { + case DeviceValueUOM.HOURS: + return formatDuration(value * 60); + case DeviceValueUOM.MINUTES: + return formatDuration(value); + case DeviceValueUOM.NONE: + return value; + case DeviceValueUOM.NUM: + return new Intl.NumberFormat().format(value); + case DeviceValueUOM.BOOLEAN: + return value ? 'on' : 'off'; + default: + return ( + new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom] + ); } - return ' ' + u; } class EMSESPDevicesForm extends Component< @@ -119,25 +149,26 @@ class EMSESPDevicesForm extends Component< event: React.ChangeEvent ) => { this.setState({ - devicevalue: { - ...this.state.devicevalue!, + edit_devicevalue: { + ...this.state.edit_devicevalue!, [name]: extractEventValue(event) } }); }; cancelEditingValue = () => { - this.setState({ - devicevalue: undefined - }); + this.setState({ edit_devicevalue: undefined }); }; doneEditingValue = () => { - const { devicevalue } = this.state; + const { edit_devicevalue, selectedDevice } = this.state; redirectingAuthorizedFetch(WRITE_VALUE_ENDPOINT, { method: 'POST', - body: JSON.stringify({ devicevalue: devicevalue }), + body: JSON.stringify({ + id: selectedDevice, + devicevalue: edit_devicevalue + }), headers: { 'Content-Type': 'application/json' } @@ -165,23 +196,13 @@ class EMSESPDevicesForm extends Component< }); }); - if (devicevalue) { - this.setState({ - devicevalue: undefined - }); + if (edit_devicevalue) { + this.setState({ edit_devicevalue: undefined }); } }; - sendCommand = (i: number) => { - this.setState({ - devicevalue: { - id: this.state.selectedDevice!, - data: this.state.deviceData?.data[i]!, - uom: this.state.deviceData?.data[i + 1]!, - name: this.state.deviceData?.data[i + 2]!, - cmd: this.state.deviceData?.data[i + 3]! - } - }); + sendCommand = (dv: DeviceValue) => { + this.setState({ edit_devicevalue: dv }); }; noDevices = () => { @@ -290,7 +311,7 @@ class EMSESPDevicesForm extends Component< {sensorData.id} - {formatTemp(sensorData.temp)} + {formatValue(sensorData.temp, DeviceValueUOM.DEGREES)} ))} @@ -318,7 +339,7 @@ class EMSESPDevicesForm extends Component< > Confirm Scan Devices - Are you sure you want to initiate a scan on the EMS bus for all new + Are you sure you want to start a scan on the EMS bus for all new devices? @@ -384,11 +405,12 @@ class EMSESPDevicesForm extends Component< }) .then((response) => { if (response.status === 200) { - return response.json(); + return response.arrayBuffer(); } throw Error('Unexpected response code: ' + response.status); }) - .then((json) => { + .then((arrayBuffer) => { + const json: any = decoder.decode(arrayBuffer); this.setState({ deviceData: json }); }) .catch((error) => { @@ -429,40 +451,33 @@ class EMSESPDevicesForm extends Component< > - {deviceData.data.map((item, i) => { - if (i % 4) { - return null; - } else { - return ( - - - {deviceData.data[i + 3] && me.admin && ( - - this.sendCommand(i)} - > - - - - )} - - - {deviceData.data[i + 2]} - - - {deviceData.data[i]} - {formatUnit(deviceData.data[i + 1])} - - - ); - } - })} + {deviceData.data.map((item, i) => ( + + + {item.c && me.admin && ( + + this.sendCommand(item)} + > + + + + )} + + + {item.n} + + + {formatValue(item.v, item.u)} + + + ))} @@ -479,7 +494,7 @@ class EMSESPDevicesForm extends Component< } render() { - const { devicevalue } = this.state; + const { edit_devicevalue } = this.state; return (

@@ -509,9 +524,9 @@ class EMSESPDevicesForm extends Component< {this.renderScanDevicesDialog()} - {devicevalue && ( + {edit_devicevalue && ( getRoot(); - root["key1"] = "key number one"; - JsonObject& nested = root.createNestedObject("nested"); - nested["key1"] = "key number one"; - - response->setLength(); - request->send(response); - }); - - -------------------- - - Async Request to use with ArduinoJson and AsyncWebServer - Written by Arsène von Wyss (avonwyss) - - Example - - AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint"); - handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { - JsonObject& jsonObj = json.as(); - // ... - }); - server.addHandler(handler); - */ + #ifndef ASYNC_JSON_H_ #define ASYNC_JSON_H_ #include #include #include -#if ARDUINOJSON_VERSION_MAJOR == 5 -#define ARDUINOJSON_5_COMPATIBILITY -#else #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 -#endif constexpr const char * JSON_MIMETYPE = "application/json"; /* * Json Response - * */ + */ class ChunkPrint : public Print { private: @@ -82,30 +50,14 @@ class ChunkPrint : public Print { } }; -class AsyncJsonResponse : public AsyncAbstractResponse { +class MsgpackAsyncJsonResponse : public AsyncAbstractResponse { protected: -#ifdef ARDUINOJSON_5_COMPATIBILITY - DynamicJsonBuffer _jsonBuffer; -#else DynamicJsonDocument _jsonBuffer; -#endif - - JsonVariant _root; - bool _isValid; + JsonVariant _root; + bool _isValid; public: -#ifdef ARDUINOJSON_5_COMPATIBILITY - AsyncJsonResponse(bool isArray = false) - : _isValid{false} { - _code = 200; - _contentType = JSON_MIMETYPE; - if (isArray) - _root = _jsonBuffer.createArray(); - else - _root = _jsonBuffer.createObject(); - } -#else - AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + MsgpackAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize) , _isValid{false} { _code = 200; @@ -115,9 +67,8 @@ class AsyncJsonResponse : public AsyncAbstractResponse { else _root = _jsonBuffer.createNestedObject(); } -#endif - ~AsyncJsonResponse() { + ~MsgpackAsyncJsonResponse() { } JsonVariant & getRoot() { return _root; @@ -126,12 +77,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse { return _isValid; } size_t setLength() { -#ifdef ARDUINOJSON_5_COMPATIBILITY - _contentLength = _root.measureLength(); -#else - _contentLength = measureJson(_root); -#endif - + _contentLength = measureMsgPack(_root); if (_contentLength) { _isValid = true; } @@ -144,33 +90,66 @@ class AsyncJsonResponse : public AsyncAbstractResponse { size_t _fillBuffer(uint8_t * data, size_t len) { ChunkPrint dest(data, _sentLength, len); + serializeMsgPack(_root, dest); + // serializeJson(_root, Serial); // for testing proddy + // Serial.println(); + return len; + } +}; -#ifdef ARDUINOJSON_5_COMPATIBILITY - _root.printTo(dest); -#else +class AsyncJsonResponse : public AsyncAbstractResponse { + protected: + DynamicJsonDocument _jsonBuffer; + + JsonVariant _root; + bool _isValid; + + public: + AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + : _jsonBuffer(maxJsonBufferSize) + , _isValid{false} { + _code = 200; + _contentType = JSON_MIMETYPE; + if (isArray) + _root = _jsonBuffer.createNestedArray(); + else + _root = _jsonBuffer.createNestedObject(); + } + + ~AsyncJsonResponse() { + } + JsonVariant & getRoot() { + return _root; + } + bool _sourceValid() const { + return _isValid; + } + size_t setLength() { + _contentLength = measureJson(_root); + if (_contentLength) { + _isValid = true; + } + return _contentLength; + } + + size_t getSize() { + return _jsonBuffer.size(); + } + + size_t _fillBuffer(uint8_t * data, size_t len) { + ChunkPrint dest(data, _sentLength, len); serializeJson(_root, dest); -#endif return len; } }; class PrettyAsyncJsonResponse : public AsyncJsonResponse { public: -#ifdef ARDUINOJSON_5_COMPATIBILITY - PrettyAsyncJsonResponse(bool isArray = false) - : AsyncJsonResponse{isArray} { - } -#else PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} { } -#endif size_t setLength() { -#ifdef ARDUINOJSON_5_COMPATIBILITY - _contentLength = _root.measurePrettyLength(); -#else _contentLength = measureJsonPretty(_root); -#endif if (_contentLength) { _isValid = true; } @@ -178,13 +157,9 @@ class PrettyAsyncJsonResponse : public AsyncJsonResponse { } size_t _fillBuffer(uint8_t * data, size_t len) { ChunkPrint dest(data, _sentLength, len); -#ifdef ARDUINOJSON_5_COMPATIBILITY - _root.prettyPrintTo(dest); -#else serializeJsonPretty(_root, dest); - // serializeJson(_root, Serial); // for testing + // serializeJson(_root, Serial); // for testing proddy // Serial.println(); -#endif return len; } }; @@ -204,14 +179,6 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { size_t _maxContentLength; public: -#ifdef ARDUINOJSON_5_COMPATIBILITY - AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest) - : _uri(uri) - , _method(HTTP_POST | HTTP_PUT | HTTP_PATCH) - , _onRequest(onRequest) - , _maxContentLength(16384) { - } -#else AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _uri(uri) , _method(HTTP_POST | HTTP_PUT | HTTP_PATCH) @@ -219,7 +186,6 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { , maxJsonBufferSize(maxJsonBufferSize) , _maxContentLength(16384) { } -#endif void setMethod(WebRequestMethodComposite method) { _method = method; @@ -251,17 +217,10 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { virtual void handleRequest(AsyncWebServerRequest * request) override final { if (_onRequest) { if (request->_tempObject != NULL) { -#ifdef ARDUINOJSON_5_COMPATIBILITY - DynamicJsonBuffer jsonBuffer; - JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject)); - if (json.success()) { -#else DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { JsonVariant json = jsonBuffer.as(); -#endif - _onRequest(request, json); return; } @@ -288,4 +247,5 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { return _onRequest ? false : true; } }; + #endif diff --git a/lib_standalone/AsyncJson.h b/lib_standalone/AsyncJson.h index 248a37400..28377eeee 100644 --- a/lib_standalone/AsyncJson.h +++ b/lib_standalone/AsyncJson.h @@ -87,6 +87,52 @@ class PrettyAsyncJsonResponse { } }; +class MsgpackAsyncJsonResponse { + protected: + DynamicJsonDocument _jsonBuffer; + JsonVariant _root; + bool _isValid; + + public: + MsgpackAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + : _jsonBuffer(maxJsonBufferSize) + , _isValid{false} { + if (isArray) + _root = _jsonBuffer.createNestedArray(); + else + _root = _jsonBuffer.createNestedObject(); + } + + ~MsgpackAsyncJsonResponse() { + } + + JsonVariant & getRoot() { + return _root; + } + + bool _sourceValid() const { + return _isValid; + } + + size_t setLength() { + return 0; + } + + void setContentType(const char * s) { + } + + size_t getSize() { + return _jsonBuffer.size(); + } + + size_t _fillBuffer(uint8_t * data, size_t len) { + return len; + } + + void setCode(uint16_t) { + } +}; + class AsyncJsonResponse { protected: DynamicJsonDocument _jsonBuffer; diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h index 8237c44f3..9de5dc09d 100644 --- a/lib_standalone/ESPAsyncWebServer.h +++ b/lib_standalone/ESPAsyncWebServer.h @@ -12,6 +12,7 @@ class AsyncWebServerRequest; class AsyncWebServerResponse; class AsyncJsonResponse; class PrettyAsyncJsonResponse; +class MsgpackAsyncJsonResponse; class AsyncWebParameter { private: @@ -98,6 +99,7 @@ class AsyncWebServerRequest { void send(AsyncWebServerResponse * response){}; void send(AsyncJsonResponse * response){}; void send(PrettyAsyncJsonResponse * response){}; + void send(MsgpackAsyncJsonResponse * response){}; void send(int code, const String & contentType = String(), const String & content = String()){}; void send(int code, const String & contentType, const __FlashStringHelper *){}; @@ -194,9 +196,9 @@ class AsyncWebServerResponse { virtual ~AsyncWebServerResponse(); }; -typedef std::function ArRequestHandlerFunction; +typedef std::function ArRequestHandlerFunction; typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; +typedef std::function ArBodyHandlerFunction; class AsyncWebServer { protected: diff --git a/mock-api/package-lock.json b/mock-api/package-lock.json index b1e6d5f24..b886b9aa0 100644 --- a/mock-api/package-lock.json +++ b/mock-api/package-lock.json @@ -9,10 +9,19 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@msgpack/msgpack": "^2.7.0", "express": "^4.17.1", "nodemon": "^2.0.7" } }, + "node_modules/@msgpack/msgpack": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.7.0.tgz", + "integrity": "sha512-mlRYq9FSsOd4m+3wZWatemn3hGFZPWNJ4JQOdrir4rrMK2PyIk26idKBoUWrqF3HJJHl+5GpRU+M0wEruJwecg==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1593,6 +1602,11 @@ } }, "dependencies": { + "@msgpack/msgpack": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.7.0.tgz", + "integrity": "sha512-mlRYq9FSsOd4m+3wZWatemn3hGFZPWNJ4JQOdrir4rrMK2PyIk26idKBoUWrqF3HJJHl+5GpRU+M0wEruJwecg==" + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", diff --git a/mock-api/package.json b/mock-api/package.json index b66e97de1..709e219bc 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -11,6 +11,7 @@ "author": "proddy", "license": "ISC", "dependencies": { + "@msgpack/msgpack": "^2.7.0", "express": "^4.17.1", "nodemon": "^2.0.7" } diff --git a/mock-api/server.js b/mock-api/server.js index f0bb29060..aac713f1c 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -1,6 +1,9 @@ const express = require('express') const path = require('path') +const msgpack = require('@msgpack/msgpack') +// import { encode } from "@msgpack/msgpack"; + const app = express() const port = process.env.PORT || 3080 @@ -284,7 +287,10 @@ const emsesp_alldevices = { version: '01.03', }, ], - sensors: [], + sensors: [ + { no: 1, id: '28-233D-9497-0C03', temp: '25.7' }, + { no: 2, id: '28-243D-7437-1E3A', temp: '26.1' }, + ], } const emsesp_status = { status: 0, @@ -293,273 +299,408 @@ const emsesp_status = { rx_quality: 100, tx_quality: 100, } + const emsesp_devicedata_1 = { name: 'Thermostat: RC20/Moduline 300', data: [ - '16:28:21 01/04/2021', - '', - 'date/time', - 'datetime', - '(0)', - '', - 'error code', - '', - 15, - '°C', - '(hc1) setpoint room temperature', - 'temp', - 20.5, - '°C', - '(hc1) current room temperature', - '', - 'auto', - '', - '(hc1) mode', - 'mode', + { + v: '(0)', + u: 0, + n: 'error code', + c: '', + }, + { + v: '14:54:39 06/06/2021', + u: 0, + n: 'date/time', + c: '', + }, + { + v: 18, + u: 1, + n: '(hc1) selected room temperature', + c: 'hc1/seltemp', + }, + { + v: 22.6, + u: 1, + n: '(hc1) current room temperature', + c: '', + }, + { + v: 'auto', + u: 0, + n: '(hc1) mode', + c: 'hc1/mode', + }, ], } + const emsesp_devicedata_2 = { name: 'Boiler: Nefit GBx72/Trendline/Cerapur/Greenstar Si/27i', data: [ - 'off', - '', - 'heating active', - '', - 'off', - '', - 'warm water active', - '', - 5, - '°C', - 'selected flow temperature', - 'selflowtemp', - 0, - '%', - 'burner selected max power', - '', - 0, - '%', - 'heating pump modulation', - '', - 42.7, - '°C', - 'current flow temperature', - '', - 39, - '°C', - 'return temperature', - '', - 1.2, - 'bar', - 'system pressure', - '', - 45.3, - '°C', - 'max boiler temperature', - '', - 'off', - '', - 'gas', - '', - 0, - 'uA', - 'flame current', - '', - 'off', - '', - 'heating pump', - '', - 'off', - '', - 'fan', - '', - 'off', - '', - 'ignition', - '', - 'on', - '', - 'heating activated', - '', - 75, - '°C', - 'heating temperature', - '', - 90, - '%', - 'burner pump max power', - '', - 55, - '%', - 'burner pump min power', - '', - 1, - null, - 'pump delay', - '', - 10, - null, - 'burner min period', - '', - 0, - '%', - 'burner min power', - '', - 75, - '%', - 'burner max power', - '', - -6, - '°C', - 'hysteresis on temperature', - '', - 6, - '°C', - 'hysteresis off temperature', - '', - 0, - '%', - 'burner current power', - '', - 295740, - '', - 'burner # starts', - '', - '344 days 2 hours 8 minutes', - null, - 'total burner operating time', - '', - '279 days 11 hours 55 minutes', - null, - 'total heat operating time', - '', - '2946 days 19 hours 8 minutes', - null, - 'total UBA operating time', - '', - '1C(210) 06.06.2020 12:07', - '', - 'last error code', - '', - '0H', - '', - 'service code', - '', - 203, - '', - 'service code number', - '', - '01.01.2012', - '', - 'maintenance set date', - '', - 'off', - '', - 'maintenance scheduled', - '', - 6000, - 'hours', - 'maintenance set time', - '', - 60, - '°C', - '(warm water) selected temperature', - '', - 62, - '°C', - '(warm water) set temperature', - '', - 'flow', - '', - '(warm water) type', - '', - 'hot', - '', - '(warm water) comfort', - '', - 40, - '', - '(warm water) flow temperature offset', - '', - 100, - '%', - '(warm water) max power', - '', - 'off', - '', - '(warm water) circulation pump available', - '', - '3-way valve', - '', - '(warm water) charging type', - '', - 70, - '°C', - '(warm water) disinfection temperature', - '', - 'off', - '', - '(warm water) circulation pump freq', - '', - 'off', - '', - '(warm water) circulation active', - '', - 34.7, - '°C', - '(warm water) current intern temperature', - '', - 0, - 'l/min', - '(warm water) current tap water flow', - '', - 34.6, - '°C', - '(warm water) storage intern temperature', - '', - 'on', - '', - '(warm water) activated', - '', - 'off', - '', - '(warm water) one time charging', - '', - 'off', - '', - '(warm water) disinfecting', - '', - 'off', - '', - '(warm water) charging', - '', - 'off', - '', - '(warm water) recharging', - '', - 'on', - '', - '(warm water) temperature ok', - '', - 'off', - '', - '(warm water) active', - '', - 'on', - '', - '(warm water) heating', - '', - 262387, - '', - '(warm water) # starts', - '', - '64 days 14 hours 13 minutes', - null, - '(warm water) active time', - '', + { + v: false, + u: 16, + n: 'heating active', + c: '', + }, + { + v: false, + u: 16, + n: 'warm water active', + c: '', + }, + { + v: 5, + u: 1, + n: 'selected flow temperature', + c: 'selflowtemp', + }, + { + v: 0, + u: 2, + n: 'burner selected max power', + c: 'selburnpow', + }, + { + v: 0, + u: 2, + n: 'heating pump modulation', + c: '', + }, + { + v: 51, + u: 1, + n: 'current flow temperature', + c: '', + }, + { + v: 49.8, + u: 1, + n: 'return temperature', + c: '', + }, + { + v: 1.1, + u: 9, + n: 'system pressure', + c: '', + }, + { + v: 52.7, + u: 1, + n: 'boiler temperature', + c: '', + }, + { + v: false, + u: 16, + n: 'gas', + c: '', + }, + { + v: 0, + u: 8, + n: 'flame current', + c: '', + }, + { + v: false, + u: 16, + n: 'heating pump', + c: '', + }, + { + v: false, + u: 16, + n: 'fan', + c: '', + }, + { + v: false, + u: 16, + n: 'ignition', + c: '', + }, + { + v: true, + u: 16, + n: 'heating activated', + c: 'heatingactivated', + }, + { + v: 75, + u: 1, + n: 'heating temperature', + c: 'heatingtemp', + }, + { + v: 90, + u: 2, + n: 'burner pump max power', + c: 'pumpmodmax', + }, + { + v: 55, + u: 2, + n: 'burner pump min power', + c: 'pumpmodmin', + }, + { + v: 1, + u: 7, + n: 'pump delay', + c: 'pumpdelay', + }, + { + v: 10, + u: 7, + n: 'burner min period', + c: 'burnminperiod', + }, + { + v: 0, + u: 2, + n: 'burner min power', + c: 'burnminpower', + }, + { + v: 75, + u: 2, + n: 'burner max power', + c: 'burnmaxpower', + }, + { + v: -6, + u: 1, + n: 'hysteresis on temperature', + c: 'boilhyston', + }, + { + v: 6, + u: 1, + n: 'hysteresis off temperature', + c: 'boilhystoff', + }, + { + v: 0, + u: 2, + n: 'burner current power', + c: '', + }, + { + v: 303226, + u: 15, + n: '# burner starts', + c: '', + }, + { + v: 510634, + u: 7, + n: 'total burner operating time', + c: '', + }, + { + v: 415235, + u: 7, + n: 'total heat operating time', + c: '', + }, + { + v: 4338730, + u: 7, + n: 'total UBA operating time', + c: '', + }, + { + v: '1C(210) 06.06.2020 12:07', + u: 0, + n: 'last error code', + c: '', + }, + { + v: '0H', + u: 0, + n: 'service code', + c: '', + }, + { + v: 203, + u: 0, + n: 'service code number', + c: '', + }, + { + v: '01.01.2012', + u: 0, + n: 'maintenance set date', + c: '', + }, + { + v: 'off', + u: 0, + n: 'maintenance scheduled', + c: 'maintenance', + }, + { + v: 6000, + u: 6, + n: 'maintenance set time', + c: '', + }, + { + v: 60, + u: 1, + n: '(ww) selected temperature', + c: '', + }, + { + v: 62, + u: 1, + n: '(ww) set temperature', + c: 'wwsettemp', + }, + { + v: 'flow', + u: 0, + n: '(ww) type', + c: '', + }, + { + v: 'hot', + u: 0, + n: '(ww) comfort', + c: 'wwcomfort', + }, + { + v: 40, + u: 0, + n: '(ww) flow temperature offset', + c: 'wwflowtempoffset', + }, + { + v: 100, + u: 2, + n: '(ww) max power', + c: 'wwmaxpower', + }, + { + v: false, + u: 16, + n: '(ww) circulation pump available', + c: 'wwcircpump', + }, + { + v: 'charge pump', + u: 0, + n: '(ww) charging type', + c: '', + }, + { + v: 70, + u: 1, + n: '(ww) disinfection temperature', + c: 'wwdisinfectiontemp', + }, + { + v: 'off', + u: 0, + n: '(ww) circulation pump frequency', + c: 'wwcircmode', + }, + { + v: false, + u: 16, + n: '(ww) circulation active', + c: 'wwcirc', + }, + { + v: 44.4, + u: 1, + n: '(ww) current intern temperature', + c: '', + }, + { + v: 0, + u: 3, + n: '(ww) current tap water flow', + c: '', + }, + { + v: 44.4, + u: 1, + n: '(ww) storage intern temperature', + c: '', + }, + { + v: true, + u: 16, + n: '(ww) activated', + c: 'wwactivated', + }, + { + v: false, + u: 16, + n: '(ww) one time charging', + c: 'wwonetime', + }, + { + v: false, + u: 16, + n: '(ww) disinfecting', + c: '', + }, + { + v: false, + u: 16, + n: '(ww) charging', + c: '', + }, + { + v: false, + u: 16, + n: '(ww) recharging', + c: '', + }, + { + v: true, + u: 16, + n: '(ww) temperature ok', + c: '', + }, + { + v: false, + u: 16, + n: '(ww) active', + c: '', + }, + { + v: true, + u: 16, + n: '(ww) heating', + c: '', + }, + { + v: 268671, + u: 15, + n: '(ww) # starts', + c: '', + }, + { + v: 95399, + u: 7, + n: '(ww) active time', + c: '', + }, ], } +const emsesp_devicedata_3 = { + name: 'Controller: BC1', + data: [], +} + // NETWORK app.get(NETWORK_STATUS_ENDPOINT, (req, res) => { res.json(network_status) @@ -672,16 +813,27 @@ app.get(EMSESP_STATUS_ENDPOINT, (req, res) => { app.post(EMSESP_DEVICEDATA_ENDPOINT, (req, res) => { const id = req.body.id if (id == 1) { - res.json(emsesp_devicedata_1) + const encoded = msgpack.encode(emsesp_devicedata_1) + res.write(encoded, 'binary') + res.end(null, 'binary') } if (id == 2) { - res.json(emsesp_devicedata_2) + const encoded = msgpack.encode(emsesp_devicedata_2) + res.write(encoded, 'binary') + res.end(null, 'binary') + } + if (id == 3) { + const encoded = msgpack.encode(emsesp_devicedata_3) + res.write(encoded, 'binary') + res.end(null, 'binary') } }) app.post(WRITE_VALUE_ENDPOINT, (req, res) => { const devicevalue = req.body.devicevalue + const id = req.body.id + console.log(id) console.log(devicevalue) res.sendStatus(200) diff --git a/src/WebDevicesService.cpp b/src/WebDevicesService.cpp index 9301ea5d3..f5ed5afab 100644 --- a/src/WebDevicesService.cpp +++ b/src/WebDevicesService.cpp @@ -26,7 +26,7 @@ WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * : _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) , _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) { + securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); @@ -83,9 +83,10 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) { } // The unique_id is the unique record ID from the Web table to identify which device to load +// Compresses the JSON using MsgPack https://msgpack.org/index.html void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { - AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); + MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { if (emsdevice->unique_id() == json["id"]) { @@ -108,27 +109,20 @@ void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant // takes a command and its data value from a specific Device, from the Web void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant & json) { - // only issue commands if the API is enabled - EMSESP::webSettingsService.read([&](WebSettings & settings) { - if (!settings.notoken_api) { - request->send(403); // forbidden error - return; - } - }); - if (json.is()) { JsonObject dv = json["devicevalue"]; + uint8_t id = json["id"]; // using the unique ID from the web find the real device type for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { - if (emsdevice->unique_id() == dv["id"].as()) { - const char * cmd = dv["cmd"]; + if (emsdevice->unique_id() == id) { + const char * cmd = dv["c"]; uint8_t device_type = emsdevice->device_type(); bool ok = false; char s[10]; // the data could be in any format, but we need string - JsonVariant data = dv["data"]; + JsonVariant data = dv["v"]; if (data.is()) { ok = Command::call(device_type, cmd, data.as()); } else if (data.is()) { @@ -139,16 +133,17 @@ void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant ok = Command::call(device_type, cmd, data.as() ? "true" : "false"); } - if (ok) { - request->send(200); - } - return; // found device, quit + // send "Write command sent to device" or "Write command failed" + AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204); + request->send(response); + return; } } } - - request->send(204); // no content error } + + AsyncWebServerResponse * response = request->beginResponse(204); // Write command failed + request->send(response); } } // namespace emsesp \ No newline at end of file diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 089bfc3e1..081551a68 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -86,7 +86,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const } // MQTT commands for boiler topic register_device_value( - TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_tapwarmwater_activated)); + TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::ENUM, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset)); // add values @@ -96,8 +96,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); id_ = product_id; - register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::NONE); - register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::NONE); + register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::BOOLEAN); register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp)); register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_burn_power)); register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, FL_(heatingPumpMod), DeviceValueUOM::PERCENT); @@ -109,13 +109,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), FL_(sysPress), DeviceValueUOM::BAR); register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), FL_(boilTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), FL_(exhaustTemp), DeviceValueUOM::DEGREES); - register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::NONE); + register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::BOOLEAN); register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), FL_(flameCurr), DeviceValueUOM::UA); - register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::NONE); - register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::NONE); - register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::NONE); + register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::BOOLEAN); register_device_value( - TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_heating_activated)); + TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heating_activated)); register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp)); register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump)); register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump)); @@ -190,8 +190,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value( TAG_DEVICE_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, FL_(wWMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower)); register_device_value( - TAG_DEVICE_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_pump)); - register_device_value(TAG_DEVICE_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE); + TAG_DEVICE_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation_pump)); + register_device_value(TAG_DEVICE_DATA_WW, &wWChargeType_, DeviceValueType::ENUM, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE); register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, @@ -206,21 +206,21 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_mode)); - register_device_value(TAG_DEVICE_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation)); + register_device_value(TAG_DEVICE_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation)); register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp2), DeviceValueUOM::DEGREES); register_device_value(TAG_DEVICE_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wWCurFlow), DeviceValueUOM::LMIN); register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp1), DeviceValueUOM::DEGREES); register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp2), DeviceValueUOM::DEGREES); register_device_value( - TAG_DEVICE_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_activated)); - register_device_value(TAG_DEVICE_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_onetime)); - register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::NONE); - register_device_value(TAG_DEVICE_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::NONE); - register_device_value(TAG_DEVICE_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::NONE); - register_device_value(TAG_DEVICE_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::NONE); - register_device_value(TAG_DEVICE_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::NONE); - register_device_value(TAG_DEVICE_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::NONE); + TAG_DEVICE_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_activated)); + register_device_value(TAG_DEVICE_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_onetime)); + register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_DEVICE_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_DEVICE_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_DEVICE_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_DEVICE_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_DEVICE_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::BOOLEAN); register_device_value(TAG_DEVICE_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wWSetPumpPower), DeviceValueUOM::PERCENT); register_device_value(TAG_DEVICE_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(mixerTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_DEVICE_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(tankMiddleTemp), DeviceValueUOM::DEGREES); @@ -311,7 +311,6 @@ void Boiler::check_active(const bool force) { } // 0x33 -// Boiler(0x08) -> Me(0x0B), UBAParameterWW(0x33), data: 08 FF 30 FB FF 28 FF 07 46 00 00 void Boiler::process_UBAParameterWW(std::shared_ptr telegram) { // has_update(telegram->read_bitvalue(wwEquipt_,0,3)); // 8=boiler has ww has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on @@ -322,8 +321,7 @@ void Boiler::process_UBAParameterWW(std::shared_ptr telegram) { has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous has_update(telegram->read_value(wWDisinfectionTemp_, 8)); - has_update(telegram->read_value(wWChargeType_, - 10)); // 0 = charge pump, 0xff = 3-way valve + has_update(telegram->read_bitvalue(wWChargeType_, 10, 0)); // 0 = charge pump, 0xff = 3-way valve telegram->read_value(wWComfort_, 9); if (wWComfort_ == 0x00) { @@ -1175,7 +1173,7 @@ bool Boiler::set_warmwater_circulation_pump(const char * value, const int8_t id) return true; } -// Set the mode of circulation, 1x3min, ... 6x3min, continuos +// Set the mode of circulation, 1x3min, ... 6x3min, continuous // true = on, false = off bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id) { int v = 0; @@ -1187,7 +1185,7 @@ bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id) if (v < 7) { LOG_INFO(F("Setting warm water circulation mode %dx3min"), v); } else if (v == 7) { - LOG_INFO(F("Setting warm water circulation mode continuos")); + LOG_INFO(F("Setting warm water circulation mode continuous")); } else { LOG_WARNING(F("Set warm water circulation mode: Invalid value")); return false; diff --git a/src/devices/mixer.cpp b/src/devices/mixer.cpp index 7fc481bd0..8abbe699a 100644 --- a/src/devices/mixer.cpp +++ b/src/devices/mixer.cpp @@ -60,7 +60,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES); register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES); - register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE); + register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::BOOLEAN); register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT); register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES); } else { @@ -69,7 +69,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s uint8_t tag = TAG_WWC1 + hc_ - 1; register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp), DeviceValueUOM::DEGREES); - register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::NONE); + register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::BOOLEAN); register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(wwTempStatus), DeviceValueUOM::NONE); } diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index 2d9cf459c..2744612a2 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -80,12 +80,11 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(collectorTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), FL_(tankBottomTemp), DeviceValueUOM::DEGREES); - register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::NONE); + register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::BOOLEAN); register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, FL_(pumpWorkTime), DeviceValueUOM::MINUTES); register_device_value(TAG_NONE, &tankMaxTemp_, DeviceValueType::UINT, nullptr, FL_(tankMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TankMaxTemp)); if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) { - // register_device_value(TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp)); register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT); register_device_value(TAG_NONE, &solarPumpMinMod_, DeviceValueType::UINT, nullptr, FL_(pumpMinMod), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_PumpMinMod)); register_device_value( @@ -96,17 +95,16 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp)); register_device_value( TAG_NONE, &collectorMinTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMinTemp)); - register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE); - // register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE); + register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN); register_device_value(TAG_NONE, &solarPower_, DeviceValueType::ULONG, nullptr, FL_(solarPower), DeviceValueUOM::W); register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH); register_device_value(TAG_NONE, &maxFlow_, DeviceValueType::UINT, FL_(div10), FL_(maxFlow), DeviceValueUOM::LMIN, MAKE_CF_CB(set_SM10MaxFlow)); register_device_value(TAG_DEVICE_DATA_WW, &wwMinTemp_, DeviceValueType::UINT, nullptr, FL_(wwMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMinTemp)); - register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE, MAKE_CF_CB(set_solarEnabled)); + register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_solarEnabled)); } if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) { - register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE); - register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE); + register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::BOOLEAN); register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH); } if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) { @@ -119,9 +117,9 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(tank2BottomTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES); register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, FL_(cylinderPumpModulation), DeviceValueUOM::PERCENT); - register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::NONE); - register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE); - register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE); + register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN); register_device_value( TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp)); register_device_value( @@ -131,12 +129,12 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH); register_device_value( - TAG_NONE, &heatTransferSystem_, DeviceValueType::BOOL, nullptr, FL_(heatTransferSystem), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatTransferSystem)); - register_device_value(TAG_NONE, &externalTank_, DeviceValueType::BOOL, nullptr, FL_(externalTank), DeviceValueUOM::NONE, MAKE_CF_CB(set_externalTank)); + TAG_NONE, &heatTransferSystem_, DeviceValueType::BOOL, nullptr, FL_(heatTransferSystem), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heatTransferSystem)); + register_device_value(TAG_NONE, &externalTank_, DeviceValueType::BOOL, nullptr, FL_(externalTank), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_externalTank)); register_device_value( - TAG_NONE, &thermalDisinfect_, DeviceValueType::BOOL, nullptr, FL_(thermalDisinfect), DeviceValueUOM::NONE, MAKE_CF_CB(set_thermalDisinfect)); - register_device_value(TAG_NONE, &heatMetering_, DeviceValueType::BOOL, nullptr, FL_(heatMetering), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatMetering)); - register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE, MAKE_CF_CB(set_solarEnabled)); + TAG_NONE, &thermalDisinfect_, DeviceValueType::BOOL, nullptr, FL_(thermalDisinfect), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_thermalDisinfect)); + register_device_value(TAG_NONE, &heatMetering_, DeviceValueType::BOOL, nullptr, FL_(heatMetering), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heatMetering)); + register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_solarEnabled)); // telegram 0x035A register_device_value( @@ -146,21 +144,21 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s DeviceValueType::BOOL, nullptr, FL_(solarPumpKick), - DeviceValueUOM::NONE, + DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_solarPumpKick)); // pump kick for vacuum collector, 00=off register_device_value(TAG_NONE, &plainWaterMode_, DeviceValueType::BOOL, nullptr, FL_(plainWaterMode), - DeviceValueUOM::NONE, + DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_plainWaterMode)); // system does not use antifreeze, 00=off register_device_value(TAG_NONE, &doubleMatchFlow_, DeviceValueType::BOOL, nullptr, FL_(doubleMatchFlow), - DeviceValueUOM::NONE, + DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_doubleMatchFlow)); // double Match Flow, 00=off // telegram 0x380 diff --git a/src/devices/switch.cpp b/src/devices/switch.cpp index 63f9f6420..3163fe89f 100644 --- a/src/devices/switch.cpp +++ b/src/devices/switch.cpp @@ -35,7 +35,7 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage)); register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); - register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE); + register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN); register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES); register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE); id_ = product_id; diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index f54231e20..d97717430 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -2306,7 +2306,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrheatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE); register_device_value( tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summermode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode)); - register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE); + register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::BOOLEAN); register_device_value( tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode)); register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); @@ -2338,8 +2338,8 @@ void Thermostat::register_device_values_hc(std::shared_ptrholidaytemp, DeviceValueType::UINT, FL_(div2), FL_(holidaytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_holidaytemp)); register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp)); - register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE); - register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday)); + register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::BOOLEAN); + register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_holiday)); register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp)); register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence)); register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp)); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index ce9c69ba2..219f16636 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -39,7 +39,8 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__ F_(kb), F_(seconds), F_(dbm), - F_(num) + F_(num), + F_(bool) }; @@ -543,7 +544,7 @@ std::string EMSdevice::get_value_uom(const char * key) { for (const auto & dv : devicevalues_) { if (dv.full_name != nullptr) { if (uuid::read_flash_string(dv.full_name) == p) { - // ignore TIME since "minutes" is already included + // ignore TIME since "minutes" is already added to the string value if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) { break; } @@ -555,37 +556,34 @@ std::string EMSdevice::get_value_uom(const char * key) { return std::string{}; // not found } -// prepare array of device values, as 3 elements serialized (name, value, uom) in array to send to Web UI -// returns number of elements -bool EMSdevice::generate_values_json_web(JsonObject & json) { +// prepare array of device values used for the Web UI +// v = value, u=uom, n=name, c=cmd +void EMSdevice::generate_values_json_web(JsonObject & json) { json["name"] = to_string_short(); JsonArray data = json.createNestedArray("data"); - uint8_t num_elements = 0; - for (const auto & dv : devicevalues_) { // ignore if full_name empty if (dv.full_name != nullptr) { + JsonObject obj; // create the object, if needed + // handle Booleans (true, false) if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { - // see if we have options for the bool's - if (dv.options_size == 2) { - data.add(*(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]); - } else { - // always render booleans as on or off - data.add(*(uint8_t *)(dv.value_p) ? F_(on) : F_(off)); - } + obj = data.createNestedObject(); + obj["v"] = *(bool *)(dv.value_p); } // handle TEXT strings else if ((dv.type == DeviceValueType::TEXT) && (Helpers::hasValue((char *)(dv.value_p)))) { - data.add((char *)(dv.value_p)); + obj = data.createNestedObject(); + obj["v"] = (char *)(dv.value_p); } // handle ENUMs else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { if (*(uint8_t *)(dv.value_p) < dv.options_size) { - data.add(dv.options[*(uint8_t *)(dv.value_p)]); + obj = data.createNestedObject(); + obj["v"] = dv.options[*(uint8_t *)(dv.value_p)]; } } @@ -596,83 +594,55 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) { // the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0; - // INT if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) { - if (divider) { - data.add(Helpers::round2(*(int8_t *)(dv.value_p), divider)); - } else { - data.add(*(int8_t *)(dv.value_p)); - } + obj = data.createNestedObject(); + obj["v"] = (divider) ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p); } else if ((dv.type == DeviceValueType::UINT) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { - if (divider) { - data.add(Helpers::round2(*(uint8_t *)(dv.value_p), divider)); - } else { - data.add(*(uint8_t *)(dv.value_p)); - } + obj = data.createNestedObject(); + obj["v"] = (divider) ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p); } else if ((dv.type == DeviceValueType::SHORT) && Helpers::hasValue(*(int16_t *)(dv.value_p))) { - if (divider) { - data.add(Helpers::round2(*(int16_t *)(dv.value_p), divider)); - } else { - data.add(*(int16_t *)(dv.value_p)); - } + obj = data.createNestedObject(); + obj["v"] = (divider) ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p); } else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) { - if (divider) { - data.add(Helpers::round2(*(uint16_t *)(dv.value_p), divider)); - } else { - data.add(*(uint16_t *)(dv.value_p)); - } + obj = data.createNestedObject(); + obj["v"] = (divider) ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p); } else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) { - if (divider) { - data.add(Helpers::round2(*(uint32_t *)(dv.value_p), divider)); - } else { - data.add(*(uint32_t *)(dv.value_p)); - } + obj = data.createNestedObject(); + obj["v"] = (divider) ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p); } else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) { uint32_t time_value = *(uint32_t *)(dv.value_p); - time_value = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60 - char time_s[40]; - snprintf_P(time_s, 40, PSTR("%d days %d hours %d minutes"), (time_value / 1440), ((time_value % 1440) / 60), (time_value % 60)); - data.add(time_s); + obj = data.createNestedObject(); + obj["v"] = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60 } } - // check if we've added a data element by comparing the size - // then add the remaining elements - uint8_t sz = data.size(); - if (sz > num_elements) { + // check if we've added a data element then add the remaining elements + if (obj.containsKey("v")) { // add the unit of measure (uom) - if (dv.uom == DeviceValueUOM::MINUTES) { - data.add(nullptr); // use null for time/date - } else { - data.add(uom_to_string(dv.uom)); - } + obj["u"] = dv.uom; // add name, prefixing the tag if it exists if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) { - data.add(dv.full_name); + obj["n"] = dv.full_name; } else { char name[50]; snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str()); - data.add(name); + obj["n"] = name; } // add the name of the Command function if it exists if (dv.has_cmd) { if (dv.tag >= DeviceValueTAG::TAG_HC1) { - data.add(tag_to_string(dv.tag) + "/" + uuid::read_flash_string(dv.short_name)); + obj["c"] = tag_to_string(dv.tag) + "/" + uuid::read_flash_string(dv.short_name); } else { - data.add(dv.short_name); + obj["c"] = dv.short_name; } } else { - data.add(""); + obj["c"] = ""; } - - num_elements = sz + 3; // increase count by 3 } } } - - return (num_elements != 0); } bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t id) { @@ -778,9 +748,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t break; case DeviceValueType::BOOL: { if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { - if (dv.options_size == 2) { - json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? dv.options[0] : dv.options[1]; - } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) { + if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) { json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off); } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF); @@ -794,10 +762,8 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t json[min] = 0; json[max] = 1; JsonArray enum_ = json.createNestedArray(F_(enum)); - if (dv.options_size == 2) { - enum_.add(dv.options[1]); - enum_.add(dv.options[0]); - } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) { + + if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) { enum_.add(F_(off)); enum_.add(F_(on)); } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { @@ -885,31 +851,26 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter // handle Booleans (true, false) if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { - // see if we have options for the bool's - if (dv.options_size == 2 && Mqtt::bool_format() != BOOL_FORMAT_10) { - json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]; + // see how to render the value depending on the setting + // when in console mode we always use on and off + if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) { + // on or off as strings + json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off); + has_value = true; + } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { + // on or off as strings + json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF); + has_value = true; + } else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) { + // true or false values (as real booleans, not strings) + json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false; has_value = true; } else { - // see how to render the value depending on the setting - // when in console mode we always use on and off - if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) { - // on or off as strings - json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off); - has_value = true; - } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { - // on or off as strings - json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF); - has_value = true; - } else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) { - // true or false values (as real booleans, not strings) - json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false; - has_value = true; - } else { - // numerical 1 or 0 - json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0; - has_value = true; - } + // numerical 1 or 0 + json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0; + has_value = true; } + } // handle TEXT strings diff --git a/src/emsdevice.h b/src/emsdevice.h index 056732c8d..a9edbab18 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -48,22 +48,23 @@ enum DeviceValueType : uint8_t { // sequence is important! enum DeviceValueUOM : uint8_t { - NONE = 0, - DEGREES, - PERCENT, - LMIN, - KWH, - WH, - HOURS, - MINUTES, - UA, - BAR, - KW, - W, - KB, - SECONDS, - DBM, - NUM + NONE = 0, // 0 + DEGREES, // 1 + PERCENT, // 2 + LMIN, // 3 + KWH, // 4 + WH, // 5 + HOURS, // 6 + MINUTES, // 7 + UA, // 8 + BAR, // 9 + KW, // 10 + W, // 11 + KB, // 12 + SECONDS, // 13 + DBM, // 14 + NUM, // 15 + BOOLEAN // 16 }; @@ -244,7 +245,7 @@ class EMSdevice { std::string get_value_uom(const char * key); bool get_value_info(JsonObject & root, const char * cmd, const int8_t id); bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool nested, const bool console = false); - bool generate_values_json_web(JsonObject & json); + void generate_values_json_web(JsonObject & json); void register_device_value(uint8_t tag, void * value_p, diff --git a/src/locale_EN.h b/src/locale_EN.h index 96542e706..ef7a90892 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -172,6 +172,7 @@ MAKE_PSTR(kb, "KB") MAKE_PSTR(seconds, "seconds") MAKE_PSTR(dbm, "dBm") MAKE_PSTR(num, " ") // this is hack so HA renders numbers correctly +MAKE_PSTR(bool, " ") // this is hack so HA renders numbers correctly // TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp // use empty string if want to suppress showing tags @@ -220,7 +221,7 @@ MAKE_PSTR_WORD(3x3min) MAKE_PSTR_WORD(4x3min) MAKE_PSTR_WORD(5x3min) MAKE_PSTR_WORD(6x3min) -MAKE_PSTR_WORD(continuos) +MAKE_PSTR_WORD(continuous) MAKE_PSTR(3wayvalve, "3-way valve") MAKE_PSTR(chargepump, "charge pump") MAKE_PSTR_WORD(hot) @@ -235,7 +236,7 @@ MAKE_PSTR_WORD(error) // boiler lists MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date)) -MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuos)) +MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuous)) MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump)) MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent)) MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer)) diff --git a/src/test/test.cpp b/src/test/test.cpp index dbe55ae24..db27da909 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -333,29 +333,30 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { run_test("boiler"); run_test("thermostat"); - // test call - DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN); - JsonObject json = doc.to(); - Command::call(EMSdevice::DeviceType::BOILER, "info", nullptr, -1, json); #if defined(EMSESP_STANDALONE) - Serial.print(COLOR_YELLOW); - if (json.size() != 0) { - serializeJson(doc, Serial); - } - shell.println(); - Serial.print(COLOR_RESET); -#endif + DynamicJsonDocument doc(8000); // some absurb high number for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { - DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN); + doc.clear(); + JsonObject json = doc.to(); + Command::call(emsdevice->device_type(), "info", nullptr, -1, json); + Serial.print(COLOR_YELLOW); + if (json.size() != 0) { + serializeJson(doc, Serial); + } + shell.println(); + Serial.print(COLOR_RESET); + + + doc.clear(); JsonObject root = doc.to(); emsdevice->generate_values_json_web(root); -#if defined(EMSESP_STANDALONE) Serial.print(COLOR_BRIGHT_MAGENTA); serializeJson(doc, Serial); + Serial.print(COLOR_RESET); Serial.println(); Serial.print("** memoryUsage="); Serial.print(doc.memoryUsage()); @@ -364,10 +365,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { Serial.print(" measureJson="); Serial.print(measureJson(doc)); Serial.println(" **"); - Serial.print(COLOR_RESET); -#endif } } +#endif return; } diff --git a/src/test/test.h b/src/test/test.h index 4c0b1edea..3d970f293 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -29,8 +29,8 @@ namespace emsesp { // #define EMSESP_DEBUG_DEFAULT "thermostat" // #define EMSESP_DEBUG_DEFAULT "solar" // #define EMSESP_DEBUG_DEFAULT "mixer" -// #define EMSESP_DEBUG_DEFAULT "web" -#define EMSESP_DEBUG_DEFAULT "general" +#define EMSESP_DEBUG_DEFAULT "web" +// #define EMSESP_DEBUG_DEFAULT "general" // #define EMSESP_DEBUG_DEFAULT "boiler" // #define EMSESP_DEBUG_DEFAULT "mqtt2" // #define EMSESP_DEBUG_DEFAULT "mqtt_nested" diff --git a/src/version.h b/src/version.h index 1e00fc7f6..050a5b450 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.1b4" +#define EMSESP_APP_VERSION "3.1.1b5"