diff --git a/.github/workflows/tagged_release.yml b/.github/workflows/tagged_release.yml index 8e169e249..9818be2af 100644 --- a/.github/workflows/tagged_release.yml +++ b/.github/workflows/tagged_release.yml @@ -31,6 +31,7 @@ jobs: cd interface npm ci npx typesafe-i18n --no-watch + sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts npm run build - name: Build firmware diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index d0b5685c1..92d278a2b 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,9 +2,14 @@ # [3.5.0] +## **IMPORTANT! BREAKING CHANGES** + +- When upgrading to v3.5 for the first time from v3.4 on a BBQKees Gateway board you will need to use the [EMS-EPS Flasher](https://github.com/emsesp/EMS-ESP-Flasher/releases) to correctly re-partition the flash. Make sure you backup the settings and customizations from the WebUI (System->Upload/Download) and restore after the upgrade. +- Since 3.5.0b11 we have added support for multiple EMS-ESPs [#759] and also renamed the HA Entity IDs. For example what was previously `sensor.boiler_actual_boiler_temperature` is now using the shortname form `sensor.boiler_boiltemp` as opposed to the English description. Unfortunately this does means any HA dashboards, automation scripts and integrations (e.g. Grafana) need to be adjusted accordingly. + ## Added -- Translations in Web UI and all device entity names (DE, NL, SE, PL, NO, ...) [#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 support for ESP32-S2 [#667](https://github.com/emsesp/EMS-ESP32/pull/667) - Add devices: Greenstar 30Ri boiler, Junkers FW500 thermostat, Buderus BC30 controller @@ -17,8 +22,9 @@ - Add min/max to customization table [#686](https://github.com/emsesp/EMS-ESP32/issues/686) - Add MD5 check [#637](https://github.com/emsesp/EMS-ESP32/issues/637) - Add more bus-ids [#673](https://github.com/emsesp/EMS-ESP32/issues/673) -- Use HA connectivity device class for Status [#751](https://github.com/emsesp/EMS-ESP32/issues/751) +- Use HA connectivity device class for Status, added boot time [#751](https://github.com/emsesp/EMS-ESP32/issues/751) - Add commands for analog sensors outputs +- Support for multiple EMS-ESPs with MQTT and HA [[#759](https://github.com/emsesp/EMS-ESP32/issues/759)] ## Fixed @@ -29,11 +35,8 @@ - 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 +- removed flash strings, to increase available heap memory - reload page after restart button is pressed - analog/dallas values command as list like ems-devices - analog/dallas HA-entities based on id - -## **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) +- MQTT Base is a mandatory field. Removed MQTT topic length from settings. diff --git a/interface/.typesafe-i18n.json b/interface/.typesafe-i18n.json index 38811b0eb..acc346bcc 100644 --- a/interface/.typesafe-i18n.json +++ b/interface/.typesafe-i18n.json @@ -1,5 +1,5 @@ { - "adapter": "react", - "baseLocale": "pl", - "$schema": "https://unpkg.com/typesafe-i18n@5.17.1/schema/typesafe-i18n.json" -} \ No newline at end of file + "adapter": "react", + "baseLocale": "pl", + "$schema": "https://unpkg.com/typesafe-i18n@5.17.1/schema/typesafe-i18n.json" +} diff --git a/interface/package-lock.json b/interface/package-lock.json index 9bd895607..59ff55056 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -12,15 +12,15 @@ "@emotion/styled": "^11.10.5", "@msgpack/msgpack": "^2.8.0", "@mui/icons-material": "^5.10.16", - "@mui/material": "^5.10.16", + "@mui/material": "^5.10.17", "@table-library/react-table-library": "4.0.23", - "@types/lodash": "^4.14.190", - "@types/node": "^18.11.9", - "@types/react": "^18.0.25", + "@types/lodash": "^4.14.191", + "@types/node": "^18.11.12", + "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", "async-validator": "^4.2.5", - "axios": "^1.2.0", + "axios": "^1.2.1", "http-proxy-middleware": "^2.0.6", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", @@ -30,12 +30,12 @@ "react-app-rewired": "^2.2.1", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", - "react-icons": "^4.6.0", - "react-router-dom": "^6.4.3", + "react-icons": "^4.7.1", + "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.17.1", - "typescript": "^4.9.3" + "typescript": "^4.9.4" }, "devDependencies": { "nodemon": "^2.0.20", @@ -66,28 +66,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", - "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", - "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.2", + "@babel/generator": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.0", "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.1", - "@babel/parser": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -128,11 +128,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", - "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "dependencies": { - "@babel/types": "^7.20.2", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -194,9 +194,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz", - "integrity": "sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", + "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", @@ -214,12 +214,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", - "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.2.1" }, "engines": { "node": ">=6.9.0" @@ -435,27 +435,27 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", - "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dependencies": { "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", - "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", "dependencies": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.0" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { "node": ">=6.9.0" @@ -475,9 +475,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", - "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -564,11 +564,11 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.2.tgz", - "integrity": "sha512-nkBH96IBmgKnbHQ5gXFrcmez+Z9S2EIDKDQGp005ROqBigc88Tky4rzCnlP/lnlj245dCEQl4/YyV0V1kYh5dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", + "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-create-class-features-plugin": "^7.20.5", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", @@ -736,13 +736,13 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1067,9 +1067,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", - "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz", + "integrity": "sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2" }, @@ -1311,12 +1311,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", - "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1355,9 +1355,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", - "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz", + "integrity": "sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2" }, @@ -1458,12 +1458,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -1760,23 +1760,23 @@ } }, "node_modules/@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", "dependencies": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", - "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", "dependencies": { "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -1796,18 +1796,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", - "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1816,9 +1816,9 @@ } }, "node_modules/@babel/types": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", - "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -3091,9 +3091,9 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.108", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.108.tgz", - "integrity": "sha512-KjzRUts2i/ODlMfywhFTqTzQl+Cr9nlDSZxJcnYjrbOV/iRyQNBTDoiFJt+XEdRi0fZBHnk74AFbnP56ehybsA==", + "version": "5.0.0-alpha.109", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.109.tgz", + "integrity": "sha512-UQxoONPI3ntzxcD/cbFHl+Lp2xsVj6HpKmU9QhUZ2kZ2K2yej2QJyU1gnADoWl/Hu94VrvwSSRnjTjR3HvXO/g==", "dependencies": { "@babel/runtime": "^7.20.1", "@emotion/is-prop-valid": "^1.2.0", @@ -3123,9 +3123,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.16.tgz", - "integrity": "sha512-eK9+olw2ZbXX+vGrtKnN01/vLP1aX0Lq0xok35bqWM1aB93Dcmky/xPNf8h31oJ/C+IzJBjZaZMEDzVZg4Qc0A==", + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.17.tgz", + "integrity": "sha512-iNwUuMA30nrN0tiEkeD3zaczv7Tk2jlZIDbXRnijAsYXkZtl/xEzQsVRIPYRDuyEz6D18vQJhV8h7gPUXEubTg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" @@ -3157,14 +3157,14 @@ } }, "node_modules/@mui/material": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.16.tgz", - "integrity": "sha512-JSHcDQQ+k30NKkCM/0KX6jq4F5LOrbFKZpS+cEl7scZWOCJpUPH5ccAT5a7O8wzrgNZ8Y9PnwzNvWBrfShpJFw==", + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.17.tgz", + "integrity": "sha512-Kuqgv1qI5HXnc/Xu426xhCGYBSKzplb+xFNLitbnIb92Qx8jmcpfNpFlDJa2kD2H6qP66rr/m4c/zMUfGX/xBQ==", "dependencies": { "@babel/runtime": "^7.20.1", - "@mui/base": "5.0.0-alpha.108", - "@mui/core-downloads-tracker": "^5.10.16", - "@mui/system": "^5.10.16", + "@mui/base": "5.0.0-alpha.109", + "@mui/core-downloads-tracker": "^5.10.17", + "@mui/system": "^5.10.17", "@mui/types": "^7.2.2", "@mui/utils": "^5.10.16", "@types/react-transition-group": "^4.4.5", @@ -3258,9 +3258,9 @@ } }, "node_modules/@mui/system": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.16.tgz", - "integrity": "sha512-OqI9B1jZ9zQ/dmoqseku4CzdEs9DbLiiMOaWxC3WeAJxM1UavlCgXz0encqm93LIlmSL7TjuHN1/rW8BJCnU8A==", + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.17.tgz", + "integrity": "sha512-UYzAOSK7uxkMsUssqrIUW3lnOuQpU8vqh4hLwfSw+GYAnQo3qjK4m4NhlDx+pFpsjjiGnr3K+vrSH+aIAMbcLg==", "dependencies": { "@babel/runtime": "^7.20.1", "@mui/private-theming": "^5.10.16", @@ -3458,9 +3458,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", - "integrity": "sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.5.tgz", + "integrity": "sha512-my0Mycd+jruq/1lQuO5LBB6WTlL/e8DTCYWp44DfMTDcXz8DcTlgF0ISaLsGewt+ctHN+yA8xMq3q/N7uWJPug==", "engines": { "node": ">=14" } @@ -3550,9 +3550,9 @@ "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" }, "node_modules/@sinonjs/commons": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", - "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dependencies": { "type-detect": "4.0.8" } @@ -3864,9 +3864,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", - "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", "dependencies": { "@babel/types": "^7.3.0" } @@ -4007,9 +4007,9 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, "node_modules/@types/lodash": { - "version": "4.14.190", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.190.tgz", - "integrity": "sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw==" + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "node_modules/@types/mime": { "version": "3.0.1", @@ -4017,9 +4017,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4052,9 +4052,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.0.25", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", - "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4184,13 +4184,13 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz", - "integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", + "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", "dependencies": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/type-utils": "5.44.0", - "@typescript-eslint/utils": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/type-utils": "5.45.1", + "@typescript-eslint/utils": "5.45.1", "debug": "^4.3.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", @@ -4230,11 +4230,11 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.44.0.tgz", - "integrity": "sha512-j8GLemAySe8oUCgILdUaT66pemdWSYcwUYG2Pb71O119hCdvkU+4q8sUTbnDg8NhlZEzSWG2N1v4IxT1kEZrGg==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.1.tgz", + "integrity": "sha512-WlXwY9dbmc0Lzu6xQOZ3yN8u/ws/1R8zPC16O217LMZJCbV2hJezqkWMUB+jMwguOJW+cukCDe92vcwwf8zwjQ==", "dependencies": { - "@typescript-eslint/utils": "5.44.0" + "@typescript-eslint/utils": "5.45.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4248,13 +4248,13 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", - "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", + "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", "dependencies": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", "debug": "^4.3.4" }, "engines": { @@ -4274,12 +4274,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", - "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", + "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", "dependencies": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0" + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4290,12 +4290,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz", - "integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", + "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", "dependencies": { - "@typescript-eslint/typescript-estree": "5.44.0", - "@typescript-eslint/utils": "5.44.0", + "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/utils": "5.45.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -4316,9 +4316,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", - "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", + "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4328,12 +4328,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", - "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", + "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", "dependencies": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4368,15 +4368,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz", - "integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", + "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -4427,11 +4427,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", - "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", + "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", "dependencies": { - "@typescript-eslint/types": "5.44.0", + "@typescript-eslint/types": "5.45.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -5030,9 +5030,9 @@ } }, "node_modules/axios": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz", - "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -5576,9 +5576,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", + "version": "1.0.30001436", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", + "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==", "funding": [ { "type": "opencollective", @@ -6233,9 +6233,9 @@ } }, "node_modules/cssdb": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.1.0.tgz", - "integrity": "sha512-Sd99PrFgx28ez4GHu8yoQIufc/70h9oYowDf4EjeIKi8mac9whxRjhM3IaMr6EllP6KKKWtJrMfN6C7T9tIWvQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz", + "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" @@ -6422,9 +6422,9 @@ } }, "node_modules/decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/dedent": { "version": "0.7.0", @@ -7004,9 +7004,9 @@ } }, "node_modules/eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "dependencies": { "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.11.6", @@ -7925,9 +7925,9 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", "dependencies": { "reusify": "^1.0.4" } @@ -8009,9 +8009,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8955,9 +8955,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", "engines": { "node": ">= 4" } @@ -11054,9 +11054,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.14.tgz", - "integrity": "sha512-9Pj7abXoW1RSTcZaL2Hk6G2XyLMlp5ECdVC/Zf2p/KBjC3srijLGgRAXOBjtFrJoIrvxdTKyKDA14bEcbxBaWw==", + "version": "17.0.16", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.16.tgz", + "integrity": "sha512-Mh3OP0oh8X7O7F9m5AplC+XHYLBWuPKNkGVD3gIZFLFebBnuFI2Nz5Sf8WLvwGxECJ8YjifQvFdh79ubODkdug==", "dependencies": { "@types/yargs-parser": "*" } @@ -11626,11 +11626,11 @@ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" }, "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", "dependencies": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" } }, "node_modules/leven": { @@ -11943,9 +11943,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.0.tgz", - "integrity": "sha512-auqtVo8KhTScMsba7MbijqZTfibbXiBNlPAQbsVt7enQfcDYLdgG57eGxMqwVU3mfeWANY4F1wUg+rMF+ycZgw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", "dependencies": { "schema-utils": "^4.0.0" }, @@ -13047,9 +13047,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "12.1.10", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", - "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -14540,9 +14540,9 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "node_modules/react-icons": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz", - "integrity": "sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.7.1.tgz", + "integrity": "sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==", "peerDependencies": { "react": "*" } @@ -14561,11 +14561,11 @@ } }, "node_modules/react-router": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.3.tgz", - "integrity": "sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.5.tgz", + "integrity": "sha512-1RQJ8bM70YEumHIlNUYc6mFfUDoWa5EgPDenK/fq0bxD8DYpQUi/S6Zoft+9DBrh2xmtg92N5HMAJgGWDhKJ5Q==", "dependencies": { - "@remix-run/router": "1.0.3" + "@remix-run/router": "1.0.5" }, "engines": { "node": ">=14" @@ -14575,12 +14575,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.3.tgz", - "integrity": "sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.5.tgz", + "integrity": "sha512-a7HsgikBR0wNfroBHcZUCd9+mLRqZS8R5U1Z1mzLWxFXEkUT3vR1XXmSIVoVpxVX8Bar0nQYYYc9Yipq8dWwAA==", "dependencies": { - "@remix-run/router": "1.0.3", - "react-router": "6.4.3" + "@remix-run/router": "1.0.5", + "react-router": "6.4.5" }, "engines": { "node": ">=14" @@ -15082,6 +15082,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dependencies": { "@babel/code-frame": "^7.10.4", "jest-worker": "^26.2.1", @@ -15593,7 +15594,8 @@ "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" }, "node_modules/spdx-correct": { "version": "3.1.1", @@ -16155,9 +16157,9 @@ } }, "node_modules/terser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", - "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -16439,9 +16441,9 @@ } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17600,25 +17602,25 @@ } }, "@babel/compat-data": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", - "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==" + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==" }, "@babel/core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", - "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.2", + "@babel/generator": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.0", "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.1", - "@babel/parser": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -17644,11 +17646,11 @@ } }, "@babel/generator": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", - "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "requires": { - "@babel/types": "^7.20.2", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -17694,9 +17696,9 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz", - "integrity": "sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", + "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", @@ -17708,12 +17710,12 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", - "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.2.1" } }, "@babel/helper-define-polyfill-provider": { @@ -17866,24 +17868,24 @@ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" }, "@babel/helper-wrap-function": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", - "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "requires": { "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" } }, "@babel/helpers": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", - "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", "requires": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.0" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" } }, "@babel/highlight": { @@ -17897,9 +17899,9 @@ } }, "@babel/parser": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", - "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==" + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -17950,11 +17952,11 @@ } }, "@babel/plugin-proposal-decorators": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.2.tgz", - "integrity": "sha512-nkBH96IBmgKnbHQ5gXFrcmez+Z9S2EIDKDQGp005ROqBigc88Tky4rzCnlP/lnlj245dCEQl4/YyV0V1kYh5dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", + "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-create-class-features-plugin": "^7.20.5", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", @@ -18056,13 +18058,13 @@ } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, @@ -18270,9 +18272,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", - "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz", + "integrity": "sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -18418,12 +18420,12 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", - "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-new-target": { @@ -18444,9 +18446,9 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", - "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz", + "integrity": "sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -18505,12 +18507,12 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" } }, "@babel/plugin-transform-reserved-words": { @@ -18720,20 +18722,20 @@ } }, "@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", "requires": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", - "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", "requires": { "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" } }, "@babel/template": { @@ -18747,26 +18749,26 @@ } }, "@babel/traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", - "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", - "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "requires": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -19640,9 +19642,9 @@ "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==" }, "@mui/base": { - "version": "5.0.0-alpha.108", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.108.tgz", - "integrity": "sha512-KjzRUts2i/ODlMfywhFTqTzQl+Cr9nlDSZxJcnYjrbOV/iRyQNBTDoiFJt+XEdRi0fZBHnk74AFbnP56ehybsA==", + "version": "5.0.0-alpha.109", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.109.tgz", + "integrity": "sha512-UQxoONPI3ntzxcD/cbFHl+Lp2xsVj6HpKmU9QhUZ2kZ2K2yej2QJyU1gnADoWl/Hu94VrvwSSRnjTjR3HvXO/g==", "requires": { "@babel/runtime": "^7.20.1", "@emotion/is-prop-valid": "^1.2.0", @@ -19655,9 +19657,9 @@ } }, "@mui/core-downloads-tracker": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.16.tgz", - "integrity": "sha512-eK9+olw2ZbXX+vGrtKnN01/vLP1aX0Lq0xok35bqWM1aB93Dcmky/xPNf8h31oJ/C+IzJBjZaZMEDzVZg4Qc0A==" + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.17.tgz", + "integrity": "sha512-iNwUuMA30nrN0tiEkeD3zaczv7Tk2jlZIDbXRnijAsYXkZtl/xEzQsVRIPYRDuyEz6D18vQJhV8h7gPUXEubTg==" }, "@mui/icons-material": { "version": "5.10.16", @@ -19668,14 +19670,14 @@ } }, "@mui/material": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.16.tgz", - "integrity": "sha512-JSHcDQQ+k30NKkCM/0KX6jq4F5LOrbFKZpS+cEl7scZWOCJpUPH5ccAT5a7O8wzrgNZ8Y9PnwzNvWBrfShpJFw==", + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.17.tgz", + "integrity": "sha512-Kuqgv1qI5HXnc/Xu426xhCGYBSKzplb+xFNLitbnIb92Qx8jmcpfNpFlDJa2kD2H6qP66rr/m4c/zMUfGX/xBQ==", "requires": { "@babel/runtime": "^7.20.1", - "@mui/base": "5.0.0-alpha.108", - "@mui/core-downloads-tracker": "^5.10.16", - "@mui/system": "^5.10.16", + "@mui/base": "5.0.0-alpha.109", + "@mui/core-downloads-tracker": "^5.10.17", + "@mui/system": "^5.10.17", "@mui/types": "^7.2.2", "@mui/utils": "^5.10.16", "@types/react-transition-group": "^4.4.5", @@ -19708,9 +19710,9 @@ } }, "@mui/system": { - "version": "5.10.16", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.16.tgz", - "integrity": "sha512-OqI9B1jZ9zQ/dmoqseku4CzdEs9DbLiiMOaWxC3WeAJxM1UavlCgXz0encqm93LIlmSL7TjuHN1/rW8BJCnU8A==", + "version": "5.10.17", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.17.tgz", + "integrity": "sha512-UYzAOSK7uxkMsUssqrIUW3lnOuQpU8vqh4hLwfSw+GYAnQo3qjK4m4NhlDx+pFpsjjiGnr3K+vrSH+aIAMbcLg==", "requires": { "@babel/runtime": "^7.20.1", "@mui/private-theming": "^5.10.16", @@ -19816,9 +19818,9 @@ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, "@remix-run/router": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", - "integrity": "sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.5.tgz", + "integrity": "sha512-my0Mycd+jruq/1lQuO5LBB6WTlL/e8DTCYWp44DfMTDcXz8DcTlgF0ISaLsGewt+ctHN+yA8xMq3q/N7uWJPug==" }, "@rollup/plugin-babel": { "version": "5.3.1", @@ -19879,9 +19881,9 @@ "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" }, "@sinonjs/commons": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", - "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "requires": { "type-detect": "4.0.8" } @@ -20077,9 +20079,9 @@ } }, "@types/babel__traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", - "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", "requires": { "@babel/types": "^7.3.0" } @@ -20220,9 +20222,9 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, "@types/lodash": { - "version": "4.14.190", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.190.tgz", - "integrity": "sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw==" + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "@types/mime": { "version": "3.0.1", @@ -20230,9 +20232,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==" }, "@types/parse-json": { "version": "4.0.0", @@ -20265,9 +20267,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@types/react": { - "version": "18.0.25", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", - "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -20397,13 +20399,13 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz", - "integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", + "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", "requires": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/type-utils": "5.44.0", - "@typescript-eslint/utils": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/type-utils": "5.45.1", + "@typescript-eslint/utils": "5.45.1", "debug": "^4.3.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", @@ -20423,56 +20425,56 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.44.0.tgz", - "integrity": "sha512-j8GLemAySe8oUCgILdUaT66pemdWSYcwUYG2Pb71O119hCdvkU+4q8sUTbnDg8NhlZEzSWG2N1v4IxT1kEZrGg==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.1.tgz", + "integrity": "sha512-WlXwY9dbmc0Lzu6xQOZ3yN8u/ws/1R8zPC16O217LMZJCbV2hJezqkWMUB+jMwguOJW+cukCDe92vcwwf8zwjQ==", "requires": { - "@typescript-eslint/utils": "5.44.0" + "@typescript-eslint/utils": "5.45.1" } }, "@typescript-eslint/parser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", - "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", + "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", "requires": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", - "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", + "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", "requires": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0" + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1" } }, "@typescript-eslint/type-utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz", - "integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", + "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", "requires": { - "@typescript-eslint/typescript-estree": "5.44.0", - "@typescript-eslint/utils": "5.44.0", + "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/utils": "5.45.1", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", - "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==" + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", + "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==" }, "@typescript-eslint/typescript-estree": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", - "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", + "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", "requires": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -20491,15 +20493,15 @@ } }, "@typescript-eslint/utils": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz", - "integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", + "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -20530,11 +20532,11 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", - "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", + "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", "requires": { - "@typescript-eslint/types": "5.44.0", + "@typescript-eslint/types": "5.45.1", "eslint-visitor-keys": "^3.3.0" } }, @@ -20998,9 +21000,9 @@ "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==" }, "axios": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz", - "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -21417,9 +21419,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==" + "version": "1.0.30001436", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", + "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -21877,9 +21879,9 @@ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" }, "cssdb": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.1.0.tgz", - "integrity": "sha512-Sd99PrFgx28ez4GHu8yoQIufc/70h9oYowDf4EjeIKi8mac9whxRjhM3IaMr6EllP6KKKWtJrMfN6C7T9tIWvQ==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz", + "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==" }, "cssesc": { "version": "3.0.0", @@ -22016,9 +22018,9 @@ } }, "decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "dedent": { "version": "0.7.0", @@ -22452,9 +22454,9 @@ } }, "eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "requires": { "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.11.6", @@ -23124,9 +23126,9 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", "requires": { "reusify": "^1.0.4" } @@ -23189,9 +23191,9 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", "requires": { "brace-expansion": "^2.0.1" } @@ -23861,9 +23863,9 @@ } }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==" }, "ignore-by-default": { "version": "1.0.1", @@ -25362,9 +25364,9 @@ } }, "@types/yargs": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.14.tgz", - "integrity": "sha512-9Pj7abXoW1RSTcZaL2Hk6G2XyLMlp5ECdVC/Zf2p/KBjC3srijLGgRAXOBjtFrJoIrvxdTKyKDA14bEcbxBaWw==", + "version": "17.0.16", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.16.tgz", + "integrity": "sha512-Mh3OP0oh8X7O7F9m5AplC+XHYLBWuPKNkGVD3gIZFLFebBnuFI2Nz5Sf8WLvwGxECJ8YjifQvFdh79ubODkdug==", "requires": { "@types/yargs-parser": "*" } @@ -25782,11 +25784,11 @@ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" }, "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", "requires": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" } }, "leven": { @@ -26026,9 +26028,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mini-css-extract-plugin": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.0.tgz", - "integrity": "sha512-auqtVo8KhTScMsba7MbijqZTfibbXiBNlPAQbsVt7enQfcDYLdgG57eGxMqwVU3mfeWANY4F1wUg+rMF+ycZgw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", "requires": { "schema-utils": "^4.0.0" }, @@ -26766,9 +26768,9 @@ } }, "postcss-custom-properties": { - "version": "12.1.10", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", - "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", "requires": { "postcss-value-parser": "^4.2.0" } @@ -27705,9 +27707,9 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "react-icons": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz", - "integrity": "sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.7.1.tgz", + "integrity": "sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==", "requires": {} }, "react-is": { @@ -27721,20 +27723,20 @@ "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, "react-router": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.3.tgz", - "integrity": "sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.5.tgz", + "integrity": "sha512-1RQJ8bM70YEumHIlNUYc6mFfUDoWa5EgPDenK/fq0bxD8DYpQUi/S6Zoft+9DBrh2xmtg92N5HMAJgGWDhKJ5Q==", "requires": { - "@remix-run/router": "1.0.3" + "@remix-run/router": "1.0.5" } }, "react-router-dom": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.3.tgz", - "integrity": "sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.5.tgz", + "integrity": "sha512-a7HsgikBR0wNfroBHcZUCd9+mLRqZS8R5U1Z1mzLWxFXEkUT3vR1XXmSIVoVpxVX8Bar0nQYYYc9Yipq8dWwAA==", "requires": { - "@remix-run/router": "1.0.3", - "react-router": "6.4.3" + "@remix-run/router": "1.0.5", + "react-router": "6.4.5" } }, "react-scripts": { @@ -28910,9 +28912,9 @@ } }, "terser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", - "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -29115,9 +29117,9 @@ "requires": {} }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/interface/package.json b/interface/package.json index 42e2ad379..4238aba64 100644 --- a/interface/package.json +++ b/interface/package.json @@ -8,15 +8,15 @@ "@emotion/styled": "^11.10.5", "@msgpack/msgpack": "^2.8.0", "@mui/icons-material": "^5.10.16", - "@mui/material": "^5.10.16", + "@mui/material": "^5.10.17", "@table-library/react-table-library": "4.0.23", - "@types/lodash": "^4.14.190", - "@types/node": "^18.11.9", - "@types/react": "^18.0.25", + "@types/lodash": "^4.14.191", + "@types/node": "^18.11.12", + "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", "async-validator": "^4.2.5", - "axios": "^1.2.0", + "axios": "^1.2.1", "http-proxy-middleware": "^2.0.6", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", @@ -26,12 +26,12 @@ "react-app-rewired": "^2.2.1", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", - "react-icons": "^4.6.0", - "react-router-dom": "^6.4.3", + "react-icons": "^4.7.1", + "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.17.1", - "typescript": "^4.9.3" + "typescript": "^4.9.4" }, "scripts": { "start": "react-app-rewired start", diff --git a/interface/src/framework/mqtt/MqttSettingsForm.tsx b/interface/src/framework/mqtt/MqttSettingsForm.tsx index b5956b034..03f305508 100644 --- a/interface/src/framework/mqtt/MqttSettingsForm.tsx +++ b/interface/src/framework/mqtt/MqttSettingsForm.tsx @@ -220,17 +220,31 @@ const MqttSettingsForm: FC = () => { /> {data.ha_enabled && ( - - - + <> + + + } + label={LL.MQTT_MULTIPLE_INSTANCES()} + /> + + + + + )} )} @@ -238,6 +252,22 @@ const MqttSettingsForm: FC = () => { {LL.MQTT_PUBLISH_INTERVALS()} (0=auto) + + {LL.SECONDS()} + }} + fullWidth + variant="outlined" + value={numberValue(data.publish_time_heartbeat)} + type="number" + onChange={updateFormValue} + margin="normal" + /> + { const { LL } = useI18nContext(); + const { enqueueSnackbar } = useSnackbar(); const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext); const [initialized, setInitialized] = useState(false); - const { loadData, saving, data, setData, saveData, errorMessage } = useRest({ + const [restarting, setRestarting] = useState(false); + const { loadData, saving, data, setData, saveData, errorMessage, restartNeeded } = useRest({ read: NetworkApi.readNetworkSettings, update: NetworkApi.updateNetworkSettings }); @@ -62,7 +69,9 @@ const WiFiSettingsForm: FC = () => { bandwidth20: false, tx_power: 20, nosleep: false, - enableMDNS: true + enableMDNS: true, + enableCORS: false, + CORSOrigin: '*' }); } setInitialized(true); @@ -90,6 +99,15 @@ const WiFiSettingsForm: FC = () => { } }; + const restart = async () => { + try { + await EMSESP.restart(); + setRestarting(true); + } catch (error) { + enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); + } + }; + return ( <> @@ -162,11 +180,6 @@ const WiFiSettingsForm: FC = () => { label={LL.NETWORK_LOW_BAND()} /> - } - label={LL.NETWORK_USE_DNS()} - /> - {LL.GENERAL_OPTIONS()} @@ -182,6 +195,28 @@ const WiFiSettingsForm: FC = () => { margin="normal" /> + } + label={LL.NETWORK_USE_DNS()} + /> + + } + label={LL.NETWORK_ENABLE_CORS()} + /> + {data.enableCORS && ( + + )} + } label={LL.NETWORK_ENABLE_IPV6()} @@ -245,25 +280,34 @@ const WiFiSettingsForm: FC = () => { /> )} - - - + {restartNeeded && ( + + + + )} + {!restartNeeded && ( + + + + )} ); }; return ( - {content()} + {restarting ? : content()} ); }; diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index 09d3e3f7c..5b55d8cb7 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -3,6 +3,7 @@ import type { Translation } from '../i18n-types'; /* eslint-disable */ const de: Translation = { +// ...en as Translation, LANGUAGE: 'Sprache', RETRY: 'Neuer Versuch', LOADING: 'Laden', @@ -37,7 +38,7 @@ const de: Translation = { PRODUCT: 'Produkt', VERSION: 'Version', ENTITY_NAME: 'Entitätsname', - VALUE: 'Wert', + VALUE: '{{Wert|wert}}', SHOW_FAV: 'nur Favoriten anzeigen', DEVICE_SENSOR_DATA: 'Geräte- und Sensordaten', DEVICES_SENSORS: 'Geräte & Sensoren', @@ -190,8 +191,8 @@ const de: Translation = { SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?', VERSION_CHECK: 'Versionsprüfung', THE_LATEST: 'Die neueste', - OFFICIAL: 'official', - DEVELOPMENT: 'development', + OFFICIAL: 'offizielle', + DEVELOPMENT: 'Entwicklungs', VERSION_IS: 'Version ist', RELEASE_NOTES: 'Versionshinweise', EMS_ESP_VER: 'EMS-ESP Version', @@ -245,8 +246,10 @@ const de: Translation = { MQTT_INT_THERMOSTATS: 'Thermostate', MQTT_INT_SOLAR: 'Solarmodule', MQTT_INT_MIXER: 'Mischermodule', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'MQTT Queue', DEFAULT: 'Standard', + MQTT_MULTIPLE_INSTANCES: 'Erlaube EMS-ESP Mehrfachinstanzen', MQTT_CLEAN_SESSION: 'Setze `Clean Session`', MQTT_RETAIN_FLAG: 'Setze `Retain flag` immer', INACTIVE: 'Inaktiv', @@ -282,6 +285,8 @@ const de: Translation = { NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus', NETWORK_LOW_BAND: 'Verwende niedrige WiFi Bandbreite', NETWORK_USE_DNS: 'Aktiviere mDNS Service', + NETWORK_ENABLE_CORS: 'Aktiviere CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Aktiviere IPv6 Unterstützung', NETWORK_FIXED_IP: 'Feste IP Adresse', NETWORK_GATEWAY: 'Gateway', diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index 2e949f2e8..e28a3bd4d 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -37,7 +37,7 @@ const en: Translation = { VERSION: 'Version', BRAND: 'Brand', ENTITY_NAME: 'Entity Name', - VALUE: 'Value', + VALUE: '{{Value|value}}', SHOW_FAV: 'only show favorites', DEVICE_SENSOR_DATA: 'Device and Sensor Data', DEVICES_SENSORS: 'Devices & Sensors', @@ -245,8 +245,10 @@ const en: Translation = { MQTT_INT_THERMOSTATS: 'Thermostats', MQTT_INT_SOLAR: 'Solar Modules', MQTT_INT_MIXER: 'Mixer Modules', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'MQTT Queue', DEFAULT: 'Default', + MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP', MQTT_CLEAN_SESSION: 'Set Clean Session', MQTT_RETAIN_FLAG: 'Always set Retain flag', INACTIVE: 'Inactive', @@ -282,6 +284,8 @@ const en: Translation = { NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode', NETWORK_LOW_BAND: 'Use Lower WiFi Bandwidth', NETWORK_USE_DNS: 'Enable mDNS Service', + NETWORK_ENABLE_CORS: 'Enable CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Enable IPv6 support', NETWORK_FIXED_IP: 'Use Fixed IP address', NETWORK_GATEWAY: 'Gateway', diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index 496ea4a35..08553526f 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -100,8 +100,8 @@ const nl: Translation = { NUM_ANALOG_SENSORS: '{num} Analoge sensor{{en}}', NUM_DAYS: '{num} dag{{en}}', NUM_SECONDS: '{num} second{{en}}', - NUM_HOURS: '{num} uur{{en}}', - NUM_MINUTES: '{num} minuut{{en}}', + NUM_HOURS: '{num} {{uur|uren}}', + NUM_MINUTES: '{num} {{minuut|minuten}}', APPLICATION_SETTINGS: 'Applicatieinstellingen', CUSTOMIZATION: 'Custom aanpassingen', APPLICATION_RESTARTING: 'EMS-ESP herstarten', @@ -245,8 +245,10 @@ const nl: Translation = { MQTT_INT_THERMOSTATS: 'Thermostaten', MQTT_INT_SOLAR: 'Solar Modules', MQTT_INT_MIXER: 'Mixer Modules', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'MQTT Queue', DEFAULT: 'Default', + MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP', MQTT_CLEAN_SESSION: 'Clean Session aan', MQTT_RETAIN_FLAG: 'Retain flag aan', INACTIVE: 'Inactief', @@ -282,6 +284,8 @@ const nl: Translation = { NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten', NETWORK_LOW_BAND: 'Lagere WiFi bandbreedte gebruiken', NETWORK_USE_DNS: 'Activeer mDNS Service', + NETWORK_ENABLE_CORS: 'Activeer CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Activeer IPv6 support', NETWORK_FIXED_IP: 'Gebruik vast IP addres', NETWORK_GATEWAY: 'Gateway', diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index 851876219..d3474b5e6 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -245,8 +245,10 @@ const no: Translation = { MQTT_INT_THERMOSTATS: 'Termostat', MQTT_INT_SOLAR: 'Solpaneler', MQTT_INT_MIXER: 'Blandeventil', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'MQTT Queue', DEFAULT: 'Standard', + MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP', MQTT_CLEAN_SESSION: 'Benytt Clean Session', MQTT_RETAIN_FLAG: 'Alltid sett Retain flag', INACTIVE: 'Innaktiv', @@ -282,6 +284,8 @@ const no: Translation = { 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_CORS: 'Aktiviser CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Aktiviser IPv6 støtte', NETWORK_FIXED_IP: 'Benytt statisk IP adresse', NETWORK_GATEWAY: 'Gateway', diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index 413d21c66..dd4714de7 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -245,8 +245,10 @@ const pl: BaseTranslation = { MQTT_INT_THERMOSTATS: 'Termostaty', MQTT_INT_SOLAR: 'Panele solarne', MQTT_INT_MIXER: 'Mieszacze', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'Kolejka MQTT', DEFAULT: '{{PozostaÅ‚e|DomyÅ›lna|}}', + MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP', MQTT_CLEAN_SESSION: 'Ustawiaj flagÄ™ "Clean session"', MQTT_RETAIN_FLAG: 'Ustawiaj flagÄ™ "Retain"', INACTIVE: 'nieaktywny', @@ -282,6 +284,8 @@ const pl: BaseTranslation = { NETWORK_DISABLE_SLEEP: 'Wyłącz tryb usypiania WiFi', NETWORK_LOW_BAND: 'Używaj zmniejszonej przepustowoÅ›ci WiFi', NETWORK_USE_DNS: 'Włącz wsparcie dla mDNS', + NETWORK_ENABLE_CORS: 'Włącz wsparcie dla CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Włącz wsparcie dla IPv6', NETWORK_FIXED_IP: 'Użyj staÅ‚ego adresu IP', NETWORK_GATEWAY: 'Brama', diff --git a/interface/src/i18n/se/index.ts b/interface/src/i18n/se/index.ts index 923c9617e..e302363dc 100644 --- a/interface/src/i18n/se/index.ts +++ b/interface/src/i18n/se/index.ts @@ -245,8 +245,10 @@ const se: Translation = { MQTT_INT_THERMOSTATS: 'Termostater', MQTT_INT_SOLAR: 'Solpaneler', MQTT_INT_MIXER: 'Blandarventiler', + MQTT_INT_HEARTBEAT: 'Heartbeat', MQTT_QUEUE: 'MQTT Queue', DEFAULT: 'Standard', + MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP', MQTT_CLEAN_SESSION: 'Använd "Clean Session"-flaggan', MQTT_RETAIN_FLAG: 'Använd "Always Retain"-flaggan', INACTIVE: 'Inaktiv', @@ -282,6 +284,8 @@ const se: Translation = { NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge', NETWORK_LOW_BAND: 'Använd lägre bandbredd', NETWORK_USE_DNS: 'Aktivera mDNS-tjänsten', + NETWORK_ENABLE_CORS: 'Aktivera CORS', + NETWORK_CORS_ORIGIN: 'CORS origin', NETWORK_ENABLE_IPV6: 'Aktivera IPv6-support', NETWORK_FIXED_IP: 'Använd statiskt IP', NETWORK_GATEWAY: 'Gateway', diff --git a/interface/src/project/DeviceIcon.tsx b/interface/src/project/DeviceIcon.tsx index 347a1f1cd..ec8910487 100644 --- a/interface/src/project/DeviceIcon.tsx +++ b/interface/src/project/DeviceIcon.tsx @@ -37,9 +37,9 @@ const DeviceIcon: FC = ({ type }) => { case 'Gateway': return ; case 'Alert': - return ; + return ; case 'Pump': - return ; + return ; default: return null; } diff --git a/interface/src/project/validators.ts b/interface/src/project/validators.ts index 6000e0769..73991f5ac 100644 --- a/interface/src/project/validators.ts +++ b/interface/src/project/validators.ts @@ -72,11 +72,11 @@ export const createSettingsValidator = (settings: Settings) => syslog_host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR], syslog_port: [ { required: true, message: 'Port is required' }, - { type: 'number', min: 0, max: 65535, message: 'Port must be between 0 and 65535' } + { type: 'number', min: 0, max: 65535, message: 'Invalid Port' } ], syslog_mark_interval: [ { required: true, message: 'Mark interval is required' }, - { type: 'number', min: 0, max: 10, message: 'Port must be between 0 and 10' } + { type: 'number', min: 0, max: 10, message: ' must be between 0 and 10' } ] }), ...(settings.shower_alert && { diff --git a/interface/src/types/mqtt.ts b/interface/src/types/mqtt.ts index 0e8a10eec..ee69da5b5 100644 --- a/interface/src/types/mqtt.ts +++ b/interface/src/types/mqtt.ts @@ -29,13 +29,14 @@ export interface MqttSettings { client_id: string; keep_alive: number; clean_session: boolean; - max_topic_length: number; + multiple_instances: boolean; publish_time_boiler: number; publish_time_thermostat: number; publish_time_solar: number; publish_time_mixer: number; publish_time_other: number; publish_time_sensor: number; + publish_time_heartbeat: number; mqtt_qos: number; mqtt_retain: boolean; ha_enabled: boolean; diff --git a/interface/src/types/network.ts b/interface/src/types/network.ts index fb6b576ba..a0de253f0 100644 --- a/interface/src/types/network.ts +++ b/interface/src/types/network.ts @@ -48,6 +48,8 @@ export interface NetworkSettings { dns_ip_1?: string; dns_ip_2?: string; enableMDNS: boolean; + enableCORS: boolean; + CORSOrigin: string; } export interface WiFiNetworkList { diff --git a/interface/src/validators/mqtt.ts b/interface/src/validators/mqtt.ts index b26619e82..a38e2d8a0 100644 --- a/interface/src/validators/mqtt.ts +++ b/interface/src/validators/mqtt.ts @@ -3,16 +3,17 @@ import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; export const MQTT_SETTINGS_VALIDATOR = new Schema({ host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR], + base: { required: true, message: 'Base is required' }, port: [ { required: true, message: 'Port is required' }, - { type: 'number', min: 0, max: 65535, message: 'Port must be between 0 and 65535' } + { type: 'number', min: 0, max: 65535, message: 'Invalid Port' } ], keep_alive: [ { required: true, message: 'Keep alive is required' }, { type: 'number', min: 1, max: 86400, message: 'Keep alive must be between 1 and 86400' } ], - max_topic_length: [ - { required: true, message: 'Max topic length is required' }, - { type: 'number', min: 16, max: 1024, message: 'Max topic length must be between 16 and 1024' } + publish_time_heartbeat: [ + { required: true, message: 'Heartbeat is required' }, + { type: 'number', min: 10, max: 86400, message: 'Heartbeat must be between 10 and 86400' } ] }); diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp index e83b284cf..e238ba54e 100644 --- a/lib/framework/ESP8266React.cpp +++ b/lib/framework/ESP8266React.cpp @@ -46,6 +46,13 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs) void ESP8266React::begin() { _networkSettingsService.begin(); + _networkSettingsService.read([&](NetworkSettings & networkSettings) { + if (networkSettings.enableCORS) { + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); + } + }); _apSettingsService.begin(); _ntpSettingsService.begin(); _otaSettingsService.begin(); diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 6a2c13413..5e4c82814 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -137,7 +137,7 @@ void MqttSettingsService::configureMqtt() { _mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); _mqttClient.setKeepAlive(_state.keepAlive); _mqttClient.setCleanSession(_state.cleanSession); - _mqttClient.setMaxTopicLength(_state.maxTopicLength); + _mqttClient.setMaxTopicLength(FACTORY_MQTT_MAX_TOPIC_LENGTH); // hardcode. We don't take this from the settings anymore. _mqttClient.connect(); // } else { // emsesp::EMSESP::logger().info("Error configuring Mqtt client"); @@ -145,16 +145,16 @@ void MqttSettingsService::configureMqtt() { } void MqttSettings::read(MqttSettings & settings, JsonObject & root) { - root["enabled"] = settings.enabled; - root["host"] = settings.host; - root["port"] = settings.port; - root["base"] = settings.base; - root["username"] = settings.username; - root["password"] = settings.password; - root["client_id"] = settings.clientId; - root["keep_alive"] = settings.keepAlive; - root["clean_session"] = settings.cleanSession; - root["max_topic_length"] = settings.maxTopicLength; + root["enabled"] = settings.enabled; + root["host"] = settings.host; + root["port"] = settings.port; + root["base"] = settings.base; + root["username"] = settings.username; + root["password"] = settings.password; + root["client_id"] = settings.clientId; + root["keep_alive"] = settings.keepAlive; + root["clean_session"] = settings.cleanSession; + root["multiple_instances"] = settings.multiple_instances; // added by proddy for EMS-ESP root["publish_time_boiler"] = settings.publish_time_boiler; @@ -163,6 +163,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) { root["publish_time_mixer"] = settings.publish_time_mixer; root["publish_time_other"] = settings.publish_time_other; root["publish_time_sensor"] = settings.publish_time_sensor; + root["publish_time_heartbeat"] = settings.publish_time_heartbeat; root["mqtt_qos"] = settings.mqtt_qos; root["mqtt_retain"] = settings.mqtt_retain; root["ha_enabled"] = settings.ha_enabled; @@ -177,18 +178,19 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting MqttSettings newSettings = {}; bool changed = false; - newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; - newSettings.host = root["host"] | FACTORY_MQTT_HOST; - newSettings.port = root["port"] | FACTORY_MQTT_PORT; - newSettings.base = root["base"] | FACTORY_MQTT_BASE; - newSettings.username = root["username"] | FACTORY_MQTT_USERNAME; - newSettings.password = root["password"] | FACTORY_MQTT_PASSWORD; - newSettings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID; - newSettings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; - newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; - newSettings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH; - newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; - newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN; + newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; + newSettings.host = root["host"] | FACTORY_MQTT_HOST; + newSettings.port = root["port"] | FACTORY_MQTT_PORT; + newSettings.base = root["base"] | FACTORY_MQTT_BASE; + newSettings.username = root["username"] | FACTORY_MQTT_USERNAME; + newSettings.password = root["password"] | FACTORY_MQTT_PASSWORD; + newSettings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID; + newSettings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; + newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; + newSettings.multiple_instances = root["multiple_instances"] | FACTORY_MQTT_MULTIPLE_INSTANCES; + + newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; + newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN; newSettings.publish_time_boiler = root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_thermostat = root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME; @@ -196,6 +198,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting newSettings.publish_time_mixer = root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_heartbeat = root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT; newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED; newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT; @@ -284,6 +287,11 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting changed = true; } + if (newSettings.publish_time_heartbeat != settings.publish_time_heartbeat) { + emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat); + changed = true; + } + if (changed) { emsesp::EMSESP::mqtt_.reset_mqtt(); } diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index 473c130ac..21ec908bd 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -57,6 +57,10 @@ static String generateClientId() { #define FACTORY_MQTT_MAX_TOPIC_LENGTH 128 #endif +#ifndef FACTORY_MQTT_MULTIPLE_INSTANCES +#define FACTORY_MQTT_MULTIPLE_INSTANCES false +#endif + class MqttSettings { public: // host and port - if enabled @@ -74,7 +78,9 @@ class MqttSettings { // connection settings uint16_t keepAlive; bool cleanSession; - uint16_t maxTopicLength; + + // multiple instances + bool multiple_instances; // proddy EMS-ESP specific String base; @@ -84,6 +90,7 @@ class MqttSettings { uint16_t publish_time_mixer; uint16_t publish_time_other; uint16_t publish_time_sensor; + uint16_t publish_time_heartbeat; uint8_t mqtt_qos; bool mqtt_retain; bool ha_enabled; diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 3df0d3118..1697ad3d1 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -83,5 +83,5 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari void NTPSettingsService::ntp_received(struct timeval * tv) { // emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec); emsesp::EMSESP::system_.ntp_connected(true); - emsesp::EMSESP::system_.send_info_mqtt("connected"); + emsesp::EMSESP::system_.send_info_mqtt("connected", true); // send info topic with NTP time } diff --git a/lib/framework/NetworkSettingsService.h b/lib/framework/NetworkSettingsService.h index fe8ddd699..407282413 100644 --- a/lib/framework/NetworkSettingsService.h +++ b/lib/framework/NetworkSettingsService.h @@ -39,6 +39,8 @@ class NetworkSettings { int8_t tx_power; bool nosleep; bool enableMDNS; + bool enableCORS; + String CORSOrigin; // optional configuration for static IP address IPAddress localIP; @@ -58,6 +60,8 @@ class NetworkSettings { root["tx_power"] = settings.tx_power; root["nosleep"] = settings.nosleep; root["enableMDNS"] = settings.enableMDNS; + root["enableCORS"] = settings.enableCORS; + root["CORSOrigin"] = settings.CORSOrigin; // extended settings JsonUtils::writeIP(root, "local_ip", settings.localIP); @@ -68,6 +72,8 @@ class NetworkSettings { } static StateUpdateResult update(JsonObject & root, NetworkSettings & settings) { + auto enableCORS = settings.enableCORS; + auto CORSOrigin = settings.CORSOrigin; settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; settings.password = root["password"] | FACTORY_WIFI_PASSWORD; settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME; @@ -77,6 +83,8 @@ class NetworkSettings { settings.tx_power = root["tx_power"] | 20; settings.nosleep = root["nosleep"] | false; settings.enableMDNS = root["enableMDNS"] | true; + settings.enableCORS = root["enableCORS"] | false; + settings.CORSOrigin = root["CORSOrigin"] | "*"; // extended settings JsonUtils::readIP(root, "local_ip", settings.localIP); @@ -97,6 +105,9 @@ class NetworkSettings { if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || IPUtils::isNotSet(settings.subnetMask))) { settings.staticIPConfig = false; } + if (enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin) { + return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed + } return StateUpdateResult::CHANGED; } diff --git a/lib/framework/UploadFileService.cpp b/lib/framework/UploadFileService.cpp index e2d07d83b..b876a195b 100644 --- a/lib/framework/UploadFileService.cpp +++ b/lib/framework/UploadFileService.cpp @@ -29,9 +29,12 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri std::string extension = fname.substr(position + 1); size_t fsize = request->contentLength(); +#ifdef EMSESP_DEBUG #if defined(EMSESP_USE_SERIAL) + Serial.println(); Serial.printf("Received filename: %s, len: %d, index: %d, ext: %s, fsize: %d", filename.c_str(), len, index, extension.c_str(), fsize); Serial.println(); +#endif #endif is_firmware = false; diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index 4c17cc4aa..f0e8b3817 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -74,13 +74,14 @@ class DummySettings { String base = "ems-esp"; bool publish_single = false; bool publish_single2cmd = false; - bool send_response = true; + bool send_response = false; // don't send response String host = "192.168.1.4"; uint16_t port = 1883; String clientId = "ems-esp"; String username = ""; uint16_t keepAlive = 60; bool cleanSession = false; + bool multiple_instances = false; uint16_t publish_time_boiler = 10; uint16_t publish_time_thermostat = 10; @@ -88,6 +89,7 @@ class DummySettings { uint16_t publish_time_mixer = 10; uint16_t publish_time_other = 10; uint16_t publish_time_sensor = 10; + uint16_t publish_time_heartbeat = 60; String hostname = "ems-esp"; String jwtSecret = "ems-esp"; @@ -101,6 +103,8 @@ class DummySettings { String dnsIP2 = ""; bool enableIPv6 = false; bool enableMDNS = true; + bool enableCORS = false; + String CORSOrigin = "*"; uint8_t phy_type = 0; uint8_t eth_power = 0; // 0 means -1 diff --git a/mock-api/server.js b/mock-api/server.js index 248257958..f73d04026 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -122,7 +122,11 @@ network_settings = { hostname: 'ems-esp', nosleep: true, tx_power: 20, + bandwidth20: false, static_ip_config: false, + enableMDNS: true, + enableCORS: false, + CORSOrigin: '*', } const network_status = { status: 3, @@ -219,13 +223,14 @@ mqtt_settings = { client_id: 'ems-esp', keep_alive: 60, clean_session: true, - max_topic_length: 128, + multiple_instances: false, publish_time_boiler: 10, publish_time_thermostat: 10, publish_time_solar: 10, publish_time_mixer: 10, publish_time_other: 10, publish_time_sensor: 10, + publish_time_heartbeat: 60, mqtt_qos: 0, mqtt_retain: false, ha_enabled: true, diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..2442bf511 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "EMS-ESP32", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index cd4b09d7c..ffb02bb7c 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -296,7 +296,7 @@ void AnalogSensor::loop() { } // update analog information name and offset -bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset, float factor, uint8_t uom, int8_t type) { +bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type) { boolean found_sensor = false; // see if we can find the sensor in our customization list EMSESP::webCustomizationService.update( @@ -375,7 +375,7 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const { 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 + Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles } } @@ -422,7 +422,7 @@ void AnalogSensor::publish_values(const bool force) { case AnalogType::PWM_0: case AnalogType::PWM_1: case AnalogType::PWM_2: - dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // float + dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double break; default: dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0 @@ -440,7 +440,7 @@ void AnalogSensor::publish_values(const bool force) { StaticJsonDocument config; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path config["stat_t"] = stat_t; char str[50]; @@ -451,16 +451,18 @@ void AnalogSensor::publish_values(const bool force) { } config["val_tpl"] = str; - // snprintf(str, sizeof(str), "%s_analog_sensor_%s", Mqtt::basename().c_str(), sensor.name().c_str()); - snprintf(str, sizeof(str), "analog_sensor_%d", sensor.gpio()); + if (Mqtt::multiple_instances()) { + snprintf(str, sizeof(str), "%s_analogsensor_%d", Mqtt::basename().c_str(), sensor.gpio()); + } else { + snprintf(str, sizeof(str), "analogsensor_%d", sensor.gpio()); + } + config["object_id"] = str; + config["uniq_id"] = str; // same as object_id snprintf(str, sizeof(str), "%s", sensor.name().c_str()); config["name"] = str; - snprintf(str, sizeof(str), "analogsensor_%d", sensor.gpio()); - config["uniq_id"] = str; - if (sensor.uom() != DeviceValueUOM::NONE) { config["unit_of_meas"] = EMSdevice::uom_to_string(sensor.uom()); } @@ -584,7 +586,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject } // this creates the sensor, initializing everything -AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type) +AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type) : gpio_(gpio) , name_(name) , offset_(offset) diff --git a/src/analogsensor.h b/src/analogsensor.h index 9a00061cf..9b3d2f6c9 100644 --- a/src/analogsensor.h +++ b/src/analogsensor.h @@ -35,10 +35,10 @@ class AnalogSensor { public: class Sensor { public: - Sensor(const uint8_t gpio, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type); + Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type); ~Sensor() = default; - void set_offset(const float offset) { + void set_offset(const double offset) { offset_ = offset; } @@ -52,23 +52,23 @@ class AnalogSensor { return gpio_; } - float value() const { + double value() const { return value_; } - void set_value(float value) { + void set_value(const double value) { value_ = value; } - float factor() const { + double factor() const { return factor_; } - void set_factor(float factor) { + void set_factor(const double factor) { factor_ = factor; } - float offset() const { + double offset() const { return offset_; } @@ -84,7 +84,7 @@ class AnalogSensor { return type_; } - void set_type(int8_t type) { + void set_type(const int8_t type) { type_ = type; } @@ -101,10 +101,10 @@ class AnalogSensor { private: uint8_t gpio_; std::string name_; - float offset_; - float factor_; + double offset_; + double factor_; uint8_t uom_; - float value_; // float because of the factor is a float + double value_; // double because of the factor is a double int8_t type_; }; @@ -157,7 +157,7 @@ class AnalogSensor { return sensors_.size(); } - bool update(uint8_t gpio, const std::string & name, float offset, float factor, uint8_t uom, int8_t type); + bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type); bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const; #ifdef EMSESP_DEBUG diff --git a/src/command.cpp b/src/command.cpp index 8c6cd7019..eebbd7730 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -52,8 +52,10 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec } } +#ifdef EMSESP_DEBUG #if defined(EMSESP_USE_SERIAL) // Serial.println(p.path().c_str()); // dump paths, for debugging +#endif #endif // re-calculate new path @@ -179,6 +181,8 @@ std::string Command::return_code_string(const uint8_t return_code) { return "Not Authorized"; case CommandRet::FAIL: return "Failed"; + case CommandRet::INVALID: + return "Invalid"; default: break; } @@ -270,18 +274,22 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * } auto description = Helpers::translated_word(cf->description_); + char info_s[100]; + if (strlen(description)) { + snprintf(info_s, sizeof(info_s), "'%s/%s' (%s)", dname, cmd, description); + } else { + snprintf(info_s, sizeof(info_s), "'%s/%s'", dname, cmd); + } + + std::string ro = EMSESP::system_.readonly_mode() ? "[readonly] " : ""; if ((value == nullptr) || (strlen(value) == 0)) { - if (EMSESP::system_.readonly_mode()) { - LOG_INFO(("[readonly] Calling command '%s/%s' (%s)"), dname, cmd, description); - } else { - LOG_DEBUG(("Calling command '%s/%s' (%s)"), dname, cmd, description); - } + LOG_DEBUG(("%sCalling command %s"), ro.c_str(), info_s); } else { - if (EMSESP::system_.readonly_mode()) { - LOG_INFO(("[readonly] Calling command '%s/%s' (%s) with value %s"), dname, cmd, description, value); + if (id > 0) { + LOG_DEBUG(("%sCalling command %s with value %s and id %d"), ro.c_str(), info_s, value, id); } else { - LOG_DEBUG(("Calling command '%s/%s' (%s) with value %s"), dname, cmd, description, value); + LOG_DEBUG(("%sCalling command %s with value %s"), ro.c_str(), info_s, value); } } @@ -290,8 +298,13 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR; } - if (cf->cmdfunction_ && !EMSESP::cmd_is_readonly(device_type, cmd, id)) { - return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR; + // check if read-only. This also checks for valid tags (e.g. heating circuits) + if (cf->cmdfunction_) { + if (EMSESP::cmd_is_readonly(device_type, cmd, id)) { + return_code = CommandRet::INVALID; // readonly or invalid hc + } else { + return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR; + } } // report back diff --git a/src/command.h b/src/command.h index d131f0574..187a213d7 100644 --- a/src/command.h +++ b/src/command.h @@ -40,12 +40,12 @@ enum CommandFlag : uint8_t { // return status after calling a Command enum CommandRet : uint8_t { - FAIL = 0, // 0 or FALSE - OK, // 1 or TRUE - NOT_FOUND, // 2 - ERROR, // 3 - NOT_ALLOWED // needs authentication - + FAIL = 0, // 0 or FALSE + OK, // 1 or TRUE + NOT_FOUND, // 2 + ERROR, // 3 + NOT_ALLOWED, // 4 - needs authentication + INVALID // 5 - invalid (tag) }; using cmd_function_p = std::function; diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index 116a43090..d58f22d7c 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -505,7 +505,7 @@ void DallasSensor::publish_values(const bool force) { config["dev_cla"] = "temperature"; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str()); // use base path config["stat_t"] = stat_t; config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES); @@ -518,16 +518,18 @@ void DallasSensor::publish_values(const bool force) { } config["val_tpl"] = str; - // snprintf(str, sizeof(str), "%s_temperature_sensor_%s", Mqtt::basename().c_str(), sensor.id().c_str()); - snprintf(str, sizeof(str), "temperature_sensor_%s", sensor.id().c_str()); + if (Mqtt::multiple_instances()) { + snprintf(str, sizeof(str), "%s_dallassensor_%s", Mqtt::basename().c_str(), sensor.id().c_str()); + } else { + snprintf(str, sizeof(str), "dallassensor_%s", sensor.id().c_str()); + } + config["object_id"] = str; + config["uniq_id"] = str; // same as object_id snprintf(str, sizeof(str), "%s", sensor.name().c_str()); config["name"] = str; - snprintf(str, sizeof(str), "dallasensor_%s", sensor.id().c_str()); - config["uniq_id"] = str; - JsonObject dev = config.createNestedObject("dev"); JsonArray ids = dev.createNestedArray("ids"); ids.add("ems-esp"); diff --git a/src/dallassensor.h b/src/dallassensor.h index 46d62a18b..78bc9e29d 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -57,7 +57,7 @@ class DallasSensor { std::string name() const; void set_name(const std::string & name) { - name_ = name; + name_ = name; } bool apply_customization(); diff --git a/src/default_settings.h b/src/default_settings.h index 64692d7cc..d372efa38 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -157,6 +157,10 @@ #define EMSESP_DEFAULT_PUBLISH_TIME 10 #endif +#ifndef EMSESP_DEFAULT_PUBLISH_HEARTBEAT +#define EMSESP_DEFAULT_PUBLISH_HEARTBEAT 60 +#endif + #ifndef EMSESP_DEFAULT_NESTED_FORMAT #define EMSESP_DEFAULT_NESTED_FORMAT 1 #endif diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 90dac5a49..d5cdd9990 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -350,14 +350,8 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const { // list all the telegram type IDs for this device, outputting to a string (max size 200) char * EMSdevice::show_telegram_handlers(char * result, const size_t len, const uint8_t handlers) { - uint8_t size = telegram_functions_.size(); - strlcpy(result, "", len); - if (!size) { - return result; - } - uint8_t i = 0; for (const auto & tf : telegram_functions_) { if (handlers == Handlers::ALL || (handlers == Handlers::RECEIVED && tf.received_ && !tf.fetch_) @@ -623,6 +617,7 @@ bool EMSdevice::is_readable(const void * value_p) const { } // check if value/command is readonly +// matches valid tags too 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_) { diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 5690fc264..5ee409283 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -589,7 +589,7 @@ void EMSESP::publish_response(std::shared_ptr telegram) { doc["value"] = value; } - Mqtt::publish(F_(response), doc.as()); + Mqtt::publish("response", doc.as()); } // builds json with the detail of each value, for a specific EMS device type or the dallas sensor @@ -1255,7 +1255,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) { } #endif // check for poll to us, if so send top message from Tx queue immediately and quit - if (poll_id == txservice_.ems_bus_id()) { + if (poll_id == txservice_.get_send_id()) { txservice_.send(); } // send remote room temperature if active diff --git a/src/helpers.cpp b/src/helpers.cpp index c2b99638d..6865f9081 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -244,7 +244,7 @@ char * Helpers::render_value(char * result, uint8_t value, int8_t format, const // float: convert float to char // format is the precision, 0 to 8 -char * Helpers::render_value(char * result, const float value, const int8_t format) { +char * Helpers::render_value(char * result, const double value, const int8_t format) { if (format > 8) { return nullptr; } diff --git a/src/helpers.h b/src/helpers.h index 95fa7a5db..cc153079b 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -27,7 +27,7 @@ namespace emsesp { class Helpers { public: - static char * render_value(char * result, const float value, const int8_t format); // format is the precision + static char * render_value(char * result, const double value, const int8_t format); // format is the precision static char * render_value(char * result, const uint8_t value, const int8_t format, const uint8_t fahrenheit = 0); static char * render_value(char * result, const int8_t value, const int8_t format, const uint8_t fahrenheit = 0); static char * render_value(char * result, const uint16_t value, const int8_t format, const uint8_t fahrenheit = 0); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 2e8b4de4a..c29c1099d 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -36,7 +36,9 @@ uint32_t Mqtt::publish_time_solar_; uint32_t Mqtt::publish_time_mixer_; uint32_t Mqtt::publish_time_sensor_; uint32_t Mqtt::publish_time_other_; +uint32_t Mqtt::publish_time_heartbeat_; bool Mqtt::mqtt_enabled_; +bool Mqtt::multiple_instances_; bool Mqtt::ha_enabled_; uint8_t Mqtt::nested_format_; std::string Mqtt::discovery_prefix_; @@ -146,6 +148,12 @@ void Mqtt::loop() { process_queue(); } + // send heartbeat + if ((currentMillis - last_publish_heartbeat_ > publish_time_heartbeat_)) { + last_publish_heartbeat_ = currentMillis; + EMSESP::system_.send_heartbeat(); // send heartbeat + } + // dallas publish on change if (!publish_time_sensor_) { EMSESP::publish_sensor_values(false); @@ -280,6 +288,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons } return; } + // for misconfigured mqtt servers and publish2command ignore echos if (publish_single_ && publish_single2cmd_ && lasttopic_ == topic && lastpayload_ == message) { LOG_DEBUG("Received echo message %s: %s", topic, message); @@ -291,11 +300,12 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons // add the base back char full_topic[MQTT_TOPIC_MAX_SIZE]; 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("error: invalid payload %s for this topic %s", message, topic); if (send_response_) { - Mqtt::publish(F_(response), "error: invalid data"); + Mqtt::publish("response", "error: invalid data"); } } return; @@ -330,12 +340,12 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons } LOG_ERROR(error); if (send_response_) { - Mqtt::publish(F_(response), error); + Mqtt::publish("response", error); } } else { // all good, send back json output from call if (send_response_) { - Mqtt::publish(F_(response), output); + Mqtt::publish("response", output); } } } @@ -420,6 +430,7 @@ void Mqtt::load_settings() { publish_single2cmd_ = mqttSettings.publish_single2cmd; send_response_ = mqttSettings.send_response; discovery_prefix_ = mqttSettings.discovery_prefix.c_str(); + multiple_instances_ = mqttSettings.multiple_instances; // convert to milliseconds publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000; @@ -428,9 +439,11 @@ void Mqtt::load_settings() { publish_time_mixer_ = mqttSettings.publish_time_mixer * 1000; publish_time_other_ = mqttSettings.publish_time_other * 1000; publish_time_sensor_ = mqttSettings.publish_time_sensor * 1000; + publish_time_heartbeat_ = mqttSettings.publish_time_heartbeat * 1000; }); // create basename from base + // by taking the MQTT base path and replacing all / with underscores mqtt_basename_ = mqtt_base_; std::replace(mqtt_basename_.begin(), mqtt_basename_.end(), '/', '_'); } @@ -510,6 +523,10 @@ void Mqtt::set_publish_time_sensor(uint16_t publish_time) { publish_time_sensor_ = publish_time * 1000; // convert to milliseconds } +void Mqtt::set_publish_time_heartbeat(uint16_t publish_time) { + publish_time_heartbeat_ = publish_time * 1000; // convert to milliseconds +} + bool Mqtt::get_publish_onchange(uint8_t device_type) { if (publish_single_ && !ha_enabled_) { return false; @@ -591,18 +608,26 @@ void Mqtt::on_connect() { void Mqtt::ha_status() { StaticJsonDocument doc; - doc["uniq_id"] = "ems-esp-system"; - doc["~"] = mqtt_base_; // default ems-esp - // 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"] = "~/status"; - doc["object_id"] = "ems_esp_status"; + char uniq[70]; + if (Mqtt::multiple_instances()) { + snprintf(uniq, sizeof(uniq), "%s_system_status", mqtt_basename_.c_str()); + } else { + strcpy(uniq, "system_status"); + } + + doc["uniq_id"] = uniq; + doc["object_id"] = uniq; + + doc["stat_t"] = mqtt_base_ + "/status"; doc["name"] = "EMS-ESP status"; doc["payload_on"] = "online"; doc["payload_off"] = "offline"; doc["state_class"] = "measurement"; doc["device_class"] = "connectivity"; + // doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes + // doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes + JsonObject dev = doc.createNestedObject("dev"); dev["name"] = "EMS-ESP"; dev["sw"] = "v" + std::string(EMSESP_APP_VERSION); @@ -612,7 +637,7 @@ void Mqtt::ha_status() { ids.add("ems-esp"); char topic[MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "binary_sensor/%s/system/config", mqtt_basename_.c_str()); + snprintf(topic, sizeof(topic), "binary_sensor/%s/system_status/config", mqtt_basename_.c_str()); Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag // create the sensors - must match the MQTT payload keys @@ -647,7 +672,7 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation, std::shared_ptr message; message = std::make_shared(operation, topic, payload, retain); -#ifdef EMSESP_DEBUG +#if defined(EMSESP_DEBUG) if (operation == Operation::PUBLISH) { if (message->payload.empty()) { LOG_INFO("[DEBUG] Adding to queue: (Publish) topic='%s' empty payload", message->topic.c_str()); @@ -780,6 +805,7 @@ void Mqtt::process_queue() { if (message->topic.find(discovery_prefix_) == 0) { strlcpy(topic, message->topic.c_str(), sizeof(topic)); // leave topic as it is } else { + // it's a discovery topic, added the mqtt base to the topic path snprintf(topic, MQTT_TOPIC_MAX_SIZE, "%s/%s", mqtt_base_.c_str(), message->topic.c_str()); // uses base } @@ -841,7 +867,9 @@ void Mqtt::process_queue() { mqtt_message.retry_count_ + 1, message->payload.size(), packet_id); - +#if defined(EMSESP_DEBUG) + LOG_DEBUG("Payload:%s", message->payload.c_str()); // extra message for #784 +#endif 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)) { @@ -904,7 +932,6 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, publish_ha_sensor_config(dv.type, dv.tag, dv.get_fullname().c_str(), - (dv.fullname ? dv.fullname[0] : nullptr), // EN name dv.device_type, dv.short_name, dv.uom, @@ -925,7 +952,7 @@ void Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, cons JsonArray ids = dev_json.createNestedArray("ids"); ids.add("ems-esp"); - publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json); + publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, dev_json); } // MQTT discovery configs @@ -934,7 +961,6 @@ void Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, cons 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, // original name const uint8_t device_type, // EMSdevice::DeviceType const char * const entity, // same as shortname const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE) @@ -946,25 +972,34 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev const int16_t dv_set_max, const JsonObject & dev_json) { // ignore if name (fullname) is empty - if (fullname == nullptr || en_name == nullptr) { + if (!fullname) { return; } // create the 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]; + // create entity by add the hc/wwc tag if present, separating with a _ + char entity_with_tag[50]; if (tag >= DeviceValueTAG::TAG_HC1) { - snprintf(new_entity, sizeof(new_entity), "%s.%s", EMSdevice::tag_to_mqtt(tag).c_str(), entity); + snprintf(entity_with_tag, sizeof(entity_with_tag), "%s_%s", EMSdevice::tag_to_mqtt(tag).c_str(), entity); } else { - snprintf(new_entity, sizeof(new_entity), "%s", entity); + snprintf(entity_with_tag, sizeof(entity_with_tag), "%s", entity); } - // build unique identifier which will be used in the topic, replacing all . with _ as not to break HA - char uniq[101]; - snprintf(uniq, sizeof(uniq), "%s_%s", device_name, new_entity); - Helpers::replace_char(uniq, '.', '_'); + // build unique identifier which will be used in the topic, also used as object_id + char uniq_id[70]; + if (Mqtt::multiple_instances()) { + // prefix base name to each uniq_id + snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag); + } else { + snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, entity_with_tag); + } + + // build a config topic that will be prefix onto a HA type (e.g. number, switch) + // e.g. homeassistant/number/ems-esp/thermostat_hc1_manualtemp + char config_topic[70]; + snprintf(config_topic, sizeof(config_topic), "%s/%s_%s/config", mqtt_basename_.c_str(), device_name, entity_with_tag); bool set_ha_classes = false; // set to true if we want to set the state class and device class @@ -981,46 +1016,43 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev case DeviceValueType::ULONG: // number - https://www.home-assistant.io/integrations/number.mqtt // https://developers.home-assistant.io/docs/core/entity/number - snprintf(topic, sizeof(topic), "number/%s/%s/config", mqtt_basename_.c_str(), uniq); + snprintf(topic, sizeof(topic), "number/%s", config_topic); break; case DeviceValueType::BOOL: // switch - https://www.home-assistant.io/integrations/switch.mqtt - snprintf(topic, sizeof(topic), "switch/%s/%s/config", mqtt_basename_.c_str(), uniq); + snprintf(topic, sizeof(topic), "switch/%s", config_topic); break; case DeviceValueType::ENUM: // select - https://www.home-assistant.io/integrations/select.mqtt - snprintf(topic, sizeof(topic), "select/%s/%s/config", mqtt_basename_.c_str(), uniq); + snprintf(topic, sizeof(topic), "select/%s", config_topic); break; default: // plain old sensor - snprintf(topic, sizeof(topic), "sensor/%s/%s/config", mqtt_basename_.c_str(), uniq); + snprintf(topic, sizeof(topic), "sensor/%s", config_topic); break; } } else { - // these are Read only sensors. We can set the device class and state class - set_ha_classes = true; + set_ha_classes = true; // these are Read only sensors. We can set the device class and state class // plain old read only device entity if (type == DeviceValueType::BOOL) { - snprintf(topic, sizeof(topic), "binary_sensor/%s/%s/config", mqtt_basename_.c_str(), uniq); // binary sensor (for booleans) + snprintf(topic, sizeof(topic), "binary_sensor/%s", config_topic); // binary sensor (for booleans) } else { - snprintf(topic, sizeof(topic), "sensor/%s/%s/config", mqtt_basename_.c_str(), uniq); // normal HA sensor + snprintf(topic, sizeof(topic), "sensor/%s", config_topic); // normal HA sensor } } // 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("Removing HA config for %s", uniq); + LOG_DEBUG("Removing HA config for %s", uniq_id); publish_ha(topic); return; } - bool have_tag = !EMSdevice::tag_to_string(tag).empty(); - // build the payload DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG); - doc["~"] = mqtt_base_; - doc["uniq_id"] = uniq; + doc["uniq_id"] = uniq_id; + doc["object_id"] = uniq_id; // same as unique_id const char * ic_ha = "ic"; // icon - only set this if there is no device class const char * sc_ha = "state_class"; // state class @@ -1031,9 +1063,12 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // note: there is no way to handle strings in HA so datetimes (e.g. set_datetime, set_holiday, set_wwswitchtime etc) are excluded if (has_cmd) { // command topic back to EMS-ESP - char command_topic[105]; - snprintf(command_topic, sizeof(command_topic), "~/%s", uniq); - Helpers::replace_char(command_topic, '_', '/'); + char command_topic[MQTT_TOPIC_MAX_SIZE]; + if (tag >= DeviceValueTAG::TAG_HC1) { + snprintf(command_topic, sizeof(command_topic), "%s/%s/%s/%s", mqtt_basename_.c_str(), device_name, EMSdevice::tag_to_mqtt(tag).c_str(), entity); + } else { + snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", mqtt_basename_.c_str(), device_name, entity); + } doc["command_topic"] = command_topic; // for enums, add options @@ -1075,38 +1110,30 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // state topic char stat_t[MQTT_TOPIC_MAX_SIZE]; - snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/%s", mqtt_basename_.c_str(), tag_to_topic(device_type, tag).c_str()); doc["stat_t"] = stat_t; // friendly name = char ha_name[70]; 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); + if (EMSdevice::tag_to_string(tag).empty()) { + snprintf(ha_name, sizeof(ha_name), "%s", F_name); // no tag } else { - snprintf(ha_name, sizeof(ha_name), "%s", F_name); + snprintf(ha_name, sizeof(ha_name), "%s %s", EMSdevice::tag_to_string(tag).c_str(), F_name); } - free(F_name); + free(F_name); // very important! doc["name"] = ha_name; - // 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 commented out) - char object_id[130]; - if (have_tag) { - snprintf(object_id, sizeof(object_id), "%s_%s_%s", device_name, EMSdevice::tag_to_string(tag, false).c_str(), en_name); - } else { - snprintf(object_id, sizeof(object_id), "%s_%s", device_name, en_name); - } - doc["object_id"] = object_id; - // value template - // if its nested mqtt format then use the appended entity name, otherwise take the original + // if its nested mqtt format then use the appended entity name, otherwise take the original name char val_tpl[75]; if (is_nested()) { - snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", new_entity); + if (tag >= DeviceValueTAG::TAG_HC1) { + snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s.%s}}", EMSdevice::tag_to_mqtt(tag).c_str(), entity); + } else { + snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity); + } } else { snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity); } @@ -1230,8 +1257,7 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, char currtemp_s[30]; char mode_str_tpl[400]; char name_s[10]; - char id_s[20]; - char uniq_id_s[30]; + char uniq_id_s[60]; char temp_cmd_s[30]; char mode_cmd_s[30]; char min_s[10]; @@ -1269,18 +1295,23 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, hc_mode_s, hc_mode_s); - snprintf(id_s, sizeof(id_s), "thermostat_hc%d", hc_num); snprintf(name_s, sizeof(name_s), "Hc%d", hc_num); - snprintf(uniq_id_s, sizeof(uniq_id_s), "thermostat_hc%d", hc_num); + + if (Mqtt::multiple_instances()) { + snprintf(uniq_id_s, sizeof(uniq_id_s), "%s_thermostat_hc%d", mqtt_basename_.c_str(), hc_num); // add basename + } else { + snprintf(uniq_id_s, sizeof(uniq_id_s), "thermostat_hc%d", hc_num); // backward compatible with v3.4 + } + snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num); snprintf(mode_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/mode", hc_num); StaticJsonDocument doc; doc["~"] = mqtt_base_; - doc["object_id"] = id_s; - doc["name"] = name_s; doc["uniq_id"] = uniq_id_s; + doc["object_id"] = uniq_id_s; // same as uniq_id + doc["name"] = name_s; doc["mode_stat_t"] = topic_t; doc["mode_stat_tpl"] = mode_str_tpl; doc["temp_cmd_t"] = temp_cmd_s; diff --git a/src/mqtt.h b/src/mqtt.h index 406752fa8..348078196 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -65,6 +65,7 @@ class Mqtt { void set_publish_time_mixer(uint16_t publish_time); void set_publish_time_other(uint16_t publish_time); void set_publish_time_sensor(uint16_t publish_time); + void set_publish_time_heartbeat(uint16_t publish_time); bool get_publish_onchange(uint8_t device_type); enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE }; @@ -94,7 +95,6 @@ class Mqtt { 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, @@ -180,6 +180,10 @@ class Mqtt { return nested_format_ == NestedFormat::NESTED; } + static bool multiple_instances() { + return multiple_instances_; + } + static void nested_format(uint8_t nested_format) { nested_format_ = nested_format; } @@ -289,6 +293,7 @@ class Mqtt { uint32_t last_publish_mixer_ = 0; uint32_t last_publish_other_ = 0; uint32_t last_publish_sensor_ = 0; + uint32_t last_publish_heartbeat_ = 0; uint32_t last_publish_queue_ = 0; static bool connecting_; @@ -312,9 +317,11 @@ class Mqtt { static uint32_t publish_time_mixer_; static uint32_t publish_time_other_; static uint32_t publish_time_sensor_; + static uint32_t publish_time_heartbeat_; static bool mqtt_enabled_; static bool ha_enabled_; static uint8_t nested_format_; + static bool multiple_instances_; static std::string discovery_prefix_; static bool publish_single_; static bool publish_single2cmd_; diff --git a/src/shower.cpp b/src/shower.cpp index d2cb1c678..fa43a6f05 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -151,10 +151,17 @@ void Shower::set_shower_state(bool state, bool force) { ha_configdone_ = true; StaticJsonDocument doc; - doc["name"] = "Shower Active"; - doc["uniq_id"] = "shower_active"; - doc["~"] = Mqtt::base(); - doc["stat_t"] = "~/shower_active"; + + doc["name"] = "Shower Active"; + + char str[70]; + snprintf(str, sizeof(str), "%s_shower_active", Mqtt::basename().c_str()); + doc["uniq_id"] = str; + doc["object_id"] = str; + + char stat_t[50]; + snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str()); // use base path + doc["stat_t"] = stat_t; // always render boolean as strings for HA char result[12]; @@ -166,7 +173,7 @@ void Shower::set_shower_state(bool state, bool force) { ids.add("ems-esp"); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::base().c_str()); + snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag } } diff --git a/src/system.cpp b/src/system.cpp index 56039652a..5275a2a1a 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -512,12 +512,6 @@ void System::loop() { led_monitor(); // check status and report back using the LED system_check(); // check system health - // send out heartbeat - uint32_t currentMillis = uuid::get_uptime(); - if (!last_heartbeat_ || (currentMillis - last_heartbeat_ > SYSTEM_HEARTBEAT_INTERVAL)) { - last_heartbeat_ = currentMillis; - send_heartbeat(); - } #ifndef EMSESP_STANDALONE #if defined(EMSESP_DEBUG) @@ -536,23 +530,23 @@ void System::loop() { } // send MQTT info topic appended with the version information as JSON, as a retained flag -void System::send_info_mqtt(const char * event_str) { +void System::send_info_mqtt(const char * event_str, bool send_ntp) { StaticJsonDocument doc; doc["event"] = event_str; doc["version"] = EMSESP_APP_VERSION; // if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38) // https://github.com/emsesp/EMS-ESP32/issues/751 - if (ntp_connected()) { + if (send_ntp) { char time_string[25]; - time_t now = time(nullptr); // grab the current instant in unix seconds - strftime(time_string, 25, "%F %T", localtime(&now)); - doc["boot_time"] = time_string; + time_t now = time(nullptr) - uuid::get_uptime_sec(); + strftime(time_string, 25, "%FT%T%z", localtime(&now)); + doc["boot time"] = time_string; } #ifndef EMSESP_STANDALONE if (EMSESP::system_.ethernet_connected()) { - doc["connection"] = "ethernet"; + doc["network"] = "ethernet"; doc["hostname"] = ETH.getHostname(); doc["MAC"] = ETH.macAddress(); doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask()); @@ -562,7 +556,7 @@ void System::send_info_mqtt(const char * event_str) { doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6()); } } else if (WiFi.status() == WL_CONNECTED) { - doc["connection"] = "wifi"; + doc["network"] = "wifi"; doc["hostname"] = WiFi.getHostname(); doc["SSID"] = WiFi.SSID(); doc["BSSID"] = WiFi.BSSIDstr(); @@ -896,19 +890,19 @@ void System::show_system(uuid::console::Shell & shell) { shell.println("Network:"); switch (WiFi.status()) { case WL_IDLE_STATUS: - shell.printfln(" Network: Idle"); + shell.printfln(" Status: Idle"); break; case WL_NO_SSID_AVAIL: - shell.printfln(" Network: Network not found"); + shell.printfln(" Status: Network not found"); break; case WL_SCAN_COMPLETED: - shell.printfln(" Network: Network scan complete"); + shell.printfln(" Status: Network scan complete"); break; case WL_CONNECTED: - shell.printfln(" Network: connected"); + shell.printfln(" Status: connected"); shell.printfln(" SSID: %s", WiFi.SSID().c_str()); shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str()); shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI())); @@ -943,7 +937,7 @@ void System::show_system(uuid::console::Shell & shell) { // show Ethernet if connected if (ethernet_connected_) { shell.println(); - shell.printfln(" Ethernet Network: connected"); + shell.printfln(" Status: Ethernet connected"); shell.printfln(" MAC address: %s", ETH.macAddress().c_str()); shell.printfln(" Hostname: %s", ETH.getHostname()); shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str()); @@ -1071,9 +1065,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); // node["uptime (seconds)"] = uuid::get_uptime_sec(); #ifndef EMSESP_STANDALONE - node["freemem"] = ESP.getFreeHeap() / 1024; // kilobytes - node["maxalloc"] = ESP.getMaxAllocHeap() / 1024; // kilobytes - node["free_app"] = EMSESP::system_.appFree(); // kilobytes + node["free mem"] = ESP.getFreeHeap() / 1024; // kilobytes + node["max alloc"] = ESP.getMaxAllocHeap() / 1024; // kilobytes + node["free app"] = EMSESP::system_.appFree(); // kilobytes #endif node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1); @@ -1081,7 +1075,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp // Network Status node = output.createNestedObject("Network Info"); if (EMSESP::system_.ethernet_connected()) { - node["connection"] = "Ethernet"; + node["network"] = "Ethernet"; node["hostname"] = ETH.getHostname(); node["MAC"] = ETH.macAddress(); node["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask()); @@ -1091,8 +1085,8 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6()); } } else if (WiFi.status() == WL_CONNECTED) { - node["connection"] = "WiFi"; - node["hostname"] = WiFi.getHostname(); + node["network"] = "WiFi"; + node["hostname"] = WiFi.getHostname(); // node["SSID"] = WiFi.SSID(); // node["BSSID"] = WiFi.BSSIDstr(); node["RSSI"] = WiFi.RSSI(); @@ -1110,6 +1104,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["enable IPv6"] = settings.enableIPv6; node["low bandwidth"] = settings.bandwidth20; node["disable sleep"] = settings.nosleep; + node["enable MDNS"] = settings.enableMDNS; + node["enable CORS"] = settings.enableCORS; + if (settings.enableCORS) { + node["CORS origin"] = settings.CORSOrigin; + } }); #ifndef EMSESP_STANDALONE EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) { @@ -1150,15 +1149,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp } EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { node["enabled"] = settings.enabled; - node["client_id"] = settings.clientId; + node["client id"] = settings.clientId; node["keep alive"] = settings.keepAlive; node["clean session"] = settings.cleanSession; + node["multiple instances"] = settings.multiple_instances; node["base"] = settings.base; node["discovery prefix"] = settings.discovery_prefix; node["nested format"] = settings.nested_format; node["ha enabled"] = settings.ha_enabled; node["mqtt qos"] = settings.mqtt_qos; node["mqtt retain"] = settings.mqtt_retain; + node["publish time heartbeat"] = settings.publish_time_heartbeat; node["publish time boiler"] = settings.publish_time_boiler; node["publish time thermostat"] = settings.publish_time_thermostat; node["publish time solar"] = settings.publish_time_solar; @@ -1245,7 +1246,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["phy type"] = settings.phy_type; if (settings.phy_type != PHY_type::PHY_TYPE_NONE) { node["eth power"] = settings.eth_power; - node["eth phy_addr"] = settings.eth_phy_addr; + node["eth phy addr"] = settings.eth_phy_addr; node["eth clock_mode"] = settings.eth_clock_mode; } node["rx gpio"] = settings.rx_gpio; @@ -1264,6 +1265,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["enum format"] = settings.enum_format; node["analog enabled"] = settings.analog_enabled; node["telnet enabled"] = settings.telnet_enabled; + node["web log buffer"] = settings.weblog_buffer; }); // Devices - show EMS devices if we have any @@ -1416,6 +1418,7 @@ void System::ntp_connected(bool b) { if (b != ntp_connected_) { LOG_INFO(b ? "NTP connected" : "NTP disconnected"); // if changed report it } + ntp_connected_ = b; ntp_last_check_ = b ? uuid::get_uptime_sec() : 0; } @@ -1426,6 +1429,7 @@ bool System::ntp_connected() { if ((uuid::get_uptime_sec() - ntp_last_check_ > 7201) && ntp_connected_) { ntp_connected(false); } + return ntp_connected_; } diff --git a/src/system.h b/src/system.h index e13efd3a8..3f4169306 100644 --- a/src/system.h +++ b/src/system.h @@ -77,7 +77,7 @@ class System { bool check_upgrade(); bool heartbeat_json(JsonObject & output); void send_heartbeat(); - void send_info_mqtt(const char * event_str); + void send_info_mqtt(const char * event_str, bool send_ntp = false); bool syslog_enabled() { return syslog_enabled_; @@ -250,7 +250,6 @@ class System { static constexpr uint32_t HEALTHCHECK_LED_FLASH_DUARATION = 150; static constexpr uint8_t HEALTHCHECK_NO_BUS = (1 << 0); // 1 static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2 - static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min) static constexpr uint8_t LED_ON = HIGH; // LED on #ifndef EMSESP_STANDALONE @@ -263,7 +262,6 @@ class System { int8_t wifi_quality(int8_t dBm); uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection - uint32_t last_heartbeat_ = 0; uint32_t last_system_check_ = 0; bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload diff --git a/src/telegram.cpp b/src/telegram.cpp index 45e86a4f9..c7d2869c9 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -268,6 +268,21 @@ void TxService::send_poll() const { } } +// get src id from next telegram to check poll in emsesp::incoming_telegram +uint8_t TxService::get_send_id() { + static uint32_t count = 0; + if (!tx_telegrams_.empty() && tx_telegrams_.front().telegram_->src != ems_bus_id()) { + if (++count > 500) { // after 500 polls (~3-10 sec) there will be no master poll for this id + tx_telegrams_.pop_front(); + count = 0; + return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src; + } + return tx_telegrams_.front().telegram_->src; + } + count = 0; + return ems_bus_id(); +} + // Process the next telegram on the Tx queue // This is sent when we receive a poll request void TxService::send() { @@ -463,7 +478,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt } // build header. src, dest and offset have fixed positions - uint8_t src = ems_bus_id(); // data[0]; we can only send data with own bus_id. + uint8_t src = operation == Telegram::Operation::TX_RAW ? data[0] : ems_bus_id(); uint8_t dest = data[1]; uint8_t offset = data[3]; @@ -499,7 +514,9 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt } if (operation == Telegram::Operation::TX_RAW) { - if (dest & 0x80) { + if (src != ems_bus_id()) { + operation = Telegram::Operation::NONE; // do not check reply/ack for other ids + } else if (dest & 0x80) { operation = Telegram::Operation::TX_READ; } else { operation = Telegram::Operation::TX_WRITE; @@ -585,8 +602,8 @@ bool TxService::send_raw(const char * telegram_data) { } } - // check valid length and src - if ((count < 4) || ((data[0] & 0x7F) != ems_bus_id())) { + // check valid length + if (count < 4) { return false; } diff --git a/src/telegram.h b/src/telegram.h index 710558f57..452edcb8b 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -290,6 +290,7 @@ class TxService : public EMSbus { void start(); void send(); + uint8_t get_send_id(); void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, diff --git a/src/test/test.cpp b/src/test/test.cpp index 3135f6ba4..d4ab3fe8e 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -1397,9 +1397,21 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // add a boiler add_device(0x08, 123); // Nefit Trendline - // add a thermostat - add_device(0x18, 157); // Bosch CR100 - https://github.com/emsesp/EMS-ESP/issues/355 + // add some boiler data + // Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25) + uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, + 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00}); + // Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2) + uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24}); + + // Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13) + uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00}); + + // add a thermostat + add_device(0x18, 157); // Bosch CR100 + + // add some thermostat data // RCPLUSStatusMessage_HC1(0x01A5) - HC1 uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); @@ -1412,11 +1424,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const char boiler_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char thermostat_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + char thermostat_topic_hc1[Mqtt::MQTT_TOPIC_MAX_SIZE]; char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; Mqtt::show_mqtt(shell); // show queue strlcpy(boiler_topic, "ems-esp/boiler", sizeof(boiler_topic)); strlcpy(thermostat_topic, "ems-esp/thermostat", sizeof(thermostat_topic)); + strlcpy(thermostat_topic_hc1, "ems-esp/thermostat/hc1", sizeof(thermostat_topic)); strlcpy(system_topic, "ems-esp/system", sizeof(system_topic)); // test publishing @@ -1425,11 +1439,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // test receiving EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values - EMSESP::mqtt_.incoming(boiler_topic, "12345"); // error: invalid format - EMSESP::mqtt_.incoming("bad_topic", "123456"); // error: no matching topic - EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"garbage\",\"data\":22.52}"); // error: should report error + // these all should fail + EMSESP::mqtt_.incoming(boiler_topic, "12345"); // error: invalid format + EMSESP::mqtt_.incoming("bad_topic", "123456"); // error: no matching topic + EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"garbage\",\"data\":22.52}"); // error: should report error + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"control\",\"data\":\"1\"}"); // RC35 only, should error - EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"comfort\",\"data\":\"eco\"}"); + // these all should pass EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"wwactivated\",\"data\":\"1\"}"); // with quotes EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"wwactivated\",\"data\":1}"); // without quotes EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"selflowtemp\",\"data\":55}"); @@ -1438,19 +1454,22 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::mqtt_.incoming("ems-esp/boiler/selflowtemp", "56"); EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"send\",\"data\":\"01 02 03 04 05\"}"); - EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"pin\",\"id\":12,\"data\":\"1\"}"); + // EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"pin\",\"id\":12,\"data\":\"1\"}"); EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"wwmode\",\"data\":\"auto\"}"); - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"control\",\"data\":\"1\"}"); // RC35 only, should error EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"typo\",\"id\":2}"); // invalid mode EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"id\":2}"); - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"hc\":2}"); // hc as number - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"temp\",\"data\":19.5,\"hc\":1}"); // data as number - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"hc\":\"2\"}"); // hc as string. should error as no hc2 - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"temp\",\"data\":22.56}"); - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"temp\",\"data\":22}"); - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"temp\",\"data\":\"22.56\"}"); - EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"temp\",\"id\":2,\"data\":22}"); + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"hc\":2}"); // hc as number + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":19.5,\"hc\":1}"); // data as number + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":\"auto\",\"hc\":\"2\"}"); // hc as string. should error as no hc2 + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":22.56}"); + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":22}"); + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":\"22.56\"}"); + EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"id\":2,\"data\":22}"); + + // test with hc + EMSESP::mqtt_.incoming("ems-esp/thermostat/hc1/seltemp", "30"); + EMSESP::mqtt_.incoming("ems-esp/thermostat/hc2/seltemp", "32"); // test single commands EMSESP::mqtt_.incoming(thermostat_topic, "auto"); diff --git a/src/test/test.h b/src/test/test.h index af16002f3..e8d7a287e 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -30,8 +30,8 @@ namespace emsesp { // #define EMSESP_DEBUG_DEFAULT "solar" // #define EMSESP_DEBUG_DEFAULT "mixer" // #define EMSESP_DEBUG_DEFAULT "web" -// #define EMSESP_DEBUG_DEFAULT "mqtt" -#define EMSESP_DEBUG_DEFAULT "general" +#define EMSESP_DEBUG_DEFAULT "mqtt" +// #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 76199a5b4..1a4fba9ca 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define EMSESP_APP_VERSION "3.5.0b10" +#define EMSESP_APP_VERSION "3.5.0b11" #if CONFIG_IDF_TARGET_ESP32C3 #define EMSESP_PLATFORM "ESP32-C3"; diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index 085aec367..5fe1a40d2 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -101,8 +101,16 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) { } // output json buffer - auto * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); - JsonObject output = response->getRoot(); + auto * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); + if (!response->getSize()) { + delete response; + response = new PrettyAsyncJsonResponse(false, 256); + response->setCode(507); // Insufficient Storage + response->setLength(); + request->send(response); + return; + } + JsonObject output = response->getRoot(); // call command uint8_t return_code = Command::process(request->url().c_str(), is_admin, input, output); @@ -136,7 +144,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) { // send the json that came back from the command call // FAIL, OK, NOT_FOUND, ERROR, NOT_ALLOWED = 400 (bad request), 200 (OK), 400 (not found), 400 (bad request), 401 (unauthorized) - int ret_codes[5] = {400, 200, 400, 400, 401}; + int ret_codes[6] = {400, 200, 400, 400, 401, 400}; response->setCode(ret_codes[return_code]); response->setLength(); response->setContentType("application/json; charset=utf-8"); diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 5a641e91a..0dffaf843 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -201,6 +201,14 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) { void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { auto * response = new MsgpackAsyncJsonResponse(true, EMSESP_JSON_SIZE_XXXLARGE_DYN); + if (!response->getSize()) { + delete response; + response = new MsgpackAsyncJsonResponse(true, 256); + response->setCode(507); // Insufficient Storage + response->setLength(); + request->send(response); + return; + } for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice->unique_id() == json["id"]) { #ifndef EMSESP_STANDALONE diff --git a/src/web/WebCustomizationService.h b/src/web/WebCustomizationService.h index e5d7a9512..d0a979807 100644 --- a/src/web/WebCustomizationService.h +++ b/src/web/WebCustomizationService.h @@ -44,8 +44,8 @@ class AnalogCustomization { public: uint8_t gpio; std::string name; - float offset; - float factor; + double offset; + double factor; uint8_t uom; // 0 is none int8_t type; // -1 is for deletion diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 81e6d0ae7..f708b8528 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -166,6 +166,14 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) { void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { auto * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXXLARGE_DYN); + if (!response->getSize()) { + delete response; + response = new MsgpackAsyncJsonResponse(false, 256); + response->setCode(507); // Insufficient Storage + response->setLength(); + request->send(response); + return; + } for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice->unique_id() == json["id"]) { // wait max 2.5 sec for updated data (post_send_delay is 2 sec) @@ -284,8 +292,8 @@ void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant & uint8_t gpio = analog["gpio"]; // this is the unique key, the GPIO std::string name = analog["name"]; - float factor = analog["factor"]; - float offset = analog["offset"]; + double factor = analog["factor"]; + double offset = analog["offset"]; uint8_t uom = analog["uom"]; int8_t type = analog["type"]; ok = EMSESP::analogsensor_.update(gpio, name, offset, factor, uom, type);