Merge pull request #2804 from proddy/dev

various minor fixes to dev-35
This commit is contained in:
Proddy
2025-12-12 00:03:08 +01:00
committed by GitHub
18 changed files with 540 additions and 491 deletions

View File

@@ -47,8 +47,8 @@ MAKEFLAGS += -j$(JOBS) -l$(shell echo $$(($(JOBS) * 2)))
#---------------------------------------------------------------------- #----------------------------------------------------------------------
TARGET := emsesp TARGET := emsesp
BUILD := build BUILD := build
SOURCES := src/core src/devices src/web src/test lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton SOURCES := src/core src/devices src/web src/test lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
INCLUDES := src/core src/devices src/web src/test lib/* lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/semver lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src
LIBRARIES := LIBRARIES :=
CPPCHECK = cppcheck CPPCHECK = cppcheck

View File

@@ -34,6 +34,7 @@
"sdkconfig.*", "sdkconfig.*",
"managed_components/**", "managed_components/**",
"pnpm-*.yaml", "pnpm-*.yaml",
"vite.config.ts" "vite.config.ts",
"lib/esp32-psram/**"
] ]
} }

View File

@@ -38,8 +38,8 @@
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"mime-types": "^3.0.2", "mime-types": "^3.0.2",
"preact": "^10.28.0", "preact": "^10.28.0",
"react": "^19.2.1", "react": "^19.2.2",
"react-dom": "^19.2.1", "react-dom": "^19.2.2",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router": "^7.10.1", "react-router": "^7.10.1",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
@@ -52,7 +52,7 @@
"@preact/compat": "^18.3.1", "@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.10.2", "@preact/preset-vite": "^2.10.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.0", "@trivago/prettier-plugin-sort-imports": "^6.0.0",
"@types/node": "^24.10.1", "@types/node": "^25.0.0",
"@types/react": "^19.2.7", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"axe-core": "^4.11.0", "axe-core": "^4.11.0",
@@ -62,10 +62,10 @@
"prettier": "^3.7.4", "prettier": "^3.7.4",
"rollup-plugin-visualizer": "^6.0.5", "rollup-plugin-visualizer": "^6.0.5",
"terser": "^5.44.1", "terser": "^5.44.1",
"typescript-eslint": "^8.48.1", "typescript-eslint": "^8.49.0",
"vite": "^7.2.6", "vite": "^7.2.7",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
}, },
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a" "packageManager": "pnpm@10.25.0+sha512.5e82639027af37cf832061bcc6d639c219634488e0f2baebe785028a793de7b525ffcd3f7ff574f5e9860654e098fe852ba8ac5dd5cefe1767d23a020a92f501"
} }

388
interface/pnpm-lock.yaml generated
View File

@@ -13,22 +13,22 @@ importers:
version: 2.3.0(alova@3.4.0) version: 2.3.0(alova@3.4.0)
'@emotion/react': '@emotion/react':
specifier: ^11.14.0 specifier: ^11.14.0
version: 11.14.0(@types/react@19.2.7)(react@19.2.1) version: 11.14.0(@types/react@19.2.7)(react@19.2.2)
'@emotion/styled': '@emotion/styled':
specifier: ^11.14.1 specifier: ^11.14.1
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@mui/icons-material': '@mui/icons-material':
specifier: ^7.3.6 specifier: ^7.3.6
version: 7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) version: 7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@mui/material': '@mui/material':
specifier: ^7.3.6 specifier: ^7.3.6
version: 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) version: 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
'@preact/compat': '@preact/compat':
specifier: ^18.3.1 specifier: ^18.3.1
version: 18.3.1(preact@10.28.0) version: 18.3.1(preact@10.28.0)
'@table-library/react-table-library': '@table-library/react-table-library':
specifier: 4.1.15 specifier: 4.1.15
version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
alova: alova:
specifier: 3.4.0 specifier: 3.4.0
version: 3.4.0 version: 3.4.0
@@ -54,20 +54,20 @@ importers:
specifier: ^10.28.0 specifier: ^10.28.0
version: 10.28.0 version: 10.28.0
react: react:
specifier: ^19.2.1 specifier: ^19.2.2
version: 19.2.1 version: 19.2.2
react-dom: react-dom:
specifier: ^19.2.1 specifier: ^19.2.2
version: 19.2.1(react@19.2.1) version: 19.2.2(react@19.2.2)
react-icons: react-icons:
specifier: ^5.5.0 specifier: ^5.5.0
version: 5.5.0(react@19.2.1) version: 5.5.0(react@19.2.2)
react-router: react-router:
specifier: ^7.10.1 specifier: ^7.10.1
version: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) version: 7.10.1(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
react-toastify: react-toastify:
specifier: ^11.0.5 specifier: ^11.0.5
version: 11.0.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1) version: 11.0.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
typesafe-i18n: typesafe-i18n:
specifier: ^5.26.2 specifier: ^5.26.2
version: 5.26.2(typescript@5.9.3) version: 5.26.2(typescript@5.9.3)
@@ -83,13 +83,13 @@ importers:
version: 9.39.1 version: 9.39.1
'@preact/preset-vite': '@preact/preset-vite':
specifier: ^2.10.2 specifier: ^2.10.2
version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)) version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))
'@trivago/prettier-plugin-sort-imports': '@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0(prettier@3.7.4) version: 6.0.0(prettier@3.7.4)
'@types/node': '@types/node':
specifier: ^24.10.1 specifier: ^25.0.0
version: 24.10.1 version: 25.0.0
'@types/react': '@types/react':
specifier: ^19.2.7 specifier: ^19.2.7
version: 19.2.7 version: 19.2.7
@@ -118,17 +118,17 @@ importers:
specifier: ^5.44.1 specifier: ^5.44.1
version: 5.44.1 version: 5.44.1
typescript-eslint: typescript-eslint:
specifier: ^8.48.1 specifier: ^8.49.0
version: 8.48.1(eslint@9.39.1)(typescript@5.9.3) version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
vite: vite:
specifier: ^7.2.6 specifier: ^7.2.7
version: 7.2.6(@types/node@24.10.1)(terser@5.44.1) version: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
vite-plugin-imagemin: vite-plugin-imagemin:
specifier: ^0.6.1 specifier: ^0.6.1
version: 0.6.1(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)) version: 0.6.1(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: ^5.1.4 specifier: ^5.1.4
version: 5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)) version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))
packages: packages:
@@ -860,8 +860,8 @@ packages:
resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==}
deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.
'@types/node@24.10.1': '@types/node@25.0.0':
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} resolution: {integrity: sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==}
'@types/parse-json@4.0.2': '@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -888,63 +888,63 @@ packages:
'@types/svgo@2.6.4': '@types/svgo@2.6.4':
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
'@typescript-eslint/eslint-plugin@8.48.1': '@typescript-eslint/eslint-plugin@8.49.0':
resolution: {integrity: sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==} resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
'@typescript-eslint/parser': ^8.48.1 '@typescript-eslint/parser': ^8.49.0
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.48.1': '@typescript-eslint/parser@8.49.0':
resolution: {integrity: sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==} resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.48.1': '@typescript-eslint/project-service@8.49.0':
resolution: {integrity: sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==} resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/scope-manager@8.48.1': '@typescript-eslint/scope-manager@8.49.0':
resolution: {integrity: sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==} resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.48.1': '@typescript-eslint/tsconfig-utils@8.49.0':
resolution: {integrity: sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==} resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.48.1': '@typescript-eslint/type-utils@8.49.0':
resolution: {integrity: sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==} resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/types@8.48.1': '@typescript-eslint/types@8.49.0':
resolution: {integrity: sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==} resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.48.1': '@typescript-eslint/typescript-estree@8.49.0':
resolution: {integrity: sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==} resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.48.1': '@typescript-eslint/utils@8.49.0':
resolution: {integrity: sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==} resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/visitor-keys@8.48.1': '@typescript-eslint/visitor-keys@8.49.0':
resolution: {integrity: sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==} resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
acorn-jsx@5.3.2: acorn-jsx@5.3.2:
@@ -1027,8 +1027,8 @@ packages:
base64-js@1.5.1: base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.9.2: baseline-browser-mapping@2.9.6:
resolution: {integrity: sha512-PxSsosKQjI38iXkmb3d0Y32efqyA0uW4s41u4IVBsLlWLhCiYNpH/AfNOVWRqCQBlD8TFJTz6OUWNd4DFJCnmw==} resolution: {integrity: sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==}
hasBin: true hasBin: true
bin-build@3.0.0: bin-build@3.0.0:
@@ -1117,8 +1117,8 @@ packages:
resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
caniuse-lite@1.0.30001759: caniuse-lite@1.0.30001760:
resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==} resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==}
caw@2.0.1: caw@2.0.1:
resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==} resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==}
@@ -1337,8 +1337,8 @@ packages:
duplexer3@0.1.5: duplexer3@0.1.5:
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
electron-to-chromium@1.5.265: electron-to-chromium@1.5.267:
resolution: {integrity: sha512-B7IkLR1/AE+9jR2LtVF/1/6PFhY5TlnEHnlrKmGk7PvkJibg5jr+mLXLLzq3QYl6PA1T/vLDthQPqIPAlS/PPA==} resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1815,9 +1815,6 @@ packages:
graceful-fs@4.2.11: graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
has-ansi@2.0.0: has-ansi@2.0.0:
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -2512,10 +2509,10 @@ packages:
rate-limiter-flexible@5.0.5: rate-limiter-flexible@5.0.5:
resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==} resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==}
react-dom@19.2.1: react-dom@19.2.2:
resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==} resolution: {integrity: sha512-fhyD2BLrew6qYf4NNtHff1rLXvzR25rq49p+FeqByOazc6TcSi2n8EYulo5C1PbH+1uBW++5S1SG7FcUU6mlDg==}
peerDependencies: peerDependencies:
react: ^19.2.1 react: ^19.2.2
react-icons@5.5.0: react-icons@5.5.0:
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
@@ -2525,8 +2522,8 @@ packages:
react-is@16.13.1: react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
react-is@19.2.1: react-is@19.2.2:
resolution: {integrity: sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA==} resolution: {integrity: sha512-ADrk8mxPwhyj+IB8EnMMRzxnon5hJrOdEZl+mCrrHXfPGGPYHdRm1962fvXUiWAu/ZC1G+OTgBN3Puq57iS4Yg==}
react-router@7.10.1: react-router@7.10.1:
resolution: {integrity: sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==} resolution: {integrity: sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==}
@@ -2563,8 +2560,8 @@ packages:
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react@19.2.1: react@19.2.2:
resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==} resolution: {integrity: sha512-BdOGOY8OKRBcgoDkwqA8Q5XvOIhoNx/Sh6BnGJlet2Abt0X5BK0BDrqGyQgLhAVjD2nAg5f6o01u/OPUhG022Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
read-pkg-up@1.0.1: read-pkg-up@1.0.1:
@@ -2927,8 +2924,8 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=3.5.1' typescript: '>=3.5.1'
typescript-eslint@8.48.1: typescript-eslint@8.49.0:
resolution: {integrity: sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==} resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
@@ -2999,8 +2996,8 @@ packages:
vite: vite:
optional: true optional: true
vite@7.2.6: vite@7.2.7:
resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -3268,17 +3265,17 @@ snapshots:
'@emotion/memoize@0.9.0': {} '@emotion/memoize@0.9.0': {}
'@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1)': '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@emotion/babel-plugin': 11.13.5 '@emotion/babel-plugin': 11.13.5
'@emotion/cache': 11.14.0 '@emotion/cache': 11.14.0
'@emotion/serialize': 1.3.3 '@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.1) '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.2)
'@emotion/utils': 1.4.2 '@emotion/utils': 1.4.2
'@emotion/weak-memoize': 0.4.0 '@emotion/weak-memoize': 0.4.0
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
transitivePeerDependencies: transitivePeerDependencies:
@@ -3294,16 +3291,16 @@ snapshots:
'@emotion/sheet@1.4.0': {} '@emotion/sheet@1.4.0': {}
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1)': '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@emotion/babel-plugin': 11.13.5 '@emotion/babel-plugin': 11.13.5
'@emotion/is-prop-valid': 1.4.0 '@emotion/is-prop-valid': 1.4.0
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.1) '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2)
'@emotion/serialize': 1.3.3 '@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.1) '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.2)
'@emotion/utils': 1.4.2 '@emotion/utils': 1.4.2
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
transitivePeerDependencies: transitivePeerDependencies:
@@ -3311,9 +3308,9 @@ snapshots:
'@emotion/unitless@0.10.0': {} '@emotion/unitless@0.10.0': {}
'@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.1)': '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.2)':
dependencies: dependencies:
react: 19.2.1 react: 19.2.2
'@emotion/utils@1.4.2': {} '@emotion/utils@1.4.2': {}
@@ -3489,45 +3486,45 @@ snapshots:
'@mui/core-downloads-tracker@7.3.6': {} '@mui/core-downloads-tracker@7.3.6': {}
'@mui/icons-material@7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(@types/react@19.2.7)(react@19.2.1)': '@mui/icons-material@7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@mui/material': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) '@mui/material': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
'@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': '@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@mui/core-downloads-tracker': 7.3.6 '@mui/core-downloads-tracker': 7.3.6
'@mui/system': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) '@mui/system': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@mui/types': 7.4.9(@types/react@19.2.7) '@mui/types': 7.4.9(@types/react@19.2.7)
'@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.1) '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2)
'@popperjs/core': 2.11.8 '@popperjs/core': 2.11.8
'@types/react-transition-group': 4.4.12(@types/react@19.2.7) '@types/react-transition-group': 4.4.12(@types/react@19.2.7)
clsx: 2.1.1 clsx: 2.1.1
csstype: 3.2.3 csstype: 3.2.3
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-is: 19.2.1 react-is: 19.2.2
react-transition-group: 4.4.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1) react-transition-group: 4.4.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
optionalDependencies: optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.1) '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@types/react': 19.2.7 '@types/react': 19.2.7
'@mui/private-theming@7.3.6(@types/react@19.2.7)(react@19.2.1)': '@mui/private-theming@7.3.6(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.1) '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2)
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
'@mui/styled-engine@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(react@19.2.1)': '@mui/styled-engine@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@emotion/cache': 11.14.0 '@emotion/cache': 11.14.0
@@ -3535,25 +3532,25 @@ snapshots:
'@emotion/sheet': 1.4.0 '@emotion/sheet': 1.4.0
csstype: 3.2.3 csstype: 3.2.3
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.1) '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@mui/system@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1)': '@mui/system@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@mui/private-theming': 7.3.6(@types/react@19.2.7)(react@19.2.1) '@mui/private-theming': 7.3.6(@types/react@19.2.7)(react@19.2.2)
'@mui/styled-engine': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1))(react@19.2.1) '@mui/styled-engine': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(react@19.2.2)
'@mui/types': 7.4.9(@types/react@19.2.7) '@mui/types': 7.4.9(@types/react@19.2.7)
'@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.1) '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2)
clsx: 2.1.1 clsx: 2.1.1
csstype: 3.2.3 csstype: 3.2.3
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
optionalDependencies: optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.1) '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(@types/react@19.2.7)(react@19.2.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)
'@types/react': 19.2.7 '@types/react': 19.2.7
'@mui/types@7.4.9(@types/react@19.2.7)': '@mui/types@7.4.9(@types/react@19.2.7)':
@@ -3562,15 +3559,15 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
'@mui/utils@7.3.6(@types/react@19.2.7)(react@19.2.1)': '@mui/utils@7.3.6(@types/react@19.2.7)(react@19.2.2)':
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
'@mui/types': 7.4.9(@types/react@19.2.7) '@mui/types': 7.4.9(@types/react@19.2.7)
'@types/prop-types': 15.7.15 '@types/prop-types': 15.7.15
clsx: 2.1.1 clsx: 2.1.1
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
react-is: 19.2.1 react-is: 19.2.2
optionalDependencies: optionalDependencies:
'@types/react': 19.2.7 '@types/react': 19.2.7
@@ -3598,18 +3595,18 @@ snapshots:
dependencies: dependencies:
preact: 10.28.0 preact: 10.28.0
'@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))': '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))':
dependencies: dependencies:
'@babel/core': 7.28.5 '@babel/core': 7.28.5
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5)
'@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5)
'@prefresh/vite': 2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)) '@prefresh/vite': 2.4.11(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))
'@rollup/pluginutils': 4.2.1 '@rollup/pluginutils': 4.2.1
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5) babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5)
debug: 4.4.3 debug: 4.4.3
picocolors: 1.1.1 picocolors: 1.1.1
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1) vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
vite-prerender-plugin: 0.5.12(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)) vite-prerender-plugin: 0.5.12(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))
transitivePeerDependencies: transitivePeerDependencies:
- preact - preact
- supports-color - supports-color
@@ -3622,7 +3619,7 @@ snapshots:
'@prefresh/utils@1.2.1': {} '@prefresh/utils@1.2.1': {}
'@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))': '@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))':
dependencies: dependencies:
'@babel/core': 7.28.5 '@babel/core': 7.28.5
'@prefresh/babel-plugin': 0.5.2 '@prefresh/babel-plugin': 0.5.2
@@ -3630,7 +3627,7 @@ snapshots:
'@prefresh/utils': 1.2.1 '@prefresh/utils': 1.2.1
'@rollup/pluginutils': 4.2.1 '@rollup/pluginutils': 4.2.1
preact: 10.28.0 preact: 10.28.0
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1) vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3707,14 +3704,14 @@ snapshots:
'@sindresorhus/is@0.7.0': {} '@sindresorhus/is@0.7.0': {}
'@table-library/react-table-library@4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': '@table-library/react-table-library@4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(react-dom@19.2.2(react@19.2.2))(react@19.2.2)':
dependencies: dependencies:
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.1) '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2)
clsx: 1.1.1 clsx: 1.1.1
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-virtualized-auto-sizer: 1.0.26(react-dom@19.2.1(react@19.2.1))(react@19.2.1) react-virtualized-auto-sizer: 1.0.26(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
react-window: 1.8.11(react-dom@19.2.1(react@19.2.1))(react@19.2.1) react-window: 1.8.11(react-dom@19.2.2(react@19.2.2))(react@19.2.2)
'@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.4)': '@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.4)':
dependencies: dependencies:
@@ -3737,7 +3734,7 @@ snapshots:
'@types/glob@7.2.0': '@types/glob@7.2.0':
dependencies: dependencies:
'@types/minimatch': 6.0.0 '@types/minimatch': 6.0.0
'@types/node': 24.10.1 '@types/node': 25.0.0
'@types/imagemin-gifsicle@7.0.4': '@types/imagemin-gifsicle@7.0.4':
dependencies: dependencies:
@@ -3766,19 +3763,19 @@ snapshots:
'@types/imagemin@7.0.1': '@types/imagemin@7.0.1':
dependencies: dependencies:
'@types/node': 24.10.1 '@types/node': 25.0.0
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/keyv@3.1.4': '@types/keyv@3.1.4':
dependencies: dependencies:
'@types/node': 24.10.1 '@types/node': 25.0.0
'@types/minimatch@6.0.0': '@types/minimatch@6.0.0':
dependencies: dependencies:
minimatch: 10.1.1 minimatch: 10.1.1
'@types/node@24.10.1': '@types/node@25.0.0':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
@@ -3800,22 +3797,21 @@ snapshots:
'@types/responselike@1.0.3': '@types/responselike@1.0.3':
dependencies: dependencies:
'@types/node': 24.10.1 '@types/node': 25.0.0
'@types/svgo@2.6.4': '@types/svgo@2.6.4':
dependencies: dependencies:
'@types/node': 24.10.1 '@types/node': 25.0.0
'@typescript-eslint/eslint-plugin@8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.48.1 '@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/type-utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
'@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.48.1 '@typescript-eslint/visitor-keys': 8.49.0
eslint: 9.39.1 eslint: 9.39.1
graphemer: 1.4.0
ignore: 7.0.5 ignore: 7.0.5
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@5.9.3) ts-api-utils: 2.1.0(typescript@5.9.3)
@@ -3823,41 +3819,41 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3)': '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.48.1 '@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.48.1 '@typescript-eslint/visitor-keys': 8.49.0
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.1 eslint: 9.39.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/project-service@8.48.1(typescript@5.9.3)': '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
debug: 4.4.3 debug: 4.4.3
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/scope-manager@8.48.1': '@typescript-eslint/scope-manager@8.49.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.48.1 '@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/tsconfig-utils@8.48.1(typescript@5.9.3)': '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)':
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
'@typescript-eslint/type-utils@8.48.1(eslint@9.39.1)(typescript@5.9.3)': '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.1 eslint: 9.39.1
ts-api-utils: 2.1.0(typescript@5.9.3) ts-api-utils: 2.1.0(typescript@5.9.3)
@@ -3865,14 +3861,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/types@8.48.1': {} '@typescript-eslint/types@8.49.0': {}
'@typescript-eslint/typescript-estree@8.48.1(typescript@5.9.3)': '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/project-service': 8.48.1(typescript@5.9.3) '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.48.1 '@typescript-eslint/visitor-keys': 8.49.0
debug: 4.4.3 debug: 4.4.3
minimatch: 9.0.5 minimatch: 9.0.5
semver: 7.7.3 semver: 7.7.3
@@ -3882,20 +3878,20 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.48.1(eslint@9.39.1)(typescript@5.9.3)': '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
'@typescript-eslint/scope-manager': 8.48.1 '@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
eslint: 9.39.1 eslint: 9.39.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/visitor-keys@8.48.1': '@typescript-eslint/visitor-keys@8.49.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.48.1 '@typescript-eslint/types': 8.49.0
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
acorn-jsx@5.3.2(acorn@8.15.0): acorn-jsx@5.3.2(acorn@8.15.0):
@@ -3962,7 +3958,7 @@ snapshots:
base64-js@1.5.1: {} base64-js@1.5.1: {}
baseline-browser-mapping@2.9.2: {} baseline-browser-mapping@2.9.6: {}
bin-build@3.0.0: bin-build@3.0.0:
dependencies: dependencies:
@@ -4019,9 +4015,9 @@ snapshots:
browserslist@4.28.1: browserslist@4.28.1:
dependencies: dependencies:
baseline-browser-mapping: 2.9.2 baseline-browser-mapping: 2.9.6
caniuse-lite: 1.0.30001759 caniuse-lite: 1.0.30001760
electron-to-chromium: 1.5.265 electron-to-chromium: 1.5.267
node-releases: 2.0.27 node-releases: 2.0.27
update-browserslist-db: 1.2.2(browserslist@4.28.1) update-browserslist-db: 1.2.2(browserslist@4.28.1)
@@ -4079,7 +4075,7 @@ snapshots:
camelcase@2.1.1: {} camelcase@2.1.1: {}
caniuse-lite@1.0.30001759: {} caniuse-lite@1.0.30001760: {}
caw@2.0.1: caw@2.0.1:
dependencies: dependencies:
@@ -4366,7 +4362,7 @@ snapshots:
duplexer3@0.1.5: {} duplexer3@0.1.5: {}
electron-to-chromium@1.5.265: {} electron-to-chromium@1.5.267: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}
@@ -4895,8 +4891,6 @@ snapshots:
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
has-ansi@2.0.0: has-ansi@2.0.0:
dependencies: dependencies:
ansi-regex: 2.1.1 ansi-regex: 2.1.1
@@ -5518,55 +5512,55 @@ snapshots:
rate-limiter-flexible@5.0.5: {} rate-limiter-flexible@5.0.5: {}
react-dom@19.2.1(react@19.2.1): react-dom@19.2.2(react@19.2.2):
dependencies: dependencies:
react: 19.2.1 react: 19.2.2
scheduler: 0.27.0 scheduler: 0.27.0
react-icons@5.5.0(react@19.2.1): react-icons@5.5.0(react@19.2.2):
dependencies: dependencies:
react: 19.2.1 react: 19.2.2
react-is@16.13.1: {} react-is@16.13.1: {}
react-is@19.2.1: {} react-is@19.2.2: {}
react-router@7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1): react-router@7.10.1(react-dom@19.2.2(react@19.2.2))(react@19.2.2):
dependencies: dependencies:
cookie: 1.1.1 cookie: 1.1.1
react: 19.2.1 react: 19.2.2
set-cookie-parser: 2.7.2 set-cookie-parser: 2.7.2
optionalDependencies: optionalDependencies:
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-toastify@11.0.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1): react-toastify@11.0.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2):
dependencies: dependencies:
clsx: 2.1.1 clsx: 2.1.1
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-transition-group@4.4.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1): react-transition-group@4.4.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2):
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
dom-helpers: 5.2.1 dom-helpers: 5.2.1
loose-envify: 1.4.0 loose-envify: 1.4.0
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-virtualized-auto-sizer@1.0.26(react-dom@19.2.1(react@19.2.1))(react@19.2.1): react-virtualized-auto-sizer@1.0.26(react-dom@19.2.2(react@19.2.2))(react@19.2.2):
dependencies: dependencies:
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react-window@1.8.11(react-dom@19.2.1(react@19.2.1))(react@19.2.1): react-window@1.8.11(react-dom@19.2.2(react@19.2.2))(react@19.2.2):
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
memoize-one: 5.2.1 memoize-one: 5.2.1
react: 19.2.1 react: 19.2.2
react-dom: 19.2.1(react@19.2.1) react-dom: 19.2.2(react@19.2.2)
react@19.2.1: {} react@19.2.2: {}
read-pkg-up@1.0.1: read-pkg-up@1.0.1:
dependencies: dependencies:
@@ -5918,12 +5912,12 @@ snapshots:
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
typescript-eslint@8.48.1(eslint@9.39.1)(typescript@5.9.3): typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
'@typescript-eslint/parser': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
eslint: 9.39.1 eslint: 9.39.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -5969,7 +5963,7 @@ snapshots:
spdx-correct: 3.2.0 spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1 spdx-expression-parse: 3.0.1
vite-plugin-imagemin@0.6.1(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)): vite-plugin-imagemin@0.6.1(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)):
dependencies: dependencies:
'@types/imagemin': 7.0.1 '@types/imagemin': 7.0.1
'@types/imagemin-gifsicle': 7.0.4 '@types/imagemin-gifsicle': 7.0.4
@@ -5994,11 +5988,11 @@ snapshots:
imagemin-webp: 6.1.0 imagemin-webp: 6.1.0
jpegtran-bin: 6.0.1 jpegtran-bin: 6.0.1
pathe: 0.2.0 pathe: 0.2.0
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1) vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
vite-prerender-plugin@0.5.12(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)): vite-prerender-plugin@0.5.12(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)):
dependencies: dependencies:
kolorist: 1.8.0 kolorist: 1.8.0
magic-string: 0.30.21 magic-string: 0.30.21
@@ -6006,20 +6000,20 @@ snapshots:
simple-code-frame: 1.3.0 simple-code-frame: 1.3.0
source-map: 0.7.6 source-map: 0.7.6
stack-trace: 1.0.0-pre2 stack-trace: 1.0.0-pre2
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1) vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)): vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)):
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
globrex: 0.1.2 globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.9.3) tsconfck: 3.1.6(typescript@5.9.3)
optionalDependencies: optionalDependencies:
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1) vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
vite@7.2.6(@types/node@24.10.1)(terser@5.44.1): vite@7.2.7(@types/node@25.0.0)(terser@5.44.1):
dependencies: dependencies:
esbuild: 0.25.12 esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)
@@ -6028,7 +6022,7 @@ snapshots:
rollup: 4.53.3 rollup: 4.53.3
tinyglobby: 0.2.15 tinyglobby: 0.2.15
optionalDependencies: optionalDependencies:
'@types/node': 24.10.1 '@types/node': 25.0.0
fsevents: 2.3.3 fsevents: 2.3.3
terser: 5.44.1 terser: 5.44.1

View File

@@ -15,5 +15,5 @@
"itty-router": "^5.0.22", "itty-router": "^5.0.22",
"prettier": "^3.7.4" "prettier": "^3.7.4"
}, },
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a" "packageManager": "pnpm@10.25.0+sha512.5e82639027af37cf832061bcc6d639c219634488e0f2baebe785028a793de7b525ffcd3f7ff574f5e9860654e098fe852ba8ac5dd5cefe1767d23a020a92f501"
} }

View File

@@ -192,6 +192,7 @@ build_src_flags =
-std=gnu++17 -Og -ggdb -std=gnu++17 -Og -ggdb
-Wall -Wextra -Wall -Wextra
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
-I./src/core -I./src/core
-I./lib_standalone -I./lib_standalone
-I./lib/uuid-common/src -I./lib/uuid-common/src

View File

@@ -792,20 +792,15 @@ void AnalogSensor::publish_values(const bool force) {
} }
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors // see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
bool is_ha_device_created = false; if (std::none_of(sensors_.begin(), sensors_.end(), [](const auto & sensor) { return sensor.ha_registered; })) {
for (auto const & sensor : sensors_) { Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Analog Sensors", nullptr, nullptr, nullptr, false);
if (sensor.ha_registered) {
is_ha_device_created = true;
break;
}
} }
// add default_entity_id // add default_entity_id
std::string topic_str(topic); std::string topic_str(topic);
doc["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s; doc["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s;
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Analog Sensors", nullptr, nullptr, nullptr, false); Mqtt::add_ha_avty_section(config.as<JsonObject>(), stat_t, val_cond);
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !is_ha_device_created, val_cond);
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>()); sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
} }

View File

@@ -237,12 +237,20 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
const char * cmd_org = command; const char * cmd_org = command;
int8_t id_org = id; int8_t id_org = id;
// convert cmd to lowercase and compare // Optimized: Use stack buffer instead of strdup() to avoid heap allocation
char * lowerCmd = strdup(command); // Most command strings are short, 64 bytes is more than enough
for (char * p = lowerCmd; *p; p++) { char lowerCmd[64];
*p = tolower(*p); size_t len = strlen(command);
if (len >= sizeof(lowerCmd)) {
len = sizeof(lowerCmd) - 1; // truncate if too long (rare case)
} }
// Convert to lowercase in place using stack buffer
for (size_t i = 0; i < len; i++) {
lowerCmd[i] = tolower(command[i]);
}
lowerCmd[len] = '\0';
// check prefix and valid number range, also check 'id' // check prefix and valid number range, also check 'id'
if (!strncmp(lowerCmd, "hc", 2) && command[2] >= '1' && command[2] <= '8') { if (!strncmp(lowerCmd, "hc", 2) && command[2] >= '1' && command[2] <= '8') {
id = command[2] - '0'; id = command[2] - '0';
@@ -279,7 +287,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
command += 3; command += 3;
} }
free(lowerCmd); // No free() needed - stack buffer is automatically cleaned up
// return original if no seperator // return original if no seperator
if (command[0] != '/' && command[0] != '.') { if (command[0] != '/' && command[0] != '.') {

View File

@@ -848,7 +848,7 @@ std::string EMSESP::device_tostring(const uint8_t device_id) {
} }
} }
// created a pretty print telegram as a text string // create a pretty print telegram as a text string
// e.g. Boiler(0x08) -> Me(0x0B), Version(0x02), data: 7B 06 01 00 00 00 00 00 00 04 (offset 1) // e.g. Boiler(0x08) -> Me(0x0B), Version(0x02), data: 7B 06 01 00 00 00 00 00 00 04 (offset 1)
std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) { std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
uint8_t src = telegram->src & 0x7F; uint8_t src = telegram->src & 0x7F;
@@ -950,27 +950,58 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
} }
} }
std::string str; // Optimized: Use stack buffer and build string once to avoid multiple temporary allocations
str.reserve(200); char buf[250];
if (telegram->operation == Telegram::Operation::RX_READ) { if (telegram->operation == Telegram::Operation::RX_READ) {
str = src_name + "(" + Helpers::hextoa(src) + ") R " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "(" auto pos = snprintf(buf,
+ Helpers::hextoa(telegram->type_id) + "), length: " + Helpers::itoa(telegram->message_data[0]) sizeof(buf),
+ ((telegram->message_length > 1) ? ", data: " + Helpers::data_to_hex(telegram->message_data + 1, telegram->message_length - 1) : ""); "%s(%s) R %s(%s), %s(%s), length: %d",
src_name.c_str(),
Helpers::hextoa(src).c_str(),
dest_name.c_str(),
Helpers::hextoa(dest).c_str(),
type_name.c_str(),
Helpers::hextoa(telegram->type_id).c_str(),
telegram->message_data[0]);
if (telegram->message_length > 1 && pos > 0 && pos < (int)sizeof(buf)) {
std::string data_hex = Helpers::data_to_hex(telegram->message_data + 1, telegram->message_length - 1);
snprintf(buf + pos, sizeof(buf) - pos, ", data: %s", data_hex.c_str());
}
} else if (telegram->dest == 0) { } else if (telegram->dest == 0) {
str = src_name + "(" + Helpers::hextoa(src) + ") B " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "(" snprintf(buf,
+ Helpers::hextoa(telegram->type_id) + "), data: " + telegram->to_string_message(); sizeof(buf),
"%s(%s) B %s(%s), %s(%s), data: %s",
src_name.c_str(),
Helpers::hextoa(src).c_str(),
dest_name.c_str(),
Helpers::hextoa(dest).c_str(),
type_name.c_str(),
Helpers::hextoa(telegram->type_id).c_str(),
telegram->to_string_message().c_str());
} else { } else {
str = src_name + "(" + Helpers::hextoa(src) + ") W " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "(" snprintf(buf,
+ Helpers::hextoa(telegram->type_id) + "), data: " + telegram->to_string_message(); sizeof(buf),
"%s(%s) W %s(%s), %s(%s), data: %s",
src_name.c_str(),
Helpers::hextoa(src).c_str(),
dest_name.c_str(),
Helpers::hextoa(dest).c_str(),
type_name.c_str(),
Helpers::hextoa(telegram->type_id).c_str(),
telegram->to_string_message().c_str());
} }
if (offset) { if (offset) {
str += " (offset " + Helpers::itoa(offset) + ")"; size_t len = strlen(buf);
if (len < sizeof(buf) - 20) {
snprintf(buf + len, sizeof(buf) - len, " (offset %d)", offset);
}
} }
return str; return std::string(buf);
} }
/* /*
* Type 0x07 - UBADevices - shows us the connected EMS devices * Type 0x07 - UBADevices - shows us the connected EMS devices
* e.g. 08 00 07 00 0B 80 00 00 00 00 00 00 00 00 00 00 00 * e.g. 08 00 07 00 0B 80 00 00 00 00 00 00 00 00 00 00 00

View File

@@ -34,12 +34,19 @@ char * Helpers::hextoa(char * result, const uint8_t value) {
} }
// same as hextoa but uses to a hex std::string // same as hextoa but uses to a hex std::string
// Optimized: Avoid string concatenation to reduce temporary allocations
std::string Helpers::hextoa(const uint8_t value, bool prefix) { std::string Helpers::hextoa(const uint8_t value, bool prefix) {
char buf[3];
if (prefix) { if (prefix) {
return std::string("0x") + hextoa(buf, value); char buf[5]; // "0x" + 2 hex chars + null
buf[0] = '0';
buf[1] = 'x';
hextoa(&buf[2], value);
return std::string(buf);
} else {
char buf[3];
hextoa(buf, value);
return std::string(buf);
} }
return std::string(hextoa(buf, value));
} }
// same for 16 bit values // same for 16 bit values
@@ -53,12 +60,19 @@ char * Helpers::hextoa(char * result, const uint16_t value) {
} }
// same as above but to a hex string // same as above but to a hex string
// Optimized: Avoid string concatenation to reduce temporary allocations
std::string Helpers::hextoa(const uint16_t value, bool prefix) { std::string Helpers::hextoa(const uint16_t value, bool prefix) {
char buf[5];
if (prefix) { if (prefix) {
return std::string("0x") + hextoa(buf, value); char buf[7]; // "0x" + 4 hex chars + null
buf[0] = '0';
buf[1] = 'x';
hextoa(&buf[2], value);
return std::string(buf);
} else {
char buf[5];
hextoa(buf, value);
return std::string(buf);
} }
return std::string(hextoa(buf, value));
} }
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
@@ -100,29 +114,34 @@ char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) {
// fast itoa returning a std::string // fast itoa returning a std::string
// http://www.strudel.org.uk/itoa/ // http://www.strudel.org.uk/itoa/
// Optimized: Use stack buffer to avoid heap allocation, then create string once
std::string Helpers::itoa(int16_t value) { std::string Helpers::itoa(int16_t value) {
std::string buf; // int16_t max: -32768 to 32767 = max 6 chars + null
buf.reserve(25); // Pre-allocate enough space. char buf[8];
int quotient = value; char * p = buf + sizeof(buf) - 1;
*p = '\0';
bool negative = value < 0;
int32_t abs_val = negative ? -(int32_t)value : value; // cast to int32 to handle -32768
// Build string in reverse
do { do {
buf += "0123456789abcdef"[std::abs(quotient % 10)]; *--p = '0' + (abs_val % 10);
quotient /= 10; abs_val /= 10;
} while (quotient); } while (abs_val > 0);
// Append the negative sign if (negative) {
if (value < 0) *--p = '-';
buf += '-'; }
std::reverse(buf.begin(), buf.end()); return std::string(p);
return buf;
} }
/* /*
* fast itoa * fast itoa
* written by Lukás Chmela, Released under GPLv3. http://www.strudel.org.uk/itoa/ version 0.4 * written by Lukás Chmela, Released under GPLv3. http://www.strudel.org.uk/itoa/ version 0.4
* optimized for ESP32 * optimized for ESP32
*/ */
char * Helpers::itoa(int32_t value, char * result, const uint8_t base) { char * Helpers::itoa(int32_t value, char * result, const uint8_t base) {
// check that the base if valid // check that the base if valid
if (base < 2 || base > 36) { if (base < 2 || base > 36) {
@@ -470,25 +489,26 @@ char * Helpers::utf8tolatin1(char * result, const char * c, const uint8_t len) {
*p = '\0'; // terminate result *p = '\0'; // terminate result
return result; return result;
} }
// creates string of hex values from an array of bytes // creates string of hex values from an array of bytes
std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) { std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
if (length == 0) { if (length == 0) {
return "<empty>"; return "<empty>";
} }
std::string str; std::vector<char> str(length * 3);
str.reserve(length * 3 + 1); memset(str.data(), 0, str.size());
char buffer[4]; char buffer[4];
char * p = str.data();
for (uint8_t i = 0; i < length; i++) { for (uint8_t i = 0; i < length; i++) {
str.append(Helpers::hextoa(buffer, data[i])); Helpers::hextoa(buffer, data[i]);
str.push_back(' '); *p++ = buffer[0];
*p++ = buffer[1];
*p++ = ' '; // space
} }
if (!str.empty()) { *--p = '\0'; // null terminate just in case, loosing the trailing space
str.pop_back();
} return std::string(str.data());
return str;
} }
// takes a hex string and convert it to an unsigned 32bit number (max 8 hex digits) // takes a hex string and convert it to an unsigned 32bit number (max 8 hex digits)

View File

@@ -222,9 +222,11 @@ void Mqtt::on_message(const char * topic, const uint8_t * payload, size_t len) {
// the payload is not terminated // the payload is not terminated
// convert payload to a null-terminated char string // convert payload to a null-terminated char string
// see https://www.emelis.net/espMqttClient/#code-samples // see https://www.emelis.net/espMqttClient/#code-samples
char message[len + 1]; // fix variable-length arrays (VLAs) "char message[len + 1]" as they are not standard C++; they're a Clang/GCC extension.
memcpy(message, payload, len); std::vector<char> message_buffer(len + 1);
message[len] = '\0'; memcpy(message_buffer.data(), payload, len);
message_buffer[len] = '\0';
char * message = message_buffer.data();
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
if (len) { if (len) {
@@ -596,15 +598,17 @@ void Mqtt::ha_status() {
// add sub or pub task to the queue. // add sub or pub task to the queue.
// the base is not included in the topic // the base is not included in the topic
bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) { bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) {
if (!mqtt_enabled_ || topic.empty() || !connected()) {
return false; // quit, not using MQTT
}
if (topic == "response" && operation == Operation::PUBLISH) { if (topic == "response" && operation == Operation::PUBLISH) {
lastresponse_ = payload; lastresponse_ = payload;
if (!send_response_) { if (!send_response_) {
return true; return true;
} }
} }
if (!mqtt_enabled_ || topic.empty() || !connected()) {
return false; // quit, not using MQTT
}
// check free mem // check free mem
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
// if (ESP.getFreeHeap() < 60 * 1024 || ESP.getMaxAllocHeap() < 40 * 1024) { // if (ESP.getFreeHeap() < 60 * 1024 || ESP.getMaxAllocHeap() < 40 * 1024) {
@@ -1094,10 +1098,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery // don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
if (discovery_type() == discoveryType::HOMEASSISTANT) { if (discovery_type() == discoveryType::HOMEASSISTANT) {
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}"; doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
add_ha_avty_section(doc.as<JsonObject>(), stat_t, val_cond); // adds availability section
// adds availability, dev, ids to the config section to HA Discovery config
// except for commands
add_ha_avail_section(doc.as<JsonObject>(), stat_t, false, val_cond);
} else { } else {
// Domoticz doesn't support value templates, so we just use the value directly // Domoticz doesn't support value templates, so we just use the value directly
// Also omit the uom and other state classes // Also omit the uom and other state classes
@@ -1367,8 +1368,8 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
modes.add("heat"); modes.add("heat");
modes.add("off"); modes.add("off");
add_ha_dev_section(doc.as<JsonObject>(), devicename, nullptr, nullptr, nullptr, false); // add dev section add_ha_dev_section(doc.as<JsonObject>(), devicename, nullptr, nullptr, nullptr, false); // add dev section
add_ha_avail_section(doc.as<JsonObject>(), topic_t, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); // add availability section add_ha_avty_section(doc.as<JsonObject>(), topic_t, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); // add availability section
return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
} }
@@ -1434,59 +1435,59 @@ void Mqtt::add_ha_dev_section(JsonObject doc, const char * name, const char * mo
} }
} }
// adds sections for HA Discovery to an existing JSON doc // adds avty section for HA Discovery to an existing JSON doc
// adds dev section with ids, name, mf, mdl, via_device void Mqtt::add_ha_avty_section(JsonObject doc, const char * state_t, const char * cond1, const char * cond2, const char * negcond) {
// adds optional availability section
void Mqtt::add_ha_avail_section(JsonObject doc, const char * state_t, const bool is_first, const char * cond1, const char * cond2, const char * negcond) {
// only works for HA // only works for HA
if (discovery_type() != discoveryType::HOMEASSISTANT) { if (discovery_type() != discoveryType::HOMEASSISTANT) {
return; return;
} }
// skip availability section if no conditions set
if (!cond1 && !cond2 && !negcond) {
return;
}
// adds "availability" section to HA Discovery config // adds "availability" section to HA Discovery config
JsonArray avty = doc["avty"].to<JsonArray>(); JsonArray avty = doc["avty"].to<JsonArray>();
JsonDocument avty_json; JsonDocument avty_json;
// make local copy of state, as the pointer will get de-referenced if (state_t != nullptr) {
char state[50]; // make local copy of state, as the pointer will get de-referenced
strlcpy(state, state_t, sizeof(state)); char state[40];
strlcpy(state, state_t, sizeof(state));
char tpl[150]; char tpl[150];
const char * tpl_draft = "{{'online' if %s else 'offline'}}";
// condition 1 // condition 1
if (cond1 != nullptr) { if (cond1 != nullptr) {
avty_json.clear(); avty_json.clear();
avty_json["t"] = state; avty_json["t"] = state;
snprintf(tpl, sizeof(tpl), tpl_draft, cond1); snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond1);
avty_json["val_tpl"] = tpl; avty_json["val_tpl"] = tpl;
avty.add(avty_json); // returns 0 if no mem if (!avty.add(avty_json)) {
LOG_WARNING("Failed to add availability condition 1 (low memory)");
}
}
// condition 2
if (cond2 != nullptr) {
avty_json.clear();
avty_json["t"] = state;
snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond2);
avty_json["val_tpl"] = tpl;
if (!avty.add(avty_json)) {
LOG_WARNING("Failed to add availability condition 2 (low memory)");
}
}
// negative condition
if (negcond != nullptr) {
avty_json.clear();
avty_json["t"] = state;
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
avty_json["val_tpl"] = tpl;
if (!avty.add(avty_json)) {
LOG_WARNING("Failed to add negative availability condition (low memory)");
}
}
} }
// condition 2 // always add LWT (Last Will and Testament)
if (cond2 != nullptr) {
avty_json.clear();
avty_json["t"] = state;
snprintf(tpl, sizeof(tpl), tpl_draft, cond2);
avty_json["val_tpl"] = tpl;
avty.add(avty_json); // returns 0 if no mem
}
// negative condition
if (negcond != nullptr) {
avty_json.clear();
avty_json["t"] = state;
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
avty_json["val_tpl"] = tpl;
avty.add(avty_json); // returns 0 if no mem
}
// add LWT (Last Will and Testament)
avty_json.clear(); avty_json.clear();
avty_json["t"] = "~/status"; // as a topic avty_json["t"] = "~/status"; // as a topic
avty.add(avty_json); avty.add(avty_json);

View File

@@ -256,12 +256,11 @@ class Mqtt {
static void static void
add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity = nullptr, bool is_discovery = true); add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity = nullptr, bool is_discovery = true);
static void add_ha_dev_section(JsonObject doc, const char * name, const char * model, const char * brand, const char * version, const bool create_model); static void add_ha_dev_section(JsonObject doc, const char * name, const char * model, const char * brand, const char * version, const bool create_model);
static void add_ha_avail_section(JsonObject doc, static void add_ha_avty_section(JsonObject doc,
const char * state_t, const char * state_t = nullptr,
const bool is_first, const char * cond1 = nullptr,
const char * cond1 = nullptr, const char * cond2 = nullptr,
const char * cond2 = nullptr, const char * negcond = nullptr);
const char * negcond = nullptr);
static void add_ha_bool(JsonObject doc); static void add_ha_bool(JsonObject doc);
static void add_value_bool(JsonObject doc, const char * name, bool value); static void add_value_bool(JsonObject doc, const char * name, bool value);

View File

@@ -212,7 +212,7 @@ void Shower::create_ha_discovery() {
Mqtt::add_ha_bool(doc.as<JsonObject>()); Mqtt::add_ha_bool(doc.as<JsonObject>());
Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, nullptr, false); Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, nullptr, false);
Mqtt::add_ha_avail_section(doc.as<JsonObject>(), "~/shower_active", true); // no conditions Mqtt::add_ha_avty_section(doc.as<JsonObject>()); // no conditions
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
@@ -240,7 +240,7 @@ void Shower::create_ha_discovery() {
// doc["ent_cat"] = "diagnostic"; // doc["ent_cat"] = "diagnostic";
Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, nullptr, false); Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, nullptr, false);
Mqtt::add_ha_avail_section(doc.as<JsonObject>(), "~/shower_data", false, "value_json.duration is defined"); Mqtt::add_ha_avty_section(doc.as<JsonObject>(), "~/shower_data", "value_json.duration is defined");
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str()); snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag

View File

@@ -1565,7 +1565,7 @@ void System::get_value_json(JsonObject output, const std::string & circuit, cons
// generate Prometheus metrics format from system values // generate Prometheus metrics format from system values
std::string System::get_metrics_prometheus() { std::string System::get_metrics_prometheus() {
std::string result; std::string result;
std::unordered_map<std::string, bool> seen_metrics; std::unordered_map<std::string, bool> seen_metrics;
// get system data // get system data
@@ -1633,156 +1633,155 @@ std::string System::get_metrics_prometheus() {
}; };
// helper function to process a JSON object recursively // helper function to process a JSON object recursively
std::function<void(const JsonObject &, const std::string &)> process_object = std::function<void(const JsonObject &, const std::string &)> process_object = [&](const JsonObject & obj, const std::string & prefix) {
[&](const JsonObject & obj, const std::string & prefix) { std::vector<std::pair<std::string, std::string>> local_info_labels;
std::vector<std::pair<std::string, std::string>> local_info_labels; bool has_nested_objects = false;
bool has_nested_objects = false;
for (JsonPair p : obj) { for (JsonPair p : obj) {
std::string key = p.key().c_str(); std::string key = p.key().c_str();
std::string path = prefix.empty() ? key : prefix + "." + key; std::string path = prefix.empty() ? key : prefix + "." + key;
std::string metric_name = prefix.empty() ? key : prefix + "_" + key; std::string metric_name = prefix.empty() ? key : prefix + "_" + key;
if (should_ignore(prefix, key)) { if (should_ignore(prefix, key)) {
continue; continue;
} }
if (p.value().is<JsonObject>()) { if (p.value().is<JsonObject>()) {
// recursive call for nested objects // recursive call for nested objects
has_nested_objects = true; has_nested_objects = true;
process_object(p.value().as<JsonObject>(), metric_name); process_object(p.value().as<JsonObject>(), metric_name);
} else if (p.value().is<JsonArray>()) { } else if (p.value().is<JsonArray>()) {
// handle arrays (devices) // handle arrays (devices)
if (key == "devices") { if (key == "devices") {
JsonArray devices = p.value().as<JsonArray>(); JsonArray devices = p.value().as<JsonArray>();
for (JsonObject device : devices) { for (JsonObject device : devices) {
std::vector<std::pair<std::string, std::string>> device_labels; std::vector<std::pair<std::string, std::string>> device_labels;
// collect labels from device object // collect labels from device object
for (JsonPair dp : device) { for (JsonPair dp : device) {
std::string dkey = dp.key().c_str(); std::string dkey = dp.key().c_str();
if (dkey == "type" || dkey == "name" || dkey == "deviceID" || dkey == "brand" || dkey == "version") { if (dkey == "type" || dkey == "name" || dkey == "deviceID" || dkey == "brand" || dkey == "version") {
if (dp.value().is<const char *>()) { if (dp.value().is<const char *>()) {
std::string val = dp.value().as<const char *>(); std::string val = dp.value().as<const char *>();
if (!val.empty()) { if (!val.empty()) {
device_labels.push_back({to_lowercase(dkey), val}); device_labels.push_back({to_lowercase(dkey), val});
}
} }
} }
} }
}
// create productID metric // create productID metric
if (device.containsKey("productID") && device["productID"].is<int>()) { if (device["productID"].is<int>()) {
std::string metric = "emsesp_device_productid"; std::string metric = "emsesp_device_productid";
if (seen_metrics.find(metric) == seen_metrics.end()) { if (seen_metrics.find(metric) == seen_metrics.end()) {
result += "# HELP emsesp_device_productid productID\n"; result += "# HELP emsesp_device_productid productID\n";
result += "# TYPE emsesp_device_productid gauge\n"; result += "# TYPE emsesp_device_productid gauge\n";
seen_metrics[metric] = true; seen_metrics[metric] = true;
}
result += metric;
if (!device_labels.empty()) {
result += "{";
bool first = true;
for (const auto & label : device_labels) {
if (!first) {
result += ", ";
}
result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
}
result += "}";
}
result += " " + std::to_string(device["productID"].as<int>()) + "\n";
} }
// create entities metric result += metric;
if (device.containsKey("entities") && device["entities"].is<int>()) { if (!device_labels.empty()) {
std::string metric = "emsesp_device_entities"; result += "{";
if (seen_metrics.find(metric) == seen_metrics.end()) { bool first = true;
result += "# HELP emsesp_device_entities entities\n"; for (const auto & label : device_labels) {
result += "# TYPE emsesp_device_entities gauge\n"; if (!first) {
seen_metrics[metric] = true; result += ", ";
}
result += metric;
if (!device_labels.empty()) {
result += "{";
bool first = true;
for (const auto & label : device_labels) {
if (!first) {
result += ", ";
}
result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
} }
result += "}"; result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
} }
result += " " + std::to_string(device["entities"].as<int>()) + "\n"; result += "}";
} }
result += " " + std::to_string(device["productID"].as<int>()) + "\n";
}
// create entities metric
if (device["entities"].is<int>()) {
std::string metric = "emsesp_device_entities";
if (seen_metrics.find(metric) == seen_metrics.end()) {
result += "# HELP emsesp_device_entities entities\n";
result += "# TYPE emsesp_device_entities gauge\n";
seen_metrics[metric] = true;
}
result += metric;
if (!device_labels.empty()) {
result += "{";
bool first = true;
for (const auto & label : device_labels) {
if (!first) {
result += ", ";
}
result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
}
result += "}";
}
result += " " + std::to_string(device["entities"].as<int>()) + "\n";
} }
} }
} else { }
// handle primitive values } else {
bool is_number = p.value().is<int>() || p.value().is<float>(); // handle primitive values
bool is_bool = p.value().is<bool>(); bool is_number = p.value().is<int>() || p.value().is<float>();
bool is_string = p.value().is<const char *>(); bool is_bool = p.value().is<bool>();
bool is_string = p.value().is<const char *>();
if (is_number || is_bool) { if (is_number || is_bool) {
// add metric // add metric
std::string full_metric_name = "emsesp_" + sanitize_name(metric_name); std::string full_metric_name = "emsesp_" + sanitize_name(metric_name);
if (seen_metrics.find(full_metric_name) == seen_metrics.end()) { if (seen_metrics.find(full_metric_name) == seen_metrics.end()) {
result += "# HELP emsesp_" + sanitize_name(metric_name) + " " + key + "\n"; result += "# HELP emsesp_" + sanitize_name(metric_name) + " " + key + "\n";
result += "# TYPE emsesp_" + sanitize_name(metric_name) + " gauge\n"; result += "# TYPE emsesp_" + sanitize_name(metric_name) + " gauge\n";
seen_metrics[full_metric_name] = true; seen_metrics[full_metric_name] = true;
} }
result += full_metric_name + " "; result += full_metric_name + " ";
if (is_bool) { if (is_bool) {
result += p.value().as<bool>() ? "1" : "0"; result += p.value().as<bool>() ? "1" : "0";
} else if (p.value().is<int>()) { } else if (p.value().is<int>()) {
result += std::to_string(p.value().as<int>()); result += std::to_string(p.value().as<int>());
} else { } else {
char val_str[30]; char val_str[30];
snprintf(val_str, sizeof(val_str), "%.2f", p.value().as<float>()); snprintf(val_str, sizeof(val_str), "%.2f", p.value().as<float>());
result += val_str; result += val_str;
} }
result += "\n"; result += "\n";
} else if (is_string) { } else if (is_string) {
// collect string for info metric (skip dynamic strings like uptime and timestamp) // collect string for info metric (skip dynamic strings like uptime and timestamp)
std::string val = p.value().as<const char *>(); std::string val = p.value().as<const char *>();
if (!val.empty() && key != "uptime" && key != "timestamp") { if (!val.empty() && key != "uptime" && key != "timestamp") {
local_info_labels.push_back({to_lowercase(key), val}); local_info_labels.push_back({to_lowercase(key), val});
}
} }
} }
} }
}
// create _info metric for this object level if we have labels and this is a leaf node (no nested objects) // create _info metric for this object level if we have labels and this is a leaf node (no nested objects)
if (!local_info_labels.empty() && !prefix.empty() && !has_nested_objects) { if (!local_info_labels.empty() && !prefix.empty() && !has_nested_objects) {
std::string info_metric = "emsesp_" + sanitize_name(prefix) + "_info"; std::string info_metric = "emsesp_" + sanitize_name(prefix) + "_info";
if (seen_metrics.find(info_metric) == seen_metrics.end()) { if (seen_metrics.find(info_metric) == seen_metrics.end()) {
result += "# HELP " + info_metric + " info\n"; result += "# HELP " + info_metric + " info\n";
result += "# TYPE " + info_metric + " gauge\n"; result += "# TYPE " + info_metric + " gauge\n";
seen_metrics[info_metric] = true; seen_metrics[info_metric] = true;
}
result += info_metric;
if (!local_info_labels.empty()) {
result += "{";
bool first = true;
for (const auto & label : local_info_labels) {
if (!first) {
result += ", ";
}
result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
}
result += "}";
}
result += " 1\n";
} }
};
result += info_metric;
if (!local_info_labels.empty()) {
result += "{";
bool first = true;
for (const auto & label : local_info_labels) {
if (!first) {
result += ", ";
}
result += label.first + "=\"" + escape_label(label.second) + "\"";
first = false;
}
result += "}";
}
result += " 1\n";
}
};
// process root object // process root object
process_object(root, ""); process_object(root, "");

View File

@@ -336,7 +336,7 @@ bool TemperatureSensor::update(const char * id, const char * name, int16_t offse
sensor.set_is_system(is_system); sensor.set_is_system(is_system);
// store the new name and offset in our configuration // store the new name and offset in our configuration
EMSESP::webCustomizationService.update([&id, &name, &offset, &sensor, &hide, &is_system](WebCustomization & settings) { EMSESP::webCustomizationService.update([&id, &name, &offset, &sensor, &is_system](WebCustomization & settings) {
// look it up to see if it exists // look it up to see if it exists
bool found = false; bool found = false;
for (auto & SensorCustomization : settings.sensorCustomizations) { for (auto & SensorCustomization : settings.sensorCustomizations) {
@@ -544,16 +544,11 @@ void TemperatureSensor::publish_values(const bool force) {
config["name"] = sensor.name(); config["name"] = sensor.name();
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors // see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
bool is_ha_device_created = false; if (std::none_of(sensors_.begin(), sensors_.end(), [](const auto & sensor) { return sensor.ha_registered; })) {
for (const auto & sensor : sensors_) { Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Temperature Sensors", nullptr, nullptr, nullptr, false);
if (sensor.ha_registered) {
is_ha_device_created = true;
break;
}
} }
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Temperature Sensors", nullptr, nullptr, nullptr, false); Mqtt::add_ha_avty_section(config.as<JsonObject>(), stat_t, val_cond);
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !is_ha_device_created, val_cond);
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id()); snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id());

View File

@@ -464,8 +464,11 @@ void WebCustomEntityService::publish(const bool force) {
config["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s; config["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s;
Mqtt::add_ha_classes(config.as<JsonObject>(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom); Mqtt::add_ha_classes(config.as<JsonObject>(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom);
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Custom Entities", nullptr, nullptr, nullptr, false);
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !ha_created, val_cond); if (!ha_created) {
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Custom Entities", nullptr, nullptr, nullptr, false);
}
Mqtt::add_ha_avty_section(config.as<JsonObject>(), stat_t, val_cond);
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>()); ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
} }
@@ -700,13 +703,13 @@ void WebCustomEntityService::load_test_data() {
auto entityItem = CustomEntityItem(); auto entityItem = CustomEntityItem();
// test 1 // test 1
entityItem.id = 1; entityItem.id = 1;
entityItem.ram = 0; entityItem.ram = 0;
entityItem.device_id = 8; entityItem.device_id = 8;
entityItem.type_id = 24; entityItem.type_id = 24;
entityItem.offset = 0; entityItem.offset = 0;
entityItem.factor = 1; entityItem.factor = 1;
strcpy(entityItem.name,"test_custom"); strcpy(entityItem.name, "test_custom");
entityItem.uom = 1; entityItem.uom = 1;
entityItem.value_type = 1; entityItem.value_type = 1;
entityItem.writeable = true; entityItem.writeable = true;
@@ -723,12 +726,12 @@ void WebCustomEntityService::load_test_data() {
CommandFlag::ADMIN_ONLY); CommandFlag::ADMIN_ONLY);
// test 2 // test 2
entityItem.id = 2; entityItem.id = 2;
entityItem.ram = 0; entityItem.ram = 0;
entityItem.device_id = 24; entityItem.device_id = 24;
entityItem.type_id = 677; entityItem.type_id = 677;
entityItem.offset = 3; entityItem.offset = 3;
entityItem.factor = 1; entityItem.factor = 1;
strcpy(entityItem.name, "test_read_only"); strcpy(entityItem.name, "test_read_only");
entityItem.uom = 0; entityItem.uom = 0;
entityItem.value_type = 2; entityItem.value_type = 2;
@@ -737,12 +740,12 @@ void WebCustomEntityService::load_test_data() {
webCustomEntity.customEntityItems.push_back(entityItem); webCustomEntity.customEntityItems.push_back(entityItem);
// test 3 // test 3
entityItem.id = 3; entityItem.id = 3;
entityItem.ram = 1; entityItem.ram = 1;
entityItem.device_id = 0; entityItem.device_id = 0;
entityItem.type_id = 0; entityItem.type_id = 0;
entityItem.offset = 0; entityItem.offset = 0;
entityItem.factor = 1; entityItem.factor = 1;
strcpy(entityItem.name, "test_ram"); strcpy(entityItem.name, "test_ram");
entityItem.uom = 0; entityItem.uom = 0;
entityItem.value_type = 8; entityItem.value_type = 8;
@@ -759,12 +762,12 @@ void WebCustomEntityService::load_test_data() {
CommandFlag::ADMIN_ONLY); CommandFlag::ADMIN_ONLY);
// test 4 // test 4
entityItem.id = 4; entityItem.id = 4;
entityItem.ram = 1; entityItem.ram = 1;
entityItem.device_id = 0; entityItem.device_id = 0;
entityItem.type_id = 0; entityItem.type_id = 0;
entityItem.offset = 0; entityItem.offset = 0;
entityItem.factor = 1; entityItem.factor = 1;
strcpy(entityItem.name, "test_seltemp"); strcpy(entityItem.name, "test_seltemp");
entityItem.uom = 0; entityItem.uom = 0;
entityItem.value_type = 8; entityItem.value_type = 8;

View File

@@ -275,8 +275,10 @@ void WebSchedulerService::publish(const bool force) {
config["cmd_t"] = command_topic; config["cmd_t"] = command_topic;
Mqtt::add_ha_bool(config.as<JsonObject>()); Mqtt::add_ha_bool(config.as<JsonObject>());
Mqtt::add_ha_dev_section(config.as<JsonObject>(), F_(scheduler), nullptr, nullptr, nullptr, false); if (!ha_created) {
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !ha_created, val_cond); Mqtt::add_ha_dev_section(config.as<JsonObject>(), F_(scheduler), nullptr, nullptr, nullptr, false);
}
Mqtt::add_ha_avty_section(config.as<JsonObject>(), stat_t, val_cond);
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>()); ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
} }

View File

@@ -109,7 +109,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
bool ha_registered_ = false; bool ha_registered_ = false;
std::list<ScheduleItem, AllocatorPSRAM<ScheduleItem>> * scheduleItems_; // pointer to the list of schedule events std::list<ScheduleItem, AllocatorPSRAM<ScheduleItem>> * scheduleItems_; // pointer to the list of schedule events
std::list<ScheduleItem *, AllocatorPSRAM<ScheduleItem *>> cmd_changed_; // pointer to commands in list that are triggert by change std::list<ScheduleItem *, AllocatorPSRAM<ScheduleItem *>> cmd_changed_; // pointer to commands in list that are triggered by change
}; };
} // namespace emsesp } // namespace emsesp