diff --git a/.github/workflows/pre_release.yml b/.github/workflows/pre_release.yml index fbb8c27d1..844740324 100644 --- a/.github/workflows/pre_release.yml +++ b/.github/workflows/pre_release.yml @@ -13,12 +13,12 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.11' - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' - name: Get EMS-ESP source code and version id: build_info diff --git a/.github/workflows/tagged_release.yml b/.github/workflows/tagged_release.yml index b1ae80694..7d7e3abf9 100644 --- a/.github/workflows/tagged_release.yml +++ b/.github/workflows/tagged_release.yml @@ -12,12 +12,12 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.11' - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' - name: Install PlatformIO run: | diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index e79684864..db5d88b8b 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -13,12 +13,12 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.11' - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' - name: Get EMS-ESP source code and version id: build_info diff --git a/.gitignore b/.gitignore index b9391d904..f680ef353 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # vscode -.vscode/* +.vscode/c_cpp_properties.json +.vscode/extensions.json +.vscode/launch.json +#.vscode/settings.json # c++ compiling .clang_complete @@ -34,7 +37,7 @@ stats.html !.yarn/sdks !.yarn/versions yarn.lock -interface/analyse.html +analyse.html interface/vite.config.ts.timestamp* # scripts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..493bf41fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,91 @@ +{ + "search.exclude": { + "**/.yarn": true, + "**/.pnp.*": true + }, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit" + }, + "eslint.nodePath": "interface/.yarn/sdks", + "eslint.workingDirectories": ["interface"], + "prettier.prettierPath": "", + "typescript.enablePromptUseWorkspaceTsdk": true, + "files.associations": { + "*.tsx": "typescriptreact", + "*.tcc": "cpp", + "optional": "cpp", + "istream": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "system_error": "cpp", + "array": "cpp", + "functional": "cpp", + "regex": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "string": "cpp", + "string_view": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "set": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + }, + "todo-tree.filtering.excludeGlobs": [ + "**/vendor/**", + "**/node_modules/**", + "**/dist/**", + "**/bower_components/**", + "**/build/**", + "**/.vscode/**", + "**/.github/**", + "**/_output/**", + "**/*.min.*", + "**/*.map", + "**/ArduinoJson/**" + ], + "cSpell.enableFiletypes": [ + "!cpp", + "!typescript" + ] + } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index af5bc5c7c..88993e790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.6.5] March 23 2024 + +## **IMPORTANT! BREAKING CHANGES** + +- The Wifi Tx Power setting in Network Settings will be reset to Auto + +## Added + +- thermostat boost mode and boost time [#1446](https://github.com/emsesp/EMS-ESP32/issues/1446) +- heatpump energy meters [#1463](https://github.com/emsesp/EMS-ESP32/issues/1463) +- heatpump max power [#1475](https://github.com/emsesp/EMS-ESP32/issues/1475) +- checkbox for MQTT-TLS enable [#1474](https://github.com/emsesp/EMS-ESP32/issues/1474) +- added SK (Slovak) language. Thanks @misa1515 +- CPU info [#1497](https://github.com/emsesp/EMS-ESP32/pull/1497) +- Show network hostname in Web UI under Network Status +- Improved HA Discovery so each section (EMS device, Scheduler, Analog, Temperature, Custom, Shower) have their own section +- boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536) +- mixer MM100 telegram 0x2CC [#1554](https://github.com/emsesp/EMS-ESP32/issues/1554) +- boiler hpSetDiffPressure [#1563](https://github.com/emsesp/EMS-ESP32/issues/1563) +- custom variables [#1423](https://github.com/emsesp/EMS-ESP32/issues/1423) +- weather compensation [#1642](https://github.com/emsesp/EMS-ESP32/issues/1642) +- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635) +- command `restart partitionname` and button long press to start with other partition [#1657](https://github.com/emsesp/EMS-ESP32/issues/1657) +- command `set service ` [#1663](https://github.com/emsesp/EMS-ESP32/issues/1663) + +## Fixed + +- exhaust temperature for some boilers +- add back boil2hyst [#1477](https://github.com/emsesp/EMS-ESP32/issues/1477) +- subscribed MQTT topics not detecting changes by EMS-ESP [#1494](https://github.com/emsesp/EMS-ESP32/issues/1494) +- changed HA name and grouping to be consistent [#1528](https://github.com/emsesp/EMS-ESP32/issues/1528) +- MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528) +- dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495) +- added writeable icon to Web's Custom Entity page for each entity shown in the table +- Wifi Tx Power not adjusted [#1614](https://github.com/emsesp/EMS-ESP32/issues/1614) +- MQTT discovery of custom entity doesn't consider type of data [#1587](https://github.com/emsesp/EMS-ESP32/issues/1587) +- WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default. +- dns w/wo IPv6 [#1644](https://github.com/emsesp/EMS-ESP32/issues/1644) + +## Changed + +- HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459) +- upgraded ArduinoJson to 7.0.0 #1538 and then 7.0.2 +- small changes to the API for analog and temperature sensors +- Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619) +- C++ optimizations - see +- Send MQTT heartbeat immediately after connection [#1628](https://github.com/emsesp/EMS-ESP32/issues/1628) +- 16MB partitions with second nvs, larger FS, Coredump, optional factory partition +- stop fetching empty telegrams after 5 min + ## [3.6.4] November 24 2023 ## **IMPORTANT! BREAKING CHANGES** diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 513b69942..701256f79 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -1,61 +1,20 @@ # Changelog -## [3.6.5] +## [3.7.0] ## **IMPORTANT! BREAKING CHANGES** - new device WATER shows dhw entities from MM100 and SM100 in dhw setting -- The Wifi Tx Power setting in Network Settings will be reset to Auto ## Added - some more entities for dhw with SM100 module -- thermostat boost mode and boost time [#1446](https://github.com/emsesp/EMS-ESP32/issues/1446) -- heatpump energy meters [#1463](https://github.com/emsesp/EMS-ESP32/issues/1463) -- heatpump max power [#1475](https://github.com/emsesp/EMS-ESP32/issues/1475) -- checkbox for MQTT-TLS enable [#1474](https://github.com/emsesp/EMS-ESP32/issues/1474) -- added SK (Slovak) language. Thanks @misa1515 -- CPU info [#1497](https://github.com/emsesp/EMS-ESP32/pull/1497) -- Show network hostname in Web UI under Network Status -- Improved HA Discovery so each section (EMS device, Scheduler, Analog, Temperature, Custom, Shower) have their own section -- boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536) -- mixer MM100 telegram 0x2CC [#1554](https://github.com/emsesp/EMS-ESP32/issues/1554) -- boiler hpSetDiffPressure [#1563](https://github.com/emsesp/EMS-ESP32/issues/1563) -- custom variables [#1423](https://github.com/emsesp/EMS-ESP32/issues/1423) - thermostat second dhw circuit [#1634](https://github.com/emsesp/EMS-ESP32/issues/1634) -- heatpump dhw energy meter [#1609](https://github.com/emsesp/EMS-ESP32/issues/1609) - remote thermostat emulation for RC100H, RC200 and FB10 [#1287](https://github.com/emsesp/EMS-ESP32/discussions/1287), [#1602](https://github.com/emsesp/EMS-ESP32/discussions/1602), [#1551](https://github.com/emsesp/EMS-ESP32/discussions/1551) -- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635) - heatpump dhw stop temperatures [#1624](https://github.com/emsesp/EMS-ESP32/issues/1624) -- weather compensation [#1642](https://github.com/emsesp/EMS-ESP32/issues/1642) -- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635) -- command `restart partitionname` and button long press to start with other partition [#1657](https://github.com/emsesp/EMS-ESP32/issues/1657) -- command `set service ` [#1663](https://github.com/emsesp/EMS-ESP32/issues/1663) ## Fixed -- exhaust temperature for some boilers -- add back boil2hyst [#1477](https://github.com/emsesp/EMS-ESP32/issues/1477) -- subscribed MQTT topics not detecting changes by EMS-ESP [#1494](https://github.com/emsesp/EMS-ESP32/issues/1494) -- changed HA name and grouping to be consistent [#1528](https://github.com/emsesp/EMS-ESP32/issues/1528) -- MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528) -- dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495) -- added writeable icon to Web's Custom Entity page for each entity shown in the table -- Wifi Tx Power not adjusted [#1614](https://github.com/emsesp/EMS-ESP32/issues/1614) -- MQTT discovery of custom entity doesn't consider type of data [#1587](https://github.com/emsesp/EMS-ESP32/issues/1587) -- WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default. -- MQTT heap check [#622](https://github.com/emsesp/EMS-ESP32/issues/1622) -- Slovak language fix [#1636](https://github.com/emsesp/EMS-ESP32/discussions/1636) -- dns w/wo IPv6 [#1644](https://github.com/emsesp/EMS-ESP32/issues/1644) - ## Changed - use flag for BC400 compatible thermostats, manage different mode settings -- HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459) -- upgraded ArduinoJson to 7.0.0 #1538 and then 7.0.2 -- small changes to the API for analog and temperature sensors -- Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619) -- C++ optimizations - see -- Send MQTT heartbeat immediately after connection [#1628](https://github.com/emsesp/EMS-ESP32/issues/1628) -- 16MB partitions with second nvs, larger FS, Coredump, optional factory partition -- stop fetching empty telegrams after 5 min diff --git a/Makefile b/Makefile index 74fb42b02..107af8c0d 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DAR DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ -DEMC_RX_BUFFER_SIZE=1500 DEFINES += $(ARGS) -DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" +DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" #---------------------------------------------------------------------- # Sources & Files diff --git a/interface/package.json b/interface/package.json index 94a2d0830..d4ff8f00c 100644 --- a/interface/package.json +++ b/interface/package.json @@ -1,6 +1,6 @@ { "name": "EMS-ESP", - "version": "3.6.5", + "version": "3.7", "description": "build EMS-ESP WebUI", "homepage": "https://emsesp.github.io/docs", "author": "proddy", @@ -23,19 +23,19 @@ }, "dependencies": { "@alova/adapter-xhr": "^1.0.3", - "@babel/core": "^7.24.0", + "@babel/core": "^7.24.3", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.15.13", - "@mui/material": "^5.15.13", + "@mui/icons-material": "^5.15.14", + "@mui/material": "^5.15.14", "@table-library/react-table-library": "4.1.7", "@types/imagemin": "^8.0.5", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.11.28", - "@types/react": "^18.2.66", + "@types/node": "^20.11.30", + "@types/react": "^18.2.69", "@types/react-dom": "^18.2.22", "@types/react-router-dom": "^5.3.3", - "alova": "^2.17.1", + "alova": "^2.18.0", "async-validator": "^4.2.5", "history": "^5.3.0", "jwt-decode": "^4.0.0", @@ -49,13 +49,13 @@ "react-toastify": "^10.0.5", "sockette": "^2.0.6", "typesafe-i18n": "^5.26.2", - "typescript": "^5.4.2" + "typescript": "^5.4.3" }, "devDependencies": { "@preact/compat": "^17.1.2", "@preact/preset-vite": "^2.8.2", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", + "@typescript-eslint/eslint-plugin": "^7.3.1", + "@typescript-eslint/parser": "^7.3.1", "concurrently": "^8.2.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -64,13 +64,13 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-prettier": "alpha", - "eslint-plugin-react": "^7.34.0", + "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", - "preact": "^10.19.6", + "preact": "^10.20.1", "prettier": "^3.2.5", "rollup-plugin-visualizer": "^5.12.0", "terser": "^5.29.2", - "vite": "^5.1.6", + "vite": "^5.2.4", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^4.3.2" }, diff --git a/interface/src/App.tsx b/interface/src/App.tsx index 77f79e41d..18398cf4e 100644 --- a/interface/src/App.tsx +++ b/interface/src/App.tsx @@ -28,7 +28,7 @@ const App: FC = () => { ( - // const location = useLocation(); - // const navigate = useNavigate(); - // const handleApiResponseError = useCallback( - // (error: AxiosError) => { - // if (error.response && error.response.status === 401) { - // AuthenticationApi.storeLoginRedirect(location); - // navigate('/unauthorized'); - // } - // return Promise.reject(error); - // }, - // [location, navigate] - // ); - // useEffect(() => { - // const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError); - // return () => AXIOS.interceptors.response.eject(axiosHandlerId); - // }, [handleApiResponseError]); - - - - } /> - - - - } - /> - } /> - - } /> - } /> - } /> - } /> - - - - } - /> - } /> - } /> - - -); +const AuthenticatedRouting: FC = () => { + const { me } = useContext(AuthenticatedContext); + return ( + + + } /> + } /> + } /> + } /> + } /> + {me.admin && ( + <> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + )} + + + ); +}; export default AuthenticatedRouting; diff --git a/interface/src/api/authentication.ts b/interface/src/api/authentication.ts index 34c7af59a..c54ed2c64 100644 --- a/interface/src/api/authentication.ts +++ b/interface/src/api/authentication.ts @@ -32,7 +32,7 @@ export function fetchLoginRedirect(): Partial { const signInSearch = getStorage().getItem(SIGN_IN_SEARCH); clearLoginRedirect(); return { - pathname: signInPathname || `/dashboard`, + pathname: signInPathname || `/devices`, search: (signInPathname && signInSearch) || undefined }; } diff --git a/interface/src/api/mqtt.ts b/interface/src/api/mqtt.ts index d75dc4134..9605d843d 100644 --- a/interface/src/api/mqtt.ts +++ b/interface/src/api/mqtt.ts @@ -1,6 +1,7 @@ import { alovaInstance } from './endpoints'; -import type { MqttSettings, MqttStatus } from 'types'; +import type { MqttSettingsType, MqttStatusType } from 'types'; -export const readMqttStatus = () => alovaInstance.Get('/rest/mqttStatus'); -export const readMqttSettings = () => alovaInstance.Get('/rest/mqttSettings'); -export const updateMqttSettings = (data: MqttSettings) => alovaInstance.Post('/rest/mqttSettings', data); +export const readMqttStatus = () => alovaInstance.Get('/rest/mqttStatus'); +export const readMqttSettings = () => alovaInstance.Get('/rest/mqttSettings'); +export const updateMqttSettings = (data: MqttSettingsType) => + alovaInstance.Post('/rest/mqttSettings', data); diff --git a/interface/src/api/system.ts b/interface/src/api/system.ts index 8b3cb72fa..447866037 100644 --- a/interface/src/api/system.ts +++ b/interface/src/api/system.ts @@ -1,7 +1,10 @@ import { alovaInstance, alovaInstanceGH } from './endpoints'; -import type { OTASettings, SystemStatus, LogSettings } from 'types'; +import type { OTASettings, SystemStatus, LogSettings, ESPSystemStatus } from 'types'; -// SystemStatus - also used to ping in Restart monitor for pinging +// ESPSystemStatus - also used to ping in Restart monitor for pinging +export const readESPSystemStatus = () => alovaInstance.Get('/rest/ESPSystemStatus'); + +// SystemStatus export const readSystemStatus = () => alovaInstance.Get('/rest/systemStatus'); // commands diff --git a/interface/src/components/SectionContent.tsx b/interface/src/components/SectionContent.tsx index 95428d982..bcbaab8e4 100644 --- a/interface/src/components/SectionContent.tsx +++ b/interface/src/components/SectionContent.tsx @@ -4,8 +4,7 @@ import type { FC } from 'react'; import type { RequiredChildrenProps } from 'utils'; interface SectionContentProps extends RequiredChildrenProps { - title: string; - titleGutter?: boolean; + title?: string; id?: string; } @@ -13,7 +12,9 @@ const SectionContent: FC = (props) => { const { children, title, id } = props; return ( - {title} + {title && ( + {title} + )} {children} ); diff --git a/interface/src/components/layout/LayoutAppBar.tsx b/interface/src/components/layout/LayoutAppBar.tsx index b93e8e820..79380c8a2 100644 --- a/interface/src/components/layout/LayoutAppBar.tsx +++ b/interface/src/components/layout/LayoutAppBar.tsx @@ -1,6 +1,5 @@ import MenuIcon from '@mui/icons-material/Menu'; -import { AppBar, Box, IconButton, Toolbar, Typography } from '@mui/material'; -import LayoutAuthMenu from './LayoutAuthMenu'; +import { AppBar, IconButton, Toolbar, Typography } from '@mui/material'; import type { FC } from 'react'; export const DRAWER_WIDTH = 210; @@ -27,8 +26,6 @@ const LayoutAppBar: FC = ({ title, onToggleDrawer }) => ( {title} - - ); diff --git a/interface/src/components/layout/LayoutAuthMenu.tsx b/interface/src/components/layout/LayoutAuthMenu.tsx deleted file mode 100644 index 4710c3540..000000000 --- a/interface/src/components/layout/LayoutAuthMenu.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import AccountCircleIcon from '@mui/icons-material/AccountCircle'; -import PersonIcon from '@mui/icons-material/Person'; -import { - Box, - Button, - Divider, - IconButton, - Popover, - Typography, - Avatar, - styled, - MenuItem, - TextField -} from '@mui/material'; -import { useState, useContext } from 'react'; -import type { TypographyProps } from '@mui/material'; -import type { Locales } from 'i18n/i18n-types'; -import type { FC, ChangeEventHandler } from 'react'; -import { AuthenticatedContext } from 'contexts/authentication'; -import DEflag from 'i18n/DE.svg'; -import FRflag from 'i18n/FR.svg'; -import GBflag from 'i18n/GB.svg'; -import ITflag from 'i18n/IT.svg'; -import NLflag from 'i18n/NL.svg'; -import NOflag from 'i18n/NO.svg'; -import PLflag from 'i18n/PL.svg'; -import SKflag from 'i18n/SK.svg'; -import SVflag from 'i18n/SV.svg'; -import TRflag from 'i18n/TR.svg'; - -import { I18nContext } from 'i18n/i18n-react'; -import { loadLocaleAsync } from 'i18n/i18n-util.async'; - -const ItemTypography = styled(Typography)({ - maxWidth: '250px', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis' -}); - -const LayoutAuthMenu: FC = () => { - const { me, signOut } = useContext(AuthenticatedContext); - - const [anchorEl, setAnchorEl] = useState(null); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const { locale, LL, setLocale } = useContext(I18nContext); - - const onLocaleSelected: ChangeEventHandler = async ({ target }) => { - const loc = target.value as Locales; - localStorage.setItem('lang', loc); - await loadLocaleAsync(loc); - setLocale(loc); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const open = Boolean(anchorEl); - const id = anchorEl ? 'app-menu-popover' : undefined; - - return ( - <> - - - -  DE - - - -  EN - - - -  FR - - - -  IT - - - -  NL - - - -  NO - - - -  PL - - - -  SK - - - -  SV - - - -  TR - - - - - - - - - - - - - {me.username} - - {me.admin ? LL.ADMIN() : LL.GUEST()} {LL.USER(2)} - - - - - - - - - - ); -}; - -export default LayoutAuthMenu; diff --git a/interface/src/components/layout/LayoutDrawer.tsx b/interface/src/components/layout/LayoutDrawer.tsx index b63955c54..29834d5ed 100644 --- a/interface/src/components/layout/LayoutDrawer.tsx +++ b/interface/src/components/layout/LayoutDrawer.tsx @@ -1,6 +1,8 @@ import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material'; import { DRAWER_WIDTH } from './Layout'; + import LayoutMenu from './LayoutMenu'; + import type { FC } from 'react'; import { PROJECT_NAME } from 'api/env'; diff --git a/interface/src/components/layout/LayoutMenu.tsx b/interface/src/components/layout/LayoutMenu.tsx index f7d32788f..026a3497a 100644 --- a/interface/src/components/layout/LayoutMenu.tsx +++ b/interface/src/components/layout/LayoutMenu.tsx @@ -1,53 +1,254 @@ -import AccessTimeIcon from '@mui/icons-material/AccessTime'; - -import DashboardIcon from '@mui/icons-material/Dashboard'; -import DeviceHubIcon from '@mui/icons-material/DeviceHub'; -import InfoIcon from '@mui/icons-material/Info'; -import LockIcon from '@mui/icons-material/Lock'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import CategoryIcon from '@mui/icons-material/Category'; +import ConstructionIcon from '@mui/icons-material/Construction'; +import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; +import LiveHelpIcon from '@mui/icons-material/LiveHelp'; +import MoreTimeIcon from '@mui/icons-material/MoreTime'; +import PersonIcon from '@mui/icons-material/Person'; +import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd'; +import SensorsIcon from '@mui/icons-material/Sensors'; import SettingsIcon from '@mui/icons-material/Settings'; -import SettingsEthernetIcon from '@mui/icons-material/SettingsEthernet'; -import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna'; -import TuneIcon from '@mui/icons-material/Tune'; -import { Divider, List } from '@mui/material'; -import { useContext } from 'react'; -import type { FC } from 'react'; +import { + Divider, + List, + Box, + Button, + Popover, + Avatar, + MenuItem, + TextField, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText +} from '@mui/material'; + +import { useContext, useState } from 'react'; +import type { Locales } from 'i18n/i18n-types'; +import type { FC, ChangeEventHandler } from 'react'; import LayoutMenuItem from 'components/layout/LayoutMenuItem'; - import { AuthenticatedContext } from 'contexts/authentication'; -import { useI18nContext } from 'i18n/i18n-react'; +import DEflag from 'i18n/DE.svg'; +import FRflag from 'i18n/FR.svg'; +import GBflag from 'i18n/GB.svg'; +import ITflag from 'i18n/IT.svg'; +import NLflag from 'i18n/NL.svg'; +import NOflag from 'i18n/NO.svg'; +import PLflag from 'i18n/PL.svg'; +import SKflag from 'i18n/SK.svg'; +import SVflag from 'i18n/SV.svg'; +import TRflag from 'i18n/TR.svg'; + +import { I18nContext } from 'i18n/i18n-react'; +import { loadLocaleAsync } from 'i18n/i18n-util.async'; const LayoutMenu: FC = () => { - const authenticatedContext = useContext(AuthenticatedContext); - const { LL } = useI18nContext(); + const { me, signOut } = useContext(AuthenticatedContext); + const { locale, LL, setLocale } = useContext(I18nContext); + + const [anchorEl, setAnchorEl] = useState(null); + + const open = Boolean(anchorEl); + const id = anchorEl ? 'app-menu-popover' : undefined; + + const [menuOpen, setMenuOpen] = useState(true); + + const onLocaleSelected: ChangeEventHandler = async ({ target }) => { + const loc = target.value as Locales; + localStorage.setItem('lang', loc); + await loadLocaleAsync(loc); + setLocale(loc); + }; + + const handleClick = (event: any) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; return ( <> - - - - + + + + + + setMenuOpen(!menuOpen)} + sx={{ + pt: 2.5, + pb: menuOpen ? 0 : 2.5, + '&:hover, &:focus': { '& svg': { opacity: 1 } } + }} + > + + + + {menuOpen && ( + <> + + + + + )} + - - - - - - - + + + + + + + + + + + + + {me.username} + + + + + + + + + + + + + + + + + + +  DE + + + +  EN + + + +  FR + + + +  IT + + + +  NL + + + +  NO + + + +  PL + + + +  SK + + + +  SV + + + +  TR + + + + + + + + ); }; diff --git a/interface/src/components/layout/LayoutMenuItem.tsx b/interface/src/components/layout/LayoutMenuItem.tsx index d9bb46ca2..02899d406 100644 --- a/interface/src/components/layout/LayoutMenuItem.tsx +++ b/interface/src/components/layout/LayoutMenuItem.tsx @@ -1,4 +1,4 @@ -import { ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; +import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; import { Link, useLocation } from 'react-router-dom'; import type { SvgIconProps } from '@mui/material'; import type { FC } from 'react'; @@ -18,14 +18,12 @@ const LayoutMenuItem: FC = ({ icon: Icon, label, to, disabl const selected = routeMatches(to, pathname); return ( - - - - - - {label} - - + + + + + {label} + ); }; diff --git a/interface/src/components/layout/ListMenuItem.tsx b/interface/src/components/layout/ListMenuItem.tsx new file mode 100644 index 000000000..67bd17ff9 --- /dev/null +++ b/interface/src/components/layout/ListMenuItem.tsx @@ -0,0 +1,52 @@ +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import { Avatar, ListItem, ListItemAvatar, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; +import { Link } from 'react-router-dom'; +import type { SvgIconProps } from '@mui/material'; +import type { FC } from 'react'; + +interface ListMenuItemProps { + icon: React.ComponentType; + bgcolor?: string; + label: string; + text: string; + to?: string; + disabled?: boolean; +} + +function RenderIcon({ icon: Icon, bgcolor, label, text }: ListMenuItemProps) { + return ( + <> + + + + + + + + ); +} + +const LayoutMenuItem: FC = ({ icon, bgcolor, label, text, to, disabled }) => ( + <> + {to && !disabled ? ( + + + + } + > + + + + + ) : ( + + + + )} + +); + +export default LayoutMenuItem; diff --git a/interface/src/components/routing/useRouterTab.ts b/interface/src/components/routing/useRouterTab.ts index 0d785f242..5e53c09c5 100644 --- a/interface/src/components/routing/useRouterTab.ts +++ b/interface/src/components/routing/useRouterTab.ts @@ -1,12 +1,8 @@ -import { useLocation } from 'react-router-dom'; +import { useMatch, useResolvedPath } from 'react-router-dom'; export const useRouterTab = () => { - const loc = useLocation().pathname; - const routerTab = loc.substring(0, loc.lastIndexOf('/')) ? loc : false; - - // const routerTabPath = useResolvedPath(':tab'); - // const routerTabPathMatch = useMatch(routerTabPath.pathname); - // const routerTab = routerTabPathMatch?.params?.tab || false; + const routerTabPathMatch = useMatch(useResolvedPath(':tab').pathname); + const routerTab = routerTabPathMatch?.params?.tab || false; return { routerTab } as const; }; diff --git a/interface/src/components/upload/SingleUpload.tsx b/interface/src/components/upload/SingleUpload.tsx index bbd3e9ce3..1a4664dab 100644 --- a/interface/src/components/upload/SingleUpload.tsx +++ b/interface/src/components/upload/SingleUpload.tsx @@ -63,7 +63,7 @@ const SingleUpload: FC = ({ onDrop, onCancel, isUploading, pr { + const { LL } = useI18nContext(); + useLayoutTitle(LL.SETTINGS(0)); + + const [confirmRestart, setConfirmRestart] = useState(false); + const [confirmFactoryReset, setConfirmFactoryReset] = useState(false); + const [processing, setProcessing] = useState(false); + const [restarting, setRestarting] = useState(); + + const { send: restartCommand } = useRequest(SystemApi.restart(), { + immediate: false + }); + + const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), { + immediate: false + }); + + const { send: partitionCommand } = useRequest(SystemApi.partition(), { + immediate: false + }); + + const restart = async () => { + setProcessing(true); + await restartCommand() + .then(() => { + setRestarting(true); + }) + .catch((err) => { + toast.error(err.message); + }) + .finally(() => { + setConfirmRestart(false); + setProcessing(false); + }); + }; + + const factoryReset = async () => { + setProcessing(true); + await factoryResetCommand() + .then(() => { + setRestarting(true); + }) + .catch((err) => { + toast.error(err.message); + }) + .finally(() => { + setConfirmFactoryReset(false); + setProcessing(false); + }); + }; + + const partition = async () => { + setProcessing(true); + await partitionCommand() + .then(() => { + setRestarting(true); + }) + .catch((err) => { + toast.error(err.message); + }) + .finally(() => { + setConfirmRestart(false); + setProcessing(false); + }); + }; + + const renderRestartDialog = () => ( + setConfirmRestart(false)}> + {LL.RESTART()} + {LL.RESTART_CONFIRM()} + + + + + + + ); + + const renderFactoryResetDialog = () => ( + setConfirmFactoryReset(false)}> + {LL.FACTORY_RESET()} + {LL.SYSTEM_FACTORY_TEXT_DIALOG()} + + + + + + ); + + const content = () => ( + <> + + {/* TODO: translate */} + + + + + + + + + + + + + {/* TODO: translate */} + + + + + {/* TODO: translate */} + + + + {renderRestartDialog()} + {renderFactoryResetDialog()} + + + + + + + + + + + + + + + ); + + return {restarting ? : content()}; +}; + +export default Settings; diff --git a/interface/src/framework/ap/APSettingsForm.tsx b/interface/src/framework/ap/APSettings.tsx similarity index 95% rename from interface/src/framework/ap/APSettingsForm.tsx rename to interface/src/framework/ap/APSettings.tsx index ade6dab56..67d56c30e 100644 --- a/interface/src/framework/ap/APSettingsForm.tsx +++ b/interface/src/framework/ap/APSettings.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import type { ValidateFieldsError } from 'async-validator'; import type { FC } from 'react'; -import type { APSettings } from 'types'; +import type { APSettingsType } from 'types'; import * as APApi from 'api/ap'; import { BlockFormControlLabel, @@ -24,10 +24,10 @@ import { numberValue, updateValueDirty, useRest } from 'utils'; import { createAPSettingsValidator, validate } from 'validators'; -export const isAPEnabled = ({ provision_mode }: APSettings) => +export const isAPEnabled = ({ provision_mode }: APSettingsType) => provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED; -const APSettingsForm: FC = () => { +const APSettings: FC = () => { const { loadData, saving, @@ -39,7 +39,7 @@ const APSettingsForm: FC = () => { blocker, saveData, errorMessage - } = useRest({ + } = useRest({ read: APApi.readAPSettings, update: APApi.updateAPSettings }); @@ -205,11 +205,11 @@ const APSettingsForm: FC = () => { }; return ( - + {blocker ? : null} {content()} ); }; -export default APSettingsForm; +export default APSettings; diff --git a/interface/src/framework/ap/APStatusForm.tsx b/interface/src/framework/ap/APStatus.tsx similarity index 89% rename from interface/src/framework/ap/APStatusForm.tsx rename to interface/src/framework/ap/APStatus.tsx index ec754f796..4eba54f17 100644 --- a/interface/src/framework/ap/APStatusForm.tsx +++ b/interface/src/framework/ap/APStatus.tsx @@ -7,14 +7,14 @@ import { useRequest } from 'alova'; import type { Theme } from '@mui/material'; import type { FC } from 'react'; -import type { APStatus } from 'types'; +import type { APStatusType } from 'types'; import * as APApi from 'api/ap'; import { ButtonRow, FormLoader, SectionContent } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { APNetworkStatus } from 'types'; -export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => { +export const apStatusHighlight = ({ status }: APStatusType, theme: Theme) => { switch (status) { case APNetworkStatus.ACTIVE: return theme.palette.success.main; @@ -27,14 +27,14 @@ export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => { } }; -const APStatusForm: FC = () => { +const APStatus: FC = () => { const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus); const { LL } = useI18nContext(); const theme = useTheme(); - const apStatus = ({ status }: APStatus) => { + const apStatus = ({ status }: APStatusType) => { switch (status) { case APNetworkStatus.ACTIVE: return LL.ACTIVE(); @@ -99,11 +99,7 @@ const APStatusForm: FC = () => { ); }; - return ( - - {content()} - - ); + return {content()}; }; -export default APStatusForm; +export default APStatus; diff --git a/interface/src/framework/ap/AccessPoint.tsx b/interface/src/framework/ap/AccessPoint.tsx index 5d74cd7f6..8b6a1042b 100644 --- a/interface/src/framework/ap/AccessPoint.tsx +++ b/interface/src/framework/ap/AccessPoint.tsx @@ -1,12 +1,10 @@ import { Tab } from '@mui/material'; -import { useContext } from 'react'; import { Navigate, Routes, Route } from 'react-router-dom'; -import APSettingsForm from './APSettingsForm'; -import APStatusForm from './APStatusForm'; +import APSettings from './APSettings'; +import APStatus from './APStatus'; import type { FC } from 'react'; -import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; +import { RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; @@ -15,32 +13,18 @@ const AccessPoint: FC = () => { useLayoutTitle(LL.ACCESS_POINT(0)); - const authenticatedContext = useContext(AuthenticatedContext); - const { routerTab } = useRouterTab(); return ( <> - - + + - } /> - } /> - - - - } - /> - } /> + } /> + } /> + } /> ); diff --git a/interface/src/framework/mqtt/Mqtt.tsx b/interface/src/framework/mqtt/Mqtt.tsx index f65ea4181..5e3ae2020 100644 --- a/interface/src/framework/mqtt/Mqtt.tsx +++ b/interface/src/framework/mqtt/Mqtt.tsx @@ -1,12 +1,10 @@ import { Tab } from '@mui/material'; -import { useContext } from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; -import MqttSettingsForm from './MqttSettingsForm'; -import MqttStatusForm from './MqttStatusForm'; +import MqttSettings from './MqttSettings'; +import MqttStatus from './MqttStatus'; import type { FC } from 'react'; -import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; +import { RouterTabs, useLayoutTitle, useRouterTab } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; @@ -15,26 +13,18 @@ const Mqtt: FC = () => { useLayoutTitle('MQTT'); - const authenticatedContext = useContext(AuthenticatedContext); const { routerTab } = useRouterTab(); return ( <> - - + + - } /> - - - - } - /> - } /> + } /> + } /> + } /> ); diff --git a/interface/src/framework/mqtt/MqttSettingsForm.tsx b/interface/src/framework/mqtt/MqttSettings.tsx similarity index 98% rename from interface/src/framework/mqtt/MqttSettingsForm.tsx rename to interface/src/framework/mqtt/MqttSettings.tsx index 645abcf3c..c66697570 100644 --- a/interface/src/framework/mqtt/MqttSettingsForm.tsx +++ b/interface/src/framework/mqtt/MqttSettings.tsx @@ -5,7 +5,7 @@ import { useState } from 'react'; import type { ValidateFieldsError } from 'async-validator'; import type { FC } from 'react'; -import type { MqttSettings } from 'types'; +import type { MqttSettingsType } from 'types'; import * as MqttApi from 'api/mqtt'; import { BlockFormControlLabel, @@ -21,7 +21,7 @@ import { numberValue, updateValueDirty, useRest } from 'utils'; import { createMqttSettingsValidator, validate } from 'validators'; -const MqttSettingsForm: FC = () => { +const MqttSettings: FC = () => { const { loadData, saving, @@ -33,7 +33,7 @@ const MqttSettingsForm: FC = () => { blocker, saveData, errorMessage - } = useRest({ + } = useRest({ read: MqttApi.readMqttSettings, update: MqttApi.updateMqttSettings }); @@ -464,11 +464,11 @@ const MqttSettingsForm: FC = () => { }; return ( - + {blocker ? : null} {content()} ); }; -export default MqttSettingsForm; +export default MqttSettings; diff --git a/interface/src/framework/mqtt/MqttStatusForm.tsx b/interface/src/framework/mqtt/MqttStatus.tsx similarity index 90% rename from interface/src/framework/mqtt/MqttStatusForm.tsx rename to interface/src/framework/mqtt/MqttStatus.tsx index 0a0d661f8..26a300b88 100644 --- a/interface/src/framework/mqtt/MqttStatusForm.tsx +++ b/interface/src/framework/mqtt/MqttStatus.tsx @@ -8,13 +8,13 @@ import { useRequest } from 'alova'; import type { Theme } from '@mui/material'; import type { FC } from 'react'; -import type { MqttStatus } from 'types'; +import type { MqttStatusType } from 'types'; import * as MqttApi from 'api/mqtt'; import { ButtonRow, FormLoader, SectionContent } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { MqttDisconnectReason } from 'types'; -export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: Theme) => { +export const mqttStatusHighlight = ({ enabled, connected }: MqttStatusType, theme: Theme) => { if (!enabled) { return theme.palette.info.main; } @@ -24,27 +24,27 @@ export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: T return theme.palette.error.main; }; -export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, theme: Theme) => { +export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatusType, theme: Theme) => { if (mqtt_fails === 0) return theme.palette.success.main; if (mqtt_fails < 10) return theme.palette.warning.main; return theme.palette.error.main; }; -export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatus, theme: Theme) => { +export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatusType, theme: Theme) => { if (mqtt_queued <= 1) return theme.palette.success.main; return theme.palette.warning.main; }; -const MqttStatusForm: FC = () => { +const MqttStatus: FC = () => { const { data: data, send: loadData, error } = useRequest(MqttApi.readMqttStatus); const { LL } = useI18nContext(); const theme = useTheme(); - const mqttStatus = ({ enabled, connected, connect_count }: MqttStatus) => { + const mqttStatus = ({ enabled, connected, connect_count }: MqttStatusType) => { if (!enabled) { return LL.NOT_ENABLED(); } @@ -54,7 +54,7 @@ const MqttStatusForm: FC = () => { return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : ''); }; - const disconnectReason = ({ disconnect_reason }: MqttStatus) => { + const disconnectReason = ({ disconnect_reason }: MqttStatusType) => { switch (disconnect_reason) { case MqttDisconnectReason.TCP_DISCONNECTED: return 'TCP disconnected'; @@ -146,11 +146,7 @@ const MqttStatusForm: FC = () => { ); }; - return ( - - {content()} - - ); + return {content()}; }; -export default MqttStatusForm; +export default MqttStatus; diff --git a/interface/src/framework/network/Network.tsx b/interface/src/framework/network/Network.tsx new file mode 100644 index 000000000..66dc255b5 --- /dev/null +++ b/interface/src/framework/network/Network.tsx @@ -0,0 +1,59 @@ +import { Tab } from '@mui/material'; +import { useCallback, useState } from 'react'; +import { Navigate, Routes, Route, useNavigate } from 'react-router-dom'; +import NetworkSettings from './NetworkSettings'; +import NetworkStatus from './NetworkStatus'; +import { WiFiConnectionContext } from './WiFiConnectionContext'; +import WiFiNetworkScanner from './WiFiNetworkScanner'; +import type { FC } from 'react'; + +import type { WiFiNetwork } from 'types'; +import { RouterTabs, useLayoutTitle, useRouterTab } from 'components'; +import { useI18nContext } from 'i18n/i18n-react'; + +const Network: FC = () => { + const { LL } = useI18nContext(); + useLayoutTitle(LL.NETWORK(0)); + + const { routerTab } = useRouterTab(); + + const navigate = useNavigate(); + + const [selectedNetwork, setSelectedNetwork] = useState(); + + const selectNetwork = useCallback( + (network: WiFiNetwork) => { + setSelectedNetwork(network); + navigate('settings'); + }, + [navigate] + ); + + const deselectNetwork = useCallback(() => { + setSelectedNetwork(undefined); + }, []); + + return ( + + + + + + + + } /> + } /> + } /> + } /> + + + ); +}; + +export default Network; diff --git a/interface/src/framework/network/NetworkConnection.tsx b/interface/src/framework/network/NetworkConnection.tsx deleted file mode 100644 index cd9113398..000000000 --- a/interface/src/framework/network/NetworkConnection.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Tab } from '@mui/material'; -import { useCallback, useContext, useState } from 'react'; -import { Navigate, Routes, Route, useNavigate } from 'react-router-dom'; -import NetworkSettingsForm from './NetworkSettingsForm'; -import NetworkStatusForm from './NetworkStatusForm'; -import { WiFiConnectionContext } from './WiFiConnectionContext'; -import WiFiNetworkScanner from './WiFiNetworkScanner'; -import type { FC } from 'react'; - -import type { WiFiNetwork } from 'types'; -import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; -import { useI18nContext } from 'i18n/i18n-react'; - -const NetworkConnection: FC = () => { - const { LL } = useI18nContext(); - useLayoutTitle(LL.NETWORK(0)); - - const { routerTab } = useRouterTab(); - - const authenticatedContext = useContext(AuthenticatedContext); - const navigate = useNavigate(); - - const [selectedNetwork, setSelectedNetwork] = useState(); - - const selectNetwork = useCallback( - (network: WiFiNetwork) => { - setSelectedNetwork(network); - navigate('settings'); - }, - [navigate] - ); - - const deselectNetwork = useCallback(() => { - setSelectedNetwork(undefined); - }, []); - - return ( - - - - - - - - } /> - - - - } - /> - - - - } - /> - } /> - - - ); -}; - -export default NetworkConnection; diff --git a/interface/src/framework/network/NetworkSettingsForm.tsx b/interface/src/framework/network/NetworkSettings.tsx similarity index 97% rename from interface/src/framework/network/NetworkSettingsForm.tsx rename to interface/src/framework/network/NetworkSettings.tsx index cf97b3d03..83f1addaa 100644 --- a/interface/src/framework/network/NetworkSettingsForm.tsx +++ b/interface/src/framework/network/NetworkSettings.tsx @@ -28,7 +28,7 @@ import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector'; import type { ValidateFieldsError } from 'async-validator'; import type { FC } from 'react'; -import type { NetworkSettings } from 'types'; +import type { NetworkSettingsType } from 'types'; import * as NetworkApi from 'api/network'; import * as SystemApi from 'api/system'; import { @@ -48,7 +48,7 @@ import { updateValueDirty, useRest } from 'utils'; import { validate } from 'validators'; import { createNetworkSettingsValidator } from 'validators/network'; -const WiFiSettingsForm: FC = () => { +const NetworkSettings: FC = () => { const { LL } = useI18nContext(); const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext); @@ -68,7 +68,7 @@ const WiFiSettingsForm: FC = () => { saveData, errorMessage, restartNeeded - } = useRest({ + } = useRest({ read: NetworkApi.readNetworkSettings, update: NetworkApi.updateNetworkSettings }); @@ -135,7 +135,7 @@ const WiFiSettingsForm: FC = () => { return ( <> - + WiFi {selectedNetwork ? ( @@ -360,11 +360,11 @@ const WiFiSettingsForm: FC = () => { }; return ( - + {blocker ? : null} {restarting ? : content()} ); }; -export default WiFiSettingsForm; +export default NetworkSettings; diff --git a/interface/src/framework/network/NetworkStatusForm.tsx b/interface/src/framework/network/NetworkStatus.tsx similarity index 88% rename from interface/src/framework/network/NetworkStatusForm.tsx rename to interface/src/framework/network/NetworkStatus.tsx index 3a731c4ca..55dfe1fb9 100644 --- a/interface/src/framework/network/NetworkStatusForm.tsx +++ b/interface/src/framework/network/NetworkStatus.tsx @@ -11,18 +11,18 @@ import { useRequest } from 'alova'; import type { Theme } from '@mui/material'; import type { FC } from 'react'; -import type { NetworkStatus } from 'types'; +import type { NetworkStatusType } from 'types'; import * as NetworkApi from 'api/network'; import { ButtonRow, FormLoader, SectionContent } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { NetworkConnectionStatus } from 'types'; -const isConnected = ({ status }: NetworkStatus) => +const isConnected = ({ status }: NetworkStatusType) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED || status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED; -const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme) => { +const networkStatusHighlight = ({ status }: NetworkStatusType, theme: Theme) => { switch (status) { case NetworkConnectionStatus.WIFI_STATUS_IDLE: case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED: @@ -39,7 +39,7 @@ const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme) => { } }; -const networkQualityHighlight = ({ rssi }: NetworkStatus, theme: Theme) => { +const networkQualityHighlight = ({ rssi }: NetworkStatusType, theme: Theme) => { if (rssi <= -85) { return theme.palette.error.main; } else if (rssi <= -75) { @@ -48,17 +48,18 @@ const networkQualityHighlight = ({ rssi }: NetworkStatus, theme: Theme) => { return theme.palette.success.main; }; -export const isWiFi = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED; -export const isEthernet = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED; +export const isWiFi = ({ status }: NetworkStatusType) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED; +export const isEthernet = ({ status }: NetworkStatusType) => + status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED; -const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatus) => { +const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatusType) => { if (!dns_ip_1) { return 'none'; } return dns_ip_1 + (!dns_ip_2 || dns_ip_2 === '0.0.0.0' ? '' : ',' + dns_ip_2); }; -const IPs = (status: NetworkStatus) => { +const IPs = (status: NetworkStatusType) => { if (!status.local_ipv6 || status.local_ipv6 === '0000:0000:0000:0000:0000:0000:0000:0000') { return status.local_ip; } @@ -68,14 +69,14 @@ const IPs = (status: NetworkStatus) => { return status.local_ip + ', ' + status.local_ipv6; }; -const NetworkStatusForm: FC = () => { +const NetworkStatus: FC = () => { const { data: data, send: loadData, error } = useRequest(NetworkApi.readNetworkStatus); const { LL } = useI18nContext(); const theme = useTheme(); - const networkStatus = ({ status }: NetworkStatus) => { + const networkStatus = ({ status }: NetworkStatusType) => { switch (status) { case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD: return LL.INACTIVE(1); @@ -193,11 +194,7 @@ const NetworkStatusForm: FC = () => { ); }; - return ( - - {content()} - - ); + return {content()}; }; -export default NetworkStatusForm; +export default NetworkStatus; diff --git a/interface/src/framework/network/WiFiNetworkScanner.tsx b/interface/src/framework/network/WiFiNetworkScanner.tsx index f7d227104..ecc5eb034 100644 --- a/interface/src/framework/network/WiFiNetworkScanner.tsx +++ b/interface/src/framework/network/WiFiNetworkScanner.tsx @@ -56,7 +56,7 @@ const WiFiNetworkScanner: FC = () => { }; return ( - + {renderNetworkScanner()} - {me.admin && data && !isNtpActive(data) && ( + {data && !isNtpActive(data) && ( + + + + + ); + }; + + return {content()}; +}; + +export default ESPSystemStatus; diff --git a/interface/src/framework/system/System.tsx b/interface/src/framework/system/System.tsx index 8dc373cf2..d734068f4 100644 --- a/interface/src/framework/system/System.tsx +++ b/interface/src/framework/system/System.tsx @@ -1,53 +1,42 @@ import { Tab } from '@mui/material'; -import { useContext } from 'react'; +import { useContext, type FC } from 'react'; import { Navigate, Routes, Route } from 'react-router-dom'; -import OTASettingsForm from './OTASettingsForm'; import SystemLog from './SystemLog'; -import SystemStatusForm from './SystemStatusForm'; -import UploadFileForm from './UploadFileForm'; -import type { FC } from 'react'; +import SystemStatus from './SystemStatus'; import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; +import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; +import SystemActivity from 'project/SystemActivity'; const System: FC = () => { const { LL } = useI18nContext(); useLayoutTitle(LL.SYSTEM(0)); - const { me } = useContext(AuthenticatedContext); const { routerTab } = useRouterTab(); + const { me } = useContext(AuthenticatedContext); return ( <> - - - - + + + - } /> - } /> + } /> + } /> - + } /> - - - - } - /> - } /> + } /> ); diff --git a/interface/src/framework/system/SystemLog.tsx b/interface/src/framework/system/SystemLog.tsx index d099872c6..3b81b5963 100644 --- a/interface/src/framework/system/SystemLog.tsx +++ b/interface/src/framework/system/SystemLog.tsx @@ -11,7 +11,7 @@ import { addAccessTokenParameter } from 'api/authentication'; import { EVENT_SOURCE_ROOT } from 'api/endpoints'; import * as SystemApi from 'api/system'; -import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation } from 'components'; +import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { LogLevel } from 'types'; @@ -50,6 +50,8 @@ const levelLabel = (level: LogLevel) => { const SystemLog: FC = () => { const { LL } = useI18nContext(); + useLayoutTitle(LL.LOG_OF('')); + const { loadData, data, updateDataValue, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } = useRest({ read: SystemApi.readLogSettings, @@ -232,7 +234,7 @@ const SystemLog: FC = () => { }; return ( - + {blocker ? : null} {content()} diff --git a/interface/src/framework/system/SystemStatus.tsx b/interface/src/framework/system/SystemStatus.tsx new file mode 100644 index 000000000..2f4881e81 --- /dev/null +++ b/interface/src/framework/system/SystemStatus.tsx @@ -0,0 +1,297 @@ +import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import BuildIcon from '@mui/icons-material/Build'; +import CancelIcon from '@mui/icons-material/Cancel'; +import CastIcon from '@mui/icons-material/Cast'; +import DeviceHubIcon from '@mui/icons-material/DeviceHub'; +import DirectionsBusIcon from '@mui/icons-material/DirectionsBus'; +import MemoryIcon from '@mui/icons-material/Memory'; +import PermScanWifiIcon from '@mui/icons-material/PermScanWifi'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna'; +import TimerIcon from '@mui/icons-material/Timer'; + +import { + Avatar, + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + List, + ListItem, + ListItemAvatar, + ListItemText, + useTheme +} from '@mui/material'; + +import { useRequest } from 'alova'; +import { useContext, type FC, useState } from 'react'; + +import { toast } from 'react-toastify'; +import { dialogStyle } from 'CustomTheme'; +import * as SystemApi from 'api/system'; +import { FormLoader, SectionContent, useLayoutTitle } from 'components'; +import ListMenuItem from 'components/layout/ListMenuItem'; +import { AuthenticatedContext } from 'contexts/authentication'; +import { useI18nContext } from 'i18n/i18n-react'; +import * as EMSESP from 'project/api'; +import { busConnectionStatus } from 'project/types'; +import { NTPSyncStatus } from 'types'; + +const SystemStatus: FC = () => { + const { LL } = useI18nContext(); + + useLayoutTitle(LL.STATUS_OF('')); + + const { me } = useContext(AuthenticatedContext); + + const [confirmScan, setConfirmScan] = useState(false); + + const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true }); + + const { send: scanDevices } = useRequest(EMSESP.scanDevices, { + immediate: false + }); + + const theme = useTheme(); + + const formatDurationSec = (duration_sec: number) => { + const days = Math.trunc((duration_sec * 1000) / 86400000); + const hours = Math.trunc((duration_sec * 1000) / 3600000) % 24; + const minutes = Math.trunc((duration_sec * 1000) / 60000) % 60; + const seconds = Math.trunc((duration_sec * 1000) / 1000) % 60; + + let formatted = ''; + if (days) { + formatted += LL.NUM_DAYS({ num: days }) + ' '; + } + if (hours) { + formatted += LL.NUM_HOURS({ num: hours }) + ' '; + } + if (minutes) { + formatted += LL.NUM_MINUTES({ num: minutes }) + ' '; + } + formatted += LL.NUM_SECONDS({ num: seconds }); + return formatted; + }; + + function formatNumber(num: number) { + return new Intl.NumberFormat().format(num); + } + + const busStatus = () => { + if (data) { + switch (data.status) { + case busConnectionStatus.BUS_STATUS_CONNECTED: + return LL.CONNECTED(0) + ' (' + formatDurationSec(data.bus_uptime) + ')'; + case busConnectionStatus.BUS_STATUS_TX_ERRORS: + return LL.TX_ISSUES(); + case busConnectionStatus.BUS_STATUS_OFFLINE: + return LL.DISCONNECTED(); + } + } + return 'Unknown'; + }; + + const busStatusHighlight = () => { + switch (data.status) { + case busConnectionStatus.BUS_STATUS_TX_ERRORS: + return theme.palette.warning.main; + case busConnectionStatus.BUS_STATUS_CONNECTED: + return theme.palette.success.main; + case busConnectionStatus.BUS_STATUS_OFFLINE: + return theme.palette.error.main; + default: + return theme.palette.warning.main; + } + }; + + const ntpStatus = () => { + switch (data.ntp_status) { + case NTPSyncStatus.NTP_DISABLED: + return LL.NOT_ENABLED(); + case NTPSyncStatus.NTP_INACTIVE: + return LL.INACTIVE(0); + case NTPSyncStatus.NTP_ACTIVE: + return LL.ACTIVE(); + default: + return LL.UNKNOWN(); + } + }; + + const ntpStatusHighlight = () => { + switch (data.ntp_status) { + case NTPSyncStatus.NTP_DISABLED: + return theme.palette.info.main; + case NTPSyncStatus.NTP_INACTIVE: + return theme.palette.error.main; + case NTPSyncStatus.NTP_ACTIVE: + return theme.palette.success.main; + default: + return theme.palette.error.main; + } + }; + + const activeHighlight = (value: boolean) => (value ? theme.palette.success.main : theme.palette.info.main); + + const scan = async () => { + await scanDevices() + .then(() => { + toast.info(LL.SCANNING() + '...'); + }) + .catch((err) => { + toast.error(err.message); + }); + setConfirmScan(false); + }; + + const renderScanDialog = () => ( + setConfirmScan(false)}> + {LL.SCAN_DEVICES()} + {LL.EMS_SCAN()} + + + + + + ); + + const content = () => { + if (!data) { + return ; + } + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + {me.admin && ( + + )} + + + + + + + {/* TODO: translate */} + + + + + + + + + + + + + + + + + {renderScanDialog()} + + + + + + ); + }; + + return {content()}; +}; + +export default SystemStatus; diff --git a/interface/src/framework/system/SystemStatusForm.tsx b/interface/src/framework/system/SystemStatusForm.tsx deleted file mode 100644 index ba710e36e..000000000 --- a/interface/src/framework/system/SystemStatusForm.tsx +++ /dev/null @@ -1,362 +0,0 @@ -import AppsIcon from '@mui/icons-material/Apps'; -import BuildIcon from '@mui/icons-material/Build'; -import CancelIcon from '@mui/icons-material/Cancel'; -import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard'; -import DevicesIcon from '@mui/icons-material/Devices'; -import FolderIcon from '@mui/icons-material/Folder'; -import MemoryIcon from '@mui/icons-material/Memory'; -import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; -import RefreshIcon from '@mui/icons-material/Refresh'; -import SdCardAlertIcon from '@mui/icons-material/SdCardAlert'; -import SdStorageIcon from '@mui/icons-material/SdStorage'; -import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore'; -import TimerIcon from '@mui/icons-material/Timer'; -import { - Avatar, - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - Divider, - List, - ListItem, - ListItemAvatar, - ListItemText -} from '@mui/material'; - -import { useRequest } from 'alova'; -import { useContext, useState } from 'react'; -import { toast } from 'react-toastify'; -import RestartMonitor from './RestartMonitor'; -import SystemStatusVersionDialog from './SystemStatusVersionDialog'; -import type { FC } from 'react'; - -import { dialogStyle } from 'CustomTheme'; -import * as SystemApi from 'api/system'; -import { ButtonRow, FormLoader, SectionContent } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; -import { useI18nContext } from 'i18n/i18n-react'; - -function formatNumber(num: number) { - return new Intl.NumberFormat().format(num); -} - -const SystemStatusForm: FC = () => { - const { LL } = useI18nContext(); - - const { me } = useContext(AuthenticatedContext); - const [confirmRestart, setConfirmRestart] = useState(false); - const [confirmFactoryReset, setConfirmFactoryReset] = useState(false); - const [processing, setProcessing] = useState(false); - const [restarting, setRestarting] = useState(); - const [versionDialogOpen, setVersionDialogOpen] = useState(false); - - const { send: restartCommand } = useRequest(SystemApi.restart(), { - immediate: false - }); - - const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), { - immediate: false - }); - - const { send: partitionCommand } = useRequest(SystemApi.partition(), { - immediate: false - }); - - const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true }); - - const restart = async () => { - setProcessing(true); - await restartCommand() - .then(() => { - setRestarting(true); - }) - .catch((err) => { - toast.error(err.message); - }) - .finally(() => { - setConfirmRestart(false); - setProcessing(false); - }); - }; - - const factoryReset = async () => { - setProcessing(true); - await factoryResetCommand() - .then(() => { - setRestarting(true); - }) - .catch((err) => { - toast.error(err.message); - }) - .finally(() => { - setConfirmFactoryReset(false); - setProcessing(false); - }); - }; - - const partition = async () => { - setProcessing(true); - await partitionCommand() - .then(() => { - setRestarting(true); - }) - .catch((err) => { - toast.error(err.message); - }) - .finally(() => { - setConfirmRestart(false); - setProcessing(false); - }); - }; - - const renderRestartDialog = () => ( - setConfirmRestart(false)}> - {LL.RESTART()} - {LL.RESTART_CONFIRM()} - - - - {data?.has_loader && ( - - )} - - - ); - - const renderFactoryResetDialog = () => ( - setConfirmFactoryReset(false)}> - {LL.FACTORY_RESET()} - {LL.SYSTEM_FACTORY_TEXT_DIALOG()} - - - - - - ); - - const content = () => { - if (!data) { - return ; - } - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {data.psram_size !== undefined && data.free_psram !== undefined && ( - <> - - - - - - - - - - - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {me.admin && ( - - - - - - - )} - - {renderRestartDialog()} - {renderFactoryResetDialog()} - - ); - }; - - return ( - - {restarting ? : content()} - {data && ( - setVersionDialogOpen(false)} - version={data.emsesp_version} - platform={data.esp_platform} - /> - )} - - ); -}; - -export default SystemStatusForm; diff --git a/interface/src/framework/system/SystemStatusVersionDialog.tsx b/interface/src/framework/system/SystemStatusVersionDialog.tsx deleted file mode 100644 index 1c7ab8876..000000000 --- a/interface/src/framework/system/SystemStatusVersionDialog.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Link, Typography } from '@mui/material'; -import { useRequest } from 'alova'; -import { useCallback, useEffect } from 'react'; -import { dialogStyle } from 'CustomTheme'; -import * as SystemApi from 'api/system'; - -import MessageBox from 'components/MessageBox'; -import { useI18nContext } from 'i18n/i18n-react'; - -type SystemStatusVersionDialogProps = { - open: boolean; - onClose: () => void; - version: string; - platform: string; -}; - -const SystemStatusVersionDialog = ({ open, onClose, version, platform }: SystemStatusVersionDialogProps) => { - const { LL } = useI18nContext(); - const { send: getLatestVersion, data: latestVersion } = useRequest(SystemApi.getStableVersion, { - immediate: false, - force: true - }); - const { send: getLatestDevVersion, data: latestDevVersion } = useRequest(SystemApi.getDevVersion, { - immediate: false, - force: true - }); - - const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/'; - const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/'; - - const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md'; - const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md'; - - const uploadURL = window.location.origin + '/system/upload'; - - const connected = latestVersion && latestDevVersion; - - const getVersions = useCallback(async () => { - await getLatestVersion(); - await getLatestDevVersion(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - if (open) { - void getVersions(); - } - }, [getVersions, open]); - - const getBinURL = (v: string) => 'EMS-ESP-' + v.replaceAll('.', '_') + '-' + platform.replaceAll('-', '_') + '.bin'; - - return ( - - {LL.VERSION_CHECK(1)} - - - {latestVersion && ( - - {LL.THE_LATEST()} {LL.OFFICIAL()} {LL.RELEASE_IS()} {latestVersion} -  ( - - {LL.RELEASE_NOTES()} - - ) ( - - {LL.DOWNLOAD(1)} - - ) - - )} - {latestDevVersion && ( - - {LL.THE_LATEST()} {LL.DEVELOPMENT()} {LL.RELEASE_IS()}  - {latestDevVersion} -  ( - - {LL.RELEASE_NOTES()} - - ) ( - - {LL.DOWNLOAD(1)} - - ) - - )} - {connected && ( - - - {LL.USE()}  - - {LL.UPLOAD()} - -  {LL.SYSTEM_APPLY_FIRMWARE()} - - - )} - {!connected && } - - - - - - ); -}; - -export default SystemStatusVersionDialog; diff --git a/interface/src/framework/system/UploadDownload.tsx b/interface/src/framework/system/UploadDownload.tsx new file mode 100644 index 000000000..a9b012310 --- /dev/null +++ b/interface/src/framework/system/UploadDownload.tsx @@ -0,0 +1,308 @@ +import DownloadIcon from '@mui/icons-material/GetApp'; +import { Typography, Button, Box, Link } from '@mui/material'; +import { useRequest } from 'alova'; +import { useState, type FC } from 'react'; +import { toast } from 'react-toastify'; +import RestartMonitor from './RestartMonitor'; + +import * as SystemApi from 'api/system'; +import { FormLoader, SectionContent, SingleUpload, useLayoutTitle } from 'components'; + +import { useI18nContext } from 'i18n/i18n-react'; +import * as EMSESP from 'project/api'; + +const UploadDownload: FC = () => { + const { LL } = useI18nContext(); + const [restarting, setRestarting] = useState(); + const [md5, setMd5] = useState(); + + const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), { + immediate: false + }); + const { send: getCustomizations, onSuccess: onSuccessGetCustomizations } = useRequest(EMSESP.getCustomizations(), { + immediate: false + }); + const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), { + immediate: false + }); + const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), { + immediate: false + }); + const { send: getAPI, onSuccess: onGetAPI } = useRequest((data) => EMSESP.API(data), { + immediate: false + }); + + const { data: data, send: loadData, error } = useRequest(SystemApi.readESPSystemStatus, { force: true }); + + const { data: latestVersion } = useRequest(SystemApi.getStableVersion, { + immediate: true, + force: true + }); + + const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion, { + immediate: true, + force: true + }); + + const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/'; + const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/'; + + const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md'; + const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md'; + + const getBinURL = (v: string) => + 'EMS-ESP-' + v.replaceAll('.', '_') + '-' + data.esp_platform.replaceAll('-', '_') + '.bin'; + + const { + loading: isUploading, + uploading: progress, + send: sendUpload, + onSuccess: onSuccessUpload, + abort: cancelUpload + } = useRequest(SystemApi.uploadFile, { + immediate: false, + force: true + }); + + onSuccessUpload(({ data }: any) => { + if (data) { + setMd5(data.md5); + toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); + } else { + setRestarting(true); + } + }); + + const startUpload = async (files: File[]) => { + await sendUpload(files[0]).catch((err) => { + if (err.message === 'The user aborted a request') { + toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED()); + } else if (err.message === 'Network Error') { + toast.warning('Invalid file extension or incompatible bin file'); + } else { + toast.error(err.message); + } + }); + }; + + const saveFile = (json: any, endpoint: string) => { + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL( + new Blob([JSON.stringify(json, null, 2)], { + type: 'text/plain' + }) + ); + anchor.download = 'emsesp_' + endpoint; + anchor.click(); + URL.revokeObjectURL(anchor.href); + toast.info(LL.DOWNLOAD_SUCCESSFUL()); + }; + + onSuccessGetSettings((event) => { + saveFile(event.data, 'settings.json'); + }); + onSuccessGetCustomizations((event) => { + saveFile(event.data, 'customizations.json'); + }); + onSuccessGetEntities((event) => { + saveFile(event.data, 'entities.json'); + }); + onSuccessGetSchedule((event) => { + saveFile(event.data, 'schedule.json'); + }); + onGetAPI((event) => { + saveFile(event.data, event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt'); + }); + + const downloadSettings = async () => { + await getSettings().catch((error) => { + toast.error(error.message); + }); + }; + + const downloadCustomizations = async () => { + await getCustomizations().catch((error) => { + toast.error(error.message); + }); + }; + + const downloadEntities = async () => { + await getEntities().catch((error) => { + toast.error(error.message); + }); + }; + + const downloadSchedule = async () => { + await getSchedule() + .catch((error) => { + toast.error(error.message); + }) + .finally(() => { + toast.info(LL.DOWNLOAD_SUCCESSFUL()); + }); + }; + + const callAPI = async (device: string, entity: string) => { + await getAPI({ device, entity, id: 0 }).catch((error) => { + toast.error(error.message); + }); + }; + + useLayoutTitle(LL.UPLOAD_DOWNLOAD()); + + const content = () => { + if (!data) { + return ; + } + + return ( + <> + + {LL.EMS_ESP_VER()} + + + {LL.VERSION_ON() + ' '} + {data.emsesp_version} ({data.esp_platform}) + {latestVersion && ( + + {LL.THE_LATEST()} {LL.OFFICIAL()} {LL.RELEASE_IS()} {latestVersion} +  ( + + {LL.RELEASE_NOTES()} + + ) ( + + {LL.DOWNLOAD(1)} + + ) + + )} + {latestDevVersion && ( + + {LL.THE_LATEST()} {LL.DEVELOPMENT()} {LL.RELEASE_IS()}  + {latestDevVersion} +  ( + + {LL.RELEASE_NOTES()} + + ) ( + + {LL.DOWNLOAD(1)} + + ) + + )} + + + + {LL.UPLOAD()} + + + + {LL.UPLOAD_TEXT()} +
+
+ {LL.RESTART_TEXT(1)}. +
+
+ {md5 && ( + + {'MD5: ' + md5} + + )} + + {!isUploading && ( + <> + + {LL.DOWNLOAD(0)} + + + + {LL.HELP_INFORMATION_4()} + + + + + + + + {LL.DOWNLOAD_SETTINGS_TEXT()} + + + + + + + {LL.DOWNLOAD_CUSTOMIZATION_TEXT()} + + + + + + {LL.DOWNLOAD_SCHEDULE_TEXT()} + + + + + + )} + + ); + }; + + return {restarting ? : content()}; +}; + +export default UploadDownload; diff --git a/interface/src/framework/system/UploadFileForm.tsx b/interface/src/framework/system/UploadFileForm.tsx deleted file mode 100644 index 490734b76..000000000 --- a/interface/src/framework/system/UploadFileForm.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import DownloadIcon from '@mui/icons-material/GetApp'; -import { Typography, Button, Box } from '@mui/material'; -import { useRequest } from 'alova'; -import { useState, type FC } from 'react'; -import { toast } from 'react-toastify'; -import RestartMonitor from './RestartMonitor'; - -import * as SystemApi from 'api/system'; -import { SectionContent, SingleUpload } from 'components'; - -import { useI18nContext } from 'i18n/i18n-react'; -import * as EMSESP from 'project/api'; - -const UploadFileForm: FC = () => { - const { LL } = useI18nContext(); - const [restarting, setRestarting] = useState(); - const [md5, setMd5] = useState(); - - const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), { - immediate: false - }); - const { send: getCustomizations, onSuccess: onSuccessGetCustomizations } = useRequest(EMSESP.getCustomizations(), { - immediate: false - }); - const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), { - immediate: false - }); - const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), { - immediate: false - }); - const { send: getAPI, onSuccess: onGetAPI } = useRequest((data) => EMSESP.API(data), { - immediate: false - }); - - const { - loading: isUploading, - uploading: progress, - send: sendUpload, - onSuccess: onSuccessUpload, - abort: cancelUpload - } = useRequest(SystemApi.uploadFile, { - immediate: false, - force: true - }); - - onSuccessUpload(({ data }: any) => { - if (data) { - setMd5(data.md5); - toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); - } else { - setRestarting(true); - } - }); - - const startUpload = async (files: File[]) => { - await sendUpload(files[0]).catch((err) => { - if (err.message === 'The user aborted a request') { - toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED()); - } else if (err.message === 'Network Error') { - toast.warning('Invalid file extension or incompatible bin file'); - } else { - toast.error(err.message); - } - }); - }; - - const saveFile = (json: any, endpoint: string) => { - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL( - new Blob([JSON.stringify(json, null, 2)], { - type: 'text/plain' - }) - ); - anchor.download = 'emsesp_' + endpoint; - anchor.click(); - URL.revokeObjectURL(anchor.href); - toast.info(LL.DOWNLOAD_SUCCESSFUL()); - }; - - onSuccessGetSettings((event) => { - saveFile(event.data, 'settings.json'); - }); - onSuccessGetCustomizations((event) => { - saveFile(event.data, 'customizations.json'); - }); - onSuccessGetEntities((event) => { - saveFile(event.data, 'entities.json'); - }); - onSuccessGetSchedule((event) => { - saveFile(event.data, 'schedule.json'); - }); - onGetAPI((event) => { - saveFile(event.data, event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt'); - }); - - const downloadSettings = async () => { - await getSettings().catch((error) => { - toast.error(error.message); - }); - }; - - const downloadCustomizations = async () => { - await getCustomizations().catch((error) => { - toast.error(error.message); - }); - }; - - const downloadEntities = async () => { - await getEntities().catch((error) => { - toast.error(error.message); - }); - }; - - const downloadSchedule = async () => { - await getSchedule() - .catch((error) => { - toast.error(error.message); - }) - .finally(() => { - toast.info(LL.DOWNLOAD_SUCCESSFUL()); - }); - }; - - const callAPI = async (device: string, entity: string) => { - await getAPI({ device, entity, id: 0 }).catch((error) => { - toast.error(error.message); - }); - }; - - const content = () => ( - <> - - {LL.UPLOAD()} - - - - {LL.UPLOAD_TEXT()} -
-
- {LL.RESTART_TEXT(1)}. -
-
- {md5 && ( - - {'MD5: ' + md5} - - )} - - {!isUploading && ( - <> - - {LL.DOWNLOAD(0)} {LL.SUPPORT_INFORMATION(1)} - - - - {LL.HELP_INFORMATION_4()} - - - - - - - {LL.DOWNLOAD(0)} {LL.SETTINGS(1)} - - - - {LL.DOWNLOAD_SETTINGS_TEXT()} - - - - - - {LL.DOWNLOAD_CUSTOMIZATION_TEXT()} - - - - - - - {LL.DOWNLOAD_SCHEDULE_TEXT()} - - - - - )} - - ); - return ( - - {restarting ? : content()} - - ); -}; - -export default UploadFileForm; diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index f0c8d3d05..4041e3a63 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -12,7 +12,6 @@ const de: Translation = { USERNAME: 'Nutzername', PASSWORD: 'Passwort', SU_PASSWORD: 'su Passwort', - DASHBOARD: 'Kontrollzentrum', SETTINGS_OF: '{0} Einstellungen', HELP_OF: '{0} Hilfe', LOGGED_IN: 'Eingeloggt als {name}', @@ -37,8 +36,6 @@ const de: Translation = { BRAND: 'Marke', ENTITY_NAME: 'Entitätsname', VALUE: '{{Wert|wert}}', - DEVICE_DATA: 'Gerätedaten', - SENSOR_DATA: 'Sensordaten', DEVICES: 'Geräte', SENSORS: 'Sensoren', RUN_COMMAND: 'Befehl ausführen', @@ -83,7 +80,6 @@ const de: Translation = { FAIL: 'FEHLER', QUALITY: 'QUALITÄT', SCAN_DEVICES: 'Nach neuen Geräten suchen', - EMS_BUS_STATUS_TITLE: 'EMS-Bus- und Aktivitätsstatus', SCAN: 'Suche', STATUS_NAMES: [ 'EMS-Telegramme empfangen (Rx)', @@ -163,9 +159,7 @@ const de: Translation = { OPTIONS: 'Optionen', NAME: 'Name', CUSTOMIZATIONS_RESET: 'Möchten Sie wirklich alle Anpassungen entfernen, einschließlich der benutzerdefinierten Einstellungen der Temperatur- und Analogsensoren?', - DEVICE_ENTITIES: 'Geräteentitäten', SUPPORT_INFORMATION: 'Unterstützende Informationen', - CLICK_HERE: 'Hier klicken', HELP_INFORMATION_1: 'EMS-ESP Konfigurationsanweisungen und mehr finden Sie im Online-Wiki', HELP_INFORMATION_2: 'Für einen Live-Community-Chat besuchen Sie unseren Discord-Server', HELP_INFORMATION_3: 'Um neue Funktionen anzufragen oder Fehler zu melden, eröffnen Sie ein Issue auf Github', @@ -181,13 +175,11 @@ const de: Translation = { STATUS_OF: '{0} Status', UPLOAD_DOWNLOAD: 'Hoch-/Herunterladen', VERSION_ON: 'Sie verwenden derzeit', - SYSTEM_APPLY_FIRMWARE: 'um die neue Firmware anzuwenden', CLOSE: 'Schließen', USE: 'Verwenden Sie', FACTORY_RESET: 'Werkseinstellung', SYSTEM_FACTORY_TEXT: 'EMS-ESP wurde auf Werkseinstellung gesetzt und startet als Zugangspunkt neu', SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?', - VERSION_CHECK: 'Versionsprüfung', THE_LATEST: 'Die neueste', OFFICIAL: 'offizielle', DEVELOPMENT: 'Entwicklungs', @@ -330,7 +322,9 @@ const de: Translation = { ACTIVEHIGH: 'Aktiv Positiv', ACTIVELOW: 'Aktiv Negativ', UNCHANGED: 'Unverändert', - ALWAYS: 'Immer' + ALWAYS: 'Immer', + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default de; diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index f5748e7ba..54df90a0a 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -12,7 +12,6 @@ const en: Translation = { USERNAME: 'Username', PASSWORD: 'Password', SU_PASSWORD: 'su Password', - DASHBOARD: 'Dashboard', SETTINGS_OF: '{0} Settings', HELP_OF: '{0} Help', LOGGED_IN: 'Logged in as {name}', @@ -37,8 +36,6 @@ const en: Translation = { BRAND: 'Brand', ENTITY_NAME: 'Entity Name', VALUE: '{{Value|value}}', - DEVICE_DATA: 'Device Data', - SENSOR_DATA: 'Sensor Data', DEVICES: 'Devices', SENSORS: 'Sensors', RUN_COMMAND: 'Call Command', @@ -83,7 +80,6 @@ const en: Translation = { FAIL: 'FAIL', QUALITY: 'QUALITY', SCAN_DEVICES: 'Scan for new devices', - EMS_BUS_STATUS_TITLE: 'EMS Bus & Activity Status', SCAN: 'Scan', STATUS_NAMES: [ 'EMS Telegrams Received (Rx)', @@ -158,18 +154,16 @@ const en: Translation = { CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API', CUSTOMIZATIONS_HELP_5: 'hide from Dashboard', CUSTOMIZATIONS_HELP_6: 'remove from memory', - SELECT_DEVICE: 'Select a device', + SELECT_DEVICE: 'select a device', SET_ALL: 'set all', OPTIONS: 'Options', NAME: 'Name', CUSTOMIZATIONS_RESET: 'Are you sure you want remove all customizations including the custom settings of the Temperature and Analog sensors?', - DEVICE_ENTITIES: 'Device Entities', SUPPORT_INFORMATION: 'Support Information', - CLICK_HERE: 'Click Here', HELP_INFORMATION_1: 'Visit the online wiki to get instructions on how to configure EMS-ESP', HELP_INFORMATION_2: 'For live community chat join our Discord server', HELP_INFORMATION_3: 'To request a feature or report a bug', - HELP_INFORMATION_4: 'Remember to download and attach your support information for a faster response when reporting an issue', + HELP_INFORMATION_4: 'Download and attach your support information for a faster response when reporting an issue', HELP_INFORMATION_5: 'EMS-ESP is a free and open-source project. Please support its future development by giving it a star on Github!', UPLOAD: 'Upload', DOWNLOAD: '{{D|d|d}}ownload', @@ -181,20 +175,17 @@ const en: Translation = { STATUS_OF: '{0} Status', UPLOAD_DOWNLOAD: 'Upload/Download', VERSION_ON: 'You are currently on version', - SYSTEM_APPLY_FIRMWARE: 'to apply the new firmware', CLOSE: 'Close', USE: 'Use', FACTORY_RESET: 'Factory Reset', SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart', SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset EMS-ESP to its factory defaults?', - VERSION_CHECK: 'Version Check', THE_LATEST: 'The latest', OFFICIAL: 'official', DEVELOPMENT: 'development', RELEASE_IS: 'release is', RELEASE_NOTES: 'release notes', EMS_ESP_VER: 'EMS-ESP Version', - PLATFORM: 'Device (Platform / SDK)', UPTIME: 'System Uptime', HEAP: 'Heap (Free / Max Alloc)', PSRAM: 'PSRAM (Size / Free)', @@ -331,7 +322,9 @@ const en: Translation = { ACTIVEHIGH: 'Active High', ACTIVELOW: 'Active Low', UNCHANGED: 'Unchanged', - ALWAYS: 'Always' + ALWAYS: 'Always', + ACTIVITY: 'Activity', + CONFIGURE: 'Configure {0}' }; export default en; diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts index 0c6db0781..8ec81d154 100644 --- a/interface/src/i18n/fr/index.ts +++ b/interface/src/i18n/fr/index.ts @@ -12,7 +12,6 @@ const fr: Translation = { USERNAME: 'Nom d\'utilisateur', PASSWORD: 'Mot de passe', SU_PASSWORD: 'Mot de passe su', - DASHBOARD: 'Tableau de bord', SETTINGS_OF: 'Paramètres {0}', HELP_OF: 'Aide {0}', LOGGED_IN: 'Connecté en tant que {name}', @@ -37,8 +36,6 @@ const fr: Translation = { BRAND: 'Marque', ENTITY_NAME: 'Nom de l\'entité', VALUE: 'Valeur', - DEVICE_DATA: 'Données des appareils', - SENSOR_DATA: 'Données des capteurs', DEVICES: 'Appareils', SENSORS: 'Capteurs', RUN_COMMAND: 'Lancer une commande', @@ -83,7 +80,6 @@ const fr: Translation = { FAIL: 'ÉCHEC', QUALITY: 'QUALITÉ', SCAN_DEVICES: 'Rechercher de nouveaux appareils', - EMS_BUS_STATUS_TITLE: 'Statut du bus et de l\'activité EMS', SCAN: 'Scan', STATUS_NAMES: [ 'Télégrammes EMS reçus (Rx)', @@ -163,9 +159,7 @@ const fr: Translation = { OPTIONS: 'Options', NAME: 'Nom', CUSTOMIZATIONS_RESET: 'Êtes-vous sûr de vouloir supprimer toutes les personnalisations, y compris les paramètres personnalisés des capteurs de température et analogiques ?', - DEVICE_ENTITIES: 'Entités de l\'appareil', SUPPORT_INFORMATION: 'Information de support', - CLICK_HERE: 'Cliquez ici', HELP_INFORMATION_1: 'Visitez le wiki en ligne pour obtenir des instructions sur la façon de configurer EMS-ESP.', HELP_INFORMATION_2: 'Pour une discussion en direct avec la communauté, rejoignez notre serveur Discord', HELP_INFORMATION_3: 'Pour demander une fonctionnalité ou signaler un problème', @@ -181,13 +175,11 @@ const fr: Translation = { STATUS_OF: 'Statut {0}', UPLOAD_DOWNLOAD: 'Upload/Download', VERSION_ON: 'You are currently on', // TODO translate - SYSTEM_APPLY_FIRMWARE: 'pour appliquer le nouveau firmware', CLOSE: 'Fermer', USE: 'Utiliser', FACTORY_RESET: 'Réinitialisation', SYSTEM_FACTORY_TEXT: 'L\'appareil a été réinitialisé et va maintenant redémarrer', SYSTEM_FACTORY_TEXT_DIALOG: 'Êtes-vous sûr de vouloir réinitialiser l\'appareil à ses paramètres d\'usine ?', - VERSION_CHECK: 'Vérification de la version', THE_LATEST: 'La dernière', OFFICIAL: 'officielle', DEVELOPMENT: 'développement', @@ -330,7 +322,9 @@ const fr: Translation = { ACTIVEHIGH: 'Active High', // TODO translate ACTIVELOW: 'Active Low', // TODO translate UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ALWAYS: 'Always', // TODO translate + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default fr; diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts index fccc3c6da..0abe8d7b3 100644 --- a/interface/src/i18n/it/index.ts +++ b/interface/src/i18n/it/index.ts @@ -12,7 +12,6 @@ const it: Translation = { USERNAME: 'Nome Utente', PASSWORD: 'Password', SU_PASSWORD: 'su Password', - DASHBOARD: 'Pannello di Controllo', SETTINGS_OF: 'Impostazioni {0}', HELP_OF: '{0} Aiuto', LOGGED_IN: 'Registrato come {name}', @@ -37,8 +36,6 @@ const it: Translation = { BRAND: 'Marca', ENTITY_NAME: 'Nome Entità', VALUE: '{{Valore|valore}}', - DEVICE_DATA: 'Device Data', - SENSOR_DATA: 'Sensor Data', DEVICES: 'Dispositivi', SENSORS: 'Sensori', RUN_COMMAND: 'Esegui', @@ -51,7 +48,6 @@ const it: Translation = { REMOVE: 'Elimina', PROBLEM_UPDATING: 'Problema aggiornamento', PROBLEM_LOADING: 'Problema caricamento', - ACCESS_DENIED: 'Accesso Negato', ANALOG_SENSOR: 'Sensore Analogico', ANALOG_SENSORS: 'Sensori Analogici', SETTINGS: 'Settings', @@ -71,7 +67,6 @@ const it: Translation = { TEMP_SENSOR: 'Sensore Temperatura', TEMP_SENSORS: 'Sensori Temperatura', WRITE_CMD_SENT: 'Scrittura comando inviata', - WRITE_CMD_FAILED: 'Scittura comando fallita', EMS_BUS_WARNING: 'EMS bus disconnesso. Se questo avvertimento persiste dopo alcuni secondi prego verificare impostazioni scheda', EMS_BUS_SCANNING: 'Scansione dispositivi EMS ...', CONNECTED: 'Connesso', @@ -85,7 +80,6 @@ const it: Translation = { FAIL: 'FALLITO', QUALITY: 'QUALITÂ', SCAN_DEVICES: 'Scansione per nuovi dispositivi', - EMS_BUS_STATUS_TITLE: 'Bus EMS & Stato Attività', SCAN: 'Scansione', STATUS_NAMES: [ 'Telegrammi EMS Ricevuti (Rx)', @@ -165,9 +159,7 @@ const it: Translation = { OPTIONS: 'Opzioni', NAME: 'Nome', CUSTOMIZATIONS_RESET: 'Sei sicuro di voler rimuovere tutte le personalizzazioni incluse le impostazioni personalizzate dei sensori di temperatura e analogici?', - DEVICE_ENTITIES: 'Entità Dispositivo', SUPPORT_INFORMATION: 'Informazioni di Supporto', - CLICK_HERE: 'Clicca qui', HELP_INFORMATION_1: 'Visita il wiki online per ottenere istruzioni su come configurare EMS-ESP', HELP_INFORMATION_2: 'Per la chat della community dal vivo unisciti al nostro server Discord', HELP_INFORMATION_3: 'Per richiedere una funzionalità o segnalare un errore', @@ -183,13 +175,11 @@ const it: Translation = { STATUS_OF: 'Stato {0}', UPLOAD_DOWNLOAD: 'Caricamento/Scaricamento', VERSION_ON: 'Attualmente stai eseguendo la versione', - SYSTEM_APPLY_FIRMWARE: 'per applicare il nuovo firmware', CLOSE: 'Chiudere', USE: 'Usa', FACTORY_RESET: 'Impostazioni di fabbrica', SYSTEM_FACTORY_TEXT: 'Il dispositivo è stato ripristinato alle impostazioni di fabbrica e ora verrà riavviato', SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??', - VERSION_CHECK: 'Verifica Versione', THE_LATEST: 'Ultima', OFFICIAL: 'ufficiale', DEVELOPMENT: 'sviluppo', @@ -332,7 +322,9 @@ const it: Translation = { ACTIVEHIGH: 'Active High', // TODO translate ACTIVELOW: 'Active Low', // TODO translate UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ALWAYS: 'Always', // TODO translate + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default it; diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index 8978409dc..56169c405 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -12,7 +12,6 @@ const nl: Translation = { USERNAME: 'Gebruikersnaam', PASSWORD: 'Wachtwoord', SU_PASSWORD: 'su Wachtwoord', - DASHBOARD: 'Dashboard', SETTINGS_OF: '{0} Instellingen', HELP_OF: '{0} Help', LOGGED_IN: 'Ingelogd als {name}', @@ -37,8 +36,6 @@ const nl: Translation = { BRAND: 'Merk', ENTITY_NAME: 'Entiteit', VALUE: '{{Waarde|waarde}}', - SENSOR_DATA: 'Sensor data', - DEVICE_DATA: 'Apparaat data', DEVICES: 'Apparaten', SENSORS: 'Sensoren', RUN_COMMAND: 'Call commando', @@ -83,7 +80,6 @@ const nl: Translation = { FAIL: 'MISLUKT', QUALITY: 'QUALITEIT', SCAN_DEVICES: 'Scannen naar nieuwe apparaten', - EMS_BUS_STATUS_TITLE: 'EMS Bus & Activiteitenstatus', SCAN: 'Scan', STATUS_NAMES: [ 'EMS Telegrammen ontvangen (Rx)', @@ -126,7 +122,7 @@ const nl: Translation = { BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen', READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)', UNDERCLOCK_CPU: 'Underclock CPU snelheid', - HEATINGOFF: 'Start boiler with forced heating off', // TODO translate + HEATINGOFF: 'Start ketel met geforceerde verwarming uit', ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)', ENABLE_SHOWER_ALERT: 'Activeer Douchemelding', TRIGGER_TIME: 'Trigger tijd', @@ -163,9 +159,7 @@ const nl: Translation = { OPTIONS: 'Opties', NAME: 'Naam', CUSTOMIZATIONS_RESET: 'Weet je zeker dat je alle custom aanpassingen wilt verwijderen inclusief de custom instellingen voor analoge temperatuursensoren?', - DEVICE_ENTITIES: 'Apparaat Entiteiten', SUPPORT_INFORMATION: 'Support Informatie', - CLICK_HERE: 'Klik Hier', HELP_INFORMATION_1: 'Bezoek de online wiki om instructies te vinden om EMS-ESP te configureren', HELP_INFORMATION_2: 'Voor de live community ga naar de Discord server', HELP_INFORMATION_3: 'Om een nieuwe feature te vragen of een bug te rapporteren', @@ -181,13 +175,11 @@ const nl: Translation = { STATUS_OF: '{0} Status', UPLOAD_DOWNLOAD: 'Upload/Download', VERSION_ON: 'U bevindt zich momenteel op versie', - SYSTEM_APPLY_FIRMWARE: 'om de nieuwe firmware te activeren', CLOSE: 'Sluiten', USE: 'Gebruik', FACTORY_RESET: 'Fabrieksinstellingen', SYSTEM_FACTORY_TEXT: 'Gateway is gereset en start nu weer op met fabrieksinstellingen', SYSTEM_FACTORY_TEXT_DIALOG: 'Weet je zeker dat je een reset naar fabrieksinstellingen uit wilt voeren?', - VERSION_CHECK: 'Versie Check', THE_LATEST: 'De laatste', OFFICIAL: 'official', DEVELOPMENT: 'development', @@ -281,7 +273,7 @@ const nl: Translation = { NETWORK_SCANNER: 'Netwerk Scanner', NETWORK_NO_WIFI: 'Geen WiFi networken gevonden', NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen', - NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate + NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken', TX_POWER: 'Tx Vermogen', HOSTNAME: 'Hostname', NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten', @@ -317,20 +309,22 @@ const nl: Translation = { SCHEDULE_TIMER_2: 'elke minuut', SCHEDULE_TIMER_3: 'elke huur', CUSTOM_ENTITIES: 'Aangepaste Entiteiten', - ENTITIES_HELP_1: 'Aangepaste entiteiten ophalen uit de EMS-bus', // TODO translate + ENTITIES_HELP_1: 'Aangepaste entiteiten ophalen uit de EMS-bus', ENTITIES_UPDATED: 'Entiteiten bijgewerkt', WRITEABLE: 'Beschrijfbare', SHOWING: 'Tonen', SEARCH: 'Zoek', - CERT: 'TLS rootcertificaat (laat leeg om TLS-insecure)', // TODO translate + CERT: 'TLS rootcertificaat (laat leeg om TLS-insecure)', ENABLE_TLS: 'Activeer TLS', - ON: 'On', // TODO translate - OFF: 'Off', // TODO translate - POLARITY: 'Polarity', // TODO translate - ACTIVEHIGH: 'Active High', // TODO translate - ACTIVELOW: 'Active Low', // TODO translate - UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ON: 'Aan', + OFF: 'Uit', + POLARITY: 'Polariteit', + ACTIVEHIGH: 'Actiev Hoog', + ACTIVELOW: 'Actiev Laag', + UNCHANGED: 'Ongewijzigd', + ALWAYS: 'Altijd', + ACTIVITY: 'Activiteit', + CONFIGURE: '{0} Configureren' }; export default nl; diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index f264430c8..8edc1bb99 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -12,7 +12,6 @@ const no: Translation = { USERNAME: 'Brukernavn', PASSWORD: 'Passord', SU_PASSWORD: 'su Passord', - DASHBOARD: 'Dashboard', SETTINGS_OF: '{0} Innstillinger', HELP_OF: '{0} Hjelp', LOGGED_IN: 'Logget in som {name}', @@ -37,8 +36,6 @@ const no: Translation = { BRAND: 'Fabrikat', ENTITY_NAME: 'Objektsnavn', VALUE: '{{Verdi|verdi}}', - DEVICE_DATA: 'Enheterdata', - SENSOR_DATA: 'Sensordata', DEVICES: 'Enheter', SENSORS: 'Sensorer', RUN_COMMAND: 'Kjør kommando', @@ -83,7 +80,6 @@ const no: Translation = { FAIL: 'MISLYKKET', QUALITY: 'KVALITET', SCAN_DEVICES: 'Søk etter nye enheter', - EMS_BUS_STATUS_TITLE: 'EMS Buss & Aktivitet Status', SCAN: 'Søk', STATUS_NAMES: [ 'EMS Telegrammer Mottatt (Rx)', @@ -163,9 +159,7 @@ const no: Translation = { OPTIONS: 'Alternativ', NAME: 'Navn', CUSTOMIZATIONS_RESET: 'Er du sikker på att du vil fjerne tilpassninger inkludert innstillinger for Temperatur og Analoge sensorer?', - DEVICE_ENTITIES: 'Enhets objekter', SUPPORT_INFORMATION: 'Supportinformasjon', - CLICK_HERE: 'Klikk her', HELP_INFORMATION_1: 'Besøk wiki for instruksjoner for å konfigurere EMS-ESP', HELP_INFORMATION_2: 'For community-support besøk vår Discord-server', HELP_INFORMATION_3: 'For å be om en ny funksjon eller melde feil', @@ -181,13 +175,11 @@ const no: Translation = { STATUS_OF: '{0} Status', UPLOAD_DOWNLOAD: 'Opp/Nedlasting', VERSION_ON: 'You are currently on', // TODO translate - SYSTEM_APPLY_FIRMWARE: 'for å aktivere ny firmware', CLOSE: 'Steng', USE: 'Bruk', FACTORY_RESET: 'Sett tilbake til fabrikkinstilling', SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte', SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?', - VERSION_CHECK: 'Versjonsjekk', THE_LATEST: 'Den nyeste', OFFICIAL: 'official', DEVELOPMENT: 'development', @@ -330,7 +322,9 @@ const no: Translation = { ACTIVEHIGH: 'Active High', // TODO translate ACTIVELOW: 'Active Low', // TODO translate UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ALWAYS: 'Always', // TODO translate + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default no; diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index 563022b67..f68300fd9 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -12,7 +12,6 @@ const pl: BaseTranslation = { USERNAME: '{{Użytkownik|Nazwa użytkownika|}}', PASSWORD: 'Hasło', SU_PASSWORD: 'Hasło "su"', - DASHBOARD: 'Pulpit', SETTINGS_OF: 'Ustawienia {0}', HELP_OF: 'Pomoc {0}', LOGGED_IN: 'Zalogowano użytkownika {name}.', @@ -37,8 +36,6 @@ const pl: BaseTranslation = { VERSION: 'Wersja', ENTITY_NAME: '{{N|n|}}azwa encji', VALUE: '{{W|w|}}artość', - DEVICE_DATA: 'Dane z urządzeń', - SENSOR_DATA: 'Dane z czujników', DEVICES: 'Urządzenia', SENSORS: 'Czujniki', RUN_COMMAND: 'Wykonaj komendę', @@ -83,7 +80,6 @@ const pl: BaseTranslation = { FAIL: 'Nieudane', QUALITY: 'Jakość', SCAN_DEVICES: 'Wyszukiwanie nowych urządzeń', - EMS_BUS_STATUS_TITLE: 'Aktywność', SCAN: 'Skanuj', STATUS_NAMES: [ 'EMS, telegramy odebrane (Rx)', @@ -163,9 +159,7 @@ const pl: BaseTranslation = { OPTIONS: 'Opcje', NAME: '{{Nazwa|nazwa|}}', CUSTOMIZATIONS_RESET: 'Na pewno chcesz usunąć wszystkie personalizacje łącznie z ustawieniami dla czujników temperatury 1-Wire® i urządzeń podłączonych do EMS-ESP?', - DEVICE_ENTITIES: 'Encje urządzenia', SUPPORT_INFORMATION: '{{I|i|}}nformacj{{e|i|}} o systemie', - CLICK_HERE: 'Kliknij tu', HELP_INFORMATION_1: 'Aby uzyskać instrukcje dotyczące konfiguracji EMS-ESP, skorzystaj z wiki w internecie', HELP_INFORMATION_2: 'Aby dołączyć do naszego serwera Discord i komunikować się na żywo ze społecznością', HELP_INFORMATION_3: 'Aby zaproponować nową funkcjonalność lub zgłosić problem', @@ -181,13 +175,11 @@ const pl: BaseTranslation = { STATUS_OF: 'Status {0}', UPLOAD_DOWNLOAD: 'Przesyłanie plików', VERSION_ON: 'Aktualnie używasz', - SYSTEM_APPLY_FIRMWARE: '', CLOSE: 'Zamknij', USE: 'Aby zaktualizować firmware skorzystaj z funkcji', FACTORY_RESET: 'Ustawienia fabryczne', SYSTEM_FACTORY_TEXT: 'Interfejs EMS-ESP został przywrócony do ustawień fabrycznych i zostanie teraz ponownie uruchomiony.', SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP? ', - VERSION_CHECK: 'Sprawd{{ź|zanie|}} wersj{{ę|i|}}', THE_LATEST: 'Najnowsze', OFFICIAL: 'oficjalne', DEVELOPMENT: 'testowe', @@ -317,7 +309,7 @@ const pl: BaseTranslation = { SCHEDULE_TIMER_2: 'co minutę', SCHEDULE_TIMER_3: 'co godzinę', CUSTOM_ENTITIES: '{{N|n|}}iestandardowe{{|j|}} encj{{e|i|}}', - ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS.', // TODO translate + ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS.', ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.', WRITEABLE: 'Zapisywalna', SHOWING: 'Wyświetlane', @@ -330,7 +322,9 @@ const pl: BaseTranslation = { ACTIVEHIGH: 'Wyzwalany stanem wysokim', ACTIVELOW: 'Wyzwalany stanem niskim', UNCHANGED: 'Zachowaj stan', - ALWAYS: 'Zawsze' + ALWAYS: 'Zawsze', + ACTIVITY: 'Aktywność', + CONFIGURE: 'Konfiguracja {0}' }; export default pl; diff --git a/interface/src/i18n/sk/index.ts b/interface/src/i18n/sk/index.ts index 10f62d995..dbbf96a26 100644 --- a/interface/src/i18n/sk/index.ts +++ b/interface/src/i18n/sk/index.ts @@ -12,7 +12,6 @@ const sk: Translation = { USERNAME: 'Užívateľské meno', PASSWORD: 'Heslo', SU_PASSWORD: 'su heslo', - DASHBOARD: 'Panel', SETTINGS_OF: '{0} Nastavenia', HELP_OF: '{0} Pomoc', LOGGED_IN: 'Prihlásený ako {name}', @@ -37,8 +36,6 @@ const sk: Translation = { BRAND: 'Značka', ENTITY_NAME: 'Názov entity', VALUE: '{{Value|value}}', - DEVICE_DATA: 'Dáta zariadenia', - SENSOR_DATA: 'Dáta snímača', DEVICES: 'Zariadenia', SENSORS: 'Snímače', RUN_COMMAND: 'Volať príkaz', @@ -83,7 +80,6 @@ const sk: Translation = { FAIL: 'ZLYHANIE', QUALITY: 'KVALITA', SCAN_DEVICES: 'Scan pre nové zariadenia', - EMS_BUS_STATUS_TITLE: 'EMS zbernica & stav aktivity', SCAN: 'Scan', STATUS_NAMES: [ 'EMS Telegramy prijaté (Rx)', @@ -163,9 +159,7 @@ const sk: Translation = { OPTIONS: 'Možnosti', NAME: 'Názov', CUSTOMIZATIONS_RESET: 'Naozaj chcete odstrániť všetky prispôsobenia vrátane vlastných nastavení snímačov teploty a analógových snímačov?', - DEVICE_ENTITIES: 'Entity zariadenia', SUPPORT_INFORMATION: 'Informácie o podpore', - CLICK_HERE: 'Kliknite tu', HELP_INFORMATION_1: 'Navštívte online wiki, kde nájdete pokyny na konfiguráciu EMS-ESP', HELP_INFORMATION_2: 'Pre živý komunitný chat sa pripojte na náš Discord server', HELP_INFORMATION_3: 'Ak chcete požiadať o funkciu alebo nahlásiť chybu', @@ -181,13 +175,11 @@ const sk: Translation = { STATUS_OF: '{0} Stav', UPLOAD_DOWNLOAD: 'Nahrať/Stiahnuť', VERSION_ON: 'Momentálne ste vo verzii', - SYSTEM_APPLY_FIRMWARE: 'na použitie nového firmvéru', CLOSE: 'Zatvoriť', USE: 'Použiť', FACTORY_RESET: 'Továrenské nastavenia', SYSTEM_FACTORY_TEXT: 'Zariadenie bolo obnovené z výroby a teraz sa reštartuje', SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?', - VERSION_CHECK: 'Kontrola verzie', THE_LATEST: 'Posledná', OFFICIAL: 'officiálna', DEVELOPMENT: 'vývojárska', @@ -316,7 +308,7 @@ const sk: Translation = { SCHEDULE_TIMER_2: 'každú minútu', SCHEDULE_TIMER_3: 'každú hodinu', CUSTOM_ENTITIES: 'Vlastné entity', - ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS', // TODO translate + ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS', ENTITIES_UPDATED: 'Aktualizované entity', WRITEABLE: 'Zapísateľný', SHOWING: 'Zobrazenie', @@ -329,7 +321,9 @@ const sk: Translation = { ACTIVEHIGH: 'Aktívny Vysoký', ACTIVELOW: 'Aktívny Nízky', UNCHANGED: 'Nezmenené', - ALWAYS: 'Vždy' + ALWAYS: 'Vždy', + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default sk; diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts index 375c4933b..d15113652 100644 --- a/interface/src/i18n/sv/index.ts +++ b/interface/src/i18n/sv/index.ts @@ -12,7 +12,6 @@ const sv: Translation = { USERNAME: 'Användarnamn', PASSWORD: 'Lösenord', SU_PASSWORD: 'su Lösenord', - DASHBOARD: 'Kontrollpanel', SETTINGS_OF: '{0} Inställningar', HELP_OF: '{0} Hjälp', LOGGED_IN: 'Inloggad som {name}', @@ -37,8 +36,6 @@ const sv: Translation = { BRAND: 'Fabrikat', ENTITY_NAME: 'Entitetsnamn', VALUE: '{{Värde|värde}}', - DEVICE_DATA: 'Enhets data', - SENSOR_DATA: 'Sensor data', DEVICES: 'Enheter', SENSORS: 'Sensorer', RUN_COMMAND: 'Kör Kommando', @@ -83,7 +80,6 @@ const sv: Translation = { FAIL: 'Misslyckades', QUALITY: 'Kvalitet', SCAN_DEVICES: 'Sök efter nya enheter', - EMS_BUS_STATUS_TITLE: 'EMS-buss & aktivitetsstatus', SCAN: 'Sök', STATUS_NAMES: [ 'EMS-telegram (Rx)', @@ -163,9 +159,7 @@ const sv: Translation = { OPTIONS: 'Alternativ', NAME: 'Namn', CUSTOMIZATIONS_RESET: 'Är du säker på att du vill ta bort alla anpassningar inklusive inställningar för Temperatur och Analoga sensorer?', - DEVICE_ENTITIES: 'Enhets-entiteter', SUPPORT_INFORMATION: 'Supportinformation', - CLICK_HERE: 'Klicka Här', HELP_INFORMATION_1: 'Besök Wikin för instruktioner för hur du kan konfigurera EMS-ESP', HELP_INFORMATION_2: 'För community-support besök vår Discord-server', HELP_INFORMATION_3: 'Önska en ny funktion eller rapportera en bugg', @@ -181,13 +175,11 @@ const sv: Translation = { STATUS_OF: '{0} Status', UPLOAD_DOWNLOAD: 'Upp/Nedladdning', VERSION_ON: 'You are currently on', // TODO translate - SYSTEM_APPLY_FIRMWARE: 'för att aktivera ny firmware', CLOSE: 'Stäng', USE: 'Använd', FACTORY_RESET: 'Fabriksåterställning', SYSTEM_FACTORY_TEXT: 'Enheten har blivit fabriksåterställd och startar nu om', SYSTEM_FACTORY_TEXT_DIALOG: 'Är du säker att du vill fabriksåterställa enheten?', - VERSION_CHECK: 'Senaste versioner', THE_LATEST: 'Den senaste', OFFICIAL: 'officiell', DEVELOPMENT: 'utveckling', @@ -330,7 +322,9 @@ const sv: Translation = { ACTIVEHIGH: 'Active High', // TODO translate ACTIVELOW: 'Active Low', // TODO translate UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ALWAYS: 'Always', // TODO translate + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default sv; diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts index 09a39c147..d98b8d42d 100644 --- a/interface/src/i18n/tr/index.ts +++ b/interface/src/i18n/tr/index.ts @@ -12,7 +12,6 @@ const tr: Translation = { USERNAME: 'Kullanıcı Adı', PASSWORD: 'Şifre', SU_PASSWORD: 'SK Şifresi', - DASHBOARD: 'Gösterge Paneli', SETTINGS_OF: '{0} Ayarlar', HELP_OF: '{0} Yardım', LOGGED_IN: '{name} olarak giriş yapıldı', @@ -37,8 +36,6 @@ const tr: Translation = { BRAND: 'Marka', ENTITY_NAME: 'Valık Adı', VALUE: '{{Değer|değer}}', - DEVICE_DATA: 'Cihaz Bilgisi', - SENSOR_DATA: 'Sensör Bilgisi', DEVICES: 'Cihazlar', SENSORS: 'Sensörler', RUN_COMMAND: 'Çalıştırma Komutu', @@ -83,7 +80,6 @@ const tr: Translation = { FAIL: 'HATA', QUALITY: 'KALİTE', SCAN_DEVICES: 'Yeni cihaz taraması', - EMS_BUS_STATUS_TITLE: 'EMS Hattı ve Aktivite Durumu', SCAN: 'Tara', STATUS_NAMES: [ 'EMS Telegramlar Alındı (Rx)', @@ -163,9 +159,7 @@ const tr: Translation = { OPTIONS: 'Seçenekler', NAME: 'İsim', CUSTOMIZATIONS_RESET: 'Sıcaklık ve Analog Sensörlerin özelleştirilmiş seçenekleri dahil bütün özelleştirmeleri kaldırmak istediğinizden emin misiniz?', - DEVICE_ENTITIES: 'Cihaz Varlıkları', SUPPORT_INFORMATION: 'Destek Bilgileri', - CLICK_HERE: 'Buraya Tıklayın', HELP_INFORMATION_1: 'EMS-ESPnin nasıl ayarlanacağı ile ilgili bilgileri edinmek için çevrimiçi WIKI sayfasını ziyaret edin', HELP_INFORMATION_2: 'Canlı topluluk sohbeti için Discord sunucumuza katılın', HELP_INFORMATION_3: 'Yeni bir özellik talep etmek yada hata bildirmek için', @@ -181,13 +175,11 @@ const tr: Translation = { STATUS_OF: '{0} Durumu', UPLOAD_DOWNLOAD: 'Yükleme/İndirme', VERSION_ON: 'You are currently on', // TODO translate - SYSTEM_APPLY_FIRMWARE: 'yeni bellenimi uygulamak için', CLOSE: 'Kapat', USE: 'KUllan', FACTORY_RESET: 'Fabrika ayarına dönme', SYSTEM_FACTORY_TEXT: 'Cihaz fabrika ayarlarına döndü ve şimdi yendiden başlatılacak', SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?', - VERSION_CHECK: 'Sürüm Kontrolü', THE_LATEST: 'En son', OFFICIAL: 'resmi', DEVELOPMENT: 'geliştirme', @@ -330,7 +322,9 @@ const tr: Translation = { ACTIVEHIGH: 'Active High', // TODO translate ACTIVELOW: 'Active Low', // TODO translate UNCHANGED: 'Unchanged', // TODO translate - ALWAYS: 'Always' // TODO translate + ALWAYS: 'Always', // TODO translate + ACTIVITY: 'Activity', // TODO translate + CONFIGURE: 'Configure {0}' // TODO translate }; export default tr; diff --git a/interface/src/project/SettingsApplication.tsx b/interface/src/project/ApplicationSettings.tsx similarity index 99% rename from interface/src/project/SettingsApplication.tsx rename to interface/src/project/ApplicationSettings.tsx index 70a9df199..3b7d267a0 100644 --- a/interface/src/project/SettingsApplication.tsx +++ b/interface/src/project/ApplicationSettings.tsx @@ -20,7 +20,8 @@ import { ValidatedTextField, ButtonRow, MessageBox, - BlockNavigation + BlockNavigation, + useLayoutTitle } from 'components'; import RestartMonitor from 'framework/system/RestartMonitor'; @@ -36,7 +37,7 @@ export function boardProfileSelectItems() { )); } -const SettingsApplication: FC = () => { +const ApplicationSettings: FC = () => { const { loadData, saveData, @@ -97,6 +98,8 @@ const SettingsApplication: FC = () => { }); }; + useLayoutTitle(LL.APPLICATION_SETTINGS()); + const content = () => { if (!data) { return ; @@ -136,7 +139,7 @@ const SettingsApplication: FC = () => { return ( <> - + {LL.INTERFACE_BOARD_PROFILE()} @@ -680,11 +683,11 @@ const SettingsApplication: FC = () => { }; return ( - + {blocker ? : null} {restarting ? : content()} ); }; -export default SettingsApplication; +export default ApplicationSettings; diff --git a/interface/src/project/SettingsCustomEntities.tsx b/interface/src/project/CustomEntities.tsx similarity index 96% rename from interface/src/project/SettingsCustomEntities.tsx rename to interface/src/project/CustomEntities.tsx index 534ecec07..65ff8ec34 100644 --- a/interface/src/project/SettingsCustomEntities.tsx +++ b/interface/src/project/CustomEntities.tsx @@ -13,17 +13,17 @@ import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; -import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog'; +import SettingsCustomEntitiesDialog from './CustomEntitiesDialog'; import * as EMSESP from './api'; import { DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import { entityItemValidation } from './validators'; import type { EntityItem } from './types'; import type { FC } from 'react'; -import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components'; +import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; -const SettingsCustomEntities: FC = () => { +const CustomEntities: FC = () => { const { LL } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); @@ -31,6 +31,8 @@ const SettingsCustomEntities: FC = () => { const [creating, setCreating] = useState(false); const [dialogOpen, setDialogOpen] = useState(false); + useLayoutTitle(LL.CUSTOM_ENTITIES(0)); + const { data: entities, send: fetchEntities, @@ -246,7 +248,7 @@ const SettingsCustomEntities: FC = () => { }; return ( - + {blocker ? : null} {LL.ENTITIES_HELP_1()} @@ -265,7 +267,7 @@ const SettingsCustomEntities: FC = () => { /> )} - + {numChanges > 0 && ( @@ -298,4 +300,4 @@ const SettingsCustomEntities: FC = () => { ); }; -export default SettingsCustomEntities; +export default CustomEntities; diff --git a/interface/src/project/SettingsCustomEntitiesDialog.tsx b/interface/src/project/CustomEntitiesDialog.tsx similarity index 98% rename from interface/src/project/SettingsCustomEntitiesDialog.tsx rename to interface/src/project/CustomEntitiesDialog.tsx index 9c293dcd1..f7e96dedb 100644 --- a/interface/src/project/SettingsCustomEntitiesDialog.tsx +++ b/interface/src/project/CustomEntitiesDialog.tsx @@ -30,7 +30,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; import { validate } from 'validators'; -type SettingsCustomEntitiesDialogProps = { +type CustomEntitiesDialogProps = { open: boolean; creating: boolean; onClose: () => void; @@ -39,14 +39,14 @@ type SettingsCustomEntitiesDialogProps = { validator: Schema; }; -const SettingsCustomEntitiesDialog = ({ +const CustomEntitiesDialog = ({ open, creating, onClose, onSave, selectedItem, validator -}: SettingsCustomEntitiesDialogProps) => { +}: CustomEntitiesDialogProps) => { const { LL } = useI18nContext(); const [editItem, setEditItem] = useState(selectedItem); const [fieldErrors, setFieldErrors] = useState(); @@ -281,4 +281,4 @@ const SettingsCustomEntitiesDialog = ({ ); }; -export default SettingsCustomEntitiesDialog; +export default CustomEntitiesDialog; diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/Customization.tsx similarity index 97% rename from interface/src/project/SettingsCustomization.tsx rename to interface/src/project/Customization.tsx index e352d4c1f..29c9a89bd 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/Customization.tsx @@ -26,9 +26,9 @@ import { useState, useEffect, useCallback } from 'react'; import { useBlocker, useLocation } from 'react-router-dom'; import { toast } from 'react-toastify'; +import SettingsCustomizationDialog from './CustomizationDialog'; import EntityMaskToggle from './EntityMaskToggle'; import OptionIcon from './OptionIcon'; -import SettingsCustomizationDialog from './SettingsCustomizationDialog'; import * as EMSESP from './api'; @@ -37,14 +37,14 @@ import type { DeviceShort, DeviceEntity } from './types'; import type { FC } from 'react'; import { dialogStyle } from 'CustomTheme'; import * as SystemApi from 'api/system'; -import { ButtonRow, SectionContent, MessageBox, BlockNavigation } from 'components'; +import { ButtonRow, SectionContent, MessageBox, BlockNavigation, useLayoutTitle } from 'components'; import RestartMonitor from 'framework/system/RestartMonitor'; import { useI18nContext } from 'i18n/i18n-react'; export const APIURL = window.location.origin + '/api/'; -const SettingsCustomization: FC = () => { +const Customization: FC = () => { const { LL } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); @@ -58,6 +58,8 @@ const SettingsCustomization: FC = () => { const [selectedDeviceEntity, setSelectedDeviceEntity] = useState(); const [dialogOpen, setDialogOpen] = useState(false); + useLayoutTitle(LL.CUSTOMIZATIONS()); + // fetch devices first const { data: devices } = useRequest(EMSESP.readDevices); @@ -508,9 +510,6 @@ const SettingsCustomization: FC = () => { const renderContent = () => ( <> - - {LL.DEVICE_ENTITIES()} - {devices && renderDeviceList()} {deviceEntities && renderDeviceData()} {restartNeeded && ( @@ -544,7 +543,7 @@ const SettingsCustomization: FC = () => { )} - + - - - - ); - - const content = () => { - if (!data) { - return ; - } - - return ( - <> - - - - - - - - - - - - - - - - - - - - {(tableList: any) => ( - <> -
- - - {LL.SUCCESS()} - {LL.FAIL()} - {LL.QUALITY()} - -
- - {tableList.map((stat: Stat) => ( - - {showName(stat.id)} - {Intl.NumberFormat().format(stat.s)} - {Intl.NumberFormat().format(stat.f)} - {showQuality(stat)} - - ))} - - - )} -
-
- {renderScanDialog()} - - - - - - - - - - - - ); - }; - - return ( - - {content()} - - ); -}; - -export default DashboardStatus; diff --git a/interface/src/project/DeviceIcon.tsx b/interface/src/project/DeviceIcon.tsx index 9205944d8..a64572dfc 100644 --- a/interface/src/project/DeviceIcon.tsx +++ b/interface/src/project/DeviceIcon.tsx @@ -1,12 +1,13 @@ +import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd'; import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai'; import { CgSmartHomeBoiler } from 'react-icons/cg'; - import { FaSolarPanel } from 'react-icons/fa'; import { GiHeatHaze, GiTap } from 'react-icons/gi'; -import { MdThermostatAuto, MdOutlineSensors, MdOutlineExtension, MdOutlineDevices, MdOutlinePool } from 'react-icons/md'; +import { MdThermostatAuto, MdOutlineSensors, MdOutlineDevices, MdOutlinePool } from 'react-icons/md'; import { TiFlowSwitch } from 'react-icons/ti'; import { VscVmConnect } from 'react-icons/vsc'; import { DeviceType } from './types'; + import type { FC } from 'react'; interface DeviceIconProps { @@ -45,7 +46,7 @@ const DeviceIcon: FC = ({ type_id }) => { case DeviceType.POOL: return ; case DeviceType.CUSTOM: - return ; + return ; default: return null; } diff --git a/interface/src/project/DashboardDevices.tsx b/interface/src/project/Devices.tsx similarity index 94% rename from interface/src/project/DashboardDevices.tsx rename to interface/src/project/Devices.tsx index d95bc9f3a..924a8ec73 100644 --- a/interface/src/project/DashboardDevices.tsx +++ b/interface/src/project/Devices.tsx @@ -33,13 +33,13 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import { useRequest } from 'alova'; -import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react'; +import { useState, useEffect, useCallback, useLayoutEffect, useContext } from 'react'; import { IconContext } from 'react-icons'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; -import DashboardDevicesDialog from './DashboardDevicesDialog'; import DeviceIcon from './DeviceIcon'; +import DashboardDevicesDialog from './DevicesDialog'; import * as EMSESP from './api'; import { formatValue } from './deviceValue'; @@ -49,14 +49,15 @@ import { deviceValueItemValidation } from './validators'; import type { Device, DeviceValue } from './types'; import type { FC } from 'react'; import { dialogStyle } from 'CustomTheme'; -import { ButtonRow, SectionContent, MessageBox } from 'components'; -import { AuthenticatedContext } from 'contexts/authentication'; +import { ButtonRow, SectionContent, MessageBox, useLayoutTitle } from 'components'; +import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; -const DashboardDevices: FC = () => { - const { me } = useContext(AuthenticatedContext); +const Devices: FC = () => { const { LL } = useI18nContext(); + const { me } = useContext(AuthenticatedContext); + const [size, setSize] = useState([0, 0]); const [selectedDeviceValue, setSelectedDeviceValue] = useState(); const [onlyFav, setOnlyFav] = useState(false); @@ -66,6 +67,8 @@ const DashboardDevices: FC = () => { const navigate = useNavigate(); + useLayoutTitle(LL.DEVICES()); + const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), { initialData: { connected: true, @@ -281,9 +284,9 @@ const DashboardDevices: FC = () => { const customize = () => { if (selectedDevice == 99) { - navigate('/settings/customentities'); + navigate('/customentities'); } else { - navigate('/settings/customization', { state: selectedDevice }); + navigate('/customizations', { state: selectedDevice }); } }; @@ -420,11 +423,8 @@ const DashboardDevices: FC = () => { }; const renderCoreData = () => ( - + {!coreData.connected && } - {/* {coreData.connected && coreData.devices.length === 0 && ( - - )} */} {coreData.connected && ( @@ -523,9 +523,11 @@ const DashboardDevices: FC = () => { setShowDeviceInfo(true)}> - - - + {me.admin && ( + + + + )} @@ -587,7 +589,7 @@ const DashboardDevices: FC = () => { {renderNameCell(dv)}{formatValue(LL, dv.v, dv.u)} - {dv.c && me.admin && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && ( + {me.admin && dv.c && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && ( showDeviceValue(dv)}> {dv.v === '' && dv.c ? ( @@ -608,7 +610,7 @@ const DashboardDevices: FC = () => { }; return ( - + {renderCoreData()} {renderDeviceData()} {renderDeviceDetails()} @@ -619,15 +621,13 @@ const DashboardDevices: FC = () => { onSave={deviceValueDialogSave} selectedItem={selectedDeviceValue} writeable={ - me.admin && - selectedDeviceValue.c !== undefined && - !hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY) + selectedDeviceValue.c !== undefined && !hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY) } validator={deviceValueItemValidation(selectedDeviceValue)} progress={submitting} /> )} - + @@ -636,4 +636,4 @@ const DashboardDevices: FC = () => { ); }; -export default DashboardDevices; +export default Devices; diff --git a/interface/src/project/DashboardDevicesDialog.tsx b/interface/src/project/DevicesDialog.tsx similarity index 98% rename from interface/src/project/DashboardDevicesDialog.tsx rename to interface/src/project/DevicesDialog.tsx index dbc943b78..e044b283a 100644 --- a/interface/src/project/DashboardDevicesDialog.tsx +++ b/interface/src/project/DevicesDialog.tsx @@ -40,7 +40,7 @@ type DashboardDevicesDialogProps = { progress: boolean; }; -const DashboardDevicesDialog = ({ +const DevicesDialog = ({ open, onClose, onSave, @@ -204,4 +204,4 @@ const DashboardDevicesDialog = ({ ); }; -export default DashboardDevicesDialog; +export default DevicesDialog; diff --git a/interface/src/project/Help.tsx b/interface/src/project/Help.tsx index 52cf0e14c..bf4b6c3a3 100644 --- a/interface/src/project/Help.tsx +++ b/interface/src/project/Help.tsx @@ -1,9 +1,19 @@ import CommentIcon from '@mui/icons-material/CommentTwoTone'; -import EastIcon from '@mui/icons-material/East'; import DownloadIcon from '@mui/icons-material/GetApp'; import GitHubIcon from '@mui/icons-material/GitHub'; import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone'; -import { Box, List, ListItem, ListItemAvatar, ListItemText, Link, Typography, Button } from '@mui/material'; +import { + Box, + List, + ListItem, + ListItemAvatar, + ListItemText, + Link, + Typography, + Button, + ListItemButton, + Avatar +} from '@mui/material'; import { useRequest } from 'alova'; import { toast } from 'react-toastify'; import type { FC } from 'react'; @@ -39,59 +49,56 @@ const Help: FC = () => { }; return ( - - + + - - - - - {LL.HELP_INFORMATION_1()}  - -   - - {LL.CLICK_HERE()} - - + + + + + + + + - - - - - {LL.HELP_INFORMATION_2()}  - -   - - {LL.CLICK_HERE()} - - + + + + + + + + - - - - - {LL.HELP_INFORMATION_3()}  - - - {LL.CLICK_HERE()} - -
-
+ + + + + + + +
- + {LL.HELP_INFORMATION_4()} + - + - + {LL.HELP_INFORMATION_5()} diff --git a/interface/src/project/SettingsScheduler.tsx b/interface/src/project/Scheduler.tsx similarity index 96% rename from interface/src/project/SettingsScheduler.tsx rename to interface/src/project/Scheduler.tsx index 6dcb83b06..986f1cb90 100644 --- a/interface/src/project/SettingsScheduler.tsx +++ b/interface/src/project/Scheduler.tsx @@ -11,18 +11,18 @@ import { updateState, useRequest } from 'alova'; import { useState, useEffect, useCallback } from 'react'; import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; -import SettingsSchedulerDialog from './SettingsSchedulerDialog'; +import SettingsSchedulerDialog from './SchedulerDialog'; import * as EMSESP from './api'; import { ScheduleFlag } from './types'; import { schedulerItemValidation } from './validators'; import type { ScheduleItem } from './types'; import type { FC } from 'react'; -import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components'; +import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; -const SettingsScheduler: FC = () => { +const Scheduler: FC = () => { const { LL, locale } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); @@ -194,6 +194,8 @@ const SettingsScheduler: FC = () => { ); + useLayoutTitle(LL.SCHEDULER()); + return (
!si.deleted).sort((a, b) => a.time.localeCompare(b.time)) }} @@ -249,7 +251,7 @@ const SettingsScheduler: FC = () => { }; return ( - + {blocker ? : null} {LL.SCHEDULER_HELP_1()} @@ -268,7 +270,7 @@ const SettingsScheduler: FC = () => { /> )} - + {numChanges !== 0 && ( @@ -298,4 +300,4 @@ const SettingsScheduler: FC = () => { ); }; -export default SettingsScheduler; +export default Scheduler; diff --git a/interface/src/project/SettingsSchedulerDialog.tsx b/interface/src/project/SchedulerDialog.tsx similarity index 96% rename from interface/src/project/SettingsSchedulerDialog.tsx rename to interface/src/project/SchedulerDialog.tsx index 111625da1..804969504 100644 --- a/interface/src/project/SettingsSchedulerDialog.tsx +++ b/interface/src/project/SchedulerDialog.tsx @@ -32,7 +32,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import { updateValue } from 'utils'; import { validate } from 'validators'; -type SettingsSchedulerDialogProps = { +type SchedulerDialogProps = { open: boolean; creating: boolean; onClose: () => void; @@ -42,15 +42,7 @@ type SettingsSchedulerDialogProps = { dow: string[]; }; -const SettingsSchedulerDialog = ({ - open, - creating, - onClose, - onSave, - selectedItem, - validator, - dow -}: SettingsSchedulerDialogProps) => { +const SchedulerDialog = ({ open, creating, onClose, onSave, selectedItem, validator, dow }: SchedulerDialogProps) => { const { LL } = useI18nContext(); const [editItem, setEditItem] = useState(selectedItem); const [fieldErrors, setFieldErrors] = useState(); @@ -246,4 +238,4 @@ const SettingsSchedulerDialog = ({ ); }; -export default SettingsSchedulerDialog; +export default SchedulerDialog; diff --git a/interface/src/project/DashboardSensors.tsx b/interface/src/project/Sensors.tsx similarity index 92% rename from interface/src/project/DashboardSensors.tsx rename to interface/src/project/Sensors.tsx index ea2bfdb90..161c1e471 100644 --- a/interface/src/project/DashboardSensors.tsx +++ b/interface/src/project/Sensors.tsx @@ -8,26 +8,27 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import { useRequest } from 'alova'; -import { useState, useContext, useEffect } from 'react'; +import { useState, useEffect, useContext } from 'react'; import { toast } from 'react-toastify'; -import DashboardSensorsAnalogDialog from './DashboardSensorsAnalogDialog'; -import DashboardSensorsTemperatureDialog from './DashboardSensorsTemperatureDialog'; +import DashboardSensorsAnalogDialog from './SensorsAnalogDialog'; +import DashboardSensorsTemperatureDialog from './SensorsTemperatureDialog'; import * as EMSESP from './api'; import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames, AnalogType } from './types'; import { temperatureSensorItemValidation, analogSensorItemValidation } from './validators'; import type { TemperatureSensor, AnalogSensor } from './types'; import type { FC } from 'react'; -import { ButtonRow, SectionContent } from 'components'; +import { ButtonRow, SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; -const DashboardSensors: FC = () => { +const Sensors: FC = () => { const { LL } = useI18nContext(); const { me } = useContext(AuthenticatedContext); + const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState(); const [selectedAnalogSensor, setSelectedAnalogSensor] = useState(); const [temperatureDialogOpen, setTemperatureDialogOpen] = useState(false); @@ -51,8 +52,6 @@ const DashboardSensors: FC = () => { immediate: false }); - const isAdmin = me.admin; - const common_theme = useTheme({ BaseRow: ` font-size: 14px; @@ -170,6 +169,8 @@ const DashboardSensors: FC = () => { }; }); + useLayoutTitle(LL.SENSORS()); + const formatDurationMin = (duration_min: number) => { const days = Math.trunc((duration_min * 60000) / 86400000); const hours = Math.trunc((duration_min * 60000) / 3600000) % 24; @@ -220,7 +221,7 @@ const DashboardSensors: FC = () => { } const updateTemperatureSensor = (ts: TemperatureSensor) => { - if (isAdmin) { + if (me.admin) { setSelectedTemperatureSensor(ts); setTemperatureDialogOpen(true); } @@ -246,7 +247,7 @@ const DashboardSensors: FC = () => { }; const updateAnalogSensor = (as: AnalogSensor) => { - if (isAdmin) { + if (me.admin) { setCreating(false); setSelectedAnalogSensor(as); setAnalogDialogOpen(true); @@ -406,25 +407,20 @@ const DashboardSensors: FC = () => { ); return ( - - {sensorData.ts.length > 0 && ( - <> - - {LL.TEMP_SENSORS()} - - - {selectedTemperatureSensor && ( - - )} - + + + {LL.TEMP_SENSORS()} + + + {selectedTemperatureSensor && ( + )} - {sensorData?.analog_enabled === true && ( <> @@ -443,15 +439,14 @@ const DashboardSensors: FC = () => { )} )} - - + - {sensorData?.analog_enabled === true && ( + {sensorData?.analog_enabled === true && me.admin && (
+ {(tableList: any) => ( + <> +
+ + + {LL.SUCCESS()} + {LL.FAIL()} + {LL.QUALITY()} + +
+ + {tableList.map((stat: Stat) => ( + + {showName(stat.id)} + {Intl.NumberFormat().format(stat.s)} + {Intl.NumberFormat().format(stat.f)} + {showQuality(stat)} + + ))} + + + )} +
+ + + + + ); + }; + + return {content()}; +}; + +export default SystemActivity; diff --git a/interface/src/project/api.ts b/interface/src/project/api.ts index 1475c4779..16cf7184e 100644 --- a/interface/src/project/api.ts +++ b/interface/src/project/api.ts @@ -1,7 +1,7 @@ import type { APIcall, Settings, - Status, + Activity, CoreData, Devices, DeviceEntity, @@ -25,7 +25,7 @@ export const readDeviceData = (id: number) => }); export const writeDeviceValue = (data: any) => alovaInstance.Post('/rest/writeDeviceValue', data); -// SettingsApplication +// Application Settings export const readSettings = () => alovaInstance.Get('/rest/settings'); export const writeSettings = (data: any) => alovaInstance.Post('/rest/settings', data); export const getBoardProfile = (boardProfile: string) => @@ -33,17 +33,18 @@ export const getBoardProfile = (boardProfile: string) => params: { boardProfile } }); -// DashboardSensors +// Sensors export const readSensorData = () => alovaInstance.Get('/rest/sensorData'); export const writeTemperatureSensor = (ts: WriteTemperatureSensor) => alovaInstance.Post('/rest/writeTemperatureSensor', ts); export const writeAnalogSensor = (as: WriteAnalogSensor) => alovaInstance.Post('/rest/writeAnalogSensor', as); -// DashboardStatus -export const readStatus = () => alovaInstance.Get('/rest/status'); +// Activity +export const readActivity = () => alovaInstance.Get('/rest/activity'); + export const scanDevices = () => alovaInstance.Post('/rest/scanDevices'); -// HelpInformation +// API, used in HelpInformation export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall); // UploadFileForm diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index 0e4a5091a..7ca11391e 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -50,13 +50,7 @@ export interface Stat { q: number; // quality } -export interface Status { - status: busConnectionStatus; - tx_mode: number; - uptime: number; - num_devices: number; - num_sensors: number; - num_analogs: number; +export interface Activity { stats: Stat[]; } diff --git a/interface/src/types/ap.ts b/interface/src/types/ap.ts index f2d81cd6f..de5fe64ad 100644 --- a/interface/src/types/ap.ts +++ b/interface/src/types/ap.ts @@ -10,14 +10,14 @@ export enum APNetworkStatus { LINGERING = 2 } -export interface APStatus { +export interface APStatusType { status: APNetworkStatus; ip_address: string; mac_address: string; station_num: number; } -export interface APSettings { +export interface APSettingsType { provision_mode: APProvisionMode; ssid: string; password: string; diff --git a/interface/src/types/mqtt.ts b/interface/src/types/mqtt.ts index 9b3dd2fb5..738dafa75 100644 --- a/interface/src/types/mqtt.ts +++ b/interface/src/types/mqtt.ts @@ -9,7 +9,7 @@ export enum MqttDisconnectReason { TCP_DISCONNECTED = 7 } -export interface MqttStatus { +export interface MqttStatusType { enabled: boolean; connected: boolean; client_id: string; @@ -19,7 +19,7 @@ export interface MqttStatus { connect_count: number; } -export interface MqttSettings { +export interface MqttSettingsType { enabled: boolean; host: string; port: number; diff --git a/interface/src/types/network.ts b/interface/src/types/network.ts index c5d8795df..c1c1ad560 100644 --- a/interface/src/types/network.ts +++ b/interface/src/types/network.ts @@ -20,7 +20,7 @@ export enum WiFiEncryptionType { WIFI_AUTH_WPA2_WPA3_PSK = 7 } -export interface NetworkStatus { +export interface NetworkStatusType { status: NetworkConnectionStatus; local_ip: string; local_ipv6: string; @@ -36,7 +36,7 @@ export interface NetworkStatus { hostname: string; } -export interface NetworkSettings { +export interface NetworkSettingsType { ssid: string; bssid: string; password: string; diff --git a/interface/src/types/ntp.ts b/interface/src/types/ntp.ts index b783cfb80..8f86303b6 100644 --- a/interface/src/types/ntp.ts +++ b/interface/src/types/ntp.ts @@ -4,14 +4,14 @@ export enum NTPSyncStatus { NTP_ACTIVE = 2 } -export interface NTPStatus { +export interface NTPStatusType { status: NTPSyncStatus; utc_time: string; local_time: string; server: string; } -export interface NTPSettings { +export interface NTPSettingsType { enabled: boolean; server: string; tz_label: string; diff --git a/interface/src/types/security.ts b/interface/src/types/security.ts index 90db06a1e..d08d64aaf 100644 --- a/interface/src/types/security.ts +++ b/interface/src/types/security.ts @@ -1,11 +1,11 @@ -export interface User { +export interface UserType { username: string; password: string; admin: boolean; } -export interface SecuritySettings { - users: User[]; +export interface SecuritySettingsType { + users: UserType[]; jwt_secret: string; } diff --git a/interface/src/types/system.ts b/interface/src/types/system.ts index e78f0b1cf..a95723388 100644 --- a/interface/src/types/system.ts +++ b/interface/src/types/system.ts @@ -1,4 +1,6 @@ -export interface SystemStatus { +import type { busConnectionStatus } from 'project/types'; + +export interface ESPSystemStatus { emsesp_version: string; esp_platform: string; max_alloc_heap: number; @@ -16,14 +18,29 @@ export interface SystemStatus { app_free: number; fs_used: number; fs_free: number; - uptime: string; free_mem: number; psram_size?: number; free_psram?: number; has_loader: boolean; } -export interface OTASettings { +export interface SystemStatus { + emsesp_version: string; + esp_platform: string; + status: busConnectionStatus; + uptime: number; + bus_uptime: number; + num_devices: number; + num_sensors: number; + num_analogs: number; + free_heap: number; + ntp_status: number; + ota_status: boolean; + mqtt_status: boolean; + ap_status: boolean; +} + +export interface OTASettingsType { enabled: boolean; port: number; password: string; diff --git a/interface/src/validators/ap.ts b/interface/src/validators/ap.ts index cd5812e8b..d66a5ff14 100644 --- a/interface/src/validators/ap.ts +++ b/interface/src/validators/ap.ts @@ -1,9 +1,9 @@ import Schema from 'async-validator'; import { IP_ADDRESS_VALIDATOR } from './shared'; -import type { APSettings } from 'types'; -import { isAPEnabled } from 'framework/ap/APSettingsForm'; +import type { APSettingsType } from 'types'; +import { isAPEnabled } from 'framework/ap/APSettings'; -export const createAPSettingsValidator = (apSettings: APSettings) => +export const createAPSettingsValidator = (apSettings: APSettingsType) => new Schema({ provision_mode: { required: true, message: 'Please provide a provision mode' }, ...(isAPEnabled(apSettings) && { diff --git a/interface/src/validators/mqtt.ts b/interface/src/validators/mqtt.ts index d8f841baa..bb6a5d46e 100644 --- a/interface/src/validators/mqtt.ts +++ b/interface/src/validators/mqtt.ts @@ -1,8 +1,8 @@ import Schema from 'async-validator'; import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; -import type { MqttSettings } from 'types'; +import type { MqttSettingsType } from 'types'; -export const createMqttSettingsValidator = (mqttSettings: MqttSettings) => +export const createMqttSettingsValidator = (mqttSettings: MqttSettingsType) => new Schema({ ...(mqttSettings.enabled && { host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR], diff --git a/interface/src/validators/network.ts b/interface/src/validators/network.ts index 7582d7af7..1e98838ae 100644 --- a/interface/src/validators/network.ts +++ b/interface/src/validators/network.ts @@ -1,8 +1,8 @@ import Schema from 'async-validator'; import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared'; -import type { NetworkSettings } from 'types'; +import type { NetworkSettingsType } from 'types'; -export const createNetworkSettingsValidator = (networkSettings: NetworkSettings) => +export const createNetworkSettingsValidator = (networkSettings: NetworkSettingsType) => new Schema({ ssid: [{ type: 'string', max: 32, message: 'SSID must be 32 characters or less' }], bssid: [{ type: 'string', max: 17, message: 'BSSID must be 17 characters or empty' }], diff --git a/interface/src/validators/security.ts b/interface/src/validators/security.ts index d1843ac90..b5a59f540 100644 --- a/interface/src/validators/security.ts +++ b/interface/src/validators/security.ts @@ -1,6 +1,6 @@ import Schema from 'async-validator'; import type { InternalRuleItem } from 'async-validator'; -import type { User } from 'types'; +import type { UserType } from 'types'; export const SECURITY_SETTINGS_VALIDATOR = new Schema({ jwt_secret: [ @@ -9,7 +9,7 @@ export const SECURITY_SETTINGS_VALIDATOR = new Schema({ ] }); -export const createUniqueUsernameValidator = (users: User[]) => ({ +export const createUniqueUsernameValidator = (users: UserType[]) => ({ validator(rule: InternalRuleItem, username: string, callback: (error?: string) => void) { if (username && users.find((u) => u.username === username)) { callback('Username already in use'); @@ -19,7 +19,7 @@ export const createUniqueUsernameValidator = (users: User[]) => ({ } }); -export const createUserValidator = (users: User[], creating: boolean) => +export const createUserValidator = (users: UserType[], creating: boolean) => new Schema({ username: [ { required: true, message: 'Username is required' }, diff --git a/interface/vite.config.ts b/interface/vite.config.ts index b5b69c560..e55510a06 100644 --- a/interface/vite.config.ts +++ b/interface/vite.config.ts @@ -29,7 +29,7 @@ export default defineConfig(({ command, mode }) => { }; } - if (command === 'build' && mode === 'hosted') { + if (mode === 'hosted') { return { plugins: [preact(), viteTsconfigPaths()], build: { @@ -38,97 +38,94 @@ export default defineConfig(({ command, mode }) => { }; } - // production build, both for hosted and building the firmware - if (command === 'build') { - return { - plugins: [ - preact(), - viteTsconfigPaths(), - splitVendorChunkPlugin(), - { - ...viteImagemin({ - verbose: false, - gifsicle: { - optimizationLevel: 7, - interlaced: false - }, - optipng: { - optimizationLevel: 7 - }, - mozjpeg: { - quality: 20 - }, - pngquant: { - quality: [0.8, 0.9], - speed: 4 - }, - svgo: { - plugins: [ - { - name: 'removeViewBox' - }, - { - name: 'removeEmptyAttrs', - active: false - } - ] - } - }), - enforce: 'pre' - }, - visualizer({ - template: 'treemap', // or sunburst - open: false, - gzipSize: true, - brotliSize: true, - filename: 'analyse.html' // will be saved in project's root - }) - ], - - build: { - // target: 'es2022', - chunkSizeWarningLimit: 1024, - minify: 'terser', - terserOptions: { - compress: { - passes: 4, - arrows: true, - drop_console: true, - drop_debugger: true, - sequences: true + return { + plugins: [ + preact(), + viteTsconfigPaths(), + splitVendorChunkPlugin(), + { + ...viteImagemin({ + verbose: false, + gifsicle: { + optimizationLevel: 7, + interlaced: false }, - mangle: { - // toplevel: true - // module: true - // properties: { - // regex: /^_/ - // } + optipng: { + optimizationLevel: 7 }, - ecma: 5, - enclose: false, - keep_classnames: false, - keep_fnames: false, - ie8: false, - module: false, - nameCache: null, - safari10: false, - toplevel: false - }, - - rollupOptions: { - output: { - manualChunks(id: string) { - if (id.includes('node_modules')) { - // creating a chunk to react routes deps. Reducing the vendor chunk size - if (id.includes('react-router-dom') || id.includes('@remix-run') || id.includes('react-router')) { - return '@react-router'; - } - return 'vendor'; + mozjpeg: { + quality: 20 + }, + pngquant: { + quality: [0.8, 0.9], + speed: 4 + }, + svgo: { + plugins: [ + { + name: 'removeViewBox' + }, + { + name: 'removeEmptyAttrs', + active: false } + ] + } + }), + enforce: 'pre' + }, + visualizer({ + template: 'treemap', // or sunburst + open: false, + gzipSize: true, + brotliSize: true, + filename: '../analyse.html' // will be saved in project's root + }) + ], + + build: { + // target: 'es2022', + chunkSizeWarningLimit: 1024, + minify: 'terser', + terserOptions: { + compress: { + passes: 4, + arrows: true, + drop_console: true, + drop_debugger: true, + sequences: true + }, + mangle: { + // toplevel: true + // module: true + // properties: { + // regex: /^_/ + // } + }, + ecma: 5, + enclose: false, + keep_classnames: false, + keep_fnames: false, + ie8: false, + module: false, + nameCache: null, + safari10: false, + toplevel: false + }, + + rollupOptions: { + output: { + manualChunks(id: string) { + if (id.includes('node_modules')) { + // creating a chunk to react routes deps. Reducing the vendor chunk size + if (id.includes('react-router-dom') || id.includes('@remix-run') || id.includes('react-router')) { + return '@react-router'; + } + return 'vendor'; } } } } - }; - } + } + }; }); diff --git a/interface/yarn.lock b/interface/yarn.lock index 34218753d..9b52958e6 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -29,55 +29,55 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/code-frame@npm:7.23.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.24.1, @babel/code-frame@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/code-frame@npm:7.24.2" dependencies: - "@babel/highlight": "npm:^7.23.4" - chalk: "npm:^2.4.2" - checksum: 10/44e58529c9d93083288dc9e649c553c5ba997475a7b0758cc3ddc4d77b8a7d985dbe78cc39c9bbc61f26d50af6da1ddf0a3427eae8cc222a9370619b671ed8f5 + "@babel/highlight": "npm:^7.24.2" + picocolors: "npm:^1.0.0" + checksum: 10/7db8f5b36ffa3f47a37f58f61e3d130b9ecad21961f3eede7e2a4ac2c7e4a5efb6e9d03a810c669bc986096831b6c0dfc2c3082673d93351b82359c1b03e0590 languageName: node linkType: hard "@babel/compat-data@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/compat-data@npm:7.23.5" - checksum: 10/088f14f646ecbddd5ef89f120a60a1b3389a50a9705d44603dca77662707d0175a5e0e0da3943c3298f1907a4ab871468656fbbf74bb7842cd8b0686b2c19736 + version: 7.24.1 + resolution: "@babel/compat-data@npm:7.24.1" + checksum: 10/d5460b99c07ff8487467c52f742a219c7e3bcdcaa2882456a13c0d0c8116405f0c85a651fb60511284dc64ed627a5e989f24c3cd6e71d07a9947e7c8954b433c languageName: node linkType: hard -"@babel/core@npm:^7.22.1, @babel/core@npm:^7.24.0": - version: 7.24.0 - resolution: "@babel/core@npm:7.24.0" +"@babel/core@npm:^7.22.1, @babel/core@npm:^7.24.3": + version: 7.24.3 + resolution: "@babel/core@npm:7.24.3" dependencies: "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.23.5" - "@babel/generator": "npm:^7.23.6" + "@babel/code-frame": "npm:^7.24.2" + "@babel/generator": "npm:^7.24.1" "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.24.0" - "@babel/parser": "npm:^7.24.0" + "@babel/helpers": "npm:^7.24.1" + "@babel/parser": "npm:^7.24.1" "@babel/template": "npm:^7.24.0" - "@babel/traverse": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/1e22215cc89e061e0cbfed72f265ad24d363f3e9b24b51e9c4cf3ccb9222260a29a1c1e62edb439cb7e2229a3fce924edd43300500416613236c13fc8d62a947 + checksum: 10/3a7b9931fe0d93c500dcdb6b36f038b0f9d5090c048818e62aa8321c8f6e8ccc3d47373f0b40591c1fe3b13e5096bacabb1ade83f9f4d86f57878c39a9d1ade1 languageName: node linkType: hard -"@babel/generator@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/generator@npm:7.23.6" +"@babel/generator@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/generator@npm:7.24.1" dependencies: - "@babel/types": "npm:^7.23.6" - "@jridgewell/gen-mapping": "npm:^0.3.2" - "@jridgewell/trace-mapping": "npm:^0.3.17" + "@babel/types": "npm:^7.24.0" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^2.5.1" - checksum: 10/864090d5122c0aa3074471fd7b79d8a880c1468480cbd28925020a3dcc7eb6e98bedcdb38983df299c12b44b166e30915b8085a7bc126e68fa7e2aadc7bd1ac5 + checksum: 10/c6160e9cd63d7ed7168dee27d827f9c46fab820c45861a5df56cd5c78047f7c3fc97c341e9ccfa1a6f97c87ec2563d9903380b5f92794e3540a6c5f99eb8f075 languageName: node linkType: hard @@ -130,11 +130,11 @@ __metadata: linkType: hard "@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-module-imports@npm:7.22.15" + version: 7.24.3 + resolution: "@babel/helper-module-imports@npm:7.24.3" dependencies: - "@babel/types": "npm:^7.22.15" - checksum: 10/5ecf9345a73b80c28677cfbe674b9f567bb0d079e37dcba9055e36cb337db24ae71992a58e1affa9d14a60d3c69907d30fe1f80aea105184501750a58d15c81c + "@babel/types": "npm:^7.24.0" + checksum: 10/42fe124130b78eeb4bb6af8c094aa749712be0f4606f46716ce74bc18a5ea91c918c547c8bb2307a2e4b33f163e4ad2cb6a7b45f80448e624eae45b597ea3499 languageName: node linkType: hard @@ -153,7 +153,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.22.5": +"@babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0": version: 7.24.0 resolution: "@babel/helper-plugin-utils@npm:7.24.0" checksum: 10/dc8c7af321baf7653d93315beffee1790eb2c464b4f529273a24c8743a3f3095bf3f2d11828cb2c52d56282ef43a4bdc67a79c9ab8dd845e35d01871f3f28a0e @@ -179,9 +179,9 @@ __metadata: linkType: hard "@babel/helper-string-parser@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/helper-string-parser@npm:7.23.4" - checksum: 10/c352082474a2ee1d2b812bd116a56b2e8b38065df9678a32a535f151ec6f58e54633cc778778374f10544b930703cca6ddf998803888a636afa27e2658068a9c + version: 7.24.1 + resolution: "@babel/helper-string-parser@npm:7.24.1" + checksum: 10/04c0ede77b908b43e6124753b48bc485528112a9335f0a21a226bff1ace75bb6e64fab24c85cb4b1610ef3494dacd1cb807caeb6b79a7b36c43d48c289b35949 languageName: node linkType: hard @@ -199,45 +199,46 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.24.0": - version: 7.24.0 - resolution: "@babel/helpers@npm:7.24.0" +"@babel/helpers@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/helpers@npm:7.24.1" dependencies: "@babel/template": "npm:^7.24.0" - "@babel/traverse": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" - checksum: 10/cc82012161b30185c2698da359c7311cf019f0932f8fcb805e985fec9e0053c354f0534dc9961f3170eee579df6724eecd34b0f5ffaa155cdd456af59fbff86e + checksum: 10/82d3cdd3beafc4583f237515ef220bc205ced8b0540c6c6e191fc367a9589bd7304b8f9800d3d7574d4db9f079bd555979816b1874c86e53b3e7dd2032ad6c7c languageName: node linkType: hard -"@babel/highlight@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/highlight@npm:7.23.4" +"@babel/highlight@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/highlight@npm:7.24.2" dependencies: "@babel/helper-validator-identifier": "npm:^7.22.20" chalk: "npm:^2.4.2" js-tokens: "npm:^4.0.0" - checksum: 10/62fef9b5bcea7131df4626d009029b1ae85332042f4648a4ce6e740c3fd23112603c740c45575caec62f260c96b11054d3be5987f4981a5479793579c3aac71f + picocolors: "npm:^1.0.0" + checksum: 10/4555124235f34403bb28f55b1de58edf598491cc181c75f8afc8fe529903cb598cd52fe3bf2faab9bc1f45c299681ef0e44eea7a848bb85c500c5a4fe13f54f6 languageName: node linkType: hard -"@babel/parser@npm:^7.24.0": - version: 7.24.0 - resolution: "@babel/parser@npm:7.24.0" +"@babel/parser@npm:^7.24.0, @babel/parser@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/parser@npm:7.24.1" bin: parser: ./bin/babel-parser.js - checksum: 10/3e5ebb903a6f71629a9d0226743e37fe3d961e79911d2698b243637f66c4df7e3e0a42c07838bc0e7cc9fcd585d9be8f4134a145b9459ee4a459420fb0d1360b + checksum: 10/561d9454091e07ecfec3828ce79204c0fc9d24e17763f36181c6984392be4ca6b79c8225f2224fdb7b1b3b70940e243368c8f83ac77ec2dc20f46d3d06bd6795 languageName: node linkType: hard "@babel/plugin-syntax-jsx@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" + version: 7.24.1 + resolution: "@babel/plugin-syntax-jsx@npm:7.24.1" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.24.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/89037694314a74e7f0e7a9c8d3793af5bf6b23d80950c29b360db1c66859d67f60711ea437e70ad6b5b4b29affe17eababda841b6c01107c2b638e0493bafb4e + checksum: 10/712f7e7918cb679f106769f57cfab0bc99b311032665c428b98f4c3e2e6d567601d45386a4f246df6a80d741e1f94192b3f008800d66c4f1daae3ad825c243f0 languageName: node linkType: hard @@ -268,11 +269,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.7": - version: 7.24.0 - resolution: "@babel/runtime@npm:7.24.0" + version: 7.24.1 + resolution: "@babel/runtime@npm:7.24.1" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/8d32c7e116606ea322b89f9fde8ffae6be9503b549dc0d0abb38bd9dc26e87469b9fb7a66964cc089ee558fd0a97d304fb0a3cfec140694764fb0d71b6a6f5e4 + checksum: 10/3a8d61400c636d1ce3a42895a106cd4dfb4e9b88832a8a754a724c68652f821d7a46dce394305d7623f9f0d3597bf0a98aeb5f9c150ef60e14bbbf66caab4654 languageName: node linkType: hard @@ -287,25 +288,25 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.24.0": - version: 7.24.0 - resolution: "@babel/traverse@npm:7.24.0" +"@babel/traverse@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/traverse@npm:7.24.1" dependencies: - "@babel/code-frame": "npm:^7.23.5" - "@babel/generator": "npm:^7.23.6" + "@babel/code-frame": "npm:^7.24.1" + "@babel/generator": "npm:^7.24.1" "@babel/helper-environment-visitor": "npm:^7.22.20" "@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.24.0" + "@babel/parser": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10/5cc482248ebb79adcbcf021aab4e0e95bafe2a1736ee4b46abe6f88b59848ad73e15e219db8f06c9a33a14c64257e5b47e53876601e998a8c596accb1b7f4996 + checksum: 10/b9b0173c286ef549e179f3725df3c4958069ad79fe5b9840adeb99692eb4a5a08db4e735c0f086aab52e7e08ec711cee9e7c06cb908d8035641d1382172308d3 languageName: node linkType: hard -"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.24.0, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.24.0, @babel/types@npm:^7.8.3": version: 7.24.0 resolution: "@babel/types@npm:7.24.0" dependencies: @@ -462,79 +463,79 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/aix-ppc64@npm:0.19.12" +"@esbuild/aix-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/aix-ppc64@npm:0.20.2" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/android-arm64@npm:0.19.12" +"@esbuild/android-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm64@npm:0.20.2" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/android-arm@npm:0.19.12" +"@esbuild/android-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm@npm:0.20.2" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/android-x64@npm:0.19.12" +"@esbuild/android-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-x64@npm:0.20.2" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/darwin-arm64@npm:0.19.12" +"@esbuild/darwin-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-arm64@npm:0.20.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/darwin-x64@npm:0.19.12" +"@esbuild/darwin-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-x64@npm:0.20.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/freebsd-arm64@npm:0.19.12" +"@esbuild/freebsd-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-arm64@npm:0.20.2" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/freebsd-x64@npm:0.19.12" +"@esbuild/freebsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-x64@npm:0.20.2" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-arm64@npm:0.19.12" +"@esbuild/linux-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm64@npm:0.20.2" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-arm@npm:0.19.12" +"@esbuild/linux-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm@npm:0.20.2" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-ia32@npm:0.19.12" +"@esbuild/linux-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ia32@npm:0.20.2" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -546,86 +547,86 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-loong64@npm:0.19.12" +"@esbuild/linux-loong64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-loong64@npm:0.20.2" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-mips64el@npm:0.19.12" +"@esbuild/linux-mips64el@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-mips64el@npm:0.20.2" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-ppc64@npm:0.19.12" +"@esbuild/linux-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ppc64@npm:0.20.2" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-riscv64@npm:0.19.12" +"@esbuild/linux-riscv64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-riscv64@npm:0.20.2" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-s390x@npm:0.19.12" +"@esbuild/linux-s390x@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-s390x@npm:0.20.2" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/linux-x64@npm:0.19.12" +"@esbuild/linux-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-x64@npm:0.20.2" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/netbsd-x64@npm:0.19.12" +"@esbuild/netbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/netbsd-x64@npm:0.20.2" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/openbsd-x64@npm:0.19.12" +"@esbuild/openbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/openbsd-x64@npm:0.20.2" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/sunos-x64@npm:0.19.12" +"@esbuild/sunos-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/sunos-x64@npm:0.20.2" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/win32-arm64@npm:0.19.12" +"@esbuild/win32-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-arm64@npm:0.20.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/win32-ia32@npm:0.19.12" +"@esbuild/win32-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-ia32@npm:0.20.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.12": - version: 0.19.12 - resolution: "@esbuild/win32-x64@npm:0.19.12" +"@esbuild/win32-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-x64@npm:0.20.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -749,7 +750,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": +"@jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" dependencies: @@ -791,7 +792,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: @@ -801,14 +802,14 @@ __metadata: languageName: node linkType: hard -"@mui/base@npm:5.0.0-beta.39": - version: 5.0.0-beta.39 - resolution: "@mui/base@npm:5.0.0-beta.39" +"@mui/base@npm:5.0.0-beta.40": + version: 5.0.0-beta.40 + resolution: "@mui/base@npm:5.0.0-beta.40" dependencies: "@babel/runtime": "npm:^7.23.9" "@floating-ui/react-dom": "npm:^2.0.8" - "@mui/types": "npm:^7.2.13" - "@mui/utils": "npm:^5.15.13" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.14" "@popperjs/core": "npm:^2.11.8" clsx: "npm:^2.1.0" prop-types: "npm:^15.8.1" @@ -819,20 +820,20 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/320aeedb6c32df9807e2581065e98c3dd0510dcd8666c1c4804fc2281fa42e4e2111152961ded0ba1c3b9dc320936ee73d1a0861c0985a0da86a7bd7bf8cbada + checksum: 10/ebee3d9e1136710dcb2af5828acc6bd8d54f6b124785d011585c2665a48dc66e35ccb344d5ebc7fd8bfd776cccb8ea434911f151a62bee193677ee9dc67fc7fc languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/core-downloads-tracker@npm:5.15.13" - checksum: 10/988e5c7ff9c185c603b6c5d533e6dbb305b5d334c3f2b4ebcc9e2a5fb7dea141ccb05f70aa4cbf972e2556a67a0d928c7bd3bf7ab5d41b139fd0532af64e2c65 +"@mui/core-downloads-tracker@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/core-downloads-tracker@npm:5.15.14" + checksum: 10/0a1c63d906af594d0a7fb63d1d574482b3916351ea8908e8621c8bfa16ac38cf4edb5a334f0e28084f583ac928b587cab6e031f992195e0a590186faba13b9a5 languageName: node linkType: hard -"@mui/icons-material@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/icons-material@npm:5.15.13" +"@mui/icons-material@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/icons-material@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" peerDependencies: @@ -842,20 +843,20 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/89e79ebe4a0ab8594934f4056034dcffe57cc7c4468717545d67310cb74626c38ea79a7129d16fbf265745ceca7e8c35d21c3f59a4519e0776f774f6ea228fd3 + checksum: 10/a5033b67d4ff455f5fdd91fc51d26d967d634e861cde194b9dde02a8cc3f557d1b3f7e0b3175bc654b8e944f2118d46620485734ecd9d2ed4a6f748386447933 languageName: node linkType: hard -"@mui/material@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/material@npm:5.15.13" +"@mui/material@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/material@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/base": "npm:5.0.0-beta.39" - "@mui/core-downloads-tracker": "npm:^5.15.13" - "@mui/system": "npm:^5.15.13" - "@mui/types": "npm:^7.2.13" - "@mui/utils": "npm:^5.15.13" + "@mui/base": "npm:5.0.0-beta.40" + "@mui/core-downloads-tracker": "npm:^5.15.14" + "@mui/system": "npm:^5.15.14" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.14" "@types/react-transition-group": "npm:^4.4.10" clsx: "npm:^2.1.0" csstype: "npm:^3.1.3" @@ -875,16 +876,16 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10/565e219d58f93bd8cdcc5e9d432046d6bfc3b2d81e3f1b8d4b93347f895f26d3f348a312fe2b943c41275fb3ea492e76d9651ce16f64e5773fe2be07309c67a2 + checksum: 10/a2c3355b39b86472bf2debb84d6c032b1ea4ba691fbda0f25803f2702f9106130bb85a7d2757545ce97540fe185f07cf24574d5786a29df26baa298ff7db063b languageName: node linkType: hard -"@mui/private-theming@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/private-theming@npm:5.15.13" +"@mui/private-theming@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/private-theming@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/utils": "npm:^5.15.13" + "@mui/utils": "npm:^5.15.14" prop-types: "npm:^15.8.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -892,13 +893,13 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/677fa31f8caa1f3ae4b44806b2cf340b23eb9132baaca1421f615d1ef1aae5560a7c452b4f79e38bdb865a54306ba65c4ac95c1f4c0216f89217dd19fb320747 + checksum: 10/6a14311ed53ee4adccfe0ba93275b43773d22fdd10c0d4ba680b9368fc0616a5e0f38f29d2080bcd7e4ed79123047e5f245c403d3fd822e960a97762be65218d languageName: node linkType: hard -"@mui/styled-engine@npm:^5.15.11": - version: 5.15.11 - resolution: "@mui/styled-engine@npm:5.15.11" +"@mui/styled-engine@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/styled-engine@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" "@emotion/cache": "npm:^11.11.0" @@ -913,19 +914,19 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 10/fedbb9891abd633e5072d30aae7405cec9e5e22ac63c9e117c49ddb66e86ec7baaed58f934efc7847ea86cc856a8c9a9ec5a08cd0072a7850184321a968704ad + checksum: 10/2a5e03bb20502aef94cfb908898c50abb769192deb32d7f4237039683ce5266104cdc4055a7f0a8342aa62447d52b7439a4f2d0dda0fa6709c227c3621468cab languageName: node linkType: hard -"@mui/system@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/system@npm:5.15.13" +"@mui/system@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/system@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/private-theming": "npm:^5.15.13" - "@mui/styled-engine": "npm:^5.15.11" - "@mui/types": "npm:^7.2.13" - "@mui/utils": "npm:^5.15.13" + "@mui/private-theming": "npm:^5.15.14" + "@mui/styled-engine": "npm:^5.15.14" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.14" clsx: "npm:^2.1.0" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -941,25 +942,25 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10/678a741d872a4badba32f7b88d7db969af96cf3e029f984abd0f58e88e95203a1ee3f4110f970a015d15f7e9171d0dea57040cdb8419901236790df4900de017 + checksum: 10/64a9eac1bebefad3042cce28a75d0af2828aa71acd4c32fb0267f5e68bc75b16a093b6fb30709db83ec32130f14f1d67c1c27457ef62733e54a9d04f9b027cee languageName: node linkType: hard -"@mui/types@npm:^7.2.13": - version: 7.2.13 - resolution: "@mui/types@npm:7.2.13" +"@mui/types@npm:^7.2.14": + version: 7.2.14 + resolution: "@mui/types@npm:7.2.14" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10/a35bff025f715073329bd7cbe11ef4ce331ea377adffc0c5cd264bea47283590ce928d1fdbbc27506d1d462151325c81e71f2378ac4335feef3042010bbf3fcd + checksum: 10/b10cca8f63ea522be4f7c185acd1f4d031947e53824cbf9dc5649c165bcfa8a2749e83fd0bd1809b8e2698f58638ab2b4ce03550095989189d14434ea5c6c0b6 languageName: node linkType: hard -"@mui/utils@npm:^5.15.13": - version: 5.15.13 - resolution: "@mui/utils@npm:5.15.13" +"@mui/utils@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/utils@npm:5.15.14" dependencies: "@babel/runtime": "npm:^7.23.9" "@types/prop-types": "npm:^15.7.11" @@ -971,7 +972,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/16f78b87bb88f6e1131ac1ff426d4fda128d56bbbca53b98ae45ce45bf0bb826dba66cbff98b25d842225fd1a983c17ae83b43ead17c9add88789eb9514d065b + checksum: 10/b3cbe2d0aa7ec65969752dababc39fc6e0b8bb1a9cf8b9bac42ca40e3dd3eaa59b79765bd259019318acc7421d64b9f421bc67e776a581d7c9da6a1c0c50bfbc languageName: node linkType: hard @@ -1396,21 +1397,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*": - version: 20.11.27 - resolution: "@types/node@npm:20.11.27" +"@types/node@npm:*, @types/node@npm:^20.11.30": + version: 20.11.30 + resolution: "@types/node@npm:20.11.30" dependencies: undici-types: "npm:~5.26.4" - checksum: 10/4be53485d499dd7c7896190e76a0ce1f6c6917d1f4d0b4b240b3670160fcbc548daed32beaac0fc92429b37dbeaa2496fc56f460acaab969bddb77394318a89b - languageName: node - linkType: hard - -"@types/node@npm:^20.11.28": - version: 20.11.28 - resolution: "@types/node@npm:20.11.28" - dependencies: - undici-types: "npm:~5.26.4" - checksum: 10/b03f69213ac6e7cd5f7efa86139f24e23ff70a12fed04adeac5413b62d6982343ce94906f74c401c5afefda48d36ae0efd6a575240996b15a5cf80b456ab4221 + checksum: 10/78515bc768d2b878e2e06a1c20eb4f5840072b79b8d28ff3dd0a7feaaf48fd3a2ac03cfa5bc7564da82db5906b43e9ba0e5df9f43d870b7aae2942306e208815 languageName: node linkType: hard @@ -1422,9 +1414,9 @@ __metadata: linkType: hard "@types/prop-types@npm:*, @types/prop-types@npm:^15.7.11": - version: 15.7.11 - resolution: "@types/prop-types@npm:15.7.11" - checksum: 10/7519ff11d06fbf6b275029fe03fff9ec377b4cb6e864cac34d87d7146c7f5a7560fd164bdc1d2dbe00b60c43713631251af1fd3d34d46c69cd354602bc0c7c54 + version: 15.7.12 + resolution: "@types/prop-types@npm:15.7.12" + checksum: 10/ac16cc3d0a84431ffa5cfdf89579ad1e2269549f32ce0c769321fdd078f84db4fbe1b461ed5a1a496caf09e637c0e367d600c541435716a55b1d9713f5035dfe languageName: node linkType: hard @@ -1467,25 +1459,14 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*": - version: 18.2.65 - resolution: "@types/react@npm:18.2.65" +"@types/react@npm:*, @types/react@npm:^18.2.69": + version: 18.2.69 + resolution: "@types/react@npm:18.2.69" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/8022689f6c68e76b5e7b3c95af794fb3d128d5b2ccac408adaa80b117724c48b04dd4a2750e5c2ca29cd70ac7719b4ed5c5b1c12cb739d6f1d52188c09fb3060 - languageName: node - linkType: hard - -"@types/react@npm:^18.2.66": - version: 18.2.66 - resolution: "@types/react@npm:18.2.66" - dependencies: - "@types/prop-types": "npm:*" - "@types/scheduler": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10/8a82bda6c254681536fa8348dc15d52345d8203d5d322406feef865f74ebfe2475ebde0be4e2f9a18ffbb587dac946dfb5d0974b598779ff282259aff7e8209a + checksum: 10/5cc185f00ad5a4c2261e127ad25241448492f391e2206738b0477cbdba6aa75692e18ece5e75854ed513342d95c9ee83315744cb6b1470d488772cef1f8b40c8 languageName: node linkType: hard @@ -1521,15 +1502,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.2.0" +"@typescript-eslint/eslint-plugin@npm:^7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/eslint-plugin@npm:7.3.1" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:7.2.0" - "@typescript-eslint/type-utils": "npm:7.2.0" - "@typescript-eslint/utils": "npm:7.2.0" - "@typescript-eslint/visitor-keys": "npm:7.2.0" + "@typescript-eslint/scope-manager": "npm:7.3.1" + "@typescript-eslint/type-utils": "npm:7.3.1" + "@typescript-eslint/utils": "npm:7.3.1" + "@typescript-eslint/visitor-keys": "npm:7.3.1" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -1542,44 +1523,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/c50366021d63dc0f31fbd4673679d41eeaf53e1d411330742ea6e36bc854d5d9d52531df9efe708078e5c798fb9a6fca45473a451c197f46ac04050d47c9a9d2 + checksum: 10/8ed276113a714d93ab3ababb1179e4785bd9378e6d97726519ea1d2ac502a94475e0be988c2ec427dcfc1e6950329d58da6e64131ee87028fce63493461cc51a languageName: node linkType: hard -"@typescript-eslint/parser@npm:^7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/parser@npm:7.2.0" +"@typescript-eslint/parser@npm:^7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/parser@npm:7.3.1" dependencies: - "@typescript-eslint/scope-manager": "npm:7.2.0" - "@typescript-eslint/types": "npm:7.2.0" - "@typescript-eslint/typescript-estree": "npm:7.2.0" - "@typescript-eslint/visitor-keys": "npm:7.2.0" + "@typescript-eslint/scope-manager": "npm:7.3.1" + "@typescript-eslint/types": "npm:7.3.1" + "@typescript-eslint/typescript-estree": "npm:7.3.1" + "@typescript-eslint/visitor-keys": "npm:7.3.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/2236acd9f794ccb34062309f3d6fa2a0e34ac6560262213807a11fb42592011cd13ff3290a2fdbdf441fb3d248cbe23383e6c7e6c744d1cacc916159d885204f + checksum: 10/018326010fec1dcefd75809ccac5102a475bf1e052d824b898d707e7c0bf3e51e101164b410d1b2a513628985c96eb412538644d2005e26b99a22db6eb9402df languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/scope-manager@npm:7.2.0" +"@typescript-eslint/scope-manager@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/scope-manager@npm:7.3.1" dependencies: - "@typescript-eslint/types": "npm:7.2.0" - "@typescript-eslint/visitor-keys": "npm:7.2.0" - checksum: 10/9b1d43c87b0fc269df1820ebcbdb08e1c5c8cc719a6af8298d87077ca78cf5ebbfa8caa6eb5141f4dfb4cbb3a641291c50c73a213faab90bc43d34abfc68a1fe + "@typescript-eslint/types": "npm:7.3.1" + "@typescript-eslint/visitor-keys": "npm:7.3.1" + checksum: 10/7384d1f46d7f3678a1135a1ac0bd8b6dfa2f01e93b19e2510c7082766cf6983a1bf80b4ccf498651199a81d9f2bdb65101fd7a19226a723260514204d0c30b34 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/type-utils@npm:7.2.0" +"@typescript-eslint/type-utils@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/type-utils@npm:7.3.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.2.0" - "@typescript-eslint/utils": "npm:7.2.0" + "@typescript-eslint/typescript-estree": "npm:7.3.1" + "@typescript-eslint/utils": "npm:7.3.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -1587,23 +1568,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/1c4efcd068987ed5bbf6f3dda1fed313eec84fc0840af6e00593338cc2605c96ab760bf83f868271a6b5fcde8a44d00e21b70a8607474a4df9d43d29775bb235 + checksum: 10/fae9003a76a8f2a2a4bb88dc0f82c0a1ca0688633183fac391920e7124a12807aac84bb287a21f61e99523c15223d1c08e7680685ebf21d07429604cba6c420b languageName: node linkType: hard -"@typescript-eslint/types@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/types@npm:7.2.0" - checksum: 10/d70cbd77f21caddbb1c3519bb523b5217a300d52682e9acfa9ff645d7250f7f07653f48930f531675216e848b5f83cb9b14cf63db76239cec1159550a989e16d +"@typescript-eslint/types@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/types@npm:7.3.1" + checksum: 10/c9c8eae1cf937cececd99a253bd65eb71b40206e79cf917ad9c3b3ab80cc7ce5fefb2804f9fd2a70e7438951f0d1e63df3031fc61e3a08dfef5fde208a12e0ed languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.2.0" +"@typescript-eslint/typescript-estree@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/typescript-estree@npm:7.3.1" dependencies: - "@typescript-eslint/types": "npm:7.2.0" - "@typescript-eslint/visitor-keys": "npm:7.2.0" + "@typescript-eslint/types": "npm:7.3.1" + "@typescript-eslint/visitor-keys": "npm:7.3.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -1613,34 +1594,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/77a81dc903da1ccb302c96bf7f845f297d87ab7871849bfabdddee51583646a1147923fc23c550c6c783229bc7bda37a3ea147478fa08b3847d0440a34587198 + checksum: 10/363ad9864b56394b4000dff7c2b77d0ea52042c3c20e3b86c0f3c66044915632d9890255527c6f3a5ef056886dec72e38fbcfce49d4ad092c160440f54128230 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/utils@npm:7.2.0" +"@typescript-eslint/utils@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/utils@npm:7.3.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:7.2.0" - "@typescript-eslint/types": "npm:7.2.0" - "@typescript-eslint/typescript-estree": "npm:7.2.0" + "@typescript-eslint/scope-manager": "npm:7.3.1" + "@typescript-eslint/types": "npm:7.3.1" + "@typescript-eslint/typescript-estree": "npm:7.3.1" semver: "npm:^7.5.4" peerDependencies: eslint: ^8.56.0 - checksum: 10/4852d43f1e0ca7e4914fef6cb5984a472d77af6fafcfad18905c0ba2ac5539a7ba8e72a4b3f7cbff712733f9cf8e8af790b4875f944aae1006ca297f8e041d32 + checksum: 10/234d9d65fe5d0f4a31345bd8f5a6f2879a578b3a531a14c2b3edaa7fb587c71d26249f86c41857382c0405384dc104955c02b588b3cee6fc2734f1ae40aef07b languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.2.0" +"@typescript-eslint/visitor-keys@npm:7.3.1": + version: 7.3.1 + resolution: "@typescript-eslint/visitor-keys@npm:7.3.1" dependencies: - "@typescript-eslint/types": "npm:7.2.0" + "@typescript-eslint/types": "npm:7.3.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: 10/e0c9c7a9bb1ae93149e7a4816aed12651fd7374d0eb17e1f45348dbfddd8ee7014d3de35b40bc46b9df73cc1c9053aaf5d82b43270d93a0b551ed14e8afde37a + checksum: 10/163a93597c1d696920a19b3c1627d02368bdd52059f811c0fadd680c38034bb6418ebefe99d8ce26e0dd44ae184f18fab186af775de1a8771256be1a7905c174 languageName: node linkType: hard @@ -1656,23 +1637,23 @@ __metadata: resolution: "EMS-ESP@workspace:." dependencies: "@alova/adapter-xhr": "npm:^1.0.3" - "@babel/core": "npm:^7.24.0" + "@babel/core": "npm:^7.24.3" "@emotion/react": "npm:^11.11.4" "@emotion/styled": "npm:^11.11.0" - "@mui/icons-material": "npm:^5.15.13" - "@mui/material": "npm:^5.15.13" + "@mui/icons-material": "npm:^5.15.14" + "@mui/material": "npm:^5.15.14" "@preact/compat": "npm:^17.1.2" "@preact/preset-vite": "npm:^2.8.2" "@table-library/react-table-library": "npm:4.1.7" "@types/imagemin": "npm:^8.0.5" "@types/lodash-es": "npm:^4.17.12" - "@types/node": "npm:^20.11.28" - "@types/react": "npm:^18.2.66" + "@types/node": "npm:^20.11.30" + "@types/react": "npm:^18.2.69" "@types/react-dom": "npm:^18.2.22" "@types/react-router-dom": "npm:^5.3.3" - "@typescript-eslint/eslint-plugin": "npm:^7.2.0" - "@typescript-eslint/parser": "npm:^7.2.0" - alova: "npm:^2.17.1" + "@typescript-eslint/eslint-plugin": "npm:^7.3.1" + "@typescript-eslint/parser": "npm:^7.3.1" + alova: "npm:^2.18.0" async-validator: "npm:^4.2.5" concurrently: "npm:^8.2.2" eslint: "npm:^8.57.0" @@ -1682,13 +1663,13 @@ __metadata: eslint-plugin-import: "npm:^2.29.1" eslint-plugin-jsx-a11y: "npm:^6.8.0" eslint-plugin-prettier: "npm:alpha" - eslint-plugin-react: "npm:^7.34.0" + eslint-plugin-react: "npm:^7.34.1" eslint-plugin-react-hooks: "npm:^4.6.0" history: "npm:^5.3.0" jwt-decode: "npm:^4.0.0" lodash-es: "npm:^4.17.21" mime-types: "npm:^2.1.35" - preact: "npm:^10.19.6" + preact: "npm:^10.20.1" prettier: "npm:^3.2.5" react: "npm:latest" react-dom: "npm:latest" @@ -1700,8 +1681,8 @@ __metadata: sockette: "npm:^2.0.6" terser: "npm:^5.29.2" typesafe-i18n: "npm:^5.26.2" - typescript: "npm:^5.4.2" - vite: "npm:^5.1.6" + typescript: "npm:^5.4.3" + vite: "npm:^5.2.4" vite-plugin-imagemin: "npm:^0.6.1" vite-tsconfig-paths: "npm:^4.3.2" languageName: unknown @@ -1763,10 +1744,10 @@ __metadata: languageName: node linkType: hard -"alova@npm:^2.17.1": - version: 2.17.1 - resolution: "alova@npm:2.17.1" - checksum: 10/8ad39e0847157cbff285be078a7f490ddcfacc4a0de8a2d5976607206e29c8fa232fcf088079bc87ef26eea2bc432a2bda835bc0d996e02a526990ef93f7a6d3 +"alova@npm:^2.18.0": + version: 2.18.0 + resolution: "alova@npm:2.18.0" + checksum: 10/6edf15157f4bce4239ba3461bf71a653fd4e904c80e5e7d4574328bb30d5704d5e4fc9c024b60f886bb010ee3e29e56cfb6ab7fc235a6a2aa4ee879cae35e387 languageName: node linkType: hard @@ -1873,15 +1854,16 @@ __metadata: linkType: hard "array-includes@npm:^3.1.6, array-includes@npm:^3.1.7": - version: 3.1.7 - resolution: "array-includes@npm:3.1.7" + version: 3.1.8 + resolution: "array-includes@npm:3.1.8" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - get-intrinsic: "npm:^1.2.1" + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" is-string: "npm:^1.0.7" - checksum: 10/856a8be5d118967665936ad33ff3b07adfc50b06753e596e91fb80c3da9b8c022e92e3cc6781156d6ad95db7109b9f603682c7df2d6a529ed01f7f6b39a4a360 + checksum: 10/290b206c9451f181fb2b1f79a3bf1c0b66bb259791290ffbada760c79b284eef6f5ae2aeb4bcff450ebc9690edd25732c4c73a3c2b340fcc0f4563aed83bf488 languageName: node linkType: hard @@ -1892,42 +1874,31 @@ __metadata: languageName: node linkType: hard -"array.prototype.filter@npm:^1.0.3": - version: 1.0.3 - resolution: "array.prototype.filter@npm:1.0.3" - dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - es-array-method-boxes-properly: "npm:^1.0.0" - is-string: "npm:^1.0.7" - checksum: 10/3da2189afb00f95559cc73fc3c50f17a071a65bb705c0b2f2e2a2b2142781215b622442368c8b4387389b6ab251adf09ad347f9a8a4cf29d24404cc5ea1e295c - languageName: node - linkType: hard - "array.prototype.findlast@npm:^1.2.4": - version: 1.2.4 - resolution: "array.prototype.findlast@npm:1.2.4" + version: 1.2.5 + resolution: "array.prototype.findlast@npm:1.2.5" dependencies: - call-bind: "npm:^1.0.5" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.3" + es-abstract: "npm:^1.23.2" es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" es-shim-unscopables: "npm:^1.0.2" - checksum: 10/1711e48058cabbad24cb694fa3721b760e56004758142c439880a19b9b206e3584b94bbad41e5f68e0da8785db1d09250061a46769baa90a0d2e09c05987c82d + checksum: 10/7dffcc665aa965718ad6de7e17ac50df0c5e38798c0a5bf9340cf24feb8594df6ec6f3fcbe714c1577728a1b18b5704b15669474b27bceeca91ef06ce2a23c31 languageName: node linkType: hard "array.prototype.findlastindex@npm:^1.2.3": - version: 1.2.4 - resolution: "array.prototype.findlastindex@npm:1.2.4" + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" dependencies: - call-bind: "npm:^1.0.5" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.3" + es-abstract: "npm:^1.23.2" es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" es-shim-unscopables: "npm:^1.0.2" - checksum: 10/12d7de8da619065b9d4c40550d11c13f2fbbc863c4270ef01d022f49ef16fbe9022441ee9d60b1e952853c661dd4b3e05c21e4348d4631c6d93ddf802a252296 + checksum: 10/7c5c821f357cd53ab6cc305de8086430dd8d7a2485db87b13f843e868055e9582b1fd338f02338f67fc3a1603ceaf9610dd2a470b0b506f9d18934780f95b246 languageName: node linkType: hard @@ -2010,15 +1981,6 @@ __metadata: languageName: node linkType: hard -"asynciterator.prototype@npm:^1.0.0": - version: 1.0.0 - resolution: "asynciterator.prototype@npm:1.0.0" - dependencies: - has-symbols: "npm:^1.0.3" - checksum: 10/e8ebfd9493ac651cf9b4165e9d64030b3da1d17181bb1963627b59e240cdaf021d9b59d44b827dc1dde4e22387ec04c2d0f8720cf58a1c282e34e40cc12721b3 - languageName: node - linkType: hard - "attr-accept@npm:^2.2.2": version: 2.2.2 resolution: "attr-accept@npm:2.2.2" @@ -2323,9 +2285,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001587": - version: 1.0.30001597 - resolution: "caniuse-lite@npm:1.0.30001597" - checksum: 10/44a268113faeee51e249cbcb3924dc3765f26cd527a134e3bb720ed20d50abd8b9291500a88beee061cc03ae9f15ddc9045d57e30d25a98efeaff4f7bb8965c1 + version: 1.0.30001600 + resolution: "caniuse-lite@npm:1.0.30001600" + checksum: 10/4c52f83ed71bc5f6e443bd17923460f1c77915adc2c2aa79ddaedceccc690b5917054b0c41b79e9138cbbd9abcdc0db9e224e79e3e734e581dfec06505f3a2b4 languageName: node linkType: hard @@ -2689,7 +2651,7 @@ __metadata: languageName: node linkType: hard -"data-view-byte-length@npm:^1.0.0": +"data-view-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "data-view-byte-length@npm:1.0.1" dependencies: @@ -3030,9 +2992,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.668": - version: 1.4.703 - resolution: "electron-to-chromium@npm:1.4.703" - checksum: 10/e7927fbe75e56508dd0b4efeb0e69dfb8ee1e6e6aaf6f07c047b96ff530d8f49e1eaf51cae64c2d3c179e3932fb37661012ccaa4f36956dd96480219f3a23013 + version: 1.4.715 + resolution: "electron-to-chromium@npm:1.4.715" + checksum: 10/0fc9fc9fe2c4082c87672d229437c918f43c19ad81274afcdf2d36fba96bfaaee0d9254c309879fbea8eeb2c13fd1b5cf51e0967d55790206697914cecff9c6b languageName: node linkType: hard @@ -3115,19 +3077,20 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.22.4": - version: 1.23.0 - resolution: "es-abstract@npm:1.23.0" +"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2": + version: 1.23.2 + resolution: "es-abstract@npm:1.23.2" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" data-view-buffer: "npm:^1.0.1" - data-view-byte-length: "npm:^1.0.0" + data-view-byte-length: "npm:^1.0.1" data-view-byte-offset: "npm:^1.0.0" es-define-property: "npm:^1.0.0" es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" es-set-tostringtag: "npm:^2.0.3" es-to-primitive: "npm:^1.2.1" function.prototype.name: "npm:^1.1.6" @@ -3138,7 +3101,7 @@ __metadata: has-property-descriptors: "npm:^1.0.2" has-proto: "npm:^1.0.3" has-symbols: "npm:^1.0.3" - hasown: "npm:^2.0.1" + hasown: "npm:^2.0.2" internal-slot: "npm:^1.0.7" is-array-buffer: "npm:^3.0.4" is-callable: "npm:^1.2.7" @@ -3153,25 +3116,18 @@ __metadata: object-keys: "npm:^1.1.1" object.assign: "npm:^4.1.5" regexp.prototype.flags: "npm:^1.5.2" - safe-array-concat: "npm:^1.1.0" + safe-array-concat: "npm:^1.1.2" safe-regex-test: "npm:^1.0.3" - string.prototype.trim: "npm:^1.2.8" - string.prototype.trimend: "npm:^1.0.7" + string.prototype.trim: "npm:^1.2.9" + string.prototype.trimend: "npm:^1.0.8" string.prototype.trimstart: "npm:^1.0.7" typed-array-buffer: "npm:^1.0.2" typed-array-byte-length: "npm:^1.0.1" typed-array-byte-offset: "npm:^1.0.2" typed-array-length: "npm:^1.0.5" unbox-primitive: "npm:^1.0.2" - which-typed-array: "npm:^1.1.14" - checksum: 10/b66cec32fcb896c7a3bbb7cb717f3f6bbbb73efe1c6003f0d7a899aecc358feed38ec2cad55e2a1d71a4a95ec7e7cc1dbbca34368deb0b98e36fe02cc5559b31 - languageName: node - linkType: hard - -"es-array-method-boxes-properly@npm:^1.0.0": - version: 1.0.0 - resolution: "es-array-method-boxes-properly@npm:1.0.0" - checksum: 10/27a8a21acf20f3f51f69dce8e643f151e380bffe569e95dc933b9ded9fcd89a765ee21b5229c93f9206c93f87395c6b75f80be8ac8c08a7ceb8771e1822ff1fb + which-typed-array: "npm:^1.1.15" + checksum: 10/f8fa0ef674b176f177f637f1af13fb895d10306e1eb1f57dc48a5aa64a643da307f96b222054ff76f3fd9029983295192c55fc54169f464ad2fcee992c5b7310 languageName: node linkType: hard @@ -3184,7 +3140,7 @@ __metadata: languageName: node linkType: hard -"es-errors@npm:^1.0.0, es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": +"es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 @@ -3192,29 +3148,37 @@ __metadata: linkType: hard "es-iterator-helpers@npm:^1.0.15, es-iterator-helpers@npm:^1.0.17": - version: 1.0.17 - resolution: "es-iterator-helpers@npm:1.0.17" + version: 1.0.18 + resolution: "es-iterator-helpers@npm:1.0.18" dependencies: - asynciterator.prototype: "npm:^1.0.0" call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.4" + es-abstract: "npm:^1.23.0" es-errors: "npm:^1.3.0" - es-set-tostringtag: "npm:^2.0.2" + es-set-tostringtag: "npm:^2.0.3" function-bind: "npm:^1.1.2" get-intrinsic: "npm:^1.2.4" globalthis: "npm:^1.0.3" has-property-descriptors: "npm:^1.0.2" - has-proto: "npm:^1.0.1" + has-proto: "npm:^1.0.3" has-symbols: "npm:^1.0.3" internal-slot: "npm:^1.0.7" iterator.prototype: "npm:^1.1.2" - safe-array-concat: "npm:^1.1.0" - checksum: 10/42c6eb65368d34b556dac1cc8d34ba753eb526bc7d4594be3b77799440be78d31fddfd60717af2d9ce6d021de8346e7a573141d789821e38836e60441f93ccfd + safe-array-concat: "npm:^1.1.2" + checksum: 10/a4fd067e148736fbe6a9883f449e0de88be14a4dff9065c457572ede10ba02a4a15c4ae18b9b7baa5c868860d2be9a6764906c3308135e57ec5bfd386bbd2836 languageName: node linkType: hard -"es-set-tostringtag@npm:^2.0.2, es-set-tostringtag@npm:^2.0.3": +"es-object-atoms@npm:^1.0.0": + version: 1.0.0 + resolution: "es-object-atoms@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/f8910cf477e53c0615f685c5c96210591841850871b81924fcf256bfbaa68c254457d994a4308c60d15b20805e7f61ce6abc669375e01a5349391a8c1767584f + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.0.3": version: 2.0.3 resolution: "es-set-tostringtag@npm:2.0.3" dependencies: @@ -3459,33 +3423,33 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.19.3": - version: 0.19.12 - resolution: "esbuild@npm:0.19.12" +"esbuild@npm:^0.20.1": + version: 0.20.2 + resolution: "esbuild@npm:0.20.2" dependencies: - "@esbuild/aix-ppc64": "npm:0.19.12" - "@esbuild/android-arm": "npm:0.19.12" - "@esbuild/android-arm64": "npm:0.19.12" - "@esbuild/android-x64": "npm:0.19.12" - "@esbuild/darwin-arm64": "npm:0.19.12" - "@esbuild/darwin-x64": "npm:0.19.12" - "@esbuild/freebsd-arm64": "npm:0.19.12" - "@esbuild/freebsd-x64": "npm:0.19.12" - "@esbuild/linux-arm": "npm:0.19.12" - "@esbuild/linux-arm64": "npm:0.19.12" - "@esbuild/linux-ia32": "npm:0.19.12" - "@esbuild/linux-loong64": "npm:0.19.12" - "@esbuild/linux-mips64el": "npm:0.19.12" - "@esbuild/linux-ppc64": "npm:0.19.12" - "@esbuild/linux-riscv64": "npm:0.19.12" - "@esbuild/linux-s390x": "npm:0.19.12" - "@esbuild/linux-x64": "npm:0.19.12" - "@esbuild/netbsd-x64": "npm:0.19.12" - "@esbuild/openbsd-x64": "npm:0.19.12" - "@esbuild/sunos-x64": "npm:0.19.12" - "@esbuild/win32-arm64": "npm:0.19.12" - "@esbuild/win32-ia32": "npm:0.19.12" - "@esbuild/win32-x64": "npm:0.19.12" + "@esbuild/aix-ppc64": "npm:0.20.2" + "@esbuild/android-arm": "npm:0.20.2" + "@esbuild/android-arm64": "npm:0.20.2" + "@esbuild/android-x64": "npm:0.20.2" + "@esbuild/darwin-arm64": "npm:0.20.2" + "@esbuild/darwin-x64": "npm:0.20.2" + "@esbuild/freebsd-arm64": "npm:0.20.2" + "@esbuild/freebsd-x64": "npm:0.20.2" + "@esbuild/linux-arm": "npm:0.20.2" + "@esbuild/linux-arm64": "npm:0.20.2" + "@esbuild/linux-ia32": "npm:0.20.2" + "@esbuild/linux-loong64": "npm:0.20.2" + "@esbuild/linux-mips64el": "npm:0.20.2" + "@esbuild/linux-ppc64": "npm:0.20.2" + "@esbuild/linux-riscv64": "npm:0.20.2" + "@esbuild/linux-s390x": "npm:0.20.2" + "@esbuild/linux-x64": "npm:0.20.2" + "@esbuild/netbsd-x64": "npm:0.20.2" + "@esbuild/openbsd-x64": "npm:0.20.2" + "@esbuild/sunos-x64": "npm:0.20.2" + "@esbuild/win32-arm64": "npm:0.20.2" + "@esbuild/win32-ia32": "npm:0.20.2" + "@esbuild/win32-x64": "npm:0.20.2" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -3535,7 +3499,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/861fa8eb2428e8d6521a4b7c7930139e3f45e8d51a86985cc29408172a41f6b18df7b3401e7e5e2d528cdf83742da601ddfdc77043ddc4f1c715a8ddb2d8a255 + checksum: 10/663215ab7e599651e00d61b528a63136e1f1d397db8b9c3712540af928c9476d61da95aefa81b7a8dfc7a9fdd7616fcf08395c27be68be8c99953fb461863ce4 languageName: node linkType: hard @@ -3708,9 +3672,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:^7.34.0": - version: 7.34.0 - resolution: "eslint-plugin-react@npm:7.34.0" +"eslint-plugin-react@npm:^7.34.1": + version: 7.34.1 + resolution: "eslint-plugin-react@npm:7.34.1" dependencies: array-includes: "npm:^3.1.7" array.prototype.findlast: "npm:^1.2.4" @@ -3732,7 +3696,7 @@ __metadata: string.prototype.matchall: "npm:^4.0.10" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 10/e09623d715e25e012cc442648616ea5f8029c17a397e7b4f54c47da7cc4edb0ffec91af3269c259c1a92b8d83802b10f9c7148280a0c8d7659b15724ee8b50d8 + checksum: 10/ee059971065ea7e73ab5d8728774235c7dbf7a5e9f937c3b47e97f8fa9a5a96ab511d2ae6d5ec76a7e705ca666673d454f1e75a94033720819d041827f50f9c8 languageName: node linkType: hard @@ -4012,13 +3976,13 @@ __metadata: linkType: hard "fast-xml-parser@npm:^4.1.3": - version: 4.3.5 - resolution: "fast-xml-parser@npm:4.3.5" + version: 4.3.6 + resolution: "fast-xml-parser@npm:4.3.6" dependencies: strnum: "npm:^1.0.5" bin: fxparser: src/cli/cli.js - checksum: 10/ccfd943e4ed400bf3acd4b6fcba9a15ec992a4a76c9a0cf825fc06f2b35a79a6d1de477b9a73c23633597c4f2a35838550ec3013a056f2591aa8cb3989d1f242 + checksum: 10/3e431e594960f04996e60a01fb51d8f4346138a7ba60d97244bf7866a3072eaf2f6dc73008d7b07871b98b606a8d7db955efdeae787992f685dd0e5bcc67c36a languageName: node linkType: hard @@ -4704,7 +4668,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0, hasown@npm:^2.0.1": +"hasown@npm:^2.0.0, hasown@npm:^2.0.1, hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -4975,7 +4939,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.5, internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" dependencies: @@ -6238,58 +6202,58 @@ __metadata: linkType: hard "object.entries@npm:^1.1.7": - version: 1.1.7 - resolution: "object.entries@npm:1.1.7" + version: 1.1.8 + resolution: "object.entries@npm:1.1.8" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/03f0bd0f23a8626c94429d15abf26ccda7723f08cd26be2c09c72d436765f8c7468605b5476ca58d4a7cec1ec7eca5be496dbd938fd4236b77ed6d05a8680048 + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/2301918fbd1ee697cf6ff7cd94f060c738c0a7d92b22fd24c7c250e9b593642c9707ad2c44d339303c1439c5967d8964251cdfc855f7f6ec55db2dd79e8dc2a7 languageName: node linkType: hard "object.fromentries@npm:^2.0.7": - version: 2.0.7 - resolution: "object.fromentries@npm:2.0.7" + version: 2.0.8 + resolution: "object.fromentries@npm:2.0.8" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/1bfbe42a51f8d84e417d193fae78e4b8eebb134514cdd44406480f8e8a0e075071e0717635d8e3eccd50fec08c1d555fe505c38804cbac0808397187653edd59 + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10/5b2e80f7af1778b885e3d06aeb335dcc86965e39464671adb7167ab06ac3b0f5dd2e637a90d8ebd7426d69c6f135a4753ba3dd7d0fe2a7030cf718dcb910fd92 languageName: node linkType: hard "object.groupby@npm:^1.0.1": - version: 1.0.2 - resolution: "object.groupby@npm:1.0.2" + version: 1.0.3 + resolution: "object.groupby@npm:1.0.3" dependencies: - array.prototype.filter: "npm:^1.0.3" - call-bind: "npm:^1.0.5" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.3" - es-errors: "npm:^1.0.0" - checksum: 10/07c1bea1772c45f7967a63358a683ef7b0bd99cabe0563e6fee3e8acc061cc5984d2f01a46472ebf10b2cb439298c46776b2134550dce457fd7240baaaa4f592 + es-abstract: "npm:^1.23.2" + checksum: 10/44cb86dd2c660434be65f7585c54b62f0425b0c96b5c948d2756be253ef06737da7e68d7106e35506ce4a44d16aa85a413d11c5034eb7ce5579ec28752eb42d0 languageName: node linkType: hard "object.hasown@npm:^1.1.3": - version: 1.1.3 - resolution: "object.hasown@npm:1.1.3" + version: 1.1.4 + resolution: "object.hasown@npm:1.1.4" dependencies: - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/735679729c25a4e0d3713adf5df9861d862f0453e87ada4d991b75cd4225365dec61a08435e1127f42c9cc1adfc8e952fa5dca75364ebda6539dadf4721dc9c4 + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10/797385577b3ef3c0d19333e03ed34bc7987978ae1ee1245069c9922e17d1128265187f729dc610260d03f8d418af26fcd7919b423793bf0af9099d9f08367d69 languageName: node linkType: hard "object.values@npm:^1.1.6, object.values@npm:^1.1.7": - version: 1.1.7 - resolution: "object.values@npm:1.1.7" + version: 1.2.0 + resolution: "object.values@npm:1.2.0" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/20ab42c0bbf984405c80e060114b18cf5d629a40a132c7eac4fb79c5d06deb97496311c19297dcf9c61f45c2539cd4c7f7c5d6230e51db360ff297bbc9910162 + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/db2e498019c354428c5dd30d02980d920ac365b155fce4dcf63eb9433f98ccf0f72624309e182ce7cc227c95e45d474e1d483418e60de2293dd23fa3ebe34903 languageName: node linkType: hard @@ -6667,21 +6631,21 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.35": - version: 8.4.35 - resolution: "postcss@npm:8.4.35" +"postcss@npm:^8.4.36": + version: 8.4.38 + resolution: "postcss@npm:8.4.38" dependencies: nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" - source-map-js: "npm:^1.0.2" - checksum: 10/93a7ce50cd6188f5f486a9ca98950ad27c19dfed996c45c414fa242944497e4d084a8760d3537f078630226f2bd3c6ab84b813b488740f4432e7c7039cd73a20 + source-map-js: "npm:^1.2.0" + checksum: 10/6e44a7ed835ffa9a2b096e8d3e5dfc6bcf331a25c48aeb862dd54e3aaecadf814fa22be224fd308f87d08adf2299164f88c5fd5ab1c4ef6cbd693ceb295377f4 languageName: node linkType: hard -"preact@npm:^10.19.6": - version: 10.19.6 - resolution: "preact@npm:10.19.6" - checksum: 10/851c7d91e6899a40fdeae0ef9a792bf3217ed8365ce96f4c5630048c82b44c637fd4c0d8a4b0c3e1c8e74e243600dd9c5787520da07552d33a06c957779b4167 +"preact@npm:^10.20.1": + version: 10.20.1 + resolution: "preact@npm:10.20.1" + checksum: 10/894ac14b3ec6f8ca308b53fb14e12e57678248fd1faa24ae857f5e37d9c11b34833e6dd1ba8210a34de4d6d523462923b1f9c93d35ce433874affd056f2d0998 languageName: node linkType: hard @@ -6986,17 +6950,17 @@ __metadata: linkType: hard "reflect.getprototypeof@npm:^1.0.4": - version: 1.0.5 - resolution: "reflect.getprototypeof@npm:1.0.5" + version: 1.0.6 + resolution: "reflect.getprototypeof@npm:1.0.6" dependencies: - call-bind: "npm:^1.0.5" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.3" - es-errors: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.3" + es-abstract: "npm:^1.23.1" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" globalthis: "npm:^1.0.3" which-builtin-type: "npm:^1.1.3" - checksum: 10/14560efa54b4b8549f5e0961ee4dfa9f034bd4b85c7805d487da30eb520ea252b566bc4098a7cb1bc2219e4d9cb095db43c05b27205bd6299bb141294cea2d14 + checksum: 10/518f6457e4bb470c9b317d239c62d4b4a05678b7eae4f1c3f4332fad379b3ea6d2d8999bfad448547fdba8fb77e4725cfe8c6440d0168ff387f16b4f19f759ad languageName: node linkType: hard @@ -7007,7 +6971,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.2": +"regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: @@ -7172,7 +7136,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.2.0": +"rollup@npm:^4.13.0": version: 4.13.0 resolution: "rollup@npm:4.13.0" dependencies: @@ -7244,7 +7208,7 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.1.0": +"safe-array-concat@npm:^1.1.2": version: 1.1.2 resolution: "safe-array-concat@npm:1.1.2" dependencies: @@ -7368,7 +7332,7 @@ __metadata: languageName: node linkType: hard -"set-function-name@npm:^2.0.0, set-function-name@npm:^2.0.1": +"set-function-name@npm:^2.0.1, set-function-name@npm:^2.0.2": version: 2.0.2 resolution: "set-function-name@npm:2.0.2" dependencies: @@ -7419,7 +7383,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.4": +"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6": version: 1.0.6 resolution: "side-channel@npm:1.0.6" dependencies: @@ -7514,10 +7478,10 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.2": - version: 1.0.2 - resolution: "source-map-js@npm:1.0.2" - checksum: 10/38e2d2dd18d2e331522001fc51b54127ef4a5d473f53b1349c5cca2123562400e0986648b52e9407e348eaaed53bce49248b6e2641e6d793ca57cb2c360d6d51 +"source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 10/74f331cfd2d121c50790c8dd6d3c9de6be21926de80583b23b37029b0f37aefc3e019fa91f9a10a5e120c08135297e1ecf312d561459c45908cb1e0e365f49e5 languageName: node linkType: hard @@ -7671,52 +7635,56 @@ __metadata: linkType: hard "string.prototype.matchall@npm:^4.0.10": - version: 4.0.10 - resolution: "string.prototype.matchall@npm:4.0.10" + version: 4.0.11 + resolution: "string.prototype.matchall@npm:4.0.11" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - get-intrinsic: "npm:^1.2.1" + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" has-symbols: "npm:^1.0.3" - internal-slot: "npm:^1.0.5" - regexp.prototype.flags: "npm:^1.5.0" - set-function-name: "npm:^2.0.0" - side-channel: "npm:^1.0.4" - checksum: 10/0f7a1a7f91790cd45f804039a16bc6389c8f4f25903e648caa3eea080b019a5c7b0cac2ca83976646140c2332b159042140bf389f23675609d869dd52450cddc + internal-slot: "npm:^1.0.7" + regexp.prototype.flags: "npm:^1.5.2" + set-function-name: "npm:^2.0.2" + side-channel: "npm:^1.0.6" + checksum: 10/a902ff4500f909f2a08e55cc5ab1ffbbc905f603b36837674370ee3921058edd0392147e15891910db62a2f31ace2adaf065eaa3bc6e9810bdbc8ca48e05a7b5 languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.8": - version: 1.2.8 - resolution: "string.prototype.trim@npm:1.2.8" +"string.prototype.trim@npm:^1.2.9": + version: 1.2.9 + resolution: "string.prototype.trim@npm:1.2.9" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/9301f6cb2b6c44f069adde1b50f4048915985170a20a1d64cf7cb2dc53c5cd6b9525b92431f1257f894f94892d6c4ae19b5aa7f577c3589e7e51772dffc9d5a4 + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10/b2170903de6a2fb5a49bb8850052144e04b67329d49f1343cdc6a87cb24fb4e4b8ad00d3e273a399b8a3d8c32c89775d93a8f43cb42fbff303f25382079fb58a languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimend@npm:1.0.7" +"string.prototype.trimend@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimend@npm:1.0.8" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/3f0d3397ab9bd95cd98ae2fe0943bd3e7b63d333c2ab88f1875cf2e7c958c75dc3355f6fe19ee7c8fca28de6f39f2475e955e103821feb41299a2764a7463ffa + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/c2e862ae724f95771da9ea17c27559d4eeced9208b9c20f69bbfcd1b9bc92375adf8af63a103194dba17c4cc4a5cb08842d929f415ff9d89c062d44689c8761b languageName: node linkType: hard "string.prototype.trimstart@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimstart@npm:1.0.7" + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - checksum: 10/6e594d3a61b127d243b8be1312e9f78683abe452cfe0bcafa3e0dc62ad6f030ccfb64d87ed3086fb7cb540fda62442c164d237cc5cc4d53c6e3eb659c29a0aeb + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/160167dfbd68e6f7cb9f51a16074eebfce1571656fc31d40c3738ca9e30e35496f2c046fe57b6ad49f65f238a152be8c86fd9a2dd58682b5eba39dad995b3674 languageName: node linkType: hard @@ -7927,8 +7895,8 @@ __metadata: linkType: hard "tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.2.0 - resolution: "tar@npm:6.2.0" + version: 6.2.1 + resolution: "tar@npm:6.2.1" dependencies: chownr: "npm:^2.0.0" fs-minipass: "npm:^2.0.0" @@ -7936,7 +7904,7 @@ __metadata: minizlib: "npm:^2.1.1" mkdirp: "npm:^1.0.3" yallist: "npm:^4.0.0" - checksum: 10/2042bbb14830b5cd0d584007db0eb0a7e933e66d1397e72a4293768d2332449bc3e312c266a0887ec20156dea388d8965e53b4fc5097f42d78593549016da089 + checksum: 10/bfbfbb2861888077fc1130b84029cdc2721efb93d1d1fb80f22a7ac3a98ec6f8972f29e564103bbebf5e97be67ebc356d37fa48dbc4960600a1eb7230fbd1ea0 languageName: node linkType: hard @@ -8153,8 +8121,8 @@ __metadata: linkType: hard "typed-array-length@npm:^1.0.5": - version: 1.0.5 - resolution: "typed-array-length@npm:1.0.5" + version: 1.0.6 + resolution: "typed-array-length@npm:1.0.6" dependencies: call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" @@ -8162,7 +8130,7 @@ __metadata: has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" possible-typed-array-names: "npm:^1.0.0" - checksum: 10/f9a0da99c41880b44e2c5e5d0d01515c2a6e0f54b10c594151804f013272d837df3b67ea84d7304ecfbab2c10d99c3372168bf3a4bd295abf13ac5a72f93054a + checksum: 10/05e96cf4ff836743ebfc593d86133b8c30e83172cb5d16c56814d7bacfed57ce97e87ada9c4b2156d9aaa59f75cdef01c25bd9081c7826e0b869afbefc3e8c39 languageName: node linkType: hard @@ -8177,23 +8145,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.4.2": - version: 5.4.2 - resolution: "typescript@npm:5.4.2" +"typescript@npm:^5.4.3": + version: 5.4.3 + resolution: "typescript@npm:5.4.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/f8cfdc630ab1672f004e9561eb2916935b2d267792d07ce93e97fc601c7a65191af32033d5e9c0169b7dc37da7db9bf320f7432bc84527cb7697effaa4e4559d + checksum: 10/de4c69f49a7ad4b1ea66a6dcc8b055ac34eb56af059a069d8988dd811c5e649be07e042e5bf573e8d0ac3ec2f30e6c999aa651cd09f6e9cbc6113749e8b6be20 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.4.2#optional!builtin": - version: 5.4.2 - resolution: "typescript@patch:typescript@npm%3A5.4.2#optional!builtin::version=5.4.2&hash=5adc0c" +"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin": + version: 5.4.3 + resolution: "typescript@patch:typescript@npm%3A5.4.3#optional!builtin::version=5.4.3&hash=5adc0c" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/f5f9a4133c2670761f0166eae5b3bafbc4a3fc24f0f42a93c9c893d9e9d6e66ea066969c5e7483fa66b4ae0e99125592553f3b92fd3599484de8be13b0615176 + checksum: 10/5aedd97595582b08aadb8a70e8e3ddebaf5a9c1e5ad4d6503c2fcfc15329b5cf8d01145b09913e9555683ac16c5123a96be32b6d72614098ebd42df520eed9b1 languageName: node linkType: hard @@ -8374,14 +8342,14 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.1.6": - version: 5.1.6 - resolution: "vite@npm:5.1.6" +"vite@npm:^5.2.4": + version: 5.2.4 + resolution: "vite@npm:5.2.4" dependencies: - esbuild: "npm:^0.19.3" + esbuild: "npm:^0.20.1" fsevents: "npm:~2.3.3" - postcss: "npm:^8.4.35" - rollup: "npm:^4.2.0" + postcss: "npm:^8.4.36" + rollup: "npm:^4.13.0" peerDependencies: "@types/node": ^18.0.0 || >=20.0.0 less: "*" @@ -8410,7 +8378,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/f48073e93ead62fa58034398442de4517c824b3e50184f8b4059fb24077a26f2c04e910e29d7fb7ec51ea53eb61b9c7d94d56b14a38851de80c67480094cc79d + checksum: 10/ed3d4fa2023642dd578e90eb02876e01729fda0af196304bb1c3a7f037b457f1a505bfa36c10f28d0466945b9da7d7972219716554d43ff3c025f26ed3d89e11 languageName: node linkType: hard @@ -8459,7 +8427,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.9": +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp index d32f1f873..be7baf8f1 100644 --- a/lib/framework/ESP8266React.cpp +++ b/lib/framework/ESP8266React.cpp @@ -1,6 +1,6 @@ #include "ESP8266React.h" -#include "WWWData.h" +#include "WWWData.h" // include auto-generated static web resources ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs) : _securitySettingsService(server, fs) @@ -17,8 +17,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs) , _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService) , _authenticationService(server, &_securitySettingsService) , _restartService(server, &_securitySettingsService) - , _factoryResetService(server, fs, &_securitySettingsService) - , _systemStatus(server, &_securitySettingsService) { + , _factoryResetService(server, fs, &_securitySettingsService) { // // Serve static web resources // diff --git a/lib/framework/ESP8266React.h b/lib/framework/ESP8266React.h index 4f5988933..3ec012f10 100644 --- a/lib/framework/ESP8266React.h +++ b/lib/framework/ESP8266React.h @@ -13,7 +13,6 @@ #include "UploadFileService.h" #include "RestartService.h" #include "SecuritySettingsService.h" -#include "SystemStatus.h" #include "WiFiScanner.h" #include "NetworkSettingsService.h" #include "NetworkStatus.h" @@ -61,10 +60,19 @@ class ESP8266React { return _mqttSettingsService.getMqttClient(); } + // + // special functions needed outside scope + // + void setWill(const char * will_topic) { _mqttSettingsService.setWill(will_topic); } + // true if AP is active + bool apStatus() { + return _apSettingsService.getAPNetworkStatus() == APNetworkStatus::ACTIVE; + } + #ifndef EMSESP_STANDALONE void factoryReset() { _factoryResetService.factoryReset(); @@ -87,7 +95,6 @@ class ESP8266React { AuthenticationService _authenticationService; RestartService _restartService; FactoryResetService _factoryResetService; - SystemStatus _systemStatus; }; #endif diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp deleted file mode 100644 index b90ef7c38..000000000 --- a/lib/framework/SystemStatus.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "SystemStatus.h" - -#include - -#include "../../src/emsesp_stub.hpp" - -SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) { - server->on(SYSTEM_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest([this](AsyncWebServerRequest * request) { systemStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED)); -} - -void SystemStatus::systemStatus(AsyncWebServerRequest * request) { - emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap - - auto * response = new AsyncJsonResponse(false); - JsonObject root = response->getRoot(); - -#ifdef EMSESP_DEBUG - root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)"; -#else -#ifdef EMSESP_TEST - root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (TEST)"; -#else - root["emsesp_version"] = EMSESP_APP_VERSION; -#endif -#endif - root["esp_platform"] = EMSESP_PLATFORM; - root["cpu_type"] = ESP.getChipModel(); - root["cpu_rev"] = ESP.getChipRevision(); - root["cpu_cores"] = ESP.getChipCores(); - root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); - root["max_alloc_heap"] = emsesp::EMSESP::system_.getMaxAllocMem(); - root["free_heap"] = emsesp::EMSESP::system_.getHeapMem(); - root["arduino_version"] = ARDUINO_VERSION; - root["sdk_version"] = ESP.getSdkVersion(); - root["partition"] = esp_ota_get_running_partition()->label; - root["flash_chip_size"] = ESP.getFlashChipSize() / 1024; - root["flash_chip_speed"] = ESP.getFlashChipSpeed(); - root["app_used"] = emsesp::EMSESP::system_.appUsed(); - root["app_free"] = emsesp::EMSESP::system_.appFree(); - uint32_t FSused = LittleFS.usedBytes() / 1024; - root["fs_used"] = FSused; - root["fs_free"] = emsesp::EMSESP::system_.FStotal() - FSused; - root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); - - if (emsesp::EMSESP::system_.PSram()) { - root["psram_size"] = emsesp::EMSESP::system_.PSram(); - root["free_psram"] = ESP.getFreePsram() / 1024; - } - const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr); - if (partition != NULL) { // factory partition found - root["has_loader"] = true; - } else { // check for not empty, smaller OTA partition - partition = esp_ota_get_next_update_partition(nullptr); - if (partition) { - uint64_t buffer; - esp_partition_read(partition, 0, &buffer, 8); - const esp_partition_t * running = esp_ota_get_running_partition(); - root["has_loader"] = (buffer != 0xFFFFFFFFFFFFFFFF && running->size != partition->size); - } - } - - response->setLength(); - request->send(response); -} diff --git a/lib/framework/SystemStatus.h b/lib/framework/SystemStatus.h deleted file mode 100644 index bd2d7ebf6..000000000 --- a/lib/framework/SystemStatus.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SystemStatus_h -#define SystemStatus_h - -#include -#include -#include -#include -#include -#include - -#include "SecurityManager.h" - -#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" - -class SystemStatus { - public: - SystemStatus(AsyncWebServer * server, SecurityManager * securityManager); - - private: - void systemStatus(AsyncWebServerRequest * request); -}; - -#endif diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index ca7b194b3..ed283a629 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -74,6 +74,8 @@ class DummySettings { String CORSOrigin = "*"; uint8_t tx_power = 0; + uint8_t provisionMode = 0; + static void read(DummySettings & settings, JsonObject root){}; static void read(DummySettings & settings){}; @@ -94,6 +96,9 @@ class DummySettingsService : public StatefulService { #define SecuritySettings DummySettings #define MqttSettings DummySettings #define NTPSettings DummySettings +#define OTASettings DummySettings +#define APSettings DummySettings + class ESP8266React { public: @@ -114,6 +119,10 @@ class ESP8266React { return _mqttClient; } + bool apStatus() { + return false; + } + void setWill(const char * will_topic) { } void onMessage(espMqttClientTypes::OnMessageCallback callback) { @@ -135,6 +144,14 @@ class ESP8266React { return &_settings; } + StatefulService * getOTASettingsService() { + return &_settings; + } + + StatefulService * getAPSettingsService() { + return &_settings; + } + private: DummySettingsService _settings; SecuritySettingsService _securitySettingsService; diff --git a/mock-api/handler.ts b/mock-api/handler.ts index b3522e695..e118a88cf 100644 --- a/mock-api/handler.ts +++ b/mock-api/handler.ts @@ -187,7 +187,7 @@ let ntp_settings = { tz_format: 'CET-1CEST,M3.5.0,M10.5.0/3' }; const ntp_status = { - status: 1, + status: 2, utc_time: '2021-04-01T14:25:42Z', local_time: '2021-04-01T16:25:42', server: 'time.google.com', @@ -316,7 +316,7 @@ const list_networks = { // OTA const OTA_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'otaSettings'; let ota_settings = { - enabled: true, + enabled: false, port: 8266, password: 'ems-esp-neo' }; @@ -362,17 +362,24 @@ const mqtt_status = { connect_count: 2 }; -// SYSTEM -const VERIFY_AUTHORIZATION_ENDPOINT = REST_ENDPOINT_ROOT + 'verifyAuthorization'; +// STATUS const SYSTEM_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'systemStatus'; +const ACTIVITY_ENDPOINT = REST_ENDPOINT_ROOT + 'activity'; + +// SETTINGS +const ESPSYSTEM_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'ESPSystemStatus'; const SECURITY_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'securitySettings'; const RESTART_ENDPOINT = REST_ENDPOINT_ROOT + 'restart'; const FACTORY_RESET_ENDPOINT = REST_ENDPOINT_ROOT + 'factoryReset'; const UPLOAD_FILE_ENDPOINT = REST_ENDPOINT_ROOT + 'uploadFile'; + +// SYSTEM SIGNIN +const VERIFY_AUTHORIZATION_ENDPOINT = REST_ENDPOINT_ROOT + 'verifyAuthorization'; const SIGN_IN_ENDPOINT = REST_ENDPOINT_ROOT + 'signIn'; const GENERATE_TOKEN_ENDPOINT = REST_ENDPOINT_ROOT + 'generateToken'; -const system_status = { - emsesp_version: '3.6-demo', + +const ESPsystem_status = { + emsesp_version: '3.7-demo', esp_platform: 'ESP32', cpu_type: 'ESP32-S3', cpu_rev: '0', @@ -390,9 +397,26 @@ const system_status = { partition: 'app0', app_used: 1863, app_free: 121, - uptime: '000+00:15:42.707', arduino_version: 'ESP32 Arduino v2.0.14' }; + +const system_status = { + emsesp_version: '3.7-demo', + esp_platform: 'ESP32', + status: 0, + // status: 2, + uptime: 77186, + bus_uptime: 77121, + num_devices: 2, + num_sensors: 1, + num_analogs: 1, + free_heap: 143, + ntp_status: 2, + ota_status: false, + mqtt_status: true, + ap_status: false +}; + let security_settings = { jwt_secret: 'naughty!', users: [ @@ -402,10 +426,19 @@ let security_settings = { }; const verify_authentication = { access_token: '1234' }; -const signin = { + +const admin_signin = { access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInZlcnNpb24iOiIzLjAuMmIwIn0.MsHSgoJKI1lyYz77EiT5ZN3ECMrb4mPv9FNy3udq0TU' }; +const guest_signin = { + access_token: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiYWRtaW4iOmZhbHNlfQ.E_lylR_vGIQFZUGNwcl5F6OkHoaELGsC5zqhi0pAiJE' +}; +// modify here to simulate admin and guest logins +const signin = admin_signin; +// const signin = guest_signin; + const generate_token = { token: '1234' }; // @@ -416,6 +449,7 @@ const EMSESP_CORE_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'coreData'; const EMSESP_SENSOR_DATA_ENDPOINT = REST_ENDPOINT_ROOT + 'sensorData'; const EMSESP_DEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'devices'; const EMSESP_SCANDEVICES_ENDPOINT = REST_ENDPOINT_ROOT + 'scanDevices'; +// for later // const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData/:id'; // const EMSESP_DEVICEENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceEntities/:id'; const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData'; @@ -440,7 +474,7 @@ const EMSESP_SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info'; const emsesp_info = { System: { - version: '3.6-demo', + version: '3.7-demo', uptime: '001+06:40:34.018', 'uptime (seconds)': 110434, freemem: 131, @@ -739,7 +773,7 @@ const emsesp_coredata = { t: 17, tn: 'Custom', b: '', - n: 'User defined entities', + n: 'Custom Entities', d: 1, p: 1, v: '', @@ -766,14 +800,7 @@ const emsesp_sensordata = { analog_enabled: true }; -const status = { - status: 0, - // status: 2, - tx_mode: 1, - uptime: 77186, - num_devices: 2, - num_sensors: 1, - num_analogs: 1, +const activity = { stats: [ { id: 0, s: 56506, f: 11, q: 100 }, { id: 1, s: 9026, f: 0, q: 100 }, @@ -2363,9 +2390,11 @@ router return new Response('OK', { status: 200 }); }); -// SYSTEM +// SYSTEM and SETTINGS router .get(SYSTEM_STATUS_ENDPOINT, () => new Response(JSON.stringify(system_status), { headers })) + .get(ACTIVITY_ENDPOINT, () => new Response(JSON.stringify(activity), { headers })) + .get(ESPSYSTEM_STATUS_ENDPOINT, () => new Response(JSON.stringify(ESPsystem_status), { headers })) .get(SECURITY_SETTINGS_ENDPOINT, () => new Response(JSON.stringify(security_settings), { headers })) .post(SECURITY_SETTINGS_ENDPOINT, async (request: any) => { security_settings = await request.json(); diff --git a/mock-api/package.json b/mock-api/package.json index d11685bb5..170160bc8 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -1,6 +1,6 @@ { "name": "api", - "version": "3.6.0", + "version": "3.7.0", "description": "mock api for EMS-ESP", "author": "proddy", "main": "server.ts", @@ -12,8 +12,8 @@ "dependencies": { "@msgpack/msgpack": "^2.8.0", "compression": "^1.7.4", - "express": "^4.18.3", - "itty-router": "^4.2.0", + "express": "^4.19.1", + "itty-router": "^4.2.2", "multer": "^1.4.5-lts.1" }, "packageManager": "yarn@4.1.1", diff --git a/mock-api/server.js b/mock-api/server.js index 72e93d791..12e17ac92 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -358,7 +358,7 @@ const EMSESP_WRITE_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities'; const emsesp_info = { System: { - version: '3.6.5', + version: '3.7.0', uptime: '001+06:40:34.018', 'uptime (seconds)': 110434, freemem: 131, @@ -552,7 +552,7 @@ const emsesp_coredata = { t: 17, tn: 'Custom', b: '', - n: 'User defined entities', + n: 'Custom Entities', d: 1, p: 1, v: '', diff --git a/mock-api/yarn.lock b/mock-api/yarn.lock index 81405d18e..d8927febb 100644 --- a/mock-api/yarn.lock +++ b/mock-api/yarn.lock @@ -139,8 +139,8 @@ __metadata: "@msgpack/msgpack": "npm:^2.8.0" "@types/multer": "npm:^1.4.11" compression: "npm:^1.7.4" - express: "npm:^4.18.3" - itty-router: "npm:^4.2.0" + express: "npm:^4.19.1" + itty-router: "npm:^4.2.2" multer: "npm:^1.4.5-lts.1" languageName: unknown linkType: soft @@ -279,10 +279,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 +"cookie@npm:0.6.0": + version: 0.6.0 + resolution: "cookie@npm:0.6.0" + checksum: 10/c1f8f2ea7d443b9331680598b0ae4e6af18a618c37606d1bbdc75bec8361cce09fe93e727059a673f2ba24467131a9fb5a4eec76bb1b149c1b3e1ccb268dc583 languageName: node linkType: hard @@ -355,16 +355,16 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.18.3": - version: 4.18.3 - resolution: "express@npm:4.18.3" +"express@npm:^4.19.1": + version: 4.19.1 + resolution: "express@npm:4.19.1" dependencies: accepts: "npm:~1.3.8" array-flatten: "npm:1.1.1" body-parser: "npm:1.20.2" content-disposition: "npm:0.5.4" content-type: "npm:~1.0.4" - cookie: "npm:0.5.0" + cookie: "npm:0.6.0" cookie-signature: "npm:1.0.6" debug: "npm:2.6.9" depd: "npm:2.0.0" @@ -390,7 +390,7 @@ __metadata: type-is: "npm:~1.6.18" utils-merge: "npm:1.0.1" vary: "npm:~1.1.2" - checksum: 10/0bf4656d0020cdc477aec884c6245dceea78992f6c747c899c87dbb0598474658d4130a29c80f02c99d1f0d6ebef706e7131c70c5454c3e07aba761c5bd3d627 + checksum: 10/7b817f21afe96e478cd7fe77cd5f52cf7d2d6b70c3e98f0e1399ce48356cef3c5f5d34bf93bdbc7bc326403005c45e2f8522f51b4cc319da52220066c9094745 languageName: node linkType: hard @@ -526,10 +526,10 @@ __metadata: languageName: node linkType: hard -"itty-router@npm:^4.2.0": - version: 4.2.0 - resolution: "itty-router@npm:4.2.0" - checksum: 10/39ee6c8b87f77de3918a9b3c1acaf2047626a69b954d7e1f5b9ab7ab9a2bf7e43c97b99ab86496aa9f5b139b024eebaf3b423f51fe000a8a0510901aeea10604 +"itty-router@npm:^4.2.2": + version: 4.2.2 + resolution: "itty-router@npm:4.2.2" + checksum: 10/ead44fd46ea358776dc2bb120970eff5ab0acb10ad82c384eba9b361c6eba7f5971408f80cbc3655004cb12ae53dd77a85de236bdb215cef896c2589f5096854 languageName: node linkType: hard diff --git a/platformio.ini b/platformio.ini index 2908e0d40..fae3d8823 100644 --- a/platformio.ini +++ b/platformio.ini @@ -191,7 +191,7 @@ platform = native build_flags = -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev.16\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -lpthread -D__linux__ -std=gnu++11 -Og -ggdb diff --git a/sonar-project.properties b/sonar-project.properties index fdd85ff20..73ece144b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,7 +1,7 @@ sonar.organization=emsesp sonar.projectKey=emsesp_EMS-ESP32 sonar.projectName=EMS-ESP32 -sonar.projectVersion=3.6.5 +sonar.projectVersion=3.7.0 sonar.sources=./src sonar.cfamily.build-wrapper-output=bw-output sonar.sourceEncoding=UTF-8 diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 1cba6d63c..f0bb7be52 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -45,10 +45,11 @@ WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&w WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); #endif -WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebDataService EMSESP::webDataService = WebDataService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebAPIService EMSESP::webAPIService = WebAPIService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebLogService EMSESP::webLogService = WebLogService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebActivityService EMSESP::webActivityService = WebActivityService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebDataService EMSESP::webDataService = WebDataService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebAPIService EMSESP::webAPIService = WebAPIService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebLogService EMSESP::webLogService = WebLogService(&webServer, EMSESP::esp8266React.getSecurityManager()); using DeviceFlags = EMSdevice; using DeviceType = EMSdevice::DeviceType; @@ -407,7 +408,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) { // show any custom entities if (webCustomEntityService.count_entities() > 0) { - shell.printfln("Custom entities:"); + shell.printfln("Custom Entities:"); JsonDocument custom_doc; // use max size JsonObject custom_output = custom_doc.to(); webCustomEntityService.show_values(custom_output); diff --git a/src/emsesp.h b/src/emsesp.h index 427476e41..60e9d4e0d 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -39,7 +39,9 @@ #include #include "ESP8266React.h" + #include "web/WebStatusService.h" +#include "web/WebActivityService.h" #include "web/WebDataService.h" #include "web/WebSettingsService.h" #include "web/WebCustomizationService.h" @@ -220,6 +222,7 @@ class EMSESP { static ESP8266React esp8266React; static WebSettingsService webSettingsService; static WebStatusService webStatusService; + static WebActivityService webActivityService; static WebDataService webDataService; static WebAPIService webAPIService; static WebLogService webLogService; diff --git a/src/locale_translations.h b/src/locale_translations.h index 8f139147f..e8f40e69f 100644 --- a/src/locale_translations.h +++ b/src/locale_translations.h @@ -53,7 +53,7 @@ MAKE_WORD_TRANSLATION(heatsource_device, "Heatsource", "Heizquelle", "Heatsource MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı", "Sensori", "Snímače") MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "Bilinmeyen", "Sconosciuto", "Neznámy") MAKE_WORD_TRANSLATION(custom_device, "Custom", "Nutzerdefiniert", "Aangepast", "", "Niestandardowe", "", "", "Özel", "Personalizzato", "Vlastné") // TODO translate -MAKE_WORD_TRANSLATION(custom_device_name, "User defined entities", "Nutzer deklarierte Entitäten", "Gebruiker gedefineerd", "", "Encje zdefiniowane przez użytkownika", "", "", "Kullanıcı tarafından tanımlanmış varlıklar", "Entità definita da utente", "Používateľom definované entity") // TODO translate +MAKE_WORD_TRANSLATION(custom_device_name, "Custom Entities", "Nutzer deklarierte Entitäten", "Gebruiker gedefineerd", "", "Encje zdefiniowane przez użytkownika", "", "", "Kullanıcı tarafından tanımlanmış varlıklar", "Entità definita da utente", "Používateľom definované entity") // TODO translate MAKE_WORD_TRANSLATION(ventilation_device, "Ventilation", "Lüftung", "Ventilatie", "", "Wentylacja", "", "", "Havalandırma", "Ventilazione", "Vetranie") // TODO translate MAKE_WORD_TRANSLATION(water_device, "Water Module", "Wassermodul", "", "", "Moduł wodny", "", "", "", "", "") // TODO translate MAKE_WORD_TRANSLATION(pool_device, "Pool Module", "Poolmodul", "", "", "Moduł basenu", "", "", "", "", "") // TODO translate diff --git a/src/system.cpp b/src/system.cpp index 08d0895c0..7064948d8 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1395,17 +1395,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["bus status"] = "unknown"; break; } - if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) { - node["bus protocol"] = EMSbus::is_ht3() ? "HT3" : "Buderus"; - node["bus telegrams received (rx)"] = EMSESP::rxservice_.telegram_count(); - node["bus reads (tx)"] = EMSESP::txservice_.telegram_read_count(); - node["bus writes (tx)"] = EMSESP::txservice_.telegram_write_count(); - node["bus incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count(); - node["bus reads failed"] = EMSESP::txservice_.telegram_read_fail_count(); - node["bus writes failed"] = EMSESP::txservice_.telegram_write_fail_count(); - node["bus rx line quality"] = EMSESP::rxservice_.quality(); - node["bus tx line quality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2; - } + // if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) { + node["bus protocol"] = EMSbus::is_ht3() ? "HT3" : "Buderus"; + node["bus telegrams received (rx)"] = EMSESP::rxservice_.telegram_count(); + node["bus reads (tx)"] = EMSESP::txservice_.telegram_read_count(); + node["bus writes (tx)"] = EMSESP::txservice_.telegram_write_count(); + node["bus incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count(); + node["bus reads failed"] = EMSESP::txservice_.telegram_read_fail_count(); + node["bus writes failed"] = EMSESP::txservice_.telegram_write_fail_count(); + node["bus rx line quality"] = EMSESP::rxservice_.quality(); + node["bus tx line quality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2; + // } // Settings node = output["Settings"].to(); diff --git a/src/version.h b/src/version.h index 1f143c4f4..1425dba79 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.5-test.18" +#define EMSESP_APP_VERSION "3.7.0-test.0" diff --git a/src/web/WebActivityService.cpp b/src/web/WebActivityService.cpp new file mode 100644 index 000000000..a72c0fc49 --- /dev/null +++ b/src/web/WebActivityService.cpp @@ -0,0 +1,101 @@ +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2024 Paul Derbyshire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "emsesp.h" + +namespace emsesp { + +WebActivityService::WebActivityService(AsyncWebServer * server, SecurityManager * securityManager) { + server->on(EMSESP_ACTIVITY_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest([this](AsyncWebServerRequest * request) { webActivityService(request); }, AuthenticationPredicates::IS_AUTHENTICATED)); +} + +void WebActivityService::webActivityService(AsyncWebServerRequest * request) { + auto * response = new AsyncJsonResponse(false); + JsonObject root = response->getRoot(); + + JsonArray statsJson = root["stats"].to(); + JsonObject statJson; + + statJson = statsJson.add(); + statJson["id"] = 0; + statJson["s"] = EMSESP::rxservice_.telegram_count(); + statJson["f"] = EMSESP::rxservice_.telegram_error_count(); + statJson["q"] = EMSESP::rxservice_.quality(); + + statJson = statsJson.add(); + statJson["id"] = 1; + statJson["s"] = EMSESP::txservice_.telegram_read_count(); + statJson["f"] = EMSESP::txservice_.telegram_read_fail_count(); + statJson["q"] = EMSESP::txservice_.read_quality(); + + statJson = statsJson.add(); + statJson["id"] = 2; + statJson["s"] = EMSESP::txservice_.telegram_write_count(); + statJson["f"] = EMSESP::txservice_.telegram_write_fail_count(); + statJson["q"] = EMSESP::txservice_.write_quality(); + + if (EMSESP::sensor_enabled()) { + statJson = statsJson.add(); + statJson["id"] = 3; + statJson["s"] = EMSESP::temperaturesensor_.reads() - EMSESP::temperaturesensor_.fails(); + statJson["f"] = EMSESP::temperaturesensor_.fails(); + statJson["q"] = + EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads()); + } + if (EMSESP::analog_enabled()) { + statJson = statsJson.add(); + statJson["id"] = 4; + statJson["s"] = EMSESP::analogsensor_.reads() - EMSESP::analogsensor_.fails(); + statJson["f"] = EMSESP::analogsensor_.fails(); + statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads()); + } + if (Mqtt::enabled()) { + statJson = statsJson.add(); + statJson["id"] = 5; + statJson["s"] = Mqtt::publish_count() - Mqtt::publish_fails(); + statJson["f"] = Mqtt::publish_fails(); + statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / Mqtt::publish_count()); + } + + statJson = statsJson.add(); + statJson["id"] = 6; + statJson["s"] = WebAPIService::api_count(); // + WebAPIService::api_fails(); + statJson["f"] = WebAPIService::api_fails(); + statJson["q"] = (WebAPIService::api_count() + WebAPIService::api_fails()) == 0 + ? 100 + : 100 - (uint8_t)((100 * WebAPIService::api_fails()) / (WebAPIService::api_count() + WebAPIService::api_fails())); + +#ifndef EMSESP_STANDALONE + if (EMSESP::system_.syslog_enabled()) { + statJson = statsJson.add(); + statJson["id"] = 7; + statJson["s"] = EMSESP::system_.syslog_count(); + statJson["f"] = EMSESP::system_.syslog_fails(); + statJson["q"] = (EMSESP::system_.syslog_count() + EMSESP::system_.syslog_fails()) == 0 + ? 100 + : 100 - (uint8_t)((100 * EMSESP::system_.syslog_fails()) / (EMSESP::system_.syslog_count() + EMSESP::system_.syslog_fails())); + } +#endif + + response->setLength(); + request->send(response); +} + +} // namespace emsesp \ No newline at end of file diff --git a/src/web/WebActivityService.h b/src/web/WebActivityService.h new file mode 100644 index 000000000..bf5a2899b --- /dev/null +++ b/src/web/WebActivityService.h @@ -0,0 +1,36 @@ +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2024 Paul Derbyshire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef WebActivityService_h +#define WebActivityService_h + +#define EMSESP_ACTIVITY_SERVICE_PATH "/rest/activity" + +namespace emsesp { + +class WebActivityService { + public: + WebActivityService(AsyncWebServer * server, SecurityManager * securityManager); + + private: + void webActivityService(AsyncWebServerRequest * request); +}; + +} // namespace emsesp + +#endif diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 9fab268c9..7916b2470 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -1,108 +1,136 @@ -/* - * EMS-ESP - https://github.com/emsesp/EMS-ESP - * Copyright 2020-2024 Paul Derbyshire - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "emsesp.h" - -namespace emsesp { - -WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) { - server->on(EMSESP_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest([this](AsyncWebServerRequest * request) { webStatusService(request); }, AuthenticationPredicates::IS_AUTHENTICATED)); -} - -void WebStatusService::webStatusService(AsyncWebServerRequest * request) { - auto * response = new AsyncJsonResponse(false); - JsonObject root = response->getRoot(); - - root["status"] = EMSESP::bus_status(); // 0, 1 or 2 - root["tx_mode"] = EMSESP::txservice_.tx_mode(); - root["uptime"] = EMSbus::bus_uptime(); - root["num_devices"] = EMSESP::count_devices(); // excluding Controller - root["num_sensors"] = EMSESP::temperaturesensor_.no_sensors(); - root["num_analogs"] = EMSESP::analogsensor_.no_sensors(); - - JsonArray statsJson = root["stats"].to(); - JsonObject statJson; - - statJson = statsJson.add(); - statJson["id"] = 0; - statJson["s"] = EMSESP::rxservice_.telegram_count(); - statJson["f"] = EMSESP::rxservice_.telegram_error_count(); - statJson["q"] = EMSESP::rxservice_.quality(); - - statJson = statsJson.add(); - statJson["id"] = 1; - statJson["s"] = EMSESP::txservice_.telegram_read_count(); - statJson["f"] = EMSESP::txservice_.telegram_read_fail_count(); - statJson["q"] = EMSESP::txservice_.read_quality(); - - statJson = statsJson.add(); - statJson["id"] = 2; - statJson["s"] = EMSESP::txservice_.telegram_write_count(); - statJson["f"] = EMSESP::txservice_.telegram_write_fail_count(); - statJson["q"] = EMSESP::txservice_.write_quality(); - - if (EMSESP::sensor_enabled()) { - statJson = statsJson.add(); - statJson["id"] = 3; - statJson["s"] = EMSESP::temperaturesensor_.reads() - EMSESP::temperaturesensor_.fails(); - statJson["f"] = EMSESP::temperaturesensor_.fails(); - statJson["q"] = - EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads()); - } - if (EMSESP::analog_enabled()) { - statJson = statsJson.add(); - statJson["id"] = 4; - statJson["s"] = EMSESP::analogsensor_.reads() - EMSESP::analogsensor_.fails(); - statJson["f"] = EMSESP::analogsensor_.fails(); - statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads()); - } - if (Mqtt::enabled()) { - statJson = statsJson.add(); - statJson["id"] = 5; - statJson["s"] = Mqtt::publish_count() - Mqtt::publish_fails(); - statJson["f"] = Mqtt::publish_fails(); - statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / Mqtt::publish_count()); - } - - statJson = statsJson.add(); - statJson["id"] = 6; - statJson["s"] = WebAPIService::api_count(); // + WebAPIService::api_fails(); - statJson["f"] = WebAPIService::api_fails(); - statJson["q"] = (WebAPIService::api_count() + WebAPIService::api_fails()) == 0 - ? 100 - : 100 - (uint8_t)((100 * WebAPIService::api_fails()) / (WebAPIService::api_count() + WebAPIService::api_fails())); - -#ifndef EMSESP_STANDALONE - if (EMSESP::system_.syslog_enabled()) { - statJson = statsJson.add(); - statJson["id"] = 7; - statJson["s"] = EMSESP::system_.syslog_count(); - statJson["f"] = EMSESP::system_.syslog_fails(); - statJson["q"] = (EMSESP::system_.syslog_count() + EMSESP::system_.syslog_fails()) == 0 - ? 100 - : 100 - (uint8_t)((100 * EMSESP::system_.syslog_fails()) / (EMSESP::system_.syslog_count() + EMSESP::system_.syslog_fails())); - } -#endif - - response->setLength(); - request->send(response); -} - -} // namespace emsesp \ No newline at end of file +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2024 Paul Derbyshire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "emsesp.h" + +#ifndef EMSESP_STANDALONE +#include +#endif + +namespace emsesp { + +WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) { + server->on(ESPSYSTEM_STATUS_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { ESPsystemStatus(request); }); + server->on(SYSTEM_STATUS_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { systemStatus(request); }); +} + +void WebStatusService::systemStatus(AsyncWebServerRequest * request) { + EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap + + auto * response = new AsyncJsonResponse(false); + JsonObject root = response->getRoot(); + +#ifdef EMSESP_DEBUG + root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)"; +#else +#ifdef EMSESP_TEST + root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (TEST)"; +#else + root["emsesp_version"] = EMSESP_APP_VERSION; +#endif +#endif + + root["esp_platform"] = EMSESP_PLATFORM; + root["status"] = EMSESP::bus_status(); // 0, 1 or 2 + root["bus_uptime"] = EMSbus::bus_uptime(); + root["num_devices"] = EMSESP::count_devices(); + root["num_sensors"] = EMSESP::temperaturesensor_.no_sensors(); + root["num_analogs"] = EMSESP::analogsensor_.no_sensors(); + root["free_heap"] = EMSESP::system_.getHeapMem(); + root["uptime"] = uuid::get_uptime_sec(); + EMSESP::esp8266React.getOTASettingsService()->read([root](OTASettings & otaSettings) { root["ota_status"] = otaSettings.enabled; }); + root["mqtt_status"] = EMSESP::mqtt_.connected(); + +#ifndef EMSESP_STANDALONE + root["ntp_status"] = [] { + if (esp_sntp_enabled()) { + if (emsesp::EMSESP::system_.ntp_connected()) { + return 2; + } else { + return 1; + } + } + return 0; + }(); +#endif + + root["ap_status"] = EMSESP::esp8266React.apStatus(); + + response->setLength(); + request->send(response); +} + +void WebStatusService::ESPsystemStatus(AsyncWebServerRequest * request) { + EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap + + auto * response = new AsyncJsonResponse(false); + JsonObject root = response->getRoot(); + +#ifdef EMSESP_DEBUG + root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)"; +#else +#ifdef EMSESP_TEST + root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (TEST)"; +#else + root["emsesp_version"] = EMSESP_APP_VERSION; +#endif +#endif + root["esp_platform"] = EMSESP_PLATFORM; + +#ifndef EMSESP_STANDALONE + root["cpu_type"] = ESP.getChipModel(); + root["cpu_rev"] = ESP.getChipRevision(); + root["cpu_cores"] = ESP.getChipCores(); + root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); + root["max_alloc_heap"] = EMSESP::system_.getMaxAllocMem(); + root["free_heap"] = EMSESP::system_.getHeapMem(); + root["arduino_version"] = ARDUINO_VERSION; + root["sdk_version"] = ESP.getSdkVersion(); + root["partition"] = esp_ota_get_running_partition()->label; + root["flash_chip_size"] = ESP.getFlashChipSize() / 1024; + root["flash_chip_speed"] = ESP.getFlashChipSpeed(); + root["app_used"] = EMSESP::system_.appUsed(); + root["app_free"] = EMSESP::system_.appFree(); + uint32_t FSused = LittleFS.usedBytes() / 1024; + root["fs_used"] = FSused; + root["fs_free"] = EMSESP::system_.FStotal() - FSused; + + if (EMSESP::system_.PSram()) { + root["psram_size"] = EMSESP::system_.PSram(); + root["free_psram"] = ESP.getFreePsram() / 1024; + } + + const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr); + if (partition != NULL) { // factory partition found + root["has_loader"] = true; + } else { // check for not empty, smaller OTA partition + partition = esp_ota_get_next_update_partition(nullptr); + if (partition) { + uint64_t buffer; + esp_partition_read(partition, 0, &buffer, 8); + const esp_partition_t * running = esp_ota_get_running_partition(); + root["has_loader"] = (buffer != 0xFFFFFFFFFFFFFFFF && running->size != partition->size); + } + } +#endif + + response->setLength(); + request->send(response); +} + +} // namespace emsesp diff --git a/src/web/WebStatusService.h b/src/web/WebStatusService.h index 97109b82b..0099364aa 100644 --- a/src/web/WebStatusService.h +++ b/src/web/WebStatusService.h @@ -1,25 +1,8 @@ -/* - * EMS-ESP - https://github.com/emsesp/EMS-ESP - * Copyright 2020-2024 Paul Derbyshire - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #ifndef WebStatusService_h #define WebStatusService_h -#define EMSESP_STATUS_SERVICE_PATH "/rest/status" +#define ESPSYSTEM_STATUS_SERVICE_PATH "/rest/ESPSystemStatus" +#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" namespace emsesp { @@ -28,7 +11,8 @@ class WebStatusService { WebStatusService(AsyncWebServer * server, SecurityManager * securityManager); private: - void webStatusService(AsyncWebServerRequest * request); + void systemStatus(AsyncWebServerRequest * request); + void ESPsystemStatus(AsyncWebServerRequest * request); }; } // namespace emsesp diff --git a/test/standalone_file_export/emsesp_settings.json b/test/standalone_file_export/emsesp_settings.json index 1ea7eef3c..4dc16697e 100644 --- a/test/standalone_file_export/emsesp_settings.json +++ b/test/standalone_file_export/emsesp_settings.json @@ -1,7 +1,7 @@ { "type": "settings", "System": { - "version": "3.6.5" + "version": "3.7.0" }, "Network": { "ssid": "fake", @@ -83,7 +83,7 @@ ] }, "Settings": { - "version": "3.6.5", + "version": "3.7.0", "locale": "en", "tx_mode": 1, "ems_bus_id": 11,