mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
1
.gitignore
vendored
1
.gitignore
vendored
@@ -74,3 +74,4 @@ sdkconfig.*
|
|||||||
sdkconfig_tasmota_esp32
|
sdkconfig_tasmota_esp32
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
package.json
|
package.json
|
||||||
|
.cache/
|
||||||
@@ -67,3 +67,4 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
|||||||
- place system message command in side scheduler loop to reduce stack memory usage by 2KB
|
- place system message command in side scheduler loop to reduce stack memory usage by 2KB
|
||||||
- syslog mark interval set to 1 hour
|
- syslog mark interval set to 1 hour
|
||||||
- handle process_telegram in oneloop
|
- handle process_telegram in oneloop
|
||||||
|
- improved GPIO validation in Analog Sensors and System settings
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
||||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
|
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
|
||||||
"typesafe-i18n": "typesafe-i18n --no-watch",
|
"typesafe-i18n": "typesafe-i18n --no-watch",
|
||||||
"webUI": "vite build && node progmem-generator.js",
|
"build_webUI": "typesafe-i18n --no-watch && vite build && node progmem-generator.js",
|
||||||
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
|
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"@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": "^24.10.1",
|
||||||
"@types/react": "^19.2.4",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"axe-core": "^4.11.0",
|
"axe-core": "^4.11.0",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
|
|||||||
130
interface/pnpm-lock.yaml
generated
130
interface/pnpm-lock.yaml
generated
@@ -13,22 +13,22 @@ importers:
|
|||||||
version: 2.2.1(alova@3.3.4)
|
version: 2.2.1(alova@3.3.4)
|
||||||
'@emotion/react':
|
'@emotion/react':
|
||||||
specifier: ^11.14.0
|
specifier: ^11.14.0
|
||||||
version: 11.14.0(@types/react@19.2.4)(react@19.2.0)
|
version: 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@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.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@mui/icons-material':
|
'@mui/icons-material':
|
||||||
specifier: ^7.3.5
|
specifier: ^7.3.5
|
||||||
version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@mui/material':
|
'@mui/material':
|
||||||
specifier: ^7.3.5
|
specifier: ^7.3.5
|
||||||
version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
'@preact/compat':
|
'@preact/compat':
|
||||||
specifier: ^18.3.1
|
specifier: ^18.3.1
|
||||||
version: 18.3.1(preact@10.27.2)
|
version: 18.3.1(preact@10.27.2)
|
||||||
'@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.4)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
alova:
|
alova:
|
||||||
specifier: 3.3.4
|
specifier: 3.3.4
|
||||||
version: 3.3.4
|
version: 3.3.4
|
||||||
@@ -91,11 +91,11 @@ importers:
|
|||||||
specifier: ^24.10.1
|
specifier: ^24.10.1
|
||||||
version: 24.10.1
|
version: 24.10.1
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^19.2.4
|
specifier: ^19.2.5
|
||||||
version: 19.2.4
|
version: 19.2.5
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^19.2.3
|
specifier: ^19.2.3
|
||||||
version: 19.2.3(@types/react@19.2.4)
|
version: 19.2.3(@types/react@19.2.5)
|
||||||
axe-core:
|
axe-core:
|
||||||
specifier: ^4.11.0
|
specifier: ^4.11.0
|
||||||
version: 4.11.0
|
version: 4.11.0
|
||||||
@@ -879,8 +879,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': '*'
|
'@types/react': '*'
|
||||||
|
|
||||||
'@types/react@19.2.4':
|
'@types/react@19.2.5':
|
||||||
resolution: {integrity: sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==}
|
resolution: {integrity: sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==}
|
||||||
|
|
||||||
'@types/responselike@1.0.3':
|
'@types/responselike@1.0.3':
|
||||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||||
@@ -1225,8 +1225,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==}
|
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
csstype@3.1.3:
|
csstype@3.2.1:
|
||||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
resolution: {integrity: sha512-98XGutrXoh75MlgLihlNxAGbUuFQc7l1cqcnEZlLNKc0UrVdPndgmaDmYTDDh929VS/eqTZV0rozmhu2qqT1/g==}
|
||||||
|
|
||||||
currently-unhandled@0.4.1:
|
currently-unhandled@0.4.1:
|
||||||
resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==}
|
resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==}
|
||||||
@@ -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.250:
|
electron-to-chromium@1.5.253:
|
||||||
resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==}
|
resolution: {integrity: sha512-O0tpQ/35rrgdiGQ0/OFWhy1itmd9A6TY9uQzlqj3hKSu/aYpe7UIn5d7CU2N9myH6biZiWF3VMZVuup8pw5U9w==}
|
||||||
|
|
||||||
emoji-regex@8.0.0:
|
emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
@@ -3268,7 +3268,7 @@ snapshots:
|
|||||||
|
|
||||||
'@emotion/memoize@0.9.0': {}
|
'@emotion/memoize@0.9.0': {}
|
||||||
|
|
||||||
'@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0)':
|
'@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@emotion/babel-plugin': 11.13.5
|
'@emotion/babel-plugin': 11.13.5
|
||||||
@@ -3280,7 +3280,7 @@ snapshots:
|
|||||||
hoist-non-react-statics: 3.3.2
|
hoist-non-react-statics: 3.3.2
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -3290,22 +3290,22 @@ snapshots:
|
|||||||
'@emotion/memoize': 0.9.0
|
'@emotion/memoize': 0.9.0
|
||||||
'@emotion/unitless': 0.10.0
|
'@emotion/unitless': 0.10.0
|
||||||
'@emotion/utils': 1.4.2
|
'@emotion/utils': 1.4.2
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
|
|
||||||
'@emotion/sheet@1.4.0': {}
|
'@emotion/sheet@1.4.0': {}
|
||||||
|
|
||||||
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)':
|
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)':
|
||||||
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.4)(react@19.2.0)
|
'@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@emotion/serialize': 1.3.3
|
'@emotion/serialize': 1.3.3
|
||||||
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0)
|
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0)
|
||||||
'@emotion/utils': 1.4.2
|
'@emotion/utils': 1.4.2
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -3489,90 +3489,90 @@ snapshots:
|
|||||||
|
|
||||||
'@mui/core-downloads-tracker@7.3.5': {}
|
'@mui/core-downloads-tracker@7.3.5': {}
|
||||||
|
|
||||||
'@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)':
|
'@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
'@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@mui/core-downloads-tracker': 7.3.5
|
'@mui/core-downloads-tracker': 7.3.5
|
||||||
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@mui/types': 7.4.8(@types/react@19.2.4)
|
'@mui/types': 7.4.8(@types/react@19.2.5)
|
||||||
'@mui/utils': 7.3.5(@types/react@19.2.4)(react@19.2.0)
|
'@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@popperjs/core': 2.11.8
|
'@popperjs/core': 2.11.8
|
||||||
'@types/react-transition-group': 4.4.12(@types/react@19.2.4)
|
'@types/react-transition-group': 4.4.12(@types/react@19.2.5)
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
react-dom: 19.2.0(react@19.2.0)
|
react-dom: 19.2.0(react@19.2.0)
|
||||||
react-is: 19.2.0
|
react-is: 19.2.0
|
||||||
react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@mui/private-theming@7.3.5(@types/react@19.2.4)(react@19.2.0)':
|
'@mui/private-theming@7.3.5(@types/react@19.2.5)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@mui/utils': 7.3.5(@types/react@19.2.4)(react@19.2.0)
|
'@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0)
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(react@19.2.0)':
|
'@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@emotion/cache': 11.14.0
|
'@emotion/cache': 11.14.0
|
||||||
'@emotion/serialize': 1.3.3
|
'@emotion/serialize': 1.3.3
|
||||||
'@emotion/sheet': 1.4.0
|
'@emotion/sheet': 1.4.0
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
|
|
||||||
'@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)':
|
'@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@mui/private-theming': 7.3.5(@types/react@19.2.4)(react@19.2.0)
|
'@mui/private-theming': 7.3.5(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(react@19.2.0)
|
'@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(react@19.2.0)
|
||||||
'@mui/types': 7.4.8(@types/react@19.2.4)
|
'@mui/types': 7.4.8(@types/react@19.2.5)
|
||||||
'@mui/utils': 7.3.5(@types/react@19.2.4)(react@19.2.0)
|
'@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0)
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@mui/types@7.4.8(@types/react@19.2.4)':
|
'@mui/types@7.4.8(@types/react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@mui/utils@7.3.5(@types/react@19.2.4)(react@19.2.0)':
|
'@mui/utils@7.3.5(@types/react@19.2.5)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
'@mui/types': 7.4.8(@types/react@19.2.4)
|
'@mui/types': 7.4.8(@types/react@19.2.5)
|
||||||
'@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.0
|
react: 19.2.0
|
||||||
react-is: 19.2.0
|
react-is: 19.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@noble/hashes@1.8.0': {}
|
'@noble/hashes@1.8.0': {}
|
||||||
|
|
||||||
@@ -3707,9 +3707,9 @@ 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.4)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
'@table-library/react-table-library@4.1.15(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0)
|
'@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0)
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
react-dom: 19.2.0(react@19.2.0)
|
react-dom: 19.2.0(react@19.2.0)
|
||||||
@@ -3786,17 +3786,17 @@ snapshots:
|
|||||||
|
|
||||||
'@types/prop-types@15.7.15': {}
|
'@types/prop-types@15.7.15': {}
|
||||||
|
|
||||||
'@types/react-dom@19.2.3(@types/react@19.2.4)':
|
'@types/react-dom@19.2.3(@types/react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@types/react-transition-group@4.4.12(@types/react@19.2.4)':
|
'@types/react-transition-group@4.4.12(@types/react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.2.4
|
'@types/react': 19.2.5
|
||||||
|
|
||||||
'@types/react@19.2.4':
|
'@types/react@19.2.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
|
|
||||||
'@types/responselike@1.0.3':
|
'@types/responselike@1.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4022,7 +4022,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
baseline-browser-mapping: 2.8.28
|
baseline-browser-mapping: 2.8.28
|
||||||
caniuse-lite: 1.0.30001754
|
caniuse-lite: 1.0.30001754
|
||||||
electron-to-chromium: 1.5.250
|
electron-to-chromium: 1.5.253
|
||||||
node-releases: 2.0.27
|
node-releases: 2.0.27
|
||||||
update-browserslist-db: 1.1.4(browserslist@4.28.0)
|
update-browserslist-db: 1.1.4(browserslist@4.28.0)
|
||||||
|
|
||||||
@@ -4211,7 +4211,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
css-tree: 1.1.3
|
css-tree: 1.1.3
|
||||||
|
|
||||||
csstype@3.1.3: {}
|
csstype@3.2.1: {}
|
||||||
|
|
||||||
currently-unhandled@0.4.1:
|
currently-unhandled@0.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4294,7 +4294,7 @@ snapshots:
|
|||||||
dom-helpers@5.2.1:
|
dom-helpers@5.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.4
|
'@babel/runtime': 7.28.4
|
||||||
csstype: 3.1.3
|
csstype: 3.2.1
|
||||||
|
|
||||||
dom-serializer@1.4.1:
|
dom-serializer@1.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4367,7 +4367,7 @@ snapshots:
|
|||||||
|
|
||||||
duplexer3@0.1.5: {}
|
duplexer3@0.1.5: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.250: {}
|
electron-to-chromium@1.5.253: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const MS_PER_SECOND = 1000;
|
|||||||
const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
||||||
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
||||||
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
||||||
const DEFAULT_GPIO = 21; // Safe GPIO for all platforms
|
const DEFAULT_GPIO = 99; // not set
|
||||||
const MIN_TEMP_ID = -100;
|
const MIN_TEMP_ID = -100;
|
||||||
const MAX_TEMP_ID = 100;
|
const MAX_TEMP_ID = 100;
|
||||||
const GPIO_25 = 25;
|
const GPIO_25 = 25;
|
||||||
@@ -134,6 +134,7 @@ const Sensors = () => {
|
|||||||
ts: [],
|
ts: [],
|
||||||
as: [],
|
as: [],
|
||||||
analog_enabled: false,
|
analog_enabled: false,
|
||||||
|
valid_gpio_list: [],
|
||||||
platform: 'ESP32'
|
platform: 'ESP32'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -573,12 +574,8 @@ const Sensors = () => {
|
|||||||
onSave={onAnalogDialogSave}
|
onSave={onAnalogDialogSave}
|
||||||
creating={creating}
|
creating={creating}
|
||||||
selectedItem={selectedAnalogSensor}
|
selectedItem={selectedAnalogSensor}
|
||||||
validator={analogSensorItemValidation(
|
analogGPIOList={sensorData.valid_gpio_list}
|
||||||
sensorData.as,
|
validator={analogSensorItemValidation(sensorData.as, selectedAnalogSensor)}
|
||||||
selectedAnalogSensor,
|
|
||||||
creating,
|
|
||||||
sensorData.platform
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sensorData?.analog_enabled === true && me.admin && (
|
{sensorData?.analog_enabled === true && me.admin && (
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ interface DashboardSensorsAnalogDialogProps {
|
|||||||
onSave: (as: AnalogSensor) => void;
|
onSave: (as: AnalogSensor) => void;
|
||||||
creating: boolean;
|
creating: boolean;
|
||||||
selectedItem: AnalogSensor;
|
selectedItem: AnalogSensor;
|
||||||
|
analogGPIOList: number[];
|
||||||
validator: Schema;
|
validator: Schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ const SensorsAnalogDialog = ({
|
|||||||
onSave,
|
onSave,
|
||||||
creating,
|
creating,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
|
analogGPIOList,
|
||||||
validator
|
validator
|
||||||
}: DashboardSensorsAnalogDialogProps) => {
|
}: DashboardSensorsAnalogDialogProps) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -151,33 +153,51 @@ const SensorsAnalogDialog = ({
|
|||||||
[creating, LL]
|
[creating, LL]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Ensure the current GPIO is in the list when no creating
|
||||||
|
// note GPIO 99 means not set
|
||||||
|
const availableGPIOs = useMemo(() => {
|
||||||
|
const filteredList = analogGPIOList.filter((gpio) => gpio !== 99);
|
||||||
|
if (
|
||||||
|
editItem.g !== undefined &&
|
||||||
|
editItem.g !== 99 &&
|
||||||
|
!filteredList.includes(editItem.g)
|
||||||
|
) {
|
||||||
|
return [...filteredList, editItem.g].sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
return filteredList;
|
||||||
|
}, [analogGPIOList, editItem.g]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid>
|
{editItem.s ? (
|
||||||
<ValidatedTextField
|
<TextField
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="g"
|
name="g"
|
||||||
label="GPIO"
|
label="GPIO"
|
||||||
sx={{ width: '11ch' }}
|
value={editItem.g}
|
||||||
value={numberValue(editItem.g)}
|
sx={{ width: '8ch' }}
|
||||||
type="number"
|
disabled={true}
|
||||||
variant="outlined"
|
></TextField>
|
||||||
|
) : (
|
||||||
|
<ValidatedTextField
|
||||||
|
name="g"
|
||||||
|
label="GPIO"
|
||||||
|
value={editItem.g}
|
||||||
|
sx={{ width: '8ch' }}
|
||||||
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
/>
|
>
|
||||||
</Grid>
|
{availableGPIOs?.map((gpio: number) => (
|
||||||
{creating && (
|
<MenuItem key={gpio} value={gpio}>
|
||||||
<Grid>
|
{gpio}
|
||||||
<Box color="warning.main" mt={2}>
|
</MenuItem>
|
||||||
<Typography variant="body2">{LL.WARN_GPIO()}</Typography>
|
))}
|
||||||
</Box>
|
</ValidatedTextField>
|
||||||
</Grid>
|
|
||||||
)}
|
)}
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
|
||||||
name="n"
|
name="n"
|
||||||
label={LL.NAME(0)}
|
label={LL.NAME(0)}
|
||||||
value={editItem.n}
|
value={editItem.n}
|
||||||
@@ -194,6 +214,7 @@ const SensorsAnalogDialog = ({
|
|||||||
fullWidth
|
fullWidth
|
||||||
select
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
{analogTypeMenuItems}
|
{analogTypeMenuItems}
|
||||||
</TextField>
|
</TextField>
|
||||||
@@ -207,6 +228,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '15ch' }}
|
sx={{ width: '15ch' }}
|
||||||
select
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
{uomMenuItems}
|
{uomMenuItems}
|
||||||
</TextField>
|
</TextField>
|
||||||
@@ -222,6 +244,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -243,6 +266,7 @@ const SensorsAnalogDialog = ({
|
|||||||
type="number"
|
type="number"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -264,6 +288,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
htmlInput: { step: '0.001' }
|
htmlInput: { step: '0.001' }
|
||||||
}}
|
}}
|
||||||
@@ -280,6 +305,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
@@ -293,6 +319,7 @@ const SensorsAnalogDialog = ({
|
|||||||
type="number"
|
type="number"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
htmlInput: { step: '0.001' }
|
htmlInput: { step: '0.001' }
|
||||||
}}
|
}}
|
||||||
@@ -309,6 +336,7 @@ const SensorsAnalogDialog = ({
|
|||||||
type="number"
|
type="number"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
htmlInput: { min: '0', max: '255', step: '1' }
|
htmlInput: { min: '0', max: '255', step: '1' }
|
||||||
}}
|
}}
|
||||||
@@ -325,6 +353,7 @@ const SensorsAnalogDialog = ({
|
|||||||
select
|
select
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
<MenuItem value={0}>{LL.OFF()}</MenuItem>
|
<MenuItem value={0}>{LL.OFF()}</MenuItem>
|
||||||
<MenuItem value={1}>{LL.ON()}</MenuItem>
|
<MenuItem value={1}>{LL.ON()}</MenuItem>
|
||||||
@@ -338,6 +367,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '15ch' }}
|
sx={{ width: '15ch' }}
|
||||||
select
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
<MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
|
<MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
|
||||||
<MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem>
|
<MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem>
|
||||||
@@ -351,6 +381,7 @@ const SensorsAnalogDialog = ({
|
|||||||
value={editItem.u}
|
value={editItem.u}
|
||||||
select
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
<MenuItem value={0}>{LL.UNCHANGED()}</MenuItem>
|
<MenuItem value={0}>{LL.UNCHANGED()}</MenuItem>
|
||||||
<MenuItem value={1}>
|
<MenuItem value={1}>
|
||||||
@@ -374,6 +405,7 @@ const SensorsAnalogDialog = ({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -393,6 +425,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -415,6 +448,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
select
|
select
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
>
|
>
|
||||||
<MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
|
<MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
|
||||||
<MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
|
<MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
|
||||||
@@ -429,6 +463,7 @@ const SensorsAnalogDialog = ({
|
|||||||
sx={{ width: '15ch' }}
|
sx={{ width: '15ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
disabled={editItem.s}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -442,6 +477,24 @@ const SensorsAnalogDialog = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
|
||||||
|
<Box mt={1}>
|
||||||
|
{Object.values(fieldErrors).map((errArr, idx) =>
|
||||||
|
Array.isArray(errArr)
|
||||||
|
? errArr.map((err, j) => (
|
||||||
|
<Typography
|
||||||
|
key={`${idx}-${j}`}
|
||||||
|
color="error"
|
||||||
|
variant="caption"
|
||||||
|
display="block"
|
||||||
|
>
|
||||||
|
{err.message}
|
||||||
|
</Typography>
|
||||||
|
))
|
||||||
|
: null
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{editItem.s && (
|
{editItem.s && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography mt={1} color="warning.main" variant="body2">
|
<Typography mt={1} color="warning.main" variant="body2">
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export interface SensorData {
|
|||||||
ts: TemperatureSensor[];
|
ts: TemperatureSensor[];
|
||||||
as: AnalogSensor[];
|
as: AnalogSensor[];
|
||||||
analog_enabled: boolean;
|
analog_enabled: boolean;
|
||||||
|
valid_gpio_list: number[];
|
||||||
platform: string;
|
platform: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,121 +45,15 @@ const VALIDATION_LIMITS = {
|
|||||||
HEX_BASE: 16
|
HEX_BASE: 16
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// Helper to create GPIO validator from invalid ranges
|
|
||||||
const createGPIOValidator = (
|
|
||||||
invalidRanges: Array<number | [number, number]>,
|
|
||||||
maxValue: number
|
|
||||||
) => ({
|
|
||||||
validator(
|
|
||||||
_rule: InternalRuleItem,
|
|
||||||
value: number,
|
|
||||||
callback: (error?: string) => void
|
|
||||||
) {
|
|
||||||
if (!value) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value < 0 || value > maxValue) {
|
|
||||||
callback(ERROR_MESSAGES.GPIO_INVALID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const range of invalidRanges) {
|
|
||||||
if (typeof range === 'number') {
|
|
||||||
if (value === range) {
|
|
||||||
callback(ERROR_MESSAGES.GPIO_INVALID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const [start, end] = range;
|
|
||||||
if (value >= start && value <= end) {
|
|
||||||
callback(ERROR_MESSAGES.GPIO_INVALID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GPIO_VALIDATOR = createGPIOValidator(
|
|
||||||
[[6, 11], 1, 20, 24, [28, 31]],
|
|
||||||
40
|
|
||||||
);
|
|
||||||
|
|
||||||
export const GPIO_VALIDATORC3 = createGPIOValidator([[11, 19]], 21);
|
|
||||||
|
|
||||||
export const GPIO_VALIDATORS2 = createGPIOValidator(
|
|
||||||
[
|
|
||||||
[19, 20],
|
|
||||||
[22, 32]
|
|
||||||
],
|
|
||||||
40
|
|
||||||
);
|
|
||||||
|
|
||||||
export const GPIO_VALIDATORS3 = createGPIOValidator(
|
|
||||||
[
|
|
||||||
[19, 20],
|
|
||||||
[22, 37],
|
|
||||||
[39, 42]
|
|
||||||
],
|
|
||||||
48
|
|
||||||
);
|
|
||||||
|
|
||||||
const GPIO_FIELD_NAMES = [
|
|
||||||
'led_gpio',
|
|
||||||
'dallas_gpio',
|
|
||||||
'pbutton_gpio',
|
|
||||||
'tx_gpio',
|
|
||||||
'rx_gpio'
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
type ValidationRules = Array<{
|
type ValidationRules = Array<{
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const createGPIOValidations = (
|
|
||||||
validator: typeof GPIO_VALIDATOR
|
|
||||||
): Record<string, ValidationRules> =>
|
|
||||||
GPIO_FIELD_NAMES.reduce(
|
|
||||||
(acc, field) => {
|
|
||||||
const fieldName = field.replace('_gpio', '').toUpperCase();
|
|
||||||
acc[field] = [
|
|
||||||
{ required: true, message: `${fieldName} GPIO is required` },
|
|
||||||
validator
|
|
||||||
];
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, ValidationRules>
|
|
||||||
);
|
|
||||||
|
|
||||||
const PLATFORM_VALIDATORS = {
|
|
||||||
ESP32: GPIO_VALIDATOR,
|
|
||||||
ESP32C3: GPIO_VALIDATORC3,
|
|
||||||
ESP32S2: GPIO_VALIDATORS2,
|
|
||||||
ESP32S3: GPIO_VALIDATORS3
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const createSettingsValidator = (settings: Settings) => {
|
export const createSettingsValidator = (settings: Settings) => {
|
||||||
const schema: Record<string, ValidationRules> = {};
|
const schema: Record<string, ValidationRules> = {};
|
||||||
|
|
||||||
// Add GPIO validations for CUSTOM board profiles
|
|
||||||
if (
|
|
||||||
settings.board_profile === 'CUSTOM' &&
|
|
||||||
settings.platform in PLATFORM_VALIDATORS
|
|
||||||
) {
|
|
||||||
Object.assign(
|
|
||||||
schema,
|
|
||||||
createGPIOValidations(
|
|
||||||
PLATFORM_VALIDATORS[settings.platform as keyof typeof PLATFORM_VALIDATORS]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Syslog validations
|
// Syslog validations
|
||||||
if (settings.syslog_enabled) {
|
if (settings.syslog_enabled) {
|
||||||
schema.syslog_host = [
|
schema.syslog_host = [
|
||||||
@@ -401,52 +295,29 @@ export const temperatureSensorItemValidation = (
|
|||||||
n: [NAME_PATTERN, uniqueTemperatureNameValidator(sensors, sensor.o_n)]
|
n: [NAME_PATTERN, uniqueTemperatureNameValidator(sensors, sensor.o_n)]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({
|
|
||||||
validator(
|
|
||||||
_rule: InternalRuleItem,
|
|
||||||
gpio: number,
|
|
||||||
callback: (error?: string) => void
|
|
||||||
) {
|
|
||||||
if (sensors.some((as) => as.g === gpio)) {
|
|
||||||
callback(ERROR_MESSAGES.GPIO_DUPLICATE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const uniqueAnalogNameValidator = (
|
export const uniqueAnalogNameValidator = (
|
||||||
sensors: AnalogSensor[],
|
sensors: AnalogSensor[],
|
||||||
o_name?: string
|
o_name?: string
|
||||||
) => createUniqueFieldNameValidator(sensors, (s) => s.n, o_name);
|
) => createUniqueFieldNameValidator(sensors, (s) => s.n, o_name);
|
||||||
|
|
||||||
const getPlatformGPIOValidator = (platform: string) => {
|
|
||||||
switch (platform) {
|
|
||||||
case 'ESP32S3':
|
|
||||||
return GPIO_VALIDATORS3;
|
|
||||||
case 'ESP32S2':
|
|
||||||
return GPIO_VALIDATORS2;
|
|
||||||
case 'ESP32C3':
|
|
||||||
return GPIO_VALIDATORC3;
|
|
||||||
default:
|
|
||||||
return GPIO_VALIDATOR;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const analogSensorItemValidation = (
|
export const analogSensorItemValidation = (
|
||||||
sensors: AnalogSensor[],
|
sensors: AnalogSensor[],
|
||||||
sensor: AnalogSensor,
|
sensor: AnalogSensor
|
||||||
creating: boolean,
|
|
||||||
platform: string
|
|
||||||
) => {
|
) => {
|
||||||
const gpioValidator = getPlatformGPIOValidator(platform);
|
|
||||||
|
|
||||||
return new Schema({
|
return new Schema({
|
||||||
n: [NAME_PATTERN, uniqueAnalogNameValidator(sensors, sensor.o_n)],
|
// name is required and must be unique
|
||||||
|
n: [
|
||||||
|
{ required: true, message: 'Name is required' },
|
||||||
|
NAME_PATTERN,
|
||||||
|
uniqueAnalogNameValidator(sensors, sensor.o_n)
|
||||||
|
],
|
||||||
g: [
|
g: [
|
||||||
{ required: true, message: 'GPIO is required' },
|
{
|
||||||
gpioValidator,
|
required: true,
|
||||||
...(creating ? [isGPIOUniqueValidator(sensors)] : [])
|
type: 'number',
|
||||||
|
min: 1,
|
||||||
|
message: 'GPIO is required'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const cz: Translation = {
|
|||||||
DUTY_CYCLE: 'Pracovní cyklus',
|
DUTY_CYCLE: 'Pracovní cyklus',
|
||||||
UNIT: 'Jednotka',
|
UNIT: 'Jednotka',
|
||||||
STARTVALUE: 'Počáteční hodnota',
|
STARTVALUE: 'Počáteční hodnota',
|
||||||
WARN_GPIO: 'Upozornění: buďte opatrní při přiřazování GPIO!',
|
|
||||||
EDIT: 'Upravit',
|
EDIT: 'Upravit',
|
||||||
SENSOR: 'Senzor',
|
SENSOR: 'Senzor',
|
||||||
TEMP_SENSOR: 'Teplotní senzor',
|
TEMP_SENSOR: 'Teplotní senzor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const de: Translation = {
|
|||||||
DUTY_CYCLE: 'Arbeitszyklus',
|
DUTY_CYCLE: 'Arbeitszyklus',
|
||||||
UNIT: 'Maßeinheit',
|
UNIT: 'Maßeinheit',
|
||||||
STARTVALUE: 'Startwert',
|
STARTVALUE: 'Startwert',
|
||||||
WARN_GPIO: 'Warnung: Vorsicht bei der korrekten Wahl des GPIO!',
|
|
||||||
EDIT: 'Editiere',
|
EDIT: 'Editiere',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
TEMP_SENSOR: 'Temperatursensor',
|
TEMP_SENSOR: 'Temperatursensor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const en: Translation = {
|
|||||||
DUTY_CYCLE: 'Duty Cycle',
|
DUTY_CYCLE: 'Duty Cycle',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Start Value',
|
STARTVALUE: 'Start Value',
|
||||||
WARN_GPIO: 'Warning: be careful when assigning a GPIO!',
|
|
||||||
EDIT: 'Edit',
|
EDIT: 'Edit',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
TEMP_SENSOR: 'Temperature Sensor',
|
TEMP_SENSOR: 'Temperature Sensor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const fr: Translation = {
|
|||||||
DUTY_CYCLE: 'Cycle de fonctionnement',
|
DUTY_CYCLE: 'Cycle de fonctionnement',
|
||||||
UNIT: 'Unité',
|
UNIT: 'Unité',
|
||||||
STARTVALUE: 'Valeur de départ',
|
STARTVALUE: 'Valeur de départ',
|
||||||
WARN_GPIO: 'Attention: soyez vigilant en choisissant un GPIO!',
|
|
||||||
EDIT: 'Éditer',
|
EDIT: 'Éditer',
|
||||||
SENSOR: 'Capteur',
|
SENSOR: 'Capteur',
|
||||||
TEMP_SENSOR: 'Capteur de température',
|
TEMP_SENSOR: 'Capteur de température',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const it: Translation = {
|
|||||||
DUTY_CYCLE: 'Ciclo di lavoro',
|
DUTY_CYCLE: 'Ciclo di lavoro',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Valore di partenza',
|
STARTVALUE: 'Valore di partenza',
|
||||||
WARN_GPIO: 'Avvertimento: prestare attenzione quando si assegna un GPIO!',
|
|
||||||
EDIT: 'Modifica',
|
EDIT: 'Modifica',
|
||||||
SENSOR: 'Sensore',
|
SENSOR: 'Sensore',
|
||||||
TEMP_SENSOR: 'Sensore Temperatura',
|
TEMP_SENSOR: 'Sensore Temperatura',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const nl: Translation = {
|
|||||||
DUTY_CYCLE: 'Duty Cycle',
|
DUTY_CYCLE: 'Duty Cycle',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Startwaarde',
|
STARTVALUE: 'Startwaarde',
|
||||||
WARN_GPIO: 'Waarschuwing: let op met het koppelen van de juiste GPIO pin!',
|
|
||||||
EDIT: 'Wijzigen',
|
EDIT: 'Wijzigen',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
TEMP_SENSOR: 'Temperatuur sensor',
|
TEMP_SENSOR: 'Temperatuur sensor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const no: Translation = {
|
|||||||
DUTY_CYCLE: 'Duty Cycle',
|
DUTY_CYCLE: 'Duty Cycle',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Startverdi',
|
STARTVALUE: 'Startverdi',
|
||||||
WARN_GPIO: 'Advarsel: vær forsiktig ved aktivering av GPIO!',
|
|
||||||
EDIT: 'Endre',
|
EDIT: 'Endre',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
TEMP_SENSOR: 'Temperatursensor',
|
TEMP_SENSOR: 'Temperatursensor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const pl: BaseTranslation = {
|
|||||||
DUTY_CYCLE: 'Wypełnienie',
|
DUTY_CYCLE: 'Wypełnienie',
|
||||||
UNIT: 'J.m.',
|
UNIT: 'J.m.',
|
||||||
STARTVALUE: 'Wartość początkowa',
|
STARTVALUE: 'Wartość początkowa',
|
||||||
WARN_GPIO: 'Uwaga! Zachowaj ostrożność przypisując GPIO do urządzenia!',
|
|
||||||
EDIT: 'Edycja',
|
EDIT: 'Edycja',
|
||||||
SENSOR: '{{c|ustawienia c||ustawień c|}}zujnika',
|
SENSOR: '{{c|ustawienia c||ustawień c|}}zujnika',
|
||||||
TEMP_SENSOR: 'czujnika temperatury',
|
TEMP_SENSOR: 'czujnika temperatury',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const sk: Translation = {
|
|||||||
DUTY_CYCLE: 'Pracovný cyklus',
|
DUTY_CYCLE: 'Pracovný cyklus',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Počiatočná hodnota',
|
STARTVALUE: 'Počiatočná hodnota',
|
||||||
WARN_GPIO: 'Upozornenie: Buďte opatrní pri priraďovaní GPIO!',
|
|
||||||
EDIT: 'Editovať',
|
EDIT: 'Editovať',
|
||||||
SENSOR: 'Snímač',
|
SENSOR: 'Snímač',
|
||||||
TEMP_SENSOR: 'Snímač teploty',
|
TEMP_SENSOR: 'Snímač teploty',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const sv: Translation = {
|
|||||||
DUTY_CYCLE: 'Pulskvot',
|
DUTY_CYCLE: 'Pulskvot',
|
||||||
UNIT: 'Måttenhet',
|
UNIT: 'Måttenhet',
|
||||||
STARTVALUE: 'Startvärde',
|
STARTVALUE: 'Startvärde',
|
||||||
WARN_GPIO: 'Varning: Var försiktig vid aktivering av GPIO!',
|
|
||||||
EDIT: 'Ändra',
|
EDIT: 'Ändra',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
TEMP_SENSOR: 'Temperatursensor',
|
TEMP_SENSOR: 'Temperatursensor',
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ const tr: Translation = {
|
|||||||
DUTY_CYCLE: 'Görev Çevrimi',
|
DUTY_CYCLE: 'Görev Çevrimi',
|
||||||
UNIT: 'ÖB',
|
UNIT: 'ÖB',
|
||||||
STARTVALUE: 'Başlangıç değeri',
|
STARTVALUE: 'Başlangıç değeri',
|
||||||
WARN_GPIO: 'Uyarı: bir GPIO atarken dikkatli olun!',
|
|
||||||
EDIT: 'Değiştir',
|
EDIT: 'Değiştir',
|
||||||
SENSOR: 'Sensör',
|
SENSOR: 'Sensör',
|
||||||
TEMP_SENSOR: 'Sıcaklık Sensörü',
|
TEMP_SENSOR: 'Sıcaklık Sensörü',
|
||||||
|
|||||||
@@ -988,7 +988,7 @@ const emsesp_sensordata = {
|
|||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
g: 37,
|
g: 37,
|
||||||
n: 'External switch',
|
n: 'External_switch',
|
||||||
v: 13,
|
v: 13,
|
||||||
u: 0,
|
u: 0,
|
||||||
o: 17,
|
o: 17,
|
||||||
@@ -999,8 +999,8 @@ const emsesp_sensordata = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
g: 39,
|
g: 37,
|
||||||
n: 'Pulse count',
|
n: 'Pulse_count',
|
||||||
v: 144,
|
v: 144,
|
||||||
u: 0,
|
u: 0,
|
||||||
o: 0,
|
o: 0,
|
||||||
@@ -1011,7 +1011,7 @@ const emsesp_sensordata = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
g: 40,
|
g: 23,
|
||||||
n: 'Pressure',
|
n: 'Pressure',
|
||||||
v: 16,
|
v: 16,
|
||||||
u: 17,
|
u: 17,
|
||||||
@@ -1046,7 +1046,8 @@ const emsesp_sensordata = {
|
|||||||
s: true
|
s: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
analog_enabled: true
|
analog_enabled: true,
|
||||||
|
valid_gpio_list: [0, 2, 5, 12, 13, 15, 18, 19, 23, 25, 26, 27, 33, 37, 38]
|
||||||
};
|
};
|
||||||
|
|
||||||
const activity = {
|
const activity = {
|
||||||
|
|||||||
@@ -1444,3 +1444,4 @@ proplow
|
|||||||
chimneysweeper
|
chimneysweeper
|
||||||
pumpopt
|
pumpopt
|
||||||
intergral
|
intergral
|
||||||
|
vchip
|
||||||
@@ -74,9 +74,7 @@ def buildWeb():
|
|||||||
# Run pnpm commands in the interface directory
|
# Run pnpm commands in the interface directory
|
||||||
commands = [
|
commands = [
|
||||||
f"{pnpm_exe} install",
|
f"{pnpm_exe} install",
|
||||||
f"{pnpm_exe} typesafe-i18n",
|
f"{pnpm_exe} build_webUI"
|
||||||
f"{pnpm_exe} build",
|
|
||||||
f"{pnpm_exe} webUI"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for command in commands:
|
for command in commands:
|
||||||
@@ -93,7 +91,8 @@ def buildWeb():
|
|||||||
w.write(text)
|
w.write(text)
|
||||||
print("Setting WebUI locale to 'en'")
|
print("Setting WebUI locale to 'en'")
|
||||||
else:
|
else:
|
||||||
print(f"Warning: {i18n_file} not found, skipping locale modification")
|
print(
|
||||||
|
f"Warning: {i18n_file} not found, skipping locale modification")
|
||||||
|
|
||||||
print("Web interface build completed successfully!")
|
print("Web interface build completed successfully!")
|
||||||
return True
|
return True
|
||||||
@@ -110,6 +109,7 @@ def build_webUI(*args, **kwargs):
|
|||||||
env.Exit(1)
|
env.Exit(1)
|
||||||
env.Exit(0)
|
env.Exit(0)
|
||||||
|
|
||||||
|
|
||||||
# Create custom target that only runs the script and then exits, without continuing with the pio workflow
|
# Create custom target that only runs the script and then exits, without continuing with the pio workflow
|
||||||
env.AddCustomTarget(
|
env.AddCustomTarget(
|
||||||
name="build",
|
name="build",
|
||||||
@@ -119,4 +119,3 @@ env.AddCustomTarget(
|
|||||||
description="installs pnpm packages, updates libraries and builds web UI",
|
description="installs pnpm packages, updates libraries and builds web UI",
|
||||||
always_build=True
|
always_build=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pnpm format
|
|||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
cd interface
|
cd interface
|
||||||
pnpm webUI
|
pnpm build_webUI
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npx cspell "**"
|
npx cspell "**"
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <AsyncJson.h>
|
#include <AsyncJson.h>
|
||||||
#include <AsyncMessagePack.h>
|
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
for (auto & sensor : sensors_) {
|
for (auto & sensor : sensors_) {
|
||||||
sensor.ha_registered = false; // force HA configs to be re-created
|
sensor.ha_registered = false; // force HA configs to be re-created
|
||||||
|
|
||||||
// first check if the GPIO is valid. If not, force set it to disabled
|
// first check if the GPIO is valid. If not, force set the sensor to disabled, but don't remove it
|
||||||
if (!System::is_valid_gpio(sensor.gpio())) {
|
if (!EMSESP::system_.is_valid_gpio(sensor.gpio())) {
|
||||||
LOG_WARNING("Bad GPIO %d for Sensor %s. Disabling.", sensor.gpio(), sensor.name().c_str());
|
LOG_WARNING("Bad GPIO %d for Sensor %s. Disabling.", sensor.gpio(), sensor.name().c_str());
|
||||||
sensor.set_type(AnalogType::NOTUSED); // set disabled
|
sensor.set_type(AnalogType::NOTUSED); // set disabled
|
||||||
continue; // skip this loop pass
|
continue; // skip this loop pass
|
||||||
@@ -542,7 +542,7 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
|
|||||||
|
|
||||||
// return false if it's an invalid GPIO, an error will show in WebUI
|
// return false if it's an invalid GPIO, an error will show in WebUI
|
||||||
// and reported as an error in the log
|
// and reported as an error in the log
|
||||||
return System::is_valid_gpio(gpio);
|
return EMSESP::system_.is_valid_gpio(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if values have been updated
|
// check to see if values have been updated
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ void EMSESP::uart_init() {
|
|||||||
EMSuart::stop();
|
EMSuart::stop();
|
||||||
|
|
||||||
// don't start UART if we have invalid GPIOs
|
// don't start UART if we have invalid GPIOs
|
||||||
if (System::is_valid_gpio(rx_gpio) && System::is_valid_gpio(tx_gpio)) {
|
if (EMSESP::system_.is_valid_gpio(rx_gpio) && EMSESP::system_.is_valid_gpio(tx_gpio)) {
|
||||||
EMSuart::start(tx_mode, rx_gpio, tx_gpio); // start UART
|
EMSuart::start(tx_mode, rx_gpio, tx_gpio); // start UART
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING("Invalid UART Rx/Tx GPIOs. Check config.");
|
LOG_WARNING("Invalid UART Rx/Tx GPIOs. Check config.");
|
||||||
|
|||||||
@@ -19,12 +19,13 @@
|
|||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "emsesp.h" // for send_raw_telegram() command
|
#include "emsesp.h" // for send_raw_telegram() command
|
||||||
|
|
||||||
#include "shuntingYard.h"
|
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#include "esp_ota_ops.h"
|
#include "esp_ota_ops.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#include <semver200.h>
|
#include <semver200.h>
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_TEST)
|
||||||
@@ -448,31 +449,9 @@ void System::reload_settings() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for valid ESP32 pins. This is very dependent on which ESP32 board is being used.
|
// check for valid ESP32 pins
|
||||||
// Typically you can't use 1, 6-11, 20, 24, 28-31 and 40+
|
bool System::is_valid_gpio(uint8_t pin) {
|
||||||
// we allow 0 as it has a special function on the NodeMCU apparently
|
return std::find(valid_gpio_list().begin(), valid_gpio_list().end(), pin) != valid_gpio_list().end();
|
||||||
// See https://diyprojects.io/esp32-how-to-use-gpio-digital-io-arduino-code/#.YFpVEq9KhjG
|
|
||||||
// and https://nodemcu.readthedocs.io/en/dev-esp32/modules/gpio/
|
|
||||||
bool System::is_valid_gpio(uint8_t pin, bool has_psram) {
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32 || defined(EMSESP_STANDALONE)
|
|
||||||
if ((pin == 1) || (pin >= 6 && pin <= 11) || (pin == 20) || (pin == 24) || (pin >= 28 && pin <= 31) || (pin > 40)
|
|
||||||
|| ((EMSESP::system_.PSram() > 0 || has_psram) && pin >= 16 && pin <= 17)) {
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
|
||||||
if ((pin >= 19 && pin <= 20) || (pin >= 22 && pin <= 32) || (pin > 40)) {
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
// https://www.wemos.cc/en/latest/c3/c3_mini.html
|
|
||||||
if ((pin >= 11 && pin <= 19) || (pin > 21)) {
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
|
||||||
if ((pin >= 19 && pin <= 20) || (pin >= 22 && pin <= 37) || (pin >= 39 && pin <= 42) || (pin > 48)) {
|
|
||||||
#endif
|
|
||||||
return false; // bad pin
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra check for pins 21 and 22 (I2C) when ethernet is onboard
|
|
||||||
if ((EMSESP::system_.ethernet_connected() || EMSESP::system_.phy_type_ != PHY_type::PHY_TYPE_NONE) && (pin >= 21 && pin <= 22)) {
|
|
||||||
return false; // bad pin
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts up the UART Serial bridge
|
// Starts up the UART Serial bridge
|
||||||
@@ -2320,4 +2299,97 @@ uint8_t System::systemStatus() {
|
|||||||
return systemStatus_;
|
return systemStatus_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// takes a string range like "6-11, 1, 23, 24-48" which has optional ranges and single values and converts to a vector of ints
|
||||||
|
std::vector<uint8_t> System::string_range_to_vector(const std::string & range) {
|
||||||
|
std::vector<uint8_t> valid_gpios;
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
std::string::size_type prev = 0;
|
||||||
|
|
||||||
|
auto process_part = [&valid_gpios](std::string part) {
|
||||||
|
// trim whitespace
|
||||||
|
part.erase(0, part.find_first_not_of(" \t"));
|
||||||
|
part.erase(part.find_last_not_of(" \t") + 1);
|
||||||
|
|
||||||
|
// check if it's a range (contains '-')
|
||||||
|
std::string::size_type dash_pos = part.find('-');
|
||||||
|
if (dash_pos != std::string::npos) {
|
||||||
|
// it's a range like "6-11"
|
||||||
|
int start = std::stoi(part.substr(0, dash_pos));
|
||||||
|
int end = std::stoi(part.substr(dash_pos + 1));
|
||||||
|
for (int i = start; i <= end; i++) {
|
||||||
|
valid_gpios.push_back(static_cast<uint8_t>(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valid_gpios.push_back(static_cast<uint8_t>(std::stoi(part)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((pos = range.find(',', prev)) != std::string::npos) {
|
||||||
|
process_part(range.substr(prev, pos - prev));
|
||||||
|
prev = pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the last part
|
||||||
|
process_part(range.substr(prev));
|
||||||
|
|
||||||
|
return valid_gpios;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a list of valid GPIOs for the ESP32 board
|
||||||
|
// notes:
|
||||||
|
// - we allow 0, which is used sometimes to indicate a disabled pin
|
||||||
|
// - also allow input only pins are accepted (34-39) on some boards
|
||||||
|
// - and allow pins 33-38 for octal SPI for 32M vchip version on some boards
|
||||||
|
std::vector<uint8_t> System::valid_gpio_list() {
|
||||||
|
// get free gpios based on board/platform type
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
// https://www.wemos.cc/en/latest/c3/c3_mini.html
|
||||||
|
std::vector<uint8_t> valid_gpios = string_range_to_vector("0-10");
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
std::vector<uint8_t> valid_gpios = string_range_to_vector("0-14, 19, 20, 21, 33-38, 45, 46");
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
std::vector<uint8_t> valid_gpios = string_range_to_vector("2, 4-14, 17, 18, 21, 33-38, 45, 46");
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32 || defined(EMSESP_STANDALONE)
|
||||||
|
std::vector<uint8_t> valid_gpios = string_range_to_vector("0, 2, 4, 5, 12-15, 18, 19, 23, 25-27, 32-39");
|
||||||
|
#else
|
||||||
|
std::vector<uint8_t> valid_gpios = {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
// if psram is enabled remove pins 16 and 17 from the list
|
||||||
|
if (ESP.getPsramSize() > 0) {
|
||||||
|
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 16), valid_gpios.end());
|
||||||
|
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 17), valid_gpios.end());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// if ethernet is enabled, remove pins 21 and 22 (I2C)
|
||||||
|
if ((EMSESP::system_.ethernet_connected() || EMSESP::system_.phy_type_ != PHY_type::PHY_TYPE_NONE)) {
|
||||||
|
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 21), valid_gpios.end());
|
||||||
|
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 22), valid_gpios.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out GPIOs already used in application settings
|
||||||
|
for (const auto & gpio : valid_gpios) {
|
||||||
|
if (gpio == EMSESP::system_.pbutton_gpio_ || gpio == EMSESP::system_.led_gpio_ || gpio == EMSESP::system_.dallas_gpio_
|
||||||
|
|| gpio == EMSESP::system_.rx_gpio_ || gpio == EMSESP::system_.tx_gpio_) {
|
||||||
|
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), gpio), valid_gpios.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out GPIOs already used in analog sensors, if enabled
|
||||||
|
if (EMSESP::system_.analog_enabled_) {
|
||||||
|
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||||
|
if (std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()) != valid_gpios.end()) {
|
||||||
|
valid_gpios.erase(std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the list of valid GPIOs
|
||||||
|
std::sort(valid_gpios.begin(), valid_gpios.end());
|
||||||
|
|
||||||
|
return valid_gpios;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class System {
|
|||||||
static void extractSettings(const char * filename, const char * section, JsonObject output);
|
static void extractSettings(const char * filename, const char * section, JsonObject output);
|
||||||
static bool saveSettings(const char * filename, const char * section, JsonObject input);
|
static bool saveSettings(const char * filename, const char * section, JsonObject input);
|
||||||
|
|
||||||
static bool is_valid_gpio(uint8_t pin, bool has_psram = false);
|
bool is_valid_gpio(uint8_t pin);
|
||||||
static bool load_board_profile(std::vector<int8_t> & data, const std::string & board_profile);
|
static bool load_board_profile(std::vector<int8_t> & data, const std::string & board_profile);
|
||||||
|
|
||||||
static bool readCommand(const char * data);
|
static bool readCommand(const char * data);
|
||||||
@@ -303,6 +303,7 @@ class System {
|
|||||||
uint32_t PSram() {
|
uint32_t PSram() {
|
||||||
return psram_;
|
return psram_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t appFree() {
|
uint32_t appFree() {
|
||||||
return appfree_;
|
return appfree_;
|
||||||
}
|
}
|
||||||
@@ -336,6 +337,8 @@ class System {
|
|||||||
test_set_all_active_ = n;
|
test_set_all_active_ = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<uint8_t> valid_gpio_list();
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
float temperature() {
|
float temperature() {
|
||||||
return temperature_;
|
return temperature_;
|
||||||
@@ -381,6 +384,8 @@ class System {
|
|||||||
void led_monitor();
|
void led_monitor();
|
||||||
void system_check();
|
void system_check();
|
||||||
|
|
||||||
|
static std::vector<uint8_t> string_range_to_vector(const std::string & range);
|
||||||
|
|
||||||
int8_t wifi_quality(int8_t dBm);
|
int8_t wifi_quality(int8_t dBm);
|
||||||
|
|
||||||
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
||||||
|
|||||||
@@ -157,6 +157,11 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
|||||||
root["analog_enabled"] = EMSESP::analog_enabled();
|
root["analog_enabled"] = EMSESP::analog_enabled();
|
||||||
root["platform"] = EMSESP_PLATFORM;
|
root["platform"] = EMSESP_PLATFORM;
|
||||||
|
|
||||||
|
JsonArray valid_gpio_list = root["valid_gpio_list"].to<JsonArray>();
|
||||||
|
for (const auto & gpio : EMSESP::system_.valid_gpio_list()) {
|
||||||
|
valid_gpio_list.add(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,12 +90,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
// load the version from the settings config. This can be blank and later used in System::check_upgrade()
|
// load the version from the settings config. This can be blank and later used in System::check_upgrade()
|
||||||
settings.version = root["version"] | EMSESP_DEFAULT_VERSION;
|
settings.version = root["version"] | EMSESP_DEFAULT_VERSION;
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
bool psram = ESP.getPsramSize() > 0; // System::PSram() is initialized later
|
|
||||||
#else
|
|
||||||
bool psram = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESP::logger().debug("NVS boot value=[%s], board profile=[%s], EMSESP_DEFAULT_BOARD_PROFILE=[%s]",
|
EMSESP::logger().debug("NVS boot value=[%s], board profile=[%s], EMSESP_DEFAULT_BOARD_PROFILE=[%s]",
|
||||||
EMSESP::nvs_.getString("boot").c_str(),
|
EMSESP::nvs_.getString("boot").c_str(),
|
||||||
@@ -143,8 +137,8 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// check valid pins in this board profile
|
// check valid pins in this board profile
|
||||||
if (!System::is_valid_gpio(data[0], psram) || !System::is_valid_gpio(data[1], psram) || !System::is_valid_gpio(data[2], psram)
|
if (!EMSESP::system_.is_valid_gpio(data[0]) || !EMSESP::system_.is_valid_gpio(data[1]) || !EMSESP::system_.is_valid_gpio(data[2])
|
||||||
|| !System::is_valid_gpio(data[3], psram) || !System::is_valid_gpio(data[4], psram) || !System::is_valid_gpio(data[6], psram)) {
|
|| !EMSESP::system_.is_valid_gpio(data[3]) || !EMSESP::system_.is_valid_gpio(data[4]) || !EMSESP::system_.is_valid_gpio(data[6])) {
|
||||||
settings.board_profile = "default"; // reset to factory default
|
settings.board_profile = "default"; // reset to factory default
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -161,8 +155,8 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
#if defined(EMSESP_STANDALONE)
|
#if defined(EMSESP_STANDALONE)
|
||||||
settings.board_profile = "S32";
|
settings.board_profile = "S32";
|
||||||
#elif CONFIG_IDF_TARGET_ESP32
|
#elif CONFIG_IDF_TARGET_ESP32
|
||||||
// check for no PSRAM, could be a E32 or S32
|
// check for no PSRAM, could be a E32 or S32?
|
||||||
if (!psram) {
|
if (!ESP.getPsramSize()) {
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||||
if (ETH.begin(1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
|
if (ETH.begin(1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
|
||||||
#else
|
#else
|
||||||
|
|||||||
Reference in New Issue
Block a user