mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd2afd02be | ||
|
|
07f8f9e704 | ||
|
|
b7b09a8c93 | ||
|
|
8628bfa983 | ||
|
|
8ef8eeb9ec | ||
|
|
765ddb6702 | ||
|
|
6cab020241 | ||
|
|
cf489f7632 | ||
|
|
6943913d30 | ||
|
|
c5eaebc4b4 | ||
|
|
6905abf9f4 | ||
|
|
ef3b8a308f | ||
|
|
8a66c056d8 | ||
|
|
7967754024 | ||
|
|
800528f843 | ||
|
|
a6a60215d4 | ||
|
|
0deaafb9ce | ||
|
|
2ab50bd0a2 | ||
|
|
ecb82bd48b | ||
|
|
5592d18e1f | ||
|
|
bcfcc7690f | ||
|
|
a2fa2515b3 | ||
|
|
c8e7eb3657 | ||
|
|
24ea975575 | ||
|
|
863bc04c21 | ||
|
|
217b424320 | ||
|
|
e022c34fe7 | ||
|
|
1af103d5ee | ||
|
|
20ddbeb709 | ||
|
|
e1ad7d3c01 | ||
|
|
8f7c65c9b5 | ||
|
|
9bf7fbfb2e | ||
|
|
fbfaea6b56 | ||
|
|
21207f88f3 | ||
|
|
7ba330176a | ||
|
|
ab9caeba9c | ||
|
|
7a5eeaa88a | ||
|
|
bb3550810d | ||
|
|
f77fb12c80 | ||
|
|
f1342e4d62 | ||
|
|
d09abc1b49 | ||
|
|
8c4fc495a3 | ||
|
|
d8b77fc056 | ||
|
|
a359618cca | ||
|
|
20165a528d | ||
|
|
3a23dae178 | ||
|
|
e50d4fafb5 | ||
|
|
673b4c2881 | ||
|
|
b676c4d164 | ||
|
|
40716f9c55 | ||
|
|
df0210bfac | ||
|
|
41ac8120d0 | ||
|
|
6a66c7def7 | ||
|
|
3b0b6d75a7 | ||
|
|
292ed242c4 | ||
|
|
bb670e97ff | ||
|
|
87542fb9df | ||
|
|
df982e3ea9 | ||
|
|
222aaca218 | ||
|
|
8a56c599e6 | ||
|
|
003d3740af | ||
|
|
74342ba654 | ||
|
|
0010f71a3c | ||
|
|
38a546d6f7 | ||
|
|
4346de27b6 | ||
|
|
d797c3371b | ||
|
|
ce33fa6535 | ||
|
|
c2be9b210e | ||
|
|
464341c2cb | ||
|
|
f765d7c31b | ||
|
|
72b64a0c30 | ||
|
|
2b88fec2ee | ||
|
|
119b2b9514 | ||
|
|
4f406e8d33 | ||
|
|
0d80f58ea6 | ||
|
|
6c7a3ad68c | ||
|
|
52a8b20c54 | ||
|
|
5213382246 | ||
|
|
0b452ddd39 | ||
|
|
355c7cbd92 | ||
|
|
6564e444ab | ||
|
|
51b0a0ae5b | ||
|
|
faa888ff36 | ||
|
|
b834c8fd89 | ||
|
|
ef47ee62a3 | ||
|
|
5532d20657 | ||
|
|
b7ce69ee2d | ||
|
|
00b3525503 | ||
|
|
1065c9eec9 | ||
|
|
8d3dd9d8e9 | ||
|
|
ce6e89338f | ||
|
|
52bb6b8218 | ||
|
|
5748bd4074 | ||
|
|
542246c142 | ||
|
|
31bea94d9c | ||
|
|
5669deeb80 | ||
|
|
1042298541 | ||
|
|
6aca61deee | ||
|
|
9266454f82 | ||
|
|
89da6d5851 | ||
|
|
1491f283a8 | ||
|
|
a8c3b21ee6 | ||
|
|
e88ede2d8b | ||
|
|
6c398109f4 | ||
|
|
74691ce34a | ||
|
|
ef6ac3848f | ||
|
|
5c490834cf | ||
|
|
7b4f76d51d | ||
|
|
16010b2223 | ||
|
|
ea2d5b77c0 | ||
|
|
81b0b77e2b | ||
|
|
af1209cb04 | ||
|
|
b6ec8e14ec | ||
|
|
63cf4bdc21 | ||
|
|
8ddc167f93 | ||
|
|
54d2f38841 | ||
|
|
f25ab5f293 | ||
|
|
025f43953a | ||
|
|
e8217b68a5 | ||
|
|
ebfe487a3a | ||
|
|
ce34567939 | ||
|
|
a81695e973 | ||
|
|
8d1a36c669 | ||
|
|
77700c4873 | ||
|
|
d035a29f24 | ||
|
|
4c51b90663 | ||
|
|
ddd1f5de5b | ||
|
|
e2544947f7 | ||
|
|
854a39fe6b | ||
|
|
a684a46404 | ||
|
|
b29c36d01d | ||
|
|
a0e1894262 | ||
|
|
29a3e79804 | ||
|
|
72f4d00cb3 | ||
|
|
8453422c9c | ||
|
|
d81b833951 | ||
|
|
510602e117 | ||
|
|
4008883627 | ||
|
|
4081a55207 | ||
|
|
1845d5060a | ||
|
|
ad577eaa2a | ||
|
|
0d4607a922 | ||
|
|
067100d375 | ||
|
|
9118cd7c5b | ||
|
|
8b0cf599f4 | ||
|
|
7d6bb6b9c8 | ||
|
|
2a6fedc6b3 | ||
|
|
e3a7e9fe33 | ||
|
|
548fdd823b | ||
|
|
2b486ffa36 | ||
|
|
c3f9d9ddd6 | ||
|
|
740f3b4ef7 | ||
|
|
932a496f47 | ||
|
|
60beeddb66 | ||
|
|
c61c34f10e | ||
|
|
96a04da1ff | ||
|
|
bd8472b34e | ||
|
|
09228e4637 | ||
|
|
40a79c51ce | ||
|
|
2edfe0f42c | ||
|
|
a2eb8dfe83 | ||
|
|
4c60545057 | ||
|
|
d06b3285bd | ||
|
|
4dcfe8e0f6 | ||
|
|
42e679d5ba | ||
|
|
4db1c7dfca | ||
|
|
64fb84dd54 | ||
|
|
a17a9b71a2 | ||
|
|
0a10e78bfd | ||
|
|
50777bd681 | ||
|
|
2f5558c311 | ||
|
|
21c3fe5d8e | ||
|
|
acb453bd4b | ||
|
|
9c6b9a5359 | ||
|
|
0d07a9e50c | ||
|
|
f9e1940c7b | ||
|
|
72c0625823 | ||
|
|
6926f6fd0b | ||
|
|
e30c476e5c | ||
|
|
509122bf4b | ||
|
|
84fab951ba | ||
|
|
f34be2a884 | ||
|
|
bed1650350 | ||
|
|
1f8a477939 | ||
|
|
6699d9ad80 | ||
|
|
253eb72dbf | ||
|
|
fe772f85bf | ||
|
|
0e55caf721 | ||
|
|
434ef2b333 | ||
|
|
e0ab208c52 | ||
|
|
5b1f3d266e | ||
|
|
4c83f5fe60 | ||
|
|
2f658a9a14 | ||
|
|
c3f487eced | ||
|
|
a8a12dd1f8 | ||
|
|
7f7e3c47ec | ||
|
|
eafeb5c5d2 | ||
|
|
caca8bf802 | ||
|
|
338091578b | ||
|
|
188bfa4525 | ||
|
|
037a9848bc | ||
|
|
b6543169de | ||
|
|
14b3b058fe | ||
|
|
fa4763309d | ||
|
|
adcc59642c | ||
|
|
d18fd4948c | ||
|
|
4e4258f9dc | ||
|
|
ab6cf78822 | ||
|
|
d105c18bf7 | ||
|
|
6bbf4e4778 | ||
|
|
3101f5e6ae |
@@ -1,7 +1,6 @@
|
|||||||
name: 'github-releases-to-discord'
|
name: 'github-releases-to-discord'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/pre_release.yml
vendored
6
.github/workflows/pre_release.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '20'
|
||||||
|
|
||||||
- name: Get EMS-ESP source code and version
|
- name: Get EMS-ESP source code and version
|
||||||
id: build_info
|
id: build_info
|
||||||
|
|||||||
6
.github/workflows/tagged_release.yml
vendored
6
.github/workflows/tagged_release.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '20'
|
||||||
|
|
||||||
- name: Install PlatformIO
|
- name: Install PlatformIO
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
6
.github/workflows/test_release.yml
vendored
6
.github/workflows/test_release.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '20'
|
||||||
|
|
||||||
- name: Get EMS-ESP source code and version
|
- name: Get EMS-ESP source code and version
|
||||||
id: build_info
|
id: build_info
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,8 @@
|
|||||||
# vscode
|
# vscode
|
||||||
.vscode/*
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/extensions.json
|
||||||
|
.vscode/launch.json
|
||||||
|
#.vscode/settings.json
|
||||||
|
|
||||||
# c++ compiling
|
# c++ compiling
|
||||||
.clang_complete
|
.clang_complete
|
||||||
@@ -34,7 +37,7 @@ stats.html
|
|||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
yarn.lock
|
yarn.lock
|
||||||
interface/analyse.html
|
analyse.html
|
||||||
interface/vite.config.ts.timestamp*
|
interface/vite.config.ts.timestamp*
|
||||||
|
|
||||||
# scripts
|
# scripts
|
||||||
|
|||||||
91
.vscode/settings.json
vendored
Normal file
91
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"search.exclude": {
|
||||||
|
"**/.yarn": true,
|
||||||
|
"**/.pnp.*": true
|
||||||
|
},
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit"
|
||||||
|
},
|
||||||
|
"eslint.nodePath": "interface/.yarn/sdks",
|
||||||
|
"eslint.workingDirectories": ["interface"],
|
||||||
|
"prettier.prettierPath": "",
|
||||||
|
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||||
|
"files.associations": {
|
||||||
|
"*.tsx": "typescriptreact",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeinfo": "cpp"
|
||||||
|
},
|
||||||
|
"todo-tree.filtering.excludeGlobs": [
|
||||||
|
"**/vendor/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/dist/**",
|
||||||
|
"**/bower_components/**",
|
||||||
|
"**/build/**",
|
||||||
|
"**/.vscode/**",
|
||||||
|
"**/.github/**",
|
||||||
|
"**/_output/**",
|
||||||
|
"**/*.min.*",
|
||||||
|
"**/*.map",
|
||||||
|
"**/ArduinoJson/**"
|
||||||
|
],
|
||||||
|
"cSpell.enableFiletypes": [
|
||||||
|
"!cpp",
|
||||||
|
"!typescript"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
## **IMPORTANT! BREAKING CHANGES**
|
||||||
|
|
||||||
Writeable Text entities have moved from type `sensor` to `text` in Home Assistant to make them also editable within an HA dashboard. Examples are `datetime`, `holidays`, `switchtime`, `vacations`, `maintenancedate`. You will need to manually remove any old discovery topics from your MQTT broker using an application like MQTT Explorer.
|
Writeable Text entities have moved from type `sensor` to `text` in Home Assistant to make them also editable within an HA dashboard. Examples are `datetime`, `holidays`, `switchtime`, `vacations`, `maintenancedate`... You will need to manually remove any old discovery topics from your MQTT broker using an application like MQTT Explorer.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [3.x]
|
## [3.7.0]
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
## **IMPORTANT! BREAKING CHANGES**
|
||||||
|
|
||||||
|
- new device WATER shows dhw entities from MM100 and SM100 in dhw setting
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
- some more entities for dhw with SM100 module
|
||||||
|
- thermostat second dhw circuit [#1634](https://github.com/emsesp/EMS-ESP32/issues/1634)
|
||||||
|
- remote thermostat emulation for RC100H, RC200 and FB10 [#1287](https://github.com/emsesp/EMS-ESP32/discussions/1287), [#1602](https://github.com/emsesp/EMS-ESP32/discussions/1602), [#1551](https://github.com/emsesp/EMS-ESP32/discussions/1551)
|
||||||
|
- heatpump dhw stop temperatures [#1624](https://github.com/emsesp/EMS-ESP32/issues/1624)
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
|
- use flag for BC400 compatible thermostats, manage different mode settings
|
||||||
|
- use factory partition for 16M flash
|
||||||
|
- store digital out states to nvs
|
||||||
|
- Refresh UI - moving settings to one location [#1665](https://github.com/emsesp/EMS-ESP32/issues/1665)
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -42,7 +42,7 @@ DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DAR
|
|||||||
DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ -DEMC_RX_BUFFER_SIZE=1500
|
DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ -DEMC_RX_BUFFER_SIZE=1500
|
||||||
DEFINES += $(ARGS)
|
DEFINES += $(ARGS)
|
||||||
|
|
||||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Sources & Files
|
# Sources & Files
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
nvs, data, nvs, 0x9000, 0x005000,
|
nvs, data, nvs, 0x9000, 0x005000,
|
||||||
otadata, data, ota, , 0x002000,
|
otadata, data, ota, , 0x002000,
|
||||||
app0, app, ota_0, , 0x5D0000,
|
boot, app, factory, , 0x280000,
|
||||||
app1, app, ota_1, , 0x5D0000,
|
app0, app, ota_0, , 0x590000,
|
||||||
|
app1, app, ota_1, , 0x590000,
|
||||||
nvs1, data, nvs, , 0x040000,
|
nvs1, data, nvs, , 0x040000,
|
||||||
spiffs, data, spiffs, , 0x400000,
|
spiffs, data, spiffs, , 0x200000,
|
||||||
coredump, data, coredump,, 0x010000,
|
coredump, data, coredump,, 0x010000,
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
|
||||||
nvs, data, nvs, 0x9000, 0x005000,
|
|
||||||
otadata, data, ota, , 0x002000,
|
|
||||||
boot, app, factory, , 0x280000,
|
|
||||||
app0, app, ota_0, , 0x590000,
|
|
||||||
app1, app, ota_1, , 0x590000,
|
|
||||||
nvs1, data, nvs, , 0x040000,
|
|
||||||
spiffs, data, spiffs, , 0x200000,
|
|
||||||
coredump, data, coredump,, 0x010000,
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "EMS-ESP",
|
"name": "EMS-ESP",
|
||||||
"version": "3.6.5",
|
"version": "3.7",
|
||||||
"description": "build EMS-ESP WebUI",
|
"description": "build EMS-ESP WebUI",
|
||||||
"homepage": "https://emsesp.github.io/docs",
|
"homepage": "https://emsesp.github.io/docs",
|
||||||
"author": "proddy",
|
"author": "proddy",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"@types/imagemin": "^8.0.5",
|
"@types/imagemin": "^8.0.5",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@types/react": "^18.2.69",
|
"@types/react": "^18.2.72",
|
||||||
"@types/react-dom": "^18.2.22",
|
"@types/react-dom": "^18.2.22",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"alova": "^2.18.0",
|
"alova": "^2.18.0",
|
||||||
@@ -54,8 +54,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/compat": "^17.1.2",
|
"@preact/compat": "^17.1.2",
|
||||||
"@preact/preset-vite": "^2.8.2",
|
"@preact/preset-vite": "^2.8.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"@typescript-eslint/parser": "^7.3.1",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"terser": "^5.29.2",
|
"terser": "^5.29.2",
|
||||||
"vite": "^5.2.4",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const App: FC = () => {
|
|||||||
<CustomTheme>
|
<CustomTheme>
|
||||||
<AppRouting />
|
<AppRouting />
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="bottom-left"
|
position="bottom-right"
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
hideProgressBar={false}
|
hideProgressBar={false}
|
||||||
newestOnTop={false}
|
newestOnTop={false}
|
||||||
|
|||||||
@@ -1,64 +1,55 @@
|
|||||||
|
import { useContext, type FC } from 'react';
|
||||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||||
import Dashboard from './project/Dashboard';
|
|
||||||
import Help from './project/Help';
|
import Help from './project/Help';
|
||||||
import Settings from './project/Settings';
|
import { Layout } from 'components';
|
||||||
import type { FC } from 'react';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
import Settings from 'framework/Settings';
|
||||||
import { Layout, RequireAdmin } from 'components';
|
|
||||||
import AccessPoint from 'framework/ap/AccessPoint';
|
import AccessPoint from 'framework/ap/AccessPoint';
|
||||||
import Mqtt from 'framework/mqtt/Mqtt';
|
import Mqtt from 'framework/mqtt/Mqtt';
|
||||||
import NetworkConnection from 'framework/network/NetworkConnection';
|
import Network from 'framework/network/Network';
|
||||||
import NetworkTime from 'framework/ntp/NetworkTime';
|
import NetworkTime from 'framework/ntp/NetworkTime';
|
||||||
|
import OTASettings from 'framework/ota/OTASettings';
|
||||||
import Security from 'framework/security/Security';
|
import Security from 'framework/security/Security';
|
||||||
|
import ESPSystemStatus from 'framework/system/ESPSystemStatus';
|
||||||
import System from 'framework/system/System';
|
import System from 'framework/system/System';
|
||||||
|
import UploadDownload from 'framework/system/UploadDownload';
|
||||||
|
import ApplicationSettings from 'project/ApplicationSettings';
|
||||||
|
import CustomEntities from 'project/CustomEntities';
|
||||||
|
import Customization from 'project/Customization';
|
||||||
|
import Devices from 'project/Devices';
|
||||||
|
import Scheduler from 'project/Scheduler';
|
||||||
|
import Sensors from 'project/Sensors';
|
||||||
|
|
||||||
const AuthenticatedRouting: FC = () => (
|
const AuthenticatedRouting: FC = () => {
|
||||||
// const location = useLocation();
|
const { me } = useContext(AuthenticatedContext);
|
||||||
// const navigate = useNavigate();
|
return (
|
||||||
// const handleApiResponseError = useCallback(
|
|
||||||
// (error: AxiosError) => {
|
|
||||||
// if (error.response && error.response.status === 401) {
|
|
||||||
// AuthenticationApi.storeLoginRedirect(location);
|
|
||||||
// navigate('/unauthorized');
|
|
||||||
// }
|
|
||||||
// return Promise.reject(error);
|
|
||||||
// },
|
|
||||||
// [location, navigate]
|
|
||||||
// );
|
|
||||||
// useEffect(() => {
|
|
||||||
// const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
|
|
||||||
// return () => AXIOS.interceptors.response.eject(axiosHandlerId);
|
|
||||||
// }, [handleApiResponseError]);
|
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
<Route path="/devices/*" element={<Devices />} />
|
||||||
<Route
|
<Route path="/sensors/*" element={<Sensors />} />
|
||||||
path="/settings/*"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<Settings />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="/help/*" element={<Help />} />
|
|
||||||
|
|
||||||
<Route path="/network/*" element={<NetworkConnection />} />
|
|
||||||
<Route path="/ap/*" element={<AccessPoint />} />
|
|
||||||
<Route path="/ntp/*" element={<NetworkTime />} />
|
|
||||||
<Route path="/mqtt/*" element={<Mqtt />} />
|
|
||||||
<Route
|
|
||||||
path="/security/*"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<Security />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="/system/*" element={<System />} />
|
<Route path="/system/*" element={<System />} />
|
||||||
|
<Route path="/help/*" element={<Help />} />
|
||||||
<Route path="/*" element={<Navigate to="/" />} />
|
<Route path="/*" element={<Navigate to="/" />} />
|
||||||
|
{me.admin && (
|
||||||
|
<>
|
||||||
|
<Route path="/customizations/*" element={<Customization />} />
|
||||||
|
<Route path="/scheduler/*" element={<Scheduler />} />
|
||||||
|
<Route path="/customentities/*" element={<CustomEntities />} />
|
||||||
|
<Route path="/settings/*" element={<Settings />} />
|
||||||
|
<Route path="/settings/network/*" element={<Network />} />
|
||||||
|
<Route path="/settings/ems-esp/*" element={<ApplicationSettings />} />
|
||||||
|
<Route path="/settings/ap/*" element={<AccessPoint />} />
|
||||||
|
<Route path="/settings/ntp/*" element={<NetworkTime />} />
|
||||||
|
<Route path="/settings/mqtt/*" element={<Mqtt />} />
|
||||||
|
<Route path="/settings/ota/*" element={<OTASettings />} />
|
||||||
|
<Route path="/settings/security/*" element={<Security />} />
|
||||||
|
<Route path="/settings/espsystemstatus/*" element={<ESPSystemStatus />} />
|
||||||
|
<Route path="/settings/upload/*" element={<UploadDownload />} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Routes>
|
</Routes>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default AuthenticatedRouting;
|
export default AuthenticatedRouting;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function fetchLoginRedirect(): Partial<Path> {
|
|||||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
||||||
clearLoginRedirect();
|
clearLoginRedirect();
|
||||||
return {
|
return {
|
||||||
pathname: signInPathname || `/dashboard`,
|
pathname: signInPathname || `/devices`,
|
||||||
search: (signInPathname && signInSearch) || undefined
|
search: (signInPathname && signInSearch) || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { alovaInstance } from './endpoints';
|
import { alovaInstance } from './endpoints';
|
||||||
import type { MqttSettings, MqttStatus } from 'types';
|
import type { MqttSettingsType, MqttStatusType } from 'types';
|
||||||
|
|
||||||
export const readMqttStatus = () => alovaInstance.Get<MqttStatus>('/rest/mqttStatus');
|
export const readMqttStatus = () => alovaInstance.Get<MqttStatusType>('/rest/mqttStatus');
|
||||||
export const readMqttSettings = () => alovaInstance.Get<MqttSettings>('/rest/mqttSettings');
|
export const readMqttSettings = () => alovaInstance.Get<MqttSettingsType>('/rest/mqttSettings');
|
||||||
export const updateMqttSettings = (data: MqttSettings) => alovaInstance.Post<MqttSettings>('/rest/mqttSettings', data);
|
export const updateMqttSettings = (data: MqttSettingsType) =>
|
||||||
|
alovaInstance.Post<MqttSettingsType>('/rest/mqttSettings', data);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
||||||
import type { OTASettings, SystemStatus, LogSettings } from 'types';
|
import type { OTASettings, SystemStatus, LogSettings, ESPSystemStatus } from 'types';
|
||||||
|
|
||||||
// SystemStatus - also used to ping in Restart monitor for pinging
|
// ESPSystemStatus - also used to ping in Restart monitor for pinging
|
||||||
|
export const readESPSystemStatus = () => alovaInstance.Get<ESPSystemStatus>('/rest/ESPSystemStatus');
|
||||||
|
|
||||||
|
// SystemStatus
|
||||||
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import type { FC } from 'react';
|
|||||||
import type { RequiredChildrenProps } from 'utils';
|
import type { RequiredChildrenProps } from 'utils';
|
||||||
|
|
||||||
interface SectionContentProps extends RequiredChildrenProps {
|
interface SectionContentProps extends RequiredChildrenProps {
|
||||||
title: string;
|
title?: string;
|
||||||
titleGutter?: boolean;
|
|
||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,7 +12,9 @@ const SectionContent: FC<SectionContentProps> = (props) => {
|
|||||||
const { children, title, id } = props;
|
const { children, title, id } = props;
|
||||||
return (
|
return (
|
||||||
<Paper id={id} sx={{ p: 2, m: 2 }}>
|
<Paper id={id} sx={{ p: 2, m: 2 }}>
|
||||||
|
{title && (
|
||||||
<Divider sx={{ pb: 2, borderColor: 'primary.main', fontSize: 20, color: 'primary.main' }}>{title}</Divider>
|
<Divider sx={{ pb: 2, borderColor: 'primary.main', fontSize: 20, color: 'primary.main' }}>{title}</Divider>
|
||||||
|
)}
|
||||||
{children}
|
{children}
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import MenuIcon from '@mui/icons-material/Menu';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import { AppBar, Box, IconButton, Toolbar, Typography } from '@mui/material';
|
import { AppBar, IconButton, Toolbar, Typography } from '@mui/material';
|
||||||
import LayoutAuthMenu from './LayoutAuthMenu';
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
export const DRAWER_WIDTH = 210;
|
export const DRAWER_WIDTH = 210;
|
||||||
@@ -27,8 +26,6 @@ const LayoutAppBar: FC<LayoutAppBarProps> = ({ title, onToggleDrawer }) => (
|
|||||||
<Typography variant="h6" noWrap component="div">
|
<Typography variant="h6" noWrap component="div">
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box flexGrow={1} />
|
|
||||||
<LayoutAuthMenu />
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
|
||||||
import PersonIcon from '@mui/icons-material/Person';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
IconButton,
|
|
||||||
Popover,
|
|
||||||
Typography,
|
|
||||||
Avatar,
|
|
||||||
styled,
|
|
||||||
MenuItem,
|
|
||||||
TextField
|
|
||||||
} from '@mui/material';
|
|
||||||
import { useState, useContext } from 'react';
|
|
||||||
import type { TypographyProps } from '@mui/material';
|
|
||||||
import type { Locales } from 'i18n/i18n-types';
|
|
||||||
import type { FC, ChangeEventHandler } from 'react';
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
import DEflag from 'i18n/DE.svg';
|
|
||||||
import FRflag from 'i18n/FR.svg';
|
|
||||||
import GBflag from 'i18n/GB.svg';
|
|
||||||
import ITflag from 'i18n/IT.svg';
|
|
||||||
import NLflag from 'i18n/NL.svg';
|
|
||||||
import NOflag from 'i18n/NO.svg';
|
|
||||||
import PLflag from 'i18n/PL.svg';
|
|
||||||
import SKflag from 'i18n/SK.svg';
|
|
||||||
import SVflag from 'i18n/SV.svg';
|
|
||||||
import TRflag from 'i18n/TR.svg';
|
|
||||||
|
|
||||||
import { I18nContext } from 'i18n/i18n-react';
|
|
||||||
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
|
||||||
|
|
||||||
const ItemTypography = styled(Typography)<TypographyProps>({
|
|
||||||
maxWidth: '250px',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis'
|
|
||||||
});
|
|
||||||
|
|
||||||
const LayoutAuthMenu: FC = () => {
|
|
||||||
const { me, signOut } = useContext(AuthenticatedContext);
|
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
setAnchorEl(event.currentTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { locale, LL, setLocale } = useContext(I18nContext);
|
|
||||||
|
|
||||||
const onLocaleSelected: ChangeEventHandler<HTMLInputElement> = async ({ target }) => {
|
|
||||||
const loc = target.value as Locales;
|
|
||||||
localStorage.setItem('lang', loc);
|
|
||||||
await loadLocaleAsync(loc);
|
|
||||||
setLocale(loc);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
|
||||||
const id = anchorEl ? 'app-menu-popover' : undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<TextField
|
|
||||||
name="locale"
|
|
||||||
InputProps={{ style: { fontSize: 10 } }}
|
|
||||||
variant="outlined"
|
|
||||||
value={locale}
|
|
||||||
onChange={onLocaleSelected}
|
|
||||||
size="small"
|
|
||||||
select
|
|
||||||
>
|
|
||||||
<MenuItem key="de" value="de">
|
|
||||||
<img src={DEflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
DE
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="en" value="en">
|
|
||||||
<img src={GBflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
EN
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="fr" value="fr">
|
|
||||||
<img src={FRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
FR
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="it" value="it">
|
|
||||||
<img src={ITflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
IT
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="nl" value="nl">
|
|
||||||
<img src={NLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
NL
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="no" value="no">
|
|
||||||
<img src={NOflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
NO
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="pl" value="pl">
|
|
||||||
<img src={PLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
PL
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="sk" value="sk">
|
|
||||||
<img src={SKflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
SK
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="sv" value="sv">
|
|
||||||
<img src={SVflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
SV
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="tr" value="tr">
|
|
||||||
<img src={TRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
TR
|
|
||||||
</MenuItem>
|
|
||||||
</TextField>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
id="open-auth-menu"
|
|
||||||
sx={{ ml: 1, padding: 0 }}
|
|
||||||
aria-describedby={id}
|
|
||||||
color="inherit"
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<AccountCircleIcon />
|
|
||||||
</IconButton>
|
|
||||||
<Popover
|
|
||||||
id="app-menu-popover"
|
|
||||||
sx={{ mt: 1 }}
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
onClose={handleClose}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'center'
|
|
||||||
}}
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'center'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box display="flex" flexDirection="row" alignItems="center" p={2}>
|
|
||||||
<Avatar sx={{ width: 80, height: 80 }}>
|
|
||||||
<PersonIcon fontSize="large" />
|
|
||||||
</Avatar>
|
|
||||||
<Box pl={2}>
|
|
||||||
<ItemTypography variant="h6">{me.username}</ItemTypography>
|
|
||||||
<ItemTypography variant="body1">
|
|
||||||
{me.admin ? LL.ADMIN() : LL.GUEST()} {LL.USER(2)}
|
|
||||||
</ItemTypography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Divider />
|
|
||||||
<Box p={1.5}>
|
|
||||||
<Button variant="outlined" fullWidth color="primary" onClick={() => signOut(true)}>
|
|
||||||
{LL.SIGN_OUT()}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Popover>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LayoutAuthMenu;
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
||||||
import { DRAWER_WIDTH } from './Layout';
|
import { DRAWER_WIDTH } from './Layout';
|
||||||
|
|
||||||
import LayoutMenu from './LayoutMenu';
|
import LayoutMenu from './LayoutMenu';
|
||||||
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { PROJECT_NAME } from 'api/env';
|
import { PROJECT_NAME } from 'api/env';
|
||||||
|
|||||||
@@ -1,53 +1,254 @@
|
|||||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
||||||
|
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
import CategoryIcon from '@mui/icons-material/Category';
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import ConstructionIcon from '@mui/icons-material/Construction';
|
||||||
import InfoIcon from '@mui/icons-material/Info';
|
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LiveHelpIcon from '@mui/icons-material/LiveHelp';
|
||||||
|
import MoreTimeIcon from '@mui/icons-material/MoreTime';
|
||||||
|
import PersonIcon from '@mui/icons-material/Person';
|
||||||
|
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
||||||
|
import SensorsIcon from '@mui/icons-material/Sensors';
|
||||||
import SettingsIcon from '@mui/icons-material/Settings';
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
import SettingsEthernetIcon from '@mui/icons-material/SettingsEthernet';
|
|
||||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
|
||||||
import TuneIcon from '@mui/icons-material/Tune';
|
|
||||||
import { Divider, List } from '@mui/material';
|
|
||||||
import { useContext } from 'react';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
Divider,
|
||||||
|
List,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Popover,
|
||||||
|
Avatar,
|
||||||
|
MenuItem,
|
||||||
|
TextField,
|
||||||
|
ListItem,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { useContext, useState } from 'react';
|
||||||
|
import type { Locales } from 'i18n/i18n-types';
|
||||||
|
import type { FC, ChangeEventHandler } from 'react';
|
||||||
import LayoutMenuItem from 'components/layout/LayoutMenuItem';
|
import LayoutMenuItem from 'components/layout/LayoutMenuItem';
|
||||||
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import DEflag from 'i18n/DE.svg';
|
||||||
|
import FRflag from 'i18n/FR.svg';
|
||||||
|
import GBflag from 'i18n/GB.svg';
|
||||||
|
import ITflag from 'i18n/IT.svg';
|
||||||
|
import NLflag from 'i18n/NL.svg';
|
||||||
|
import NOflag from 'i18n/NO.svg';
|
||||||
|
import PLflag from 'i18n/PL.svg';
|
||||||
|
import SKflag from 'i18n/SK.svg';
|
||||||
|
import SVflag from 'i18n/SV.svg';
|
||||||
|
import TRflag from 'i18n/TR.svg';
|
||||||
|
|
||||||
|
import { I18nContext } from 'i18n/i18n-react';
|
||||||
|
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
||||||
|
|
||||||
const LayoutMenu: FC = () => {
|
const LayoutMenu: FC = () => {
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
const { me, signOut } = useContext(AuthenticatedContext);
|
||||||
const { LL } = useI18nContext();
|
const { locale, LL, setLocale } = useContext(I18nContext);
|
||||||
|
|
||||||
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const id = anchorEl ? 'app-menu-popover' : undefined;
|
||||||
|
|
||||||
|
const [menuOpen, setMenuOpen] = useState(true);
|
||||||
|
|
||||||
|
const onLocaleSelected: ChangeEventHandler<HTMLInputElement> = async ({ target }) => {
|
||||||
|
const loc = target.value as Locales;
|
||||||
|
localStorage.setItem('lang', loc);
|
||||||
|
await loadLocaleAsync(loc);
|
||||||
|
setLocale(loc);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = (event: any) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<List disablePadding component="nav">
|
<List component="nav">
|
||||||
<LayoutMenuItem icon={DashboardIcon} label={LL.DASHBOARD()} to={`/dashboard`} />
|
<LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} />
|
||||||
<LayoutMenuItem
|
<LayoutMenuItem icon={SensorsIcon} label={LL.SENSORS()} to={`/sensors`} />
|
||||||
icon={TuneIcon}
|
|
||||||
label={LL.SETTINGS_OF('')}
|
|
||||||
to={`/settings`}
|
|
||||||
disabled={!authenticatedContext.me.admin}
|
|
||||||
/>
|
|
||||||
<LayoutMenuItem icon={InfoIcon} label={LL.HELP_OF('')} to={`/help`} />
|
|
||||||
<Divider />
|
<Divider />
|
||||||
</List>
|
|
||||||
<List disablePadding component="nav">
|
<Box
|
||||||
<LayoutMenuItem icon={SettingsEthernetIcon} label={LL.NETWORK(0)} to="/network" />
|
sx={{
|
||||||
<LayoutMenuItem icon={SettingsInputAntennaIcon} label={LL.ACCESS_POINT(0)} to="/ap" />
|
bgcolor: menuOpen ? 'rgba(71, 98, 130, 0.2)' : null,
|
||||||
<LayoutMenuItem icon={AccessTimeIcon} label="NTP" to="/ntp" />
|
pb: menuOpen ? 2 : 0
|
||||||
<LayoutMenuItem icon={DeviceHubIcon} label="MQTT" to="/mqtt" />
|
}}
|
||||||
<LayoutMenuItem
|
>
|
||||||
icon={LockIcon}
|
<ListItemButton
|
||||||
label={LL.SECURITY(0)}
|
alignItems="flex-start"
|
||||||
to="/security"
|
onClick={() => setMenuOpen(!menuOpen)}
|
||||||
disabled={!authenticatedContext.me.admin}
|
sx={{
|
||||||
|
pt: 2.5,
|
||||||
|
pb: menuOpen ? 0 : 2.5,
|
||||||
|
'&:hover, &:focus': { '& svg': { opacity: 1 } }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
// TODO: translate
|
||||||
|
primary="Customize"
|
||||||
|
primaryTypographyProps={{
|
||||||
|
fontWeight: '600',
|
||||||
|
mb: '2px',
|
||||||
|
color: 'lightblue'
|
||||||
|
}}
|
||||||
|
// TODO: translate
|
||||||
|
secondary="Customizations, Scheduler and Custom Entities"
|
||||||
|
secondaryTypographyProps={{
|
||||||
|
noWrap: true,
|
||||||
|
fontSize: 12,
|
||||||
|
color: menuOpen ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)'
|
||||||
|
}}
|
||||||
|
sx={{ my: 0 }}
|
||||||
/>
|
/>
|
||||||
<LayoutMenuItem icon={SettingsIcon} label={LL.SYSTEM(0)} to="/system" />
|
<KeyboardArrowDown
|
||||||
|
sx={{
|
||||||
|
mr: -1,
|
||||||
|
opacity: 0,
|
||||||
|
transform: menuOpen ? 'rotate(-180deg)' : 'rotate(0)',
|
||||||
|
transition: '0.2s'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
{menuOpen && (
|
||||||
|
<>
|
||||||
|
<LayoutMenuItem
|
||||||
|
icon={ConstructionIcon}
|
||||||
|
label={LL.CUSTOMIZATIONS()}
|
||||||
|
disabled={!me.admin}
|
||||||
|
to={`/customizations`}
|
||||||
|
/>
|
||||||
|
<LayoutMenuItem icon={MoreTimeIcon} label={LL.SCHEDULER()} disabled={!me.admin} to={`/scheduler`} />
|
||||||
|
<LayoutMenuItem
|
||||||
|
icon={PlaylistAddIcon}
|
||||||
|
label={LL.CUSTOM_ENTITIES(0)}
|
||||||
|
disabled={!me.admin}
|
||||||
|
to={`/customentities`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
|
<List style={{ marginTop: `auto` }}>
|
||||||
|
<LayoutMenuItem icon={AssessmentIcon} label={LL.SYSTEM(0)} to="/system" />
|
||||||
|
<LayoutMenuItem icon={SettingsIcon} label={LL.SETTINGS(0)} disabled={!me.admin} to="/settings" />
|
||||||
|
<LayoutMenuItem icon={LiveHelpIcon} label={LL.HELP_OF('')} to={`/help`} />
|
||||||
|
</List>
|
||||||
|
<Divider />
|
||||||
|
<List>
|
||||||
|
<ListItem disablePadding onClick={handleClick}>
|
||||||
|
<ListItemButton>
|
||||||
|
<ListItemIcon>
|
||||||
|
<AccountCircleIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>{me.username}</ListItemText>
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
id={id}
|
||||||
|
sx={{ mt: 1 }}
|
||||||
|
open={open}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
onClose={handleClose}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'center'
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
p={2}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
border: '2px solid grey'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<List>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
<Avatar sx={{ bgcolor: '#b1395f', color: 'white' }}>
|
||||||
|
<PersonIcon />
|
||||||
|
</Avatar>
|
||||||
|
<ListItemText
|
||||||
|
sx={{ pl: 2 }}
|
||||||
|
primary={me.username}
|
||||||
|
secondary={(me.admin ? LL.ADMIN() : LL.GUEST()) + ' Account'}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
<Box p={2}>
|
||||||
|
<TextField
|
||||||
|
name="locale"
|
||||||
|
InputProps={{ style: { fontSize: 10 } }}
|
||||||
|
variant="outlined"
|
||||||
|
value={locale}
|
||||||
|
onChange={onLocaleSelected}
|
||||||
|
size="small"
|
||||||
|
select
|
||||||
|
>
|
||||||
|
<MenuItem key="de" value="de">
|
||||||
|
<img src={DEflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
DE
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="en" value="en">
|
||||||
|
<img src={GBflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
EN
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="fr" value="fr">
|
||||||
|
<img src={FRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
FR
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="it" value="it">
|
||||||
|
<img src={ITflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
IT
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="nl" value="nl">
|
||||||
|
<img src={NLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="no" value="no">
|
||||||
|
<img src={NOflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NO
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="pl" value="pl">
|
||||||
|
<img src={PLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
PL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="sk" value="sk">
|
||||||
|
<img src={SKflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
SK
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="sv" value="sv">
|
||||||
|
<img src={SVflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
SV
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="tr" value="tr">
|
||||||
|
<img src={TRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
TR
|
||||||
|
</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button variant="outlined" fullWidth color="primary" onClick={() => signOut(true)}>
|
||||||
|
{LL.SIGN_OUT()}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import type { SvgIconProps } from '@mui/material';
|
import type { SvgIconProps } from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
@@ -18,14 +18,12 @@ const LayoutMenuItem: FC<LayoutMenuItemProps> = ({ icon: Icon, label, to, disabl
|
|||||||
const selected = routeMatches(to, pathname);
|
const selected = routeMatches(to, pathname);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem disablePadding>
|
|
||||||
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
|
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
|
||||||
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
|
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
|
||||||
<Icon />
|
<Icon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText sx={{ color: selected ? '#90caf9' : '#f5f5f5' }}>{label}</ListItemText>
|
<ListItemText sx={{ color: selected ? '#90caf9' : '#f5f5f5' }}>{label}</ListItemText>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
52
interface/src/components/layout/ListMenuItem.tsx
Normal file
52
interface/src/components/layout/ListMenuItem.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
||||||
|
import { Avatar, ListItem, ListItemAvatar, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import type { SvgIconProps } from '@mui/material';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
interface ListMenuItemProps {
|
||||||
|
icon: React.ComponentType<SvgIconProps>;
|
||||||
|
bgcolor?: string;
|
||||||
|
label: string;
|
||||||
|
text: string;
|
||||||
|
to?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderIcon({ icon: Icon, bgcolor, label, text }: ListMenuItemProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor, color: 'white' }}>
|
||||||
|
<Icon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary={label} secondary={text} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LayoutMenuItem: FC<ListMenuItemProps> = ({ icon, bgcolor, label, text, to, disabled }) => (
|
||||||
|
<>
|
||||||
|
{to && !disabled ? (
|
||||||
|
<ListItem
|
||||||
|
disablePadding
|
||||||
|
secondaryAction={
|
||||||
|
<ListItemIcon style={{ justifyContent: 'right', color: 'lightblue', verticalAlign: 'middle' }}>
|
||||||
|
<NavigateNextIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemButton component={Link} to={to}>
|
||||||
|
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} to="" />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
) : (
|
||||||
|
<ListItem>
|
||||||
|
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} to="" />
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default LayoutMenuItem;
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useMatch, useResolvedPath } from 'react-router-dom';
|
||||||
|
|
||||||
export const useRouterTab = () => {
|
export const useRouterTab = () => {
|
||||||
const loc = useLocation().pathname;
|
const routerTabPathMatch = useMatch(useResolvedPath(':tab').pathname);
|
||||||
const routerTab = loc.substring(0, loc.lastIndexOf('/')) ? loc : false;
|
const routerTab = routerTabPathMatch?.params?.tab || false;
|
||||||
|
|
||||||
// const routerTabPath = useResolvedPath(':tab');
|
|
||||||
// const routerTabPathMatch = useMatch(routerTabPath.pathname);
|
|
||||||
// const routerTab = routerTabPathMatch?.params?.tab || false;
|
|
||||||
|
|
||||||
return { routerTab } as const;
|
return { routerTab } as const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
|||||||
<Box
|
<Box
|
||||||
{...getRootProps({
|
{...getRootProps({
|
||||||
sx: {
|
sx: {
|
||||||
py: 8,
|
py: 4,
|
||||||
px: 2,
|
px: 2,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
|
|||||||
250
interface/src/framework/Settings.tsx
Normal file
250
interface/src/framework/Settings.tsx
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
import CastIcon from '@mui/icons-material/Cast';
|
||||||
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
|
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
||||||
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
|
import MemoryIcon from '@mui/icons-material/Memory';
|
||||||
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
|
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
|
||||||
|
import SettingsEthernetIcon from '@mui/icons-material/SettingsEthernet';
|
||||||
|
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||||
|
import TuneIcon from '@mui/icons-material/Tune';
|
||||||
|
|
||||||
|
import { List, Button, Dialog, DialogActions, DialogContent, DialogTitle, Box } from '@mui/material';
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import { useState, type FC } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import RestartMonitor from './system/RestartMonitor';
|
||||||
|
import { dialogStyle } from 'CustomTheme';
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
import { ButtonRow, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
const Settings: FC = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
useLayoutTitle(LL.SETTINGS(0));
|
||||||
|
|
||||||
|
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
||||||
|
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||||
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
|
||||||
|
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { send: partitionCommand } = useRequest(SystemApi.partition(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const restart = async () => {
|
||||||
|
setProcessing(true);
|
||||||
|
await restartCommand()
|
||||||
|
.then(() => {
|
||||||
|
setRestarting(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(err.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setConfirmRestart(false);
|
||||||
|
setProcessing(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const factoryReset = async () => {
|
||||||
|
setProcessing(true);
|
||||||
|
await factoryResetCommand()
|
||||||
|
.then(() => {
|
||||||
|
setRestarting(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(err.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setConfirmFactoryReset(false);
|
||||||
|
setProcessing(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const partition = async () => {
|
||||||
|
setProcessing(true);
|
||||||
|
await partitionCommand()
|
||||||
|
.then(() => {
|
||||||
|
setRestarting(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(err.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setConfirmRestart(false);
|
||||||
|
setProcessing(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderRestartDialog = () => (
|
||||||
|
<Dialog sx={dialogStyle} open={confirmRestart} onClose={() => setConfirmRestart(false)}>
|
||||||
|
<DialogTitle>{LL.RESTART()}</DialogTitle>
|
||||||
|
<DialogContent dividers>{LL.RESTART_CONFIRM()}</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setConfirmRestart(false)}
|
||||||
|
disabled={processing}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={restart}
|
||||||
|
disabled={processing}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.RESTART()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={partition}
|
||||||
|
disabled={processing}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
EMS-ESP Loader
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderFactoryResetDialog = () => (
|
||||||
|
<Dialog sx={dialogStyle} open={confirmFactoryReset} onClose={() => setConfirmFactoryReset(false)}>
|
||||||
|
<DialogTitle>{LL.FACTORY_RESET()}</DialogTitle>
|
||||||
|
<DialogContent dividers>{LL.SYSTEM_FACTORY_TEXT_DIALOG()}</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setConfirmFactoryReset(false)}
|
||||||
|
disabled={processing}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={factoryReset}
|
||||||
|
disabled={processing}
|
||||||
|
color="error"
|
||||||
|
>
|
||||||
|
{LL.FACTORY_RESET()}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
const content = () => (
|
||||||
|
<>
|
||||||
|
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||||
|
{/* TODO: translate */}
|
||||||
|
<ListMenuItem
|
||||||
|
icon={TuneIcon}
|
||||||
|
bgcolor="#134ba2"
|
||||||
|
label={LL.APPLICATION_SETTINGS()}
|
||||||
|
text="Modify EMS-ESP Application Settings"
|
||||||
|
to="ems-esp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
icon={SettingsEthernetIcon}
|
||||||
|
bgcolor="#40828f"
|
||||||
|
label={LL.NETWORK(0)}
|
||||||
|
text={LL.CONFIGURE(LL.SETTINGS_OF(LL.NETWORK(0)))}
|
||||||
|
to="network"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
icon={SettingsInputAntennaIcon}
|
||||||
|
bgcolor="#5f9a5f"
|
||||||
|
label={LL.ACCESS_POINT(0)}
|
||||||
|
text={LL.CONFIGURE(LL.ACCESS_POINT(0))}
|
||||||
|
to="ap"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
icon={AccessTimeIcon}
|
||||||
|
bgcolor="#c5572c"
|
||||||
|
label="NTP"
|
||||||
|
text={LL.CONFIGURE(LL.LOCAL_TIME())}
|
||||||
|
to="ntp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListMenuItem icon={DeviceHubIcon} bgcolor="#68374d" label="MQTT" text={LL.CONFIGURE('MQTT')} to="mqtt" />
|
||||||
|
|
||||||
|
<ListMenuItem icon={CastIcon} bgcolor="#efc34b" label="OTA" text={LL.CONFIGURE('OTA')} to="ota" />
|
||||||
|
|
||||||
|
{/* TODO: translate */}
|
||||||
|
<ListMenuItem icon={LockIcon} label={LL.SECURITY(0)} text="Add/Remove Users" to="security" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
icon={MemoryIcon}
|
||||||
|
bgcolor="#b1395f"
|
||||||
|
label={LL.STATUS_OF('ESP32')}
|
||||||
|
text="ESP32 Information"
|
||||||
|
to="espsystemstatus"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* TODO: translate */}
|
||||||
|
<ListMenuItem
|
||||||
|
icon={ImportExportIcon}
|
||||||
|
bgcolor="#5d89f7"
|
||||||
|
label={LL.UPLOAD_DOWNLOAD()}
|
||||||
|
text="Upload/Download Settings and Firmware"
|
||||||
|
to="upload"
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
|
||||||
|
{renderRestartDialog()}
|
||||||
|
{renderFactoryResetDialog()}
|
||||||
|
|
||||||
|
<Box mt={1} display="flex" flexWrap="wrap">
|
||||||
|
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
||||||
|
<ButtonRow>
|
||||||
|
<Button
|
||||||
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => setConfirmRestart(true)}
|
||||||
|
>
|
||||||
|
{LL.RESTART()}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
|
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||||
|
<ButtonRow>
|
||||||
|
<Button
|
||||||
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setConfirmFactoryReset(true)}
|
||||||
|
color="error"
|
||||||
|
>
|
||||||
|
{LL.FACTORY_RESET()}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Settings;
|
||||||
@@ -6,7 +6,7 @@ import { useState } from 'react';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { APSettings } from 'types';
|
import type { APSettingsType } from 'types';
|
||||||
import * as APApi from 'api/ap';
|
import * as APApi from 'api/ap';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -24,10 +24,10 @@ import { numberValue, updateValueDirty, useRest } from 'utils';
|
|||||||
|
|
||||||
import { createAPSettingsValidator, validate } from 'validators';
|
import { createAPSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
export const isAPEnabled = ({ provision_mode }: APSettings) =>
|
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
||||||
provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
||||||
|
|
||||||
const APSettingsForm: FC = () => {
|
const APSettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -39,7 +39,7 @@ const APSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<APSettings>({
|
} = useRest<APSettingsType>({
|
||||||
read: APApi.readAPSettings,
|
read: APApi.readAPSettings,
|
||||||
update: APApi.updateAPSettings
|
update: APApi.updateAPSettings
|
||||||
});
|
});
|
||||||
@@ -205,11 +205,11 @@ const APSettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default APSettingsForm;
|
export default APSettings;
|
||||||
@@ -7,14 +7,14 @@ import { useRequest } from 'alova';
|
|||||||
import type { Theme } from '@mui/material';
|
import type { Theme } from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { APStatus } from 'types';
|
import type { APStatusType } from 'types';
|
||||||
import * as APApi from 'api/ap';
|
import * as APApi from 'api/ap';
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { APNetworkStatus } from 'types';
|
import { APNetworkStatus } from 'types';
|
||||||
|
|
||||||
export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
|
export const apStatusHighlight = ({ status }: APStatusType, theme: Theme) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case APNetworkStatus.ACTIVE:
|
case APNetworkStatus.ACTIVE:
|
||||||
return theme.palette.success.main;
|
return theme.palette.success.main;
|
||||||
@@ -27,14 +27,14 @@ export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const APStatusForm: FC = () => {
|
const APStatus: FC = () => {
|
||||||
const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus);
|
const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus);
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const apStatus = ({ status }: APStatus) => {
|
const apStatus = ({ status }: APStatusType) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case APNetworkStatus.ACTIVE:
|
case APNetworkStatus.ACTIVE:
|
||||||
return LL.ACTIVE();
|
return LL.ACTIVE();
|
||||||
@@ -99,11 +99,7 @@ const APStatusForm: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
<SectionContent title={LL.STATUS_OF(LL.ACCESS_POINT(1))} titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default APStatusForm;
|
export default APStatus;
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useContext } from 'react';
|
|
||||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
import APSettingsForm from './APSettingsForm';
|
import APSettings from './APSettings';
|
||||||
import APStatusForm from './APStatusForm';
|
import APStatus from './APStatus';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -15,32 +13,18 @@ const AccessPoint: FC = () => {
|
|||||||
|
|
||||||
useLayoutTitle(LL.ACCESS_POINT(0));
|
useLayoutTitle(LL.ACCESS_POINT(0));
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
|
||||||
|
|
||||||
const { routerTab } = useRouterTab();
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="/ap/status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} />
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} />
|
||||||
<Tab
|
<Tab value="status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} />
|
||||||
value="/ap/settings"
|
|
||||||
label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))}
|
|
||||||
disabled={!authenticatedContext.me.admin}
|
|
||||||
/>
|
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<APStatusForm />} />
|
<Route path="status" element={<APStatus />} />
|
||||||
<Route index element={<Navigate to="status" />} />
|
<Route path="settings" element={<APSettings />} />
|
||||||
<Route
|
<Route path="*" element={<Navigate replace to="settings" />} />
|
||||||
path="settings"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<APSettingsForm />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<Navigate replace to="/ap/status" />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useContext } from 'react';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
import MqttSettingsForm from './MqttSettingsForm';
|
import MqttSettings from './MqttSettings';
|
||||||
import MqttStatusForm from './MqttStatusForm';
|
import MqttStatus from './MqttStatus';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -15,26 +13,18 @@ const Mqtt: FC = () => {
|
|||||||
|
|
||||||
useLayoutTitle('MQTT');
|
useLayoutTitle('MQTT');
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
|
||||||
const { routerTab } = useRouterTab();
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="/mqtt/status" label={LL.STATUS_OF('MQTT')} />
|
<Tab value="settings" label={LL.SETTINGS_OF('MQTT')} />
|
||||||
<Tab value="/mqtt/settings" label={LL.SETTINGS_OF('MQTT')} disabled={!authenticatedContext.me.admin} />
|
<Tab value="status" label={LL.STATUS_OF('MQTT')} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<MqttStatusForm />} />
|
<Route path="status" element={<MqttStatus />} />
|
||||||
<Route
|
<Route path="settings" element={<MqttSettings />} />
|
||||||
path="settings"
|
<Route path="*" element={<Navigate replace to="settings" />} />
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<MqttSettingsForm />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<Navigate replace to="/mqtt/status" />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useState } from 'react';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { MqttSettings } from 'types';
|
import type { MqttSettingsType } from 'types';
|
||||||
import * as MqttApi from 'api/mqtt';
|
import * as MqttApi from 'api/mqtt';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -21,7 +21,7 @@ import { numberValue, updateValueDirty, useRest } from 'utils';
|
|||||||
|
|
||||||
import { createMqttSettingsValidator, validate } from 'validators';
|
import { createMqttSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
const MqttSettingsForm: FC = () => {
|
const MqttSettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -33,7 +33,7 @@ const MqttSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<MqttSettings>({
|
} = useRest<MqttSettingsType>({
|
||||||
read: MqttApi.readMqttSettings,
|
read: MqttApi.readMqttSettings,
|
||||||
update: MqttApi.updateMqttSettings
|
update: MqttApi.updateMqttSettings
|
||||||
});
|
});
|
||||||
@@ -388,6 +388,21 @@ const MqttSettingsForm: FC = () => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
|
<TextField
|
||||||
|
name="publish_time_water"
|
||||||
|
label={LL.MQTT_INT_WATER()}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">{LL.SECONDS()}</InputAdornment>
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={numberValue(data.publish_time_water)}
|
||||||
|
type="number"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={4}>
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
<TextField
|
<TextField
|
||||||
name="publish_time_sensor"
|
name="publish_time_sensor"
|
||||||
@@ -449,11 +464,11 @@ const MqttSettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF('MQTT')} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MqttSettingsForm;
|
export default MqttSettings;
|
||||||
@@ -8,13 +8,13 @@ import { useRequest } from 'alova';
|
|||||||
import type { Theme } from '@mui/material';
|
import type { Theme } from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { MqttStatus } from 'types';
|
import type { MqttStatusType } from 'types';
|
||||||
import * as MqttApi from 'api/mqtt';
|
import * as MqttApi from 'api/mqtt';
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { MqttDisconnectReason } from 'types';
|
import { MqttDisconnectReason } from 'types';
|
||||||
|
|
||||||
export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: Theme) => {
|
export const mqttStatusHighlight = ({ enabled, connected }: MqttStatusType, theme: Theme) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return theme.palette.info.main;
|
return theme.palette.info.main;
|
||||||
}
|
}
|
||||||
@@ -24,27 +24,27 @@ export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: T
|
|||||||
return theme.palette.error.main;
|
return theme.palette.error.main;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, theme: Theme) => {
|
export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatusType, theme: Theme) => {
|
||||||
if (mqtt_fails === 0) return theme.palette.success.main;
|
if (mqtt_fails === 0) return theme.palette.success.main;
|
||||||
if (mqtt_fails < 10) return theme.palette.warning.main;
|
if (mqtt_fails < 10) return theme.palette.warning.main;
|
||||||
|
|
||||||
return theme.palette.error.main;
|
return theme.palette.error.main;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatus, theme: Theme) => {
|
export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatusType, theme: Theme) => {
|
||||||
if (mqtt_queued <= 1) return theme.palette.success.main;
|
if (mqtt_queued <= 1) return theme.palette.success.main;
|
||||||
|
|
||||||
return theme.palette.warning.main;
|
return theme.palette.warning.main;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MqttStatusForm: FC = () => {
|
const MqttStatus: FC = () => {
|
||||||
const { data: data, send: loadData, error } = useRequest(MqttApi.readMqttStatus);
|
const { data: data, send: loadData, error } = useRequest(MqttApi.readMqttStatus);
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const mqttStatus = ({ enabled, connected, connect_count }: MqttStatus) => {
|
const mqttStatus = ({ enabled, connected, connect_count }: MqttStatusType) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return LL.NOT_ENABLED();
|
return LL.NOT_ENABLED();
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ const MqttStatusForm: FC = () => {
|
|||||||
return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
|
return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const disconnectReason = ({ disconnect_reason }: MqttStatus) => {
|
const disconnectReason = ({ disconnect_reason }: MqttStatusType) => {
|
||||||
switch (disconnect_reason) {
|
switch (disconnect_reason) {
|
||||||
case MqttDisconnectReason.TCP_DISCONNECTED:
|
case MqttDisconnectReason.TCP_DISCONNECTED:
|
||||||
return 'TCP disconnected';
|
return 'TCP disconnected';
|
||||||
@@ -146,11 +146,7 @@ const MqttStatusForm: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
<SectionContent title={LL.STATUS_OF('MQTT')} titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MqttStatusForm;
|
export default MqttStatus;
|
||||||
59
interface/src/framework/network/Network.tsx
Normal file
59
interface/src/framework/network/Network.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Tab } from '@mui/material';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
|
import NetworkSettings from './NetworkSettings';
|
||||||
|
import NetworkStatus from './NetworkStatus';
|
||||||
|
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||||
|
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import type { WiFiNetwork } from 'types';
|
||||||
|
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
const Network: FC = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
useLayoutTitle(LL.NETWORK(0));
|
||||||
|
|
||||||
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>();
|
||||||
|
|
||||||
|
const selectNetwork = useCallback(
|
||||||
|
(network: WiFiNetwork) => {
|
||||||
|
setSelectedNetwork(network);
|
||||||
|
navigate('settings');
|
||||||
|
},
|
||||||
|
[navigate]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deselectNetwork = useCallback(() => {
|
||||||
|
setSelectedNetwork(undefined);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiFiConnectionContext.Provider
|
||||||
|
value={{
|
||||||
|
selectedNetwork,
|
||||||
|
selectNetwork,
|
||||||
|
deselectNetwork
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RouterTabs value={routerTab}>
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.NETWORK(1))} />
|
||||||
|
<Tab value="status" label={LL.STATUS_OF(LL.NETWORK(1))} />
|
||||||
|
<Tab value="scan" label={LL.NETWORK_SCAN()} />
|
||||||
|
</RouterTabs>
|
||||||
|
<Routes>
|
||||||
|
<Route path="status" element={<NetworkStatus />} />
|
||||||
|
<Route path="scan" element={<WiFiNetworkScanner />} />
|
||||||
|
<Route path="settings" element={<NetworkSettings />} />
|
||||||
|
<Route path="*" element={<Navigate replace to="settings" />} />
|
||||||
|
</Routes>
|
||||||
|
</WiFiConnectionContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Network;
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import { Tab } from '@mui/material';
|
|
||||||
import { useCallback, useContext, useState } from 'react';
|
|
||||||
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
|
|
||||||
import NetworkSettingsForm from './NetworkSettingsForm';
|
|
||||||
import NetworkStatusForm from './NetworkStatusForm';
|
|
||||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
|
||||||
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import type { WiFiNetwork } from 'types';
|
|
||||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
const NetworkConnection: FC = () => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
useLayoutTitle(LL.NETWORK(0));
|
|
||||||
|
|
||||||
const { routerTab } = useRouterTab();
|
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>();
|
|
||||||
|
|
||||||
const selectNetwork = useCallback(
|
|
||||||
(network: WiFiNetwork) => {
|
|
||||||
setSelectedNetwork(network);
|
|
||||||
navigate('settings');
|
|
||||||
},
|
|
||||||
[navigate]
|
|
||||||
);
|
|
||||||
|
|
||||||
const deselectNetwork = useCallback(() => {
|
|
||||||
setSelectedNetwork(undefined);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WiFiConnectionContext.Provider
|
|
||||||
value={{
|
|
||||||
selectedNetwork,
|
|
||||||
selectNetwork,
|
|
||||||
deselectNetwork
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RouterTabs value={routerTab}>
|
|
||||||
<Tab value="/network/status" label={LL.STATUS_OF(LL.NETWORK(1))} />
|
|
||||||
<Tab value="/network/scan" label={LL.NETWORK_SCAN()} disabled={!authenticatedContext.me.admin} />
|
|
||||||
<Tab
|
|
||||||
value="/network/settings"
|
|
||||||
label={LL.SETTINGS_OF(LL.NETWORK(1))}
|
|
||||||
disabled={!authenticatedContext.me.admin}
|
|
||||||
/>
|
|
||||||
</RouterTabs>
|
|
||||||
<Routes>
|
|
||||||
<Route path="status" element={<NetworkStatusForm />} />
|
|
||||||
<Route
|
|
||||||
path="scan"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<WiFiNetworkScanner />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="settings"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<NetworkSettingsForm />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<Navigate replace to="/network/status" />} />
|
|
||||||
</Routes>
|
|
||||||
</WiFiConnectionContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NetworkConnection;
|
|
||||||
@@ -28,7 +28,7 @@ import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { NetworkSettings } from 'types';
|
import type { NetworkSettingsType } from 'types';
|
||||||
import * as NetworkApi from 'api/network';
|
import * as NetworkApi from 'api/network';
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import {
|
import {
|
||||||
@@ -48,7 +48,7 @@ import { updateValueDirty, useRest } from 'utils';
|
|||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { createNetworkSettingsValidator } from 'validators/network';
|
import { createNetworkSettingsValidator } from 'validators/network';
|
||||||
|
|
||||||
const WiFiSettingsForm: FC = () => {
|
const NetworkSettings: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
||||||
@@ -68,7 +68,7 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
saveData,
|
saveData,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
restartNeeded
|
restartNeeded
|
||||||
} = useRest<NetworkSettings>({
|
} = useRest<NetworkSettingsType>({
|
||||||
read: NetworkApi.readNetworkSettings,
|
read: NetworkApi.readNetworkSettings,
|
||||||
update: NetworkApi.updateNetworkSettings
|
update: NetworkApi.updateNetworkSettings
|
||||||
});
|
});
|
||||||
@@ -135,7 +135,7 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
<Typography variant="h6" color="primary">
|
||||||
WiFi
|
WiFi
|
||||||
</Typography>
|
</Typography>
|
||||||
{selectedNetwork ? (
|
{selectedNetwork ? (
|
||||||
@@ -360,11 +360,11 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF(LL.NETWORK(1))} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
{restarting ? <RestartMonitor /> : content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WiFiSettingsForm;
|
export default NetworkSettings;
|
||||||
@@ -11,18 +11,18 @@ import { useRequest } from 'alova';
|
|||||||
import type { Theme } from '@mui/material';
|
import type { Theme } from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { NetworkStatus } from 'types';
|
import type { NetworkStatusType } from 'types';
|
||||||
import * as NetworkApi from 'api/network';
|
import * as NetworkApi from 'api/network';
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { NetworkConnectionStatus } from 'types';
|
import { NetworkConnectionStatus } from 'types';
|
||||||
|
|
||||||
const isConnected = ({ status }: NetworkStatus) =>
|
const isConnected = ({ status }: NetworkStatusType) =>
|
||||||
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
||||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||||
|
|
||||||
const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme) => {
|
const networkStatusHighlight = ({ status }: NetworkStatusType, theme: Theme) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
||||||
@@ -39,7 +39,7 @@ const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const networkQualityHighlight = ({ rssi }: NetworkStatus, theme: Theme) => {
|
const networkQualityHighlight = ({ rssi }: NetworkStatusType, theme: Theme) => {
|
||||||
if (rssi <= -85) {
|
if (rssi <= -85) {
|
||||||
return theme.palette.error.main;
|
return theme.palette.error.main;
|
||||||
} else if (rssi <= -75) {
|
} else if (rssi <= -75) {
|
||||||
@@ -48,17 +48,18 @@ const networkQualityHighlight = ({ rssi }: NetworkStatus, theme: Theme) => {
|
|||||||
return theme.palette.success.main;
|
return theme.palette.success.main;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isWiFi = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
export const isWiFi = ({ status }: NetworkStatusType) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
||||||
export const isEthernet = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
export const isEthernet = ({ status }: NetworkStatusType) =>
|
||||||
|
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||||
|
|
||||||
const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatus) => {
|
const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatusType) => {
|
||||||
if (!dns_ip_1) {
|
if (!dns_ip_1) {
|
||||||
return 'none';
|
return 'none';
|
||||||
}
|
}
|
||||||
return dns_ip_1 + (!dns_ip_2 || dns_ip_2 === '0.0.0.0' ? '' : ',' + dns_ip_2);
|
return dns_ip_1 + (!dns_ip_2 || dns_ip_2 === '0.0.0.0' ? '' : ',' + dns_ip_2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const IPs = (status: NetworkStatus) => {
|
const IPs = (status: NetworkStatusType) => {
|
||||||
if (!status.local_ipv6 || status.local_ipv6 === '0000:0000:0000:0000:0000:0000:0000:0000') {
|
if (!status.local_ipv6 || status.local_ipv6 === '0000:0000:0000:0000:0000:0000:0000:0000') {
|
||||||
return status.local_ip;
|
return status.local_ip;
|
||||||
}
|
}
|
||||||
@@ -68,14 +69,14 @@ const IPs = (status: NetworkStatus) => {
|
|||||||
return status.local_ip + ', ' + status.local_ipv6;
|
return status.local_ip + ', ' + status.local_ipv6;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NetworkStatusForm: FC = () => {
|
const NetworkStatus: FC = () => {
|
||||||
const { data: data, send: loadData, error } = useRequest(NetworkApi.readNetworkStatus);
|
const { data: data, send: loadData, error } = useRequest(NetworkApi.readNetworkStatus);
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const networkStatus = ({ status }: NetworkStatus) => {
|
const networkStatus = ({ status }: NetworkStatusType) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
||||||
return LL.INACTIVE(1);
|
return LL.INACTIVE(1);
|
||||||
@@ -193,11 +194,7 @@ const NetworkStatusForm: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
<SectionContent title={LL.STATUS_OF(LL.NETWORK(1))} titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NetworkStatusForm;
|
export default NetworkStatus;
|
||||||
@@ -56,7 +56,7 @@ const WiFiNetworkScanner: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.NETWORK_SCANNER()}>
|
<SectionContent>
|
||||||
{renderNetworkScanner()}
|
{renderNetworkScanner()}
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { selectedTimeZone, timeZoneSelectItems, TIME_ZONES } from './TZ';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { NTPSettings } from 'types';
|
import type { NTPSettingsType } from 'types';
|
||||||
import * as NTPApi from 'api/ntp';
|
import * as NTPApi from 'api/ntp';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -23,7 +23,7 @@ import { updateValueDirty, useRest } from 'utils';
|
|||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||||
|
|
||||||
const NTPSettingsForm: FC = () => {
|
const NTPSettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -35,7 +35,7 @@ const NTPSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<NTPSettings>({
|
} = useRest<NTPSettingsType>({
|
||||||
read: NTPApi.readNTPSettings,
|
read: NTPApi.readNTPSettings,
|
||||||
update: NTPApi.updateNTPSettings
|
update: NTPApi.updateNTPSettings
|
||||||
});
|
});
|
||||||
@@ -130,11 +130,11 @@ const NTPSettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF('NTP')} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NTPSettingsForm;
|
export default NTPSettings;
|
||||||
@@ -22,25 +22,39 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useContext, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import type { Theme } from '@mui/material';
|
import type { Theme } from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { NTPStatus } from 'types';
|
import type { NTPStatusType } from 'types';
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import * as NTPApi from 'api/ntp';
|
import * as NTPApi from 'api/ntp';
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { NTPSyncStatus } from 'types';
|
import { NTPSyncStatus } from 'types';
|
||||||
import { formatDateTime, formatLocalDateTime } from 'utils';
|
import { formatDateTime, formatLocalDateTime } from 'utils';
|
||||||
|
|
||||||
export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE;
|
const NTPStatus: FC = () => {
|
||||||
export const isNtpEnabled = ({ status }: NTPStatus) => status !== NTPSyncStatus.NTP_DISABLED;
|
const { data: data, send: loadData, error } = useRequest(NTPApi.readNTPStatus);
|
||||||
|
|
||||||
export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => {
|
const [localTime, setLocalTime] = useState<string>('');
|
||||||
|
const [settingTime, setSettingTime] = useState<boolean>(false);
|
||||||
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const { send: updateTime } = useRequest((local_time) => NTPApi.updateTime(local_time), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
NTPApi.updateTime;
|
||||||
|
|
||||||
|
const isNtpActive = ({ status }: NTPStatusType) => status === NTPSyncStatus.NTP_ACTIVE;
|
||||||
|
const isNtpEnabled = ({ status }: NTPStatusType) => status !== NTPSyncStatus.NTP_DISABLED;
|
||||||
|
|
||||||
|
const ntpStatusHighlight = ({ status }: NTPStatusType, theme: Theme) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NTPSyncStatus.NTP_DISABLED:
|
case NTPSyncStatus.NTP_DISABLED:
|
||||||
return theme.palette.info.main;
|
return theme.palette.info.main;
|
||||||
@@ -53,22 +67,6 @@ export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const NTPStatusForm: FC = () => {
|
|
||||||
const { data: data, send: loadData, error } = useRequest(NTPApi.readNTPStatus);
|
|
||||||
|
|
||||||
const [localTime, setLocalTime] = useState<string>('');
|
|
||||||
const [settingTime, setSettingTime] = useState<boolean>(false);
|
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
const { send: updateTime } = useRequest((local_time) => NTPApi.updateTime(local_time), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
NTPApi.updateTime;
|
|
||||||
|
|
||||||
const updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => setLocalTime(event.target.value);
|
const updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => setLocalTime(event.target.value);
|
||||||
|
|
||||||
const openSetTime = () => {
|
const openSetTime = () => {
|
||||||
@@ -78,7 +76,7 @@ const NTPStatusForm: FC = () => {
|
|||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const ntpStatus = ({ status }: NTPStatus) => {
|
const ntpStatus = ({ status }: NTPStatusType) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NTPSyncStatus.NTP_DISABLED:
|
case NTPSyncStatus.NTP_DISABLED:
|
||||||
return LL.NOT_ENABLED();
|
return LL.NOT_ENABLED();
|
||||||
@@ -201,7 +199,7 @@ const NTPStatusForm: FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Box>
|
</Box>
|
||||||
{me.admin && data && !isNtpActive(data) && (
|
{data && !isNtpActive(data) && (
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button onClick={openSetTime} variant="outlined" color="primary" startIcon={<AccessTimeIcon />}>
|
<Button onClick={openSetTime} variant="outlined" color="primary" startIcon={<AccessTimeIcon />}>
|
||||||
@@ -216,11 +214,7 @@ const NTPStatusForm: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
<SectionContent title={LL.STATUS_OF('NTP')} titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NTPStatusForm;
|
export default NTPStatus;
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useContext } from 'react';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
import NTPSettingsForm from './NTPSettingsForm';
|
import NTPSettings from './NTPSettings';
|
||||||
import NTPStatusForm from './NTPStatusForm';
|
import NTPStatus from './NTPStatus';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -14,26 +12,18 @@ const NetworkTime: FC = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle('NTP');
|
useLayoutTitle('NTP');
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
|
||||||
const { routerTab } = useRouterTab();
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="/ntp/status" label={LL.STATUS_OF('NTP')} />
|
<Tab value="settings" label={LL.SETTINGS_OF('NTP')} />
|
||||||
<Tab value="/ntp/settings" label={LL.SETTINGS_OF('NTP')} disabled={!authenticatedContext.me.admin} />
|
<Tab value="status" label={LL.STATUS_OF('NTP')} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<NTPStatusForm />} />
|
<Route path="status" element={<NTPStatus />} />
|
||||||
<Route
|
<Route path="settings" element={<NTPSettings />} />
|
||||||
path="settings"
|
<Route path="*" element={<Navigate replace to="settings" />} />
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<NTPSettingsForm />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<Navigate replace to="/ntp/status" />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useState } from 'react';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { OTASettings } from 'types';
|
import type { OTASettingsType } from 'types';
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
SectionContent,
|
SectionContent,
|
||||||
ValidatedPasswordField,
|
ValidatedPasswordField,
|
||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
BlockNavigation
|
BlockNavigation,
|
||||||
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
@@ -23,7 +24,7 @@ import { numberValue, updateValueDirty, useRest } from 'utils';
|
|||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
||||||
|
|
||||||
const OTASettingsForm: FC = () => {
|
const OTASettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saveData,
|
saveData,
|
||||||
@@ -35,7 +36,7 @@ const OTASettingsForm: FC = () => {
|
|||||||
setDirtyFlags,
|
setDirtyFlags,
|
||||||
blocker,
|
blocker,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<OTASettings>({
|
} = useRest<OTASettingsType>({
|
||||||
read: SystemApi.readOTASettings,
|
read: SystemApi.readOTASettings,
|
||||||
update: SystemApi.updateOTASettings
|
update: SystemApi.updateOTASettings
|
||||||
});
|
});
|
||||||
@@ -61,6 +62,8 @@ const OTASettingsForm: FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useLayoutTitle('OTA');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
@@ -117,11 +120,11 @@ const OTASettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF('OTA')} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OTASettingsForm;
|
export default OTASettings;
|
||||||
@@ -14,9 +14,9 @@ import { useContext, useState } from 'react';
|
|||||||
|
|
||||||
import { useBlocker } from 'react-router-dom';
|
import { useBlocker } from 'react-router-dom';
|
||||||
import GenerateToken from './GenerateToken';
|
import GenerateToken from './GenerateToken';
|
||||||
import UserForm from './UserForm';
|
import User from './User';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { SecuritySettings, User } from 'types';
|
import type { SecuritySettingsType, UserType } from 'types';
|
||||||
import * as SecurityApi from 'api/security';
|
import * as SecurityApi from 'api/security';
|
||||||
import { ButtonRow, FormLoader, MessageBox, SectionContent, BlockNavigation } from 'components';
|
import { ButtonRow, FormLoader, MessageBox, SectionContent, BlockNavigation } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
@@ -24,13 +24,13 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
import { useRest } from 'utils';
|
import { useRest } from 'utils';
|
||||||
import { createUserValidator } from 'validators';
|
import { createUserValidator } from 'validators';
|
||||||
|
|
||||||
const ManageUsersForm: FC = () => {
|
const ManageUsers: FC = () => {
|
||||||
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettings>({
|
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettingsType>({
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
|
|
||||||
const [user, setUser] = useState<User>();
|
const [user, setUser] = useState<UserType>();
|
||||||
const [creating, setCreating] = useState<boolean>(false);
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
const [changed, setChanged] = useState<number>(0);
|
const [changed, setChanged] = useState<number>(0);
|
||||||
const [generatingToken, setGeneratingToken] = useState<string>();
|
const [generatingToken, setGeneratingToken] = useState<string>();
|
||||||
@@ -86,7 +86,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
|
|
||||||
const noAdminConfigured = () => !data.users.find((u) => u.admin);
|
const noAdminConfigured = () => !data.users.find((u) => u.admin);
|
||||||
|
|
||||||
const removeUser = (toRemove: User) => {
|
const removeUser = (toRemove: UserType) => {
|
||||||
const users = data.users.filter((u) => u.username !== toRemove.username);
|
const users = data.users.filter((u) => u.username !== toRemove.username);
|
||||||
updateDataValue({ ...data, users });
|
updateDataValue({ ...data, users });
|
||||||
setChanged(changed + 1);
|
setChanged(changed + 1);
|
||||||
@@ -101,7 +101,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const editUser = (toEdit: User) => {
|
const editUser = (toEdit: UserType) => {
|
||||||
setCreating(false);
|
setCreating(false);
|
||||||
setUser({ ...toEdit });
|
setUser({ ...toEdit });
|
||||||
};
|
};
|
||||||
@@ -219,7 +219,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
|
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
|
||||||
<UserForm
|
<User
|
||||||
user={user}
|
user={user}
|
||||||
setUser={setUser}
|
setUser={setUser}
|
||||||
creating={creating}
|
creating={creating}
|
||||||
@@ -232,11 +232,11 @@ const ManageUsersForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.MANAGE_USERS()} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ManageUsersForm;
|
export default ManageUsers;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||||
import ManageUsersForm from './ManageUsersForm';
|
import ManageUsers from './ManageUsers';
|
||||||
import SecuritySettingsForm from './SecuritySettingsForm';
|
import SecuritySettings from './SecuritySettings';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
|
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
|
||||||
@@ -17,13 +17,13 @@ const Security: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="/security/users" label={LL.MANAGE_USERS()} />
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.SECURITY(1))} />
|
||||||
<Tab value="/security/settings" label={LL.SETTINGS_OF(LL.SECURITY(1))} />
|
<Tab value="users" label={LL.MANAGE_USERS()} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="users" element={<ManageUsersForm />} />
|
<Route path="users" element={<ManageUsers />} />
|
||||||
<Route path="settings" element={<SecuritySettingsForm />} />
|
<Route path="settings" element={<SecuritySettings />} />
|
||||||
<Route path="*" element={<Navigate replace to="/security/users" />} />
|
<Route path="*" element={<Navigate replace to="settings" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import { useContext, useState } from 'react';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { SecuritySettings } from 'types';
|
import type { SecuritySettingsType } from 'types';
|
||||||
import * as SecurityApi from 'api/security';
|
import * as SecurityApi from 'api/security';
|
||||||
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField, BlockNavigation } from 'components';
|
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField, BlockNavigation } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { updateValueDirty, useRest } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
|
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
|
||||||
|
|
||||||
const SecuritySettingsForm: FC = () => {
|
const SecuritySettings: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
@@ -29,7 +29,7 @@ const SecuritySettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<SecuritySettings>({
|
} = useRest<SecuritySettingsType>({
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
@@ -96,11 +96,11 @@ const SecuritySettingsForm: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SETTINGS_OF(LL.SECURITY(1))} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SecuritySettingsForm;
|
export default SecuritySettings;
|
||||||
@@ -8,7 +8,7 @@ import type Schema from 'async-validator';
|
|||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { User } from 'types';
|
import type { UserType } from 'types';
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { BlockFormControlLabel, ValidatedPasswordField, ValidatedTextField } from 'components';
|
import { BlockFormControlLabel, ValidatedPasswordField, ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
@@ -19,14 +19,14 @@ interface UserFormProps {
|
|||||||
creating: boolean;
|
creating: boolean;
|
||||||
validator: Schema;
|
validator: Schema;
|
||||||
|
|
||||||
user?: User;
|
user?: UserType;
|
||||||
setUser: React.Dispatch<React.SetStateAction<User | undefined>>;
|
setUser: React.Dispatch<React.SetStateAction<UserType | undefined>>;
|
||||||
|
|
||||||
onDoneEditing: () => void;
|
onDoneEditing: () => void;
|
||||||
onCancelEditing: () => void;
|
onCancelEditing: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserForm: FC<UserFormProps> = ({ creating, validator, user, setUser, onDoneEditing, onCancelEditing }) => {
|
const User: FC<UserFormProps> = ({ creating, validator, user, setUser, onDoneEditing, onCancelEditing }) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setUser);
|
const updateFormValue = updateValue(setUser);
|
||||||
@@ -104,4 +104,4 @@ const UserForm: FC<UserFormProps> = ({ creating, validator, user, setUser, onDon
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserForm;
|
export default User;
|
||||||
154
interface/src/framework/system/ESPSystemStatus.tsx
Normal file
154
interface/src/framework/system/ESPSystemStatus.tsx
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import AppsIcon from '@mui/icons-material/Apps';
|
||||||
|
import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard';
|
||||||
|
import DevicesIcon from '@mui/icons-material/Devices';
|
||||||
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
|
import MemoryIcon from '@mui/icons-material/Memory';
|
||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
import SdCardAlertIcon from '@mui/icons-material/SdCardAlert';
|
||||||
|
import SdStorageIcon from '@mui/icons-material/SdStorage';
|
||||||
|
import { Avatar, Box, Button, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@mui/material';
|
||||||
|
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
function formatNumber(num: number) {
|
||||||
|
return new Intl.NumberFormat().format(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESPSystemStatus: FC = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
useLayoutTitle(LL.STATUS_OF('ESP32'));
|
||||||
|
|
||||||
|
const { data: data, send: loadData, error } = useRequest(SystemApi.readESPSystemStatus, { force: true });
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<DevicesIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="SDK" secondary={data.arduino_version + ' / ESP-IDF ' + data.sdk_version} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<DeveloperBoardIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary="CPU"
|
||||||
|
secondary={
|
||||||
|
data.esp_platform +
|
||||||
|
'/' +
|
||||||
|
data.cpu_type +
|
||||||
|
' (rev.' +
|
||||||
|
data.cpu_rev +
|
||||||
|
', ' +
|
||||||
|
(data.cpu_cores == 1 ? 'single-core)' : 'dual-core)') +
|
||||||
|
' @ ' +
|
||||||
|
data.cpu_freq_mhz +
|
||||||
|
' Mhz'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<MemoryIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.HEAP()}
|
||||||
|
secondary={formatNumber(data.free_heap) + ' KB / ' + formatNumber(data.max_alloc_heap) + ' KB '}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
{data.psram_size !== undefined && data.free_psram !== undefined && (
|
||||||
|
<>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<AppsIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.PSRAM()}
|
||||||
|
secondary={formatNumber(data.psram_size) + ' KB / ' + formatNumber(data.free_psram) + ' KB'}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<SdStorageIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.FLASH()}
|
||||||
|
secondary={
|
||||||
|
formatNumber(data.flash_chip_size) + ' KB / ' + (data.flash_chip_speed / 1000000).toFixed(0) + ' MHz'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<SdCardAlertIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.APPSIZE()}
|
||||||
|
secondary={
|
||||||
|
data.partition + ': ' + formatNumber(data.app_used) + ' KB / ' + formatNumber(data.app_free) + ' KB'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5f9a5f', color: 'white' }}>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.FILESYSTEM()}
|
||||||
|
secondary={formatNumber(data.fs_used) + ' KB / ' + formatNumber(data.fs_free) + ' KB'}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
</List>
|
||||||
|
<Box display="flex" flexWrap="wrap">
|
||||||
|
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
||||||
|
<ButtonRow>
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
||||||
|
{LL.REFRESH()}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ESPSystemStatus;
|
||||||
@@ -1,53 +1,42 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useContext } from 'react';
|
import { useContext, type FC } from 'react';
|
||||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||||
import OTASettingsForm from './OTASettingsForm';
|
|
||||||
import SystemLog from './SystemLog';
|
import SystemLog from './SystemLog';
|
||||||
import SystemStatusForm from './SystemStatusForm';
|
import SystemStatus from './SystemStatus';
|
||||||
import UploadFileForm from './UploadFileForm';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from 'components';
|
import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import SystemActivity from 'project/SystemActivity';
|
||||||
|
|
||||||
const System: FC = () => {
|
const System: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
useLayoutTitle(LL.SYSTEM(0));
|
useLayoutTitle(LL.SYSTEM(0));
|
||||||
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
const { routerTab } = useRouterTab();
|
const { routerTab } = useRouterTab();
|
||||||
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="/system/status" label={LL.STATUS_OF(LL.SYSTEM(1))} />
|
<Tab value="status" label={LL.STATUS_OF('')} />
|
||||||
<Tab value="/system/log" label={LL.LOG_OF(LL.SYSTEM(2))} />
|
<Tab value="activity" label={LL.ACTIVITY()} />
|
||||||
<Tab value="/system/ota" label={LL.SETTINGS_OF('OTA')} disabled={!me.admin} />
|
<Tab disabled={!me.admin} value="log" label={me.admin ? LL.LOG_OF('') : ''} />
|
||||||
<Tab value="/system/upload" label={LL.UPLOAD_DOWNLOAD()} disabled={!me.admin} />
|
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<SystemStatusForm />} />
|
<Route path="status" element={<SystemStatus />} />
|
||||||
<Route path="log" element={<SystemLog />} />
|
<Route path="activity" element={<SystemActivity />} />
|
||||||
<Route
|
<Route
|
||||||
path="ota"
|
path="log"
|
||||||
element={
|
element={
|
||||||
<RequireAdmin>
|
<RequireAdmin>
|
||||||
<OTASettingsForm />
|
<SystemLog />
|
||||||
</RequireAdmin>
|
</RequireAdmin>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route path="*" element={<Navigate replace to="status" />} />
|
||||||
path="upload"
|
|
||||||
element={
|
|
||||||
<RequireAdmin>
|
|
||||||
<UploadFileForm />
|
|
||||||
</RequireAdmin>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<Navigate replace to="/system/status" />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { addAccessTokenParameter } from 'api/authentication';
|
|||||||
import { EVENT_SOURCE_ROOT } from 'api/endpoints';
|
import { EVENT_SOURCE_ROOT } from 'api/endpoints';
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
|
|
||||||
import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation } from 'components';
|
import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { LogLevel } from 'types';
|
import { LogLevel } from 'types';
|
||||||
@@ -50,6 +50,8 @@ const levelLabel = (level: LogLevel) => {
|
|||||||
const SystemLog: FC = () => {
|
const SystemLog: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
useLayoutTitle(LL.LOG_OF(''));
|
||||||
|
|
||||||
const { loadData, data, updateDataValue, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
|
const { loadData, data, updateDataValue, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
|
||||||
useRest<LogSettings>({
|
useRest<LogSettings>({
|
||||||
read: SystemApi.readLogSettings,
|
read: SystemApi.readLogSettings,
|
||||||
@@ -232,7 +234,7 @@ const SystemLog: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.LOG_OF(LL.SYSTEM(2))} titleGutter id="log-window">
|
<SectionContent id="log-window">
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
|
|||||||
297
interface/src/framework/system/SystemStatus.tsx
Normal file
297
interface/src/framework/system/SystemStatus.tsx
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
import CastIcon from '@mui/icons-material/Cast';
|
||||||
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
|
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
||||||
|
import MemoryIcon from '@mui/icons-material/Memory';
|
||||||
|
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
|
||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||||
|
import TimerIcon from '@mui/icons-material/Timer';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Divider,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
useTheme
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import { useContext, type FC, useState } from 'react';
|
||||||
|
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { dialogStyle } from 'CustomTheme';
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import * as EMSESP from 'project/api';
|
||||||
|
import { busConnectionStatus } from 'project/types';
|
||||||
|
import { NTPSyncStatus } from 'types';
|
||||||
|
|
||||||
|
const SystemStatus: FC = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
useLayoutTitle(LL.STATUS_OF(''));
|
||||||
|
|
||||||
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
|
const [confirmScan, setConfirmScan] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||||
|
|
||||||
|
const { send: scanDevices } = useRequest(EMSESP.scanDevices, {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const formatDurationSec = (duration_sec: number) => {
|
||||||
|
const days = Math.trunc((duration_sec * 1000) / 86400000);
|
||||||
|
const hours = Math.trunc((duration_sec * 1000) / 3600000) % 24;
|
||||||
|
const minutes = Math.trunc((duration_sec * 1000) / 60000) % 60;
|
||||||
|
const seconds = Math.trunc((duration_sec * 1000) / 1000) % 60;
|
||||||
|
|
||||||
|
let formatted = '';
|
||||||
|
if (days) {
|
||||||
|
formatted += LL.NUM_DAYS({ num: days }) + ' ';
|
||||||
|
}
|
||||||
|
if (hours) {
|
||||||
|
formatted += LL.NUM_HOURS({ num: hours }) + ' ';
|
||||||
|
}
|
||||||
|
if (minutes) {
|
||||||
|
formatted += LL.NUM_MINUTES({ num: minutes }) + ' ';
|
||||||
|
}
|
||||||
|
formatted += LL.NUM_SECONDS({ num: seconds });
|
||||||
|
return formatted;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatNumber(num: number) {
|
||||||
|
return new Intl.NumberFormat().format(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
const busStatus = () => {
|
||||||
|
if (data) {
|
||||||
|
switch (data.status) {
|
||||||
|
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||||
|
return LL.CONNECTED(0) + ' (' + formatDurationSec(data.bus_uptime) + ')';
|
||||||
|
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||||
|
return LL.TX_ISSUES();
|
||||||
|
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||||
|
return LL.DISCONNECTED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'Unknown';
|
||||||
|
};
|
||||||
|
|
||||||
|
const busStatusHighlight = () => {
|
||||||
|
switch (data.status) {
|
||||||
|
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||||
|
return theme.palette.warning.main;
|
||||||
|
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||||
|
return theme.palette.success.main;
|
||||||
|
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||||
|
return theme.palette.error.main;
|
||||||
|
default:
|
||||||
|
return theme.palette.warning.main;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ntpStatus = () => {
|
||||||
|
switch (data.ntp_status) {
|
||||||
|
case NTPSyncStatus.NTP_DISABLED:
|
||||||
|
return LL.NOT_ENABLED();
|
||||||
|
case NTPSyncStatus.NTP_INACTIVE:
|
||||||
|
return LL.INACTIVE(0);
|
||||||
|
case NTPSyncStatus.NTP_ACTIVE:
|
||||||
|
return LL.ACTIVE();
|
||||||
|
default:
|
||||||
|
return LL.UNKNOWN();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ntpStatusHighlight = () => {
|
||||||
|
switch (data.ntp_status) {
|
||||||
|
case NTPSyncStatus.NTP_DISABLED:
|
||||||
|
return theme.palette.info.main;
|
||||||
|
case NTPSyncStatus.NTP_INACTIVE:
|
||||||
|
return theme.palette.error.main;
|
||||||
|
case NTPSyncStatus.NTP_ACTIVE:
|
||||||
|
return theme.palette.success.main;
|
||||||
|
default:
|
||||||
|
return theme.palette.error.main;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeHighlight = (value: boolean) => (value ? theme.palette.success.main : theme.palette.info.main);
|
||||||
|
|
||||||
|
const scan = async () => {
|
||||||
|
await scanDevices()
|
||||||
|
.then(() => {
|
||||||
|
toast.info(LL.SCANNING() + '...');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(err.message);
|
||||||
|
});
|
||||||
|
setConfirmScan(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderScanDialog = () => (
|
||||||
|
<Dialog sx={dialogStyle} open={confirmScan} onClose={() => setConfirmScan(false)}>
|
||||||
|
<DialogTitle>{LL.SCAN_DEVICES()}</DialogTitle>
|
||||||
|
<DialogContent dividers>{LL.EMS_SCAN()}</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button startIcon={<CancelIcon />} variant="outlined" onClick={() => setConfirmScan(false)} color="secondary">
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button startIcon={<PermScanWifiIcon />} variant="outlined" onClick={scan} color="primary">
|
||||||
|
{LL.SCAN()}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: busStatusHighlight(), color: 'white' }}>
|
||||||
|
<DirectionsBusIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary={LL.EMS_BUS_STATUS()} secondary={busStatus()} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={TimerIcon}
|
||||||
|
bgcolor="#c5572c"
|
||||||
|
label={LL.UPTIME()}
|
||||||
|
text={formatDurationSec(data.uptime)}
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: '#5d89f7', color: 'white' }}>
|
||||||
|
<DeviceHubIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={LL.ACTIVE_DEVICES()}
|
||||||
|
secondary={
|
||||||
|
LL.NUM_DEVICES({ num: data.num_devices }) +
|
||||||
|
', ' +
|
||||||
|
LL.NUM_TEMP_SENSORS({ num: data.num_sensors }) +
|
||||||
|
', ' +
|
||||||
|
LL.NUM_ANALOG_SENSORS({ num: data.num_analogs })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{me.admin && (
|
||||||
|
<Button
|
||||||
|
startIcon={<PermScanWifiIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => setConfirmScan(true)}
|
||||||
|
>
|
||||||
|
{LL.SCAN_DEVICES()}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={BuildIcon}
|
||||||
|
bgcolor="#134ba2"
|
||||||
|
label={LL.EMS_ESP_VER()}
|
||||||
|
text={data.emsesp_version}
|
||||||
|
to="/settings/upload"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
{/* TODO: translate */}
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={MemoryIcon}
|
||||||
|
bgcolor="#68374d"
|
||||||
|
label="System Memory"
|
||||||
|
text={formatNumber(data.free_heap) + ' KB'}
|
||||||
|
to="/settings/espsystemstatus"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={DeviceHubIcon}
|
||||||
|
bgcolor={activeHighlight(data.mqtt_status)}
|
||||||
|
label={LL.STATUS_OF('MQTT')}
|
||||||
|
text={data.mqtt_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
|
to="/settings/mqtt/status"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={AccessTimeIcon}
|
||||||
|
bgcolor={ntpStatusHighlight()}
|
||||||
|
label={LL.STATUS_OF('NTP')}
|
||||||
|
text={ntpStatus()}
|
||||||
|
to="/settings/ntp/status"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={CastIcon}
|
||||||
|
bgcolor={activeHighlight(data.ota_status)}
|
||||||
|
label={LL.STATUS_OF('OTA')}
|
||||||
|
text={data.ota_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
|
to="/settings/ota"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
<ListMenuItem
|
||||||
|
disabled={!me.admin}
|
||||||
|
icon={SettingsInputAntennaIcon}
|
||||||
|
bgcolor={activeHighlight(data.ota_status)}
|
||||||
|
label={LL.ACCESS_POINT(0)}
|
||||||
|
text={data.ap_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
|
to="/settings/ap/status"
|
||||||
|
/>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
</List>
|
||||||
|
|
||||||
|
{renderScanDialog()}
|
||||||
|
|
||||||
|
<Box mt={2} display="flex" flexWrap="wrap">
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
||||||
|
{LL.REFRESH()}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemStatus;
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
import AppsIcon from '@mui/icons-material/Apps';
|
|
||||||
import BuildIcon from '@mui/icons-material/Build';
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
|
||||||
import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard';
|
|
||||||
import DevicesIcon from '@mui/icons-material/Devices';
|
|
||||||
import FolderIcon from '@mui/icons-material/Folder';
|
|
||||||
import MemoryIcon from '@mui/icons-material/Memory';
|
|
||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
||||||
import SdCardAlertIcon from '@mui/icons-material/SdCardAlert';
|
|
||||||
import SdStorageIcon from '@mui/icons-material/SdStorage';
|
|
||||||
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
|
|
||||||
import TimerIcon from '@mui/icons-material/Timer';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
Divider,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
import { useRequest } from 'alova';
|
|
||||||
import { useContext, useState } from 'react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import RestartMonitor from './RestartMonitor';
|
|
||||||
import SystemStatusVersionDialog from './SystemStatusVersionDialog';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
|
||||||
import * as SystemApi from 'api/system';
|
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
function formatNumber(num: number) {
|
|
||||||
return new Intl.NumberFormat().format(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SystemStatusForm: FC = () => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
|
|
||||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
|
||||||
const [versionDialogOpen, setVersionDialogOpen] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { send: factoryResetCommand } = useRequest(SystemApi.factoryReset(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { send: partitionCommand } = useRequest(SystemApi.partition(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
|
||||||
|
|
||||||
const restart = async () => {
|
|
||||||
setProcessing(true);
|
|
||||||
await restartCommand()
|
|
||||||
.then(() => {
|
|
||||||
setRestarting(true);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error(err.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setConfirmRestart(false);
|
|
||||||
setProcessing(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const factoryReset = async () => {
|
|
||||||
setProcessing(true);
|
|
||||||
await factoryResetCommand()
|
|
||||||
.then(() => {
|
|
||||||
setRestarting(true);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error(err.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setConfirmFactoryReset(false);
|
|
||||||
setProcessing(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const partition = async () => {
|
|
||||||
setProcessing(true);
|
|
||||||
await partitionCommand()
|
|
||||||
.then(() => {
|
|
||||||
setRestarting(true);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error(err.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setConfirmRestart(false);
|
|
||||||
setProcessing(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderRestartDialog = () => (
|
|
||||||
<Dialog sx={dialogStyle} open={confirmRestart} onClose={() => setConfirmRestart(false)}>
|
|
||||||
<DialogTitle>{LL.RESTART()}</DialogTitle>
|
|
||||||
<DialogContent dividers>{LL.RESTART_CONFIRM()}</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
startIcon={<CancelIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setConfirmRestart(false)}
|
|
||||||
disabled={processing}
|
|
||||||
color="secondary"
|
|
||||||
>
|
|
||||||
{LL.CANCEL()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<PowerSettingsNewIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={restart}
|
|
||||||
disabled={processing}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{LL.RESTART()}
|
|
||||||
</Button>
|
|
||||||
{data?.has_loader && (
|
|
||||||
<Button
|
|
||||||
startIcon={<PowerSettingsNewIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={partition}
|
|
||||||
disabled={processing}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
EMS-ESP-Loader
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderFactoryResetDialog = () => (
|
|
||||||
<Dialog sx={dialogStyle} open={confirmFactoryReset} onClose={() => setConfirmFactoryReset(false)}>
|
|
||||||
<DialogTitle>{LL.FACTORY_RESET()}</DialogTitle>
|
|
||||||
<DialogContent dividers>{LL.SYSTEM_FACTORY_TEXT_DIALOG()}</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
startIcon={<CancelIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setConfirmFactoryReset(false)}
|
|
||||||
disabled={processing}
|
|
||||||
color="secondary"
|
|
||||||
>
|
|
||||||
{LL.CANCEL()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={factoryReset}
|
|
||||||
disabled={processing}
|
|
||||||
color="error"
|
|
||||||
>
|
|
||||||
{LL.FACTORY_RESET()}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = () => {
|
|
||||||
if (!data) {
|
|
||||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<BuildIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary={LL.EMS_ESP_VER()} secondary={data.emsesp_version} />
|
|
||||||
<Button color="primary" onClick={() => setVersionDialogOpen(true)}>
|
|
||||||
{LL.VERSION_CHECK(0)}
|
|
||||||
</Button>
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<TimerIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary={LL.UPTIME()} secondary={data.uptime} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<DevicesIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="SDK" secondary={data.arduino_version + ' / ESP-IDF ' + data.sdk_version} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<DeveloperBoardIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary="CPU"
|
|
||||||
secondary={
|
|
||||||
data.esp_platform +
|
|
||||||
'/' +
|
|
||||||
data.cpu_type +
|
|
||||||
' (rev.' +
|
|
||||||
data.cpu_rev +
|
|
||||||
', ' +
|
|
||||||
(data.cpu_cores == 1 ? 'single-core)' : 'dual-core)') +
|
|
||||||
' @ ' +
|
|
||||||
data.cpu_freq_mhz +
|
|
||||||
' Mhz'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<MemoryIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.HEAP()}
|
|
||||||
secondary={formatNumber(data.free_heap) + ' KB / ' + formatNumber(data.max_alloc_heap) + ' KB '}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{data.psram_size !== undefined && data.free_psram !== undefined && (
|
|
||||||
<>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<AppsIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.PSRAM()}
|
|
||||||
secondary={formatNumber(data.psram_size) + ' KB / ' + formatNumber(data.free_psram) + ' KB'}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<SdStorageIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.FLASH()}
|
|
||||||
secondary={
|
|
||||||
formatNumber(data.flash_chip_size) + ' KB / ' + (data.flash_chip_speed / 1000000).toFixed(0) + ' MHz'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<SdCardAlertIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.APPSIZE()}
|
|
||||||
secondary={
|
|
||||||
data.partition + ': ' + formatNumber(data.app_used) + ' KB / ' + formatNumber(data.app_free) + ' KB'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<FolderIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.FILESYSTEM()}
|
|
||||||
secondary={formatNumber(data.fs_used) + ' KB / ' + formatNumber(data.fs_free) + ' KB'}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
</List>
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
|
||||||
<ButtonRow>
|
|
||||||
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
|
||||||
{LL.REFRESH()}
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</Box>
|
|
||||||
{me.admin && (
|
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
|
||||||
<ButtonRow>
|
|
||||||
<Button
|
|
||||||
startIcon={<PowerSettingsNewIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => setConfirmRestart(true)}
|
|
||||||
>
|
|
||||||
{LL.RESTART()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setConfirmFactoryReset(true)}
|
|
||||||
color="error"
|
|
||||||
>
|
|
||||||
{LL.FACTORY_RESET()}
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
{renderRestartDialog()}
|
|
||||||
{renderFactoryResetDialog()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionContent title={LL.STATUS_OF(LL.SYSTEM(1))} titleGutter>
|
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
|
||||||
{data && (
|
|
||||||
<SystemStatusVersionDialog
|
|
||||||
open={versionDialogOpen}
|
|
||||||
onClose={() => setVersionDialogOpen(false)}
|
|
||||||
version={data.emsesp_version}
|
|
||||||
platform={data.esp_platform}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SystemStatusForm;
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Link, Typography } from '@mui/material';
|
|
||||||
import { useRequest } from 'alova';
|
|
||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
|
||||||
import * as SystemApi from 'api/system';
|
|
||||||
|
|
||||||
import MessageBox from 'components/MessageBox';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
type SystemStatusVersionDialogProps = {
|
|
||||||
open: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
version: string;
|
|
||||||
platform: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SystemStatusVersionDialog = ({ open, onClose, version, platform }: SystemStatusVersionDialogProps) => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
const { send: getLatestVersion, data: latestVersion } = useRequest(SystemApi.getStableVersion, {
|
|
||||||
immediate: false,
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
const { send: getLatestDevVersion, data: latestDevVersion } = useRequest(SystemApi.getDevVersion, {
|
|
||||||
immediate: false,
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
|
||||||
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
|
||||||
|
|
||||||
const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
|
|
||||||
const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
|
|
||||||
|
|
||||||
const uploadURL = window.location.origin + '/system/upload';
|
|
||||||
|
|
||||||
const connected = latestVersion && latestDevVersion;
|
|
||||||
|
|
||||||
const getVersions = useCallback(async () => {
|
|
||||||
await getLatestVersion();
|
|
||||||
await getLatestDevVersion();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (open) {
|
|
||||||
void getVersions();
|
|
||||||
}
|
|
||||||
}, [getVersions, open]);
|
|
||||||
|
|
||||||
const getBinURL = (v: string) => 'EMS-ESP-' + v.replaceAll('.', '_') + '-' + platform.replaceAll('-', '_') + '.bin';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
|
|
||||||
<DialogTitle>{LL.VERSION_CHECK(1)}</DialogTitle>
|
|
||||||
<DialogContent dividers>
|
|
||||||
<MessageBox my={0} level="info" message={LL.VERSION_ON() + ' ' + version + ' (' + platform + ')'} />
|
|
||||||
{latestVersion && (
|
|
||||||
<Box mt={2} mb={2}>
|
|
||||||
{LL.THE_LATEST()} <b>{LL.OFFICIAL()}</b> {LL.RELEASE_IS()} <b>{latestVersion}</b>
|
|
||||||
(
|
|
||||||
<Link target="_blank" href={STABLE_RELNOTES_URL} color="primary">
|
|
||||||
{LL.RELEASE_NOTES()}
|
|
||||||
</Link>
|
|
||||||
) (
|
|
||||||
<Link
|
|
||||||
target="_blank"
|
|
||||||
href={STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion)}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{LL.DOWNLOAD(1)}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{latestDevVersion && (
|
|
||||||
<Box mt={2} mb={2}>
|
|
||||||
{LL.THE_LATEST()} <b>{LL.DEVELOPMENT()}</b> {LL.RELEASE_IS()}
|
|
||||||
<b>{latestDevVersion}</b>
|
|
||||||
(
|
|
||||||
<Link target="_blank" href={DEV_RELNOTES_URL} color="primary">
|
|
||||||
{LL.RELEASE_NOTES()}
|
|
||||||
</Link>
|
|
||||||
) (
|
|
||||||
<Link target="_blank" href={DEV_URL + getBinURL(latestDevVersion)} color="primary">
|
|
||||||
{LL.DOWNLOAD(1)}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{connected && (
|
|
||||||
<Box color="warning.main" mt={2}>
|
|
||||||
<Typography variant="body2">
|
|
||||||
{LL.USE()}
|
|
||||||
<Link href={uploadURL} color="primary">
|
|
||||||
{LL.UPLOAD()}
|
|
||||||
</Link>
|
|
||||||
{LL.SYSTEM_APPLY_FIRMWARE()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{!connected && <MessageBox my={2} level="warning" message="No internet connection" />}
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button variant="outlined" onClick={onClose} color="secondary">
|
|
||||||
{LL.CLOSE()}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SystemStatusVersionDialog;
|
|
||||||
308
interface/src/framework/system/UploadDownload.tsx
Normal file
308
interface/src/framework/system/UploadDownload.tsx
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
|
import { Typography, Button, Box, Link } from '@mui/material';
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import { useState, type FC } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import RestartMonitor from './RestartMonitor';
|
||||||
|
|
||||||
|
import * as SystemApi from 'api/system';
|
||||||
|
import { FormLoader, SectionContent, SingleUpload, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
import * as EMSESP from 'project/api';
|
||||||
|
|
||||||
|
const UploadDownload: FC = () => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
const [md5, setMd5] = useState<string>();
|
||||||
|
|
||||||
|
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
const { send: getCustomizations, onSuccess: onSuccessGetCustomizations } = useRequest(EMSESP.getCustomizations(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
const { send: getAPI, onSuccess: onGetAPI } = useRequest((data) => EMSESP.API(data), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: data, send: loadData, error } = useRequest(SystemApi.readESPSystemStatus, { force: true });
|
||||||
|
|
||||||
|
const { data: latestVersion } = useRequest(SystemApi.getStableVersion, {
|
||||||
|
immediate: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion, {
|
||||||
|
immediate: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
||||||
|
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
||||||
|
|
||||||
|
const STABLE_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
|
||||||
|
const DEV_RELNOTES_URL = 'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
|
||||||
|
|
||||||
|
const getBinURL = (v: string) =>
|
||||||
|
'EMS-ESP-' + v.replaceAll('.', '_') + '-' + data.esp_platform.replaceAll('-', '_') + '.bin';
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading: isUploading,
|
||||||
|
uploading: progress,
|
||||||
|
send: sendUpload,
|
||||||
|
onSuccess: onSuccessUpload,
|
||||||
|
abort: cancelUpload
|
||||||
|
} = useRequest(SystemApi.uploadFile, {
|
||||||
|
immediate: false,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
onSuccessUpload(({ data }: any) => {
|
||||||
|
if (data) {
|
||||||
|
setMd5(data.md5);
|
||||||
|
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||||
|
} else {
|
||||||
|
setRestarting(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const startUpload = async (files: File[]) => {
|
||||||
|
await sendUpload(files[0]).catch((err) => {
|
||||||
|
if (err.message === 'The user aborted a request') {
|
||||||
|
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||||
|
} else if (err.message === 'Network Error') {
|
||||||
|
toast.warning('Invalid file extension or incompatible bin file');
|
||||||
|
} else {
|
||||||
|
toast.error(err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveFile = (json: any, endpoint: string) => {
|
||||||
|
const anchor = document.createElement('a');
|
||||||
|
anchor.href = URL.createObjectURL(
|
||||||
|
new Blob([JSON.stringify(json, null, 2)], {
|
||||||
|
type: 'text/plain'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
anchor.download = 'emsesp_' + endpoint;
|
||||||
|
anchor.click();
|
||||||
|
URL.revokeObjectURL(anchor.href);
|
||||||
|
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||||
|
};
|
||||||
|
|
||||||
|
onSuccessGetSettings((event) => {
|
||||||
|
saveFile(event.data, 'settings.json');
|
||||||
|
});
|
||||||
|
onSuccessGetCustomizations((event) => {
|
||||||
|
saveFile(event.data, 'customizations.json');
|
||||||
|
});
|
||||||
|
onSuccessGetEntities((event) => {
|
||||||
|
saveFile(event.data, 'entities.json');
|
||||||
|
});
|
||||||
|
onSuccessGetSchedule((event) => {
|
||||||
|
saveFile(event.data, 'schedule.json');
|
||||||
|
});
|
||||||
|
onGetAPI((event) => {
|
||||||
|
saveFile(event.data, event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt');
|
||||||
|
});
|
||||||
|
|
||||||
|
const downloadSettings = async () => {
|
||||||
|
await getSettings().catch((error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadCustomizations = async () => {
|
||||||
|
await getCustomizations().catch((error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadEntities = async () => {
|
||||||
|
await getEntities().catch((error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadSchedule = async () => {
|
||||||
|
await getSchedule()
|
||||||
|
.catch((error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const callAPI = async (device: string, entity: string) => {
|
||||||
|
await getAPI({ device, entity, id: 0 }).catch((error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutTitle(LL.UPLOAD_DOWNLOAD());
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
||||||
|
{LL.EMS_ESP_VER()}
|
||||||
|
</Typography>
|
||||||
|
<Box p={2} border="2px solid grey" borderRadius={2}>
|
||||||
|
{LL.VERSION_ON() + ' '}
|
||||||
|
<b>{data.emsesp_version}</b> ({data.esp_platform})
|
||||||
|
{latestVersion && (
|
||||||
|
<Box mt={2}>
|
||||||
|
{LL.THE_LATEST()} {LL.OFFICIAL()} {LL.RELEASE_IS()} <b>{latestVersion}</b>
|
||||||
|
(
|
||||||
|
<Link target="_blank" href={STABLE_RELNOTES_URL} color="primary">
|
||||||
|
{LL.RELEASE_NOTES()}
|
||||||
|
</Link>
|
||||||
|
) (
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion)}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.DOWNLOAD(1)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{latestDevVersion && (
|
||||||
|
<Box mt={2}>
|
||||||
|
{LL.THE_LATEST()} {LL.DEVELOPMENT()} {LL.RELEASE_IS()}
|
||||||
|
<b>{latestDevVersion}</b>
|
||||||
|
(
|
||||||
|
<Link target="_blank" href={DEV_RELNOTES_URL} color="primary">
|
||||||
|
{LL.RELEASE_NOTES()}
|
||||||
|
</Link>
|
||||||
|
) (
|
||||||
|
<Link target="_blank" href={DEV_URL + getBinURL(latestDevVersion)} color="primary">
|
||||||
|
{LL.DOWNLOAD(1)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
|
{LL.UPLOAD()}
|
||||||
|
</Typography>
|
||||||
|
<Box mb={2} color="warning.main">
|
||||||
|
<Typography variant="body2">
|
||||||
|
{LL.UPLOAD_TEXT()}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{LL.RESTART_TEXT(1)}.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
{md5 && (
|
||||||
|
<Box mb={2}>
|
||||||
|
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<SingleUpload onDrop={startUpload} onCancel={cancelUpload} isUploading={isUploading} progress={progress} />
|
||||||
|
{!isUploading && (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary">
|
||||||
|
{LL.DOWNLOAD(0)}
|
||||||
|
</Typography>
|
||||||
|
<Box color="warning.main">
|
||||||
|
<Typography mb={1} variant="body2">
|
||||||
|
{LL.HELP_INFORMATION_4()}
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => callAPI('system', 'info')}
|
||||||
|
>
|
||||||
|
{LL.SUPPORT_INFORMATION(0)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => callAPI('system', 'allvalues')}
|
||||||
|
>
|
||||||
|
All Values
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box color="warning.main">
|
||||||
|
<Typography mt={2} mb={1} variant="body2">
|
||||||
|
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={downloadSettings}
|
||||||
|
>
|
||||||
|
{LL.SETTINGS_OF('')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box color="warning.main">
|
||||||
|
<Typography mt={2} mb={1} variant="body2">
|
||||||
|
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={downloadCustomizations}
|
||||||
|
>
|
||||||
|
{LL.CUSTOMIZATIONS()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={downloadEntities}
|
||||||
|
>
|
||||||
|
{LL.CUSTOM_ENTITIES(0)}
|
||||||
|
</Button>
|
||||||
|
<Box color="warning.main">
|
||||||
|
<Typography mt={2} mb={1} variant="body2">
|
||||||
|
{LL.DOWNLOAD_SCHEDULE_TEXT()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={downloadSchedule}
|
||||||
|
>
|
||||||
|
{LL.SCHEDULE(0)}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UploadDownload;
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
|
||||||
import { Typography, Button, Box } from '@mui/material';
|
|
||||||
import { useRequest } from 'alova';
|
|
||||||
import { useState, type FC } from 'react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import RestartMonitor from './RestartMonitor';
|
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
|
||||||
import { SectionContent, SingleUpload } from 'components';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
import * as EMSESP from 'project/api';
|
|
||||||
|
|
||||||
const UploadFileForm: FC = () => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
|
||||||
const [md5, setMd5] = useState<string>();
|
|
||||||
|
|
||||||
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
const { send: getCustomizations, onSuccess: onSuccessGetCustomizations } = useRequest(EMSESP.getCustomizations(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
const { send: getAPI, onSuccess: onGetAPI } = useRequest((data) => EMSESP.API(data), {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
loading: isUploading,
|
|
||||||
uploading: progress,
|
|
||||||
send: sendUpload,
|
|
||||||
onSuccess: onSuccessUpload,
|
|
||||||
abort: cancelUpload
|
|
||||||
} = useRequest(SystemApi.uploadFile, {
|
|
||||||
immediate: false,
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
|
|
||||||
onSuccessUpload(({ data }: any) => {
|
|
||||||
if (data) {
|
|
||||||
setMd5(data.md5);
|
|
||||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
|
||||||
} else {
|
|
||||||
setRestarting(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const startUpload = async (files: File[]) => {
|
|
||||||
await sendUpload(files[0]).catch((err) => {
|
|
||||||
if (err.message === 'The user aborted a request') {
|
|
||||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
|
||||||
} else if (err.message === 'Network Error') {
|
|
||||||
toast.warning('Invalid file extension or incompatible bin file');
|
|
||||||
} else {
|
|
||||||
toast.error(err.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveFile = (json: any, endpoint: string) => {
|
|
||||||
const anchor = document.createElement('a');
|
|
||||||
anchor.href = URL.createObjectURL(
|
|
||||||
new Blob([JSON.stringify(json, null, 2)], {
|
|
||||||
type: 'text/plain'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
anchor.download = 'emsesp_' + endpoint;
|
|
||||||
anchor.click();
|
|
||||||
URL.revokeObjectURL(anchor.href);
|
|
||||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
|
||||||
};
|
|
||||||
|
|
||||||
onSuccessGetSettings((event) => {
|
|
||||||
saveFile(event.data, 'settings.json');
|
|
||||||
});
|
|
||||||
onSuccessGetCustomizations((event) => {
|
|
||||||
saveFile(event.data, 'customizations.json');
|
|
||||||
});
|
|
||||||
onSuccessGetEntities((event) => {
|
|
||||||
saveFile(event.data, 'entities.json');
|
|
||||||
});
|
|
||||||
onSuccessGetSchedule((event) => {
|
|
||||||
saveFile(event.data, 'schedule.json');
|
|
||||||
});
|
|
||||||
onGetAPI((event) => {
|
|
||||||
saveFile(event.data, event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt');
|
|
||||||
});
|
|
||||||
|
|
||||||
const downloadSettings = async () => {
|
|
||||||
await getSettings().catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadCustomizations = async () => {
|
|
||||||
await getCustomizations().catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadEntities = async () => {
|
|
||||||
await getEntities().catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadSchedule = async () => {
|
|
||||||
await getSchedule()
|
|
||||||
.catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const callAPI = async (device: string, entity: string) => {
|
|
||||||
await getAPI({ device, entity, id: 0 }).catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = () => (
|
|
||||||
<>
|
|
||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
|
||||||
{LL.UPLOAD()}
|
|
||||||
</Typography>
|
|
||||||
<Box mb={2} color="warning.main">
|
|
||||||
<Typography variant="body2">
|
|
||||||
{LL.UPLOAD_TEXT()}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{LL.RESTART_TEXT(1)}.
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
{md5 && (
|
|
||||||
<Box mb={2}>
|
|
||||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<SingleUpload onDrop={startUpload} onCancel={cancelUpload} isUploading={isUploading} progress={progress} />
|
|
||||||
{!isUploading && (
|
|
||||||
<>
|
|
||||||
<Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary">
|
|
||||||
{LL.DOWNLOAD(0)} {LL.SUPPORT_INFORMATION(1)}
|
|
||||||
</Typography>
|
|
||||||
<Box color="warning.main">
|
|
||||||
<Typography mb={1} variant="body2">
|
|
||||||
{LL.HELP_INFORMATION_4()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
startIcon={<DownloadIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => callAPI('system', 'info')}
|
|
||||||
>
|
|
||||||
{LL.SUPPORT_INFORMATION(0)}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
sx={{ ml: 2 }}
|
|
||||||
startIcon={<DownloadIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => callAPI('system', 'allvalues')}
|
|
||||||
>
|
|
||||||
All Values
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary">
|
|
||||||
{LL.DOWNLOAD(0)} {LL.SETTINGS(1)}
|
|
||||||
</Typography>
|
|
||||||
<Box color="warning.main">
|
|
||||||
<Typography mb={1} variant="body2">
|
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSettings}>
|
|
||||||
{LL.SETTINGS_OF('')}
|
|
||||||
</Button>
|
|
||||||
<Box color="warning.main">
|
|
||||||
<Typography mt={2} mb={1} variant="body2">
|
|
||||||
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadCustomizations}>
|
|
||||||
{LL.CUSTOMIZATIONS()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
sx={{ ml: 2 }}
|
|
||||||
startIcon={<DownloadIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={downloadEntities}
|
|
||||||
>
|
|
||||||
{LL.CUSTOM_ENTITIES(0)}
|
|
||||||
</Button>
|
|
||||||
<Box color="warning.main">
|
|
||||||
<Typography mt={2} mb={1} variant="body2">
|
|
||||||
{LL.DOWNLOAD_SCHEDULE_TEXT()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSchedule}>
|
|
||||||
{LL.SCHEDULE(0)}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<SectionContent title={LL.UPLOAD_DOWNLOAD()} titleGutter>
|
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UploadFileForm;
|
|
||||||
@@ -12,7 +12,6 @@ const de: Translation = {
|
|||||||
USERNAME: 'Nutzername',
|
USERNAME: 'Nutzername',
|
||||||
PASSWORD: 'Passwort',
|
PASSWORD: 'Passwort',
|
||||||
SU_PASSWORD: 'su Passwort',
|
SU_PASSWORD: 'su Passwort',
|
||||||
DASHBOARD: 'Kontrollzentrum',
|
|
||||||
SETTINGS_OF: '{0} Einstellungen',
|
SETTINGS_OF: '{0} Einstellungen',
|
||||||
HELP_OF: '{0} Hilfe',
|
HELP_OF: '{0} Hilfe',
|
||||||
LOGGED_IN: 'Eingeloggt als {name}',
|
LOGGED_IN: 'Eingeloggt als {name}',
|
||||||
@@ -37,8 +36,6 @@ const de: Translation = {
|
|||||||
BRAND: 'Marke',
|
BRAND: 'Marke',
|
||||||
ENTITY_NAME: 'Entitätsname',
|
ENTITY_NAME: 'Entitätsname',
|
||||||
VALUE: '{{Wert|wert}}',
|
VALUE: '{{Wert|wert}}',
|
||||||
DEVICE_DATA: 'Gerätedaten',
|
|
||||||
SENSOR_DATA: 'Sensordaten',
|
|
||||||
DEVICES: 'Geräte',
|
DEVICES: 'Geräte',
|
||||||
SENSORS: 'Sensoren',
|
SENSORS: 'Sensoren',
|
||||||
RUN_COMMAND: 'Befehl ausführen',
|
RUN_COMMAND: 'Befehl ausführen',
|
||||||
@@ -83,7 +80,6 @@ const de: Translation = {
|
|||||||
FAIL: 'FEHLER',
|
FAIL: 'FEHLER',
|
||||||
QUALITY: 'QUALITÄT',
|
QUALITY: 'QUALITÄT',
|
||||||
SCAN_DEVICES: 'Nach neuen Geräten suchen',
|
SCAN_DEVICES: 'Nach neuen Geräten suchen',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS-Bus- und Aktivitätsstatus',
|
|
||||||
SCAN: 'Suche',
|
SCAN: 'Suche',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS-Telegramme empfangen (Rx)',
|
'EMS-Telegramme empfangen (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const de: Translation = {
|
|||||||
OPTIONS: 'Optionen',
|
OPTIONS: 'Optionen',
|
||||||
NAME: 'Name',
|
NAME: 'Name',
|
||||||
CUSTOMIZATIONS_RESET: 'Möchten Sie wirklich alle Anpassungen entfernen, einschließlich der benutzerdefinierten Einstellungen der Temperatur- und Analogsensoren?',
|
CUSTOMIZATIONS_RESET: 'Möchten Sie wirklich alle Anpassungen entfernen, einschließlich der benutzerdefinierten Einstellungen der Temperatur- und Analogsensoren?',
|
||||||
DEVICE_ENTITIES: 'Geräteentitäten',
|
|
||||||
SUPPORT_INFORMATION: 'Unterstützende Informationen',
|
SUPPORT_INFORMATION: 'Unterstützende Informationen',
|
||||||
CLICK_HERE: 'Hier klicken',
|
|
||||||
HELP_INFORMATION_1: 'EMS-ESP Konfigurationsanweisungen und mehr finden Sie im Online-Wiki',
|
HELP_INFORMATION_1: 'EMS-ESP Konfigurationsanweisungen und mehr finden Sie im Online-Wiki',
|
||||||
HELP_INFORMATION_2: 'Für einen Live-Community-Chat besuchen Sie unseren Discord-Server',
|
HELP_INFORMATION_2: 'Für einen Live-Community-Chat besuchen Sie unseren Discord-Server',
|
||||||
HELP_INFORMATION_3: 'Um neue Funktionen anzufragen oder Fehler zu melden, eröffnen Sie ein Issue auf Github',
|
HELP_INFORMATION_3: 'Um neue Funktionen anzufragen oder Fehler zu melden, eröffnen Sie ein Issue auf Github',
|
||||||
@@ -181,13 +175,11 @@ const de: Translation = {
|
|||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Hoch-/Herunterladen',
|
UPLOAD_DOWNLOAD: 'Hoch-/Herunterladen',
|
||||||
VERSION_ON: 'Sie verwenden derzeit',
|
VERSION_ON: 'Sie verwenden derzeit',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'um die neue Firmware anzuwenden',
|
|
||||||
CLOSE: 'Schließen',
|
CLOSE: 'Schließen',
|
||||||
USE: 'Verwenden Sie',
|
USE: 'Verwenden Sie',
|
||||||
FACTORY_RESET: 'Werkseinstellung',
|
FACTORY_RESET: 'Werkseinstellung',
|
||||||
SYSTEM_FACTORY_TEXT: 'EMS-ESP wurde auf Werkseinstellung gesetzt und startet als Zugangspunkt neu',
|
SYSTEM_FACTORY_TEXT: 'EMS-ESP wurde auf Werkseinstellung gesetzt und startet als Zugangspunkt neu',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?',
|
||||||
VERSION_CHECK: 'Versionsprüfung',
|
|
||||||
THE_LATEST: 'Die neueste',
|
THE_LATEST: 'Die neueste',
|
||||||
OFFICIAL: 'offizielle',
|
OFFICIAL: 'offizielle',
|
||||||
DEVELOPMENT: 'Entwicklungs',
|
DEVELOPMENT: 'Entwicklungs',
|
||||||
@@ -244,6 +236,7 @@ const de: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Thermostate',
|
MQTT_INT_THERMOSTATS: 'Thermostate',
|
||||||
MQTT_INT_SOLAR: 'Solarmodule',
|
MQTT_INT_SOLAR: 'Solarmodule',
|
||||||
MQTT_INT_MIXER: 'Mischermodule',
|
MQTT_INT_MIXER: 'Mischermodule',
|
||||||
|
MQTT_INT_WATER: 'Warmwassermodule',
|
||||||
MQTT_QUEUE: 'MQTT Queue',
|
MQTT_QUEUE: 'MQTT Queue',
|
||||||
DEFAULT: 'Standard',
|
DEFAULT: 'Standard',
|
||||||
MQTT_ENTITY_FORMAT: 'Entitäts-ID Format',
|
MQTT_ENTITY_FORMAT: 'Entitäts-ID Format',
|
||||||
@@ -329,7 +322,9 @@ const de: Translation = {
|
|||||||
ACTIVEHIGH: 'Aktiv Positiv',
|
ACTIVEHIGH: 'Aktiv Positiv',
|
||||||
ACTIVELOW: 'Aktiv Negativ',
|
ACTIVELOW: 'Aktiv Negativ',
|
||||||
UNCHANGED: 'Unverändert',
|
UNCHANGED: 'Unverändert',
|
||||||
ALWAYS: 'Immer'
|
ALWAYS: 'Immer',
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const en: Translation = {
|
|||||||
USERNAME: 'Username',
|
USERNAME: 'Username',
|
||||||
PASSWORD: 'Password',
|
PASSWORD: 'Password',
|
||||||
SU_PASSWORD: 'su Password',
|
SU_PASSWORD: 'su Password',
|
||||||
DASHBOARD: 'Dashboard',
|
|
||||||
SETTINGS_OF: '{0} Settings',
|
SETTINGS_OF: '{0} Settings',
|
||||||
HELP_OF: '{0} Help',
|
HELP_OF: '{0} Help',
|
||||||
LOGGED_IN: 'Logged in as {name}',
|
LOGGED_IN: 'Logged in as {name}',
|
||||||
@@ -37,8 +36,6 @@ const en: Translation = {
|
|||||||
BRAND: 'Brand',
|
BRAND: 'Brand',
|
||||||
ENTITY_NAME: 'Entity Name',
|
ENTITY_NAME: 'Entity Name',
|
||||||
VALUE: '{{Value|value}}',
|
VALUE: '{{Value|value}}',
|
||||||
DEVICE_DATA: 'Device Data',
|
|
||||||
SENSOR_DATA: 'Sensor Data',
|
|
||||||
DEVICES: 'Devices',
|
DEVICES: 'Devices',
|
||||||
SENSORS: 'Sensors',
|
SENSORS: 'Sensors',
|
||||||
RUN_COMMAND: 'Call Command',
|
RUN_COMMAND: 'Call Command',
|
||||||
@@ -83,7 +80,6 @@ const en: Translation = {
|
|||||||
FAIL: 'FAIL',
|
FAIL: 'FAIL',
|
||||||
QUALITY: 'QUALITY',
|
QUALITY: 'QUALITY',
|
||||||
SCAN_DEVICES: 'Scan for new devices',
|
SCAN_DEVICES: 'Scan for new devices',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS Bus & Activity Status',
|
|
||||||
SCAN: 'Scan',
|
SCAN: 'Scan',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrams Received (Rx)',
|
'EMS Telegrams Received (Rx)',
|
||||||
@@ -158,18 +154,16 @@ const en: Translation = {
|
|||||||
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
|
CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
|
CUSTOMIZATIONS_HELP_5: 'hide from Dashboard',
|
||||||
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
CUSTOMIZATIONS_HELP_6: 'remove from memory',
|
||||||
SELECT_DEVICE: 'Select a device',
|
SELECT_DEVICE: 'select a device',
|
||||||
SET_ALL: 'set all',
|
SET_ALL: 'set all',
|
||||||
OPTIONS: 'Options',
|
OPTIONS: 'Options',
|
||||||
NAME: 'Name',
|
NAME: 'Name',
|
||||||
CUSTOMIZATIONS_RESET: 'Are you sure you want remove all customizations including the custom settings of the Temperature and Analog sensors?',
|
CUSTOMIZATIONS_RESET: 'Are you sure you want remove all customizations including the custom settings of the Temperature and Analog sensors?',
|
||||||
DEVICE_ENTITIES: 'Device Entities',
|
|
||||||
SUPPORT_INFORMATION: 'Support Information',
|
SUPPORT_INFORMATION: 'Support Information',
|
||||||
CLICK_HERE: 'Click Here',
|
|
||||||
HELP_INFORMATION_1: 'Visit the online wiki to get instructions on how to configure EMS-ESP',
|
HELP_INFORMATION_1: 'Visit the online wiki to get instructions on how to configure EMS-ESP',
|
||||||
HELP_INFORMATION_2: 'For live community chat join our Discord server',
|
HELP_INFORMATION_2: 'For live community chat join our Discord server',
|
||||||
HELP_INFORMATION_3: 'To request a feature or report a bug',
|
HELP_INFORMATION_3: 'To request a feature or report a bug',
|
||||||
HELP_INFORMATION_4: 'Remember to download and attach your support information for a faster response when reporting an issue',
|
HELP_INFORMATION_4: 'Download and attach your support information for a faster response when reporting an issue',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP is a free and open-source project. Please support its future development by giving it a star on Github!',
|
HELP_INFORMATION_5: 'EMS-ESP is a free and open-source project. Please support its future development by giving it a star on Github!',
|
||||||
UPLOAD: 'Upload',
|
UPLOAD: 'Upload',
|
||||||
DOWNLOAD: '{{D|d|d}}ownload',
|
DOWNLOAD: '{{D|d|d}}ownload',
|
||||||
@@ -181,20 +175,17 @@ const en: Translation = {
|
|||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||||
VERSION_ON: 'You are currently on version',
|
VERSION_ON: 'You are currently on version',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'to apply the new firmware',
|
|
||||||
CLOSE: 'Close',
|
CLOSE: 'Close',
|
||||||
USE: 'Use',
|
USE: 'Use',
|
||||||
FACTORY_RESET: 'Factory Reset',
|
FACTORY_RESET: 'Factory Reset',
|
||||||
SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart',
|
SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset EMS-ESP to its factory defaults?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset EMS-ESP to its factory defaults?',
|
||||||
VERSION_CHECK: 'Version Check',
|
|
||||||
THE_LATEST: 'The latest',
|
THE_LATEST: 'The latest',
|
||||||
OFFICIAL: 'official',
|
OFFICIAL: 'official',
|
||||||
DEVELOPMENT: 'development',
|
DEVELOPMENT: 'development',
|
||||||
RELEASE_IS: 'release is',
|
RELEASE_IS: 'release is',
|
||||||
RELEASE_NOTES: 'release notes',
|
RELEASE_NOTES: 'release notes',
|
||||||
EMS_ESP_VER: 'EMS-ESP Version',
|
EMS_ESP_VER: 'EMS-ESP Version',
|
||||||
PLATFORM: 'Device (Platform / SDK)',
|
|
||||||
UPTIME: 'System Uptime',
|
UPTIME: 'System Uptime',
|
||||||
HEAP: 'Heap (Free / Max Alloc)',
|
HEAP: 'Heap (Free / Max Alloc)',
|
||||||
PSRAM: 'PSRAM (Size / Free)',
|
PSRAM: 'PSRAM (Size / Free)',
|
||||||
@@ -245,6 +236,7 @@ const en: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Thermostats',
|
MQTT_INT_THERMOSTATS: 'Thermostats',
|
||||||
MQTT_INT_SOLAR: 'Solar Modules',
|
MQTT_INT_SOLAR: 'Solar Modules',
|
||||||
MQTT_INT_MIXER: 'Mixer Modules',
|
MQTT_INT_MIXER: 'Mixer Modules',
|
||||||
|
MQTT_INT_WATER: 'Water Modules',
|
||||||
MQTT_QUEUE: 'MQTT Queue',
|
MQTT_QUEUE: 'MQTT Queue',
|
||||||
DEFAULT: 'Default',
|
DEFAULT: 'Default',
|
||||||
MQTT_ENTITY_FORMAT: 'Entity ID format',
|
MQTT_ENTITY_FORMAT: 'Entity ID format',
|
||||||
@@ -330,7 +322,9 @@ const en: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High',
|
ACTIVEHIGH: 'Active High',
|
||||||
ACTIVELOW: 'Active Low',
|
ACTIVELOW: 'Active Low',
|
||||||
UNCHANGED: 'Unchanged',
|
UNCHANGED: 'Unchanged',
|
||||||
ALWAYS: 'Always'
|
ALWAYS: 'Always',
|
||||||
|
ACTIVITY: 'Activity',
|
||||||
|
CONFIGURE: 'Configure {0}'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const fr: Translation = {
|
|||||||
USERNAME: 'Nom d\'utilisateur',
|
USERNAME: 'Nom d\'utilisateur',
|
||||||
PASSWORD: 'Mot de passe',
|
PASSWORD: 'Mot de passe',
|
||||||
SU_PASSWORD: 'Mot de passe su',
|
SU_PASSWORD: 'Mot de passe su',
|
||||||
DASHBOARD: 'Tableau de bord',
|
|
||||||
SETTINGS_OF: 'Paramètres {0}',
|
SETTINGS_OF: 'Paramètres {0}',
|
||||||
HELP_OF: 'Aide {0}',
|
HELP_OF: 'Aide {0}',
|
||||||
LOGGED_IN: 'Connecté en tant que {name}',
|
LOGGED_IN: 'Connecté en tant que {name}',
|
||||||
@@ -37,8 +36,6 @@ const fr: Translation = {
|
|||||||
BRAND: 'Marque',
|
BRAND: 'Marque',
|
||||||
ENTITY_NAME: 'Nom de l\'entité',
|
ENTITY_NAME: 'Nom de l\'entité',
|
||||||
VALUE: 'Valeur',
|
VALUE: 'Valeur',
|
||||||
DEVICE_DATA: 'Données des appareils',
|
|
||||||
SENSOR_DATA: 'Données des capteurs',
|
|
||||||
DEVICES: 'Appareils',
|
DEVICES: 'Appareils',
|
||||||
SENSORS: 'Capteurs',
|
SENSORS: 'Capteurs',
|
||||||
RUN_COMMAND: 'Lancer une commande',
|
RUN_COMMAND: 'Lancer une commande',
|
||||||
@@ -83,7 +80,6 @@ const fr: Translation = {
|
|||||||
FAIL: 'ÉCHEC',
|
FAIL: 'ÉCHEC',
|
||||||
QUALITY: 'QUALITÉ',
|
QUALITY: 'QUALITÉ',
|
||||||
SCAN_DEVICES: 'Rechercher de nouveaux appareils',
|
SCAN_DEVICES: 'Rechercher de nouveaux appareils',
|
||||||
EMS_BUS_STATUS_TITLE: 'Statut du bus et de l\'activité EMS',
|
|
||||||
SCAN: 'Scan',
|
SCAN: 'Scan',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'Télégrammes EMS reçus (Rx)',
|
'Télégrammes EMS reçus (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const fr: Translation = {
|
|||||||
OPTIONS: 'Options',
|
OPTIONS: 'Options',
|
||||||
NAME: 'Nom',
|
NAME: 'Nom',
|
||||||
CUSTOMIZATIONS_RESET: 'Êtes-vous sûr de vouloir supprimer toutes les personnalisations, y compris les paramètres personnalisés des capteurs de température et analogiques ?',
|
CUSTOMIZATIONS_RESET: 'Êtes-vous sûr de vouloir supprimer toutes les personnalisations, y compris les paramètres personnalisés des capteurs de température et analogiques ?',
|
||||||
DEVICE_ENTITIES: 'Entités de l\'appareil',
|
|
||||||
SUPPORT_INFORMATION: 'Information de support',
|
SUPPORT_INFORMATION: 'Information de support',
|
||||||
CLICK_HERE: 'Cliquez ici',
|
|
||||||
HELP_INFORMATION_1: 'Visitez le wiki en ligne pour obtenir des instructions sur la façon de configurer EMS-ESP.',
|
HELP_INFORMATION_1: 'Visitez le wiki en ligne pour obtenir des instructions sur la façon de configurer EMS-ESP.',
|
||||||
HELP_INFORMATION_2: 'Pour une discussion en direct avec la communauté, rejoignez notre serveur Discord',
|
HELP_INFORMATION_2: 'Pour une discussion en direct avec la communauté, rejoignez notre serveur Discord',
|
||||||
HELP_INFORMATION_3: 'Pour demander une fonctionnalité ou signaler un problème',
|
HELP_INFORMATION_3: 'Pour demander une fonctionnalité ou signaler un problème',
|
||||||
@@ -181,13 +175,11 @@ const fr: Translation = {
|
|||||||
STATUS_OF: 'Statut {0}',
|
STATUS_OF: 'Statut {0}',
|
||||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||||
VERSION_ON: 'You are currently on', // TODO translate
|
VERSION_ON: 'You are currently on', // TODO translate
|
||||||
SYSTEM_APPLY_FIRMWARE: 'pour appliquer le nouveau firmware',
|
|
||||||
CLOSE: 'Fermer',
|
CLOSE: 'Fermer',
|
||||||
USE: 'Utiliser',
|
USE: 'Utiliser',
|
||||||
FACTORY_RESET: 'Réinitialisation',
|
FACTORY_RESET: 'Réinitialisation',
|
||||||
SYSTEM_FACTORY_TEXT: 'L\'appareil a été réinitialisé et va maintenant redémarrer',
|
SYSTEM_FACTORY_TEXT: 'L\'appareil a été réinitialisé et va maintenant redémarrer',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Êtes-vous sûr de vouloir réinitialiser l\'appareil à ses paramètres d\'usine ?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Êtes-vous sûr de vouloir réinitialiser l\'appareil à ses paramètres d\'usine ?',
|
||||||
VERSION_CHECK: 'Vérification de la version',
|
|
||||||
THE_LATEST: 'La dernière',
|
THE_LATEST: 'La dernière',
|
||||||
OFFICIAL: 'officielle',
|
OFFICIAL: 'officielle',
|
||||||
DEVELOPMENT: 'développement',
|
DEVELOPMENT: 'développement',
|
||||||
@@ -244,6 +236,7 @@ const fr: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Thermostats',
|
MQTT_INT_THERMOSTATS: 'Thermostats',
|
||||||
MQTT_INT_SOLAR: 'Modules solaires',
|
MQTT_INT_SOLAR: 'Modules solaires',
|
||||||
MQTT_INT_MIXER: 'Modules mélangeurs',
|
MQTT_INT_MIXER: 'Modules mélangeurs',
|
||||||
|
MQTT_INT_WATER: 'Modules eau',
|
||||||
MQTT_QUEUE: 'Queue MQTT',
|
MQTT_QUEUE: 'Queue MQTT',
|
||||||
DEFAULT: 'Défaut',
|
DEFAULT: 'Défaut',
|
||||||
MQTT_ENTITY_FORMAT: 'Entity ID format', // TODO translate
|
MQTT_ENTITY_FORMAT: 'Entity ID format', // TODO translate
|
||||||
@@ -329,7 +322,9 @@ const fr: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Active High', // TODO translate
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Active Low', // TODO translate
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Unchanged', // TODO translate
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Always', // TODO translate
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const it: Translation = {
|
|||||||
USERNAME: 'Nome Utente',
|
USERNAME: 'Nome Utente',
|
||||||
PASSWORD: 'Password',
|
PASSWORD: 'Password',
|
||||||
SU_PASSWORD: 'su Password',
|
SU_PASSWORD: 'su Password',
|
||||||
DASHBOARD: 'Pannello di Controllo',
|
|
||||||
SETTINGS_OF: 'Impostazioni {0}',
|
SETTINGS_OF: 'Impostazioni {0}',
|
||||||
HELP_OF: '{0} Aiuto',
|
HELP_OF: '{0} Aiuto',
|
||||||
LOGGED_IN: 'Registrato come {name}',
|
LOGGED_IN: 'Registrato come {name}',
|
||||||
@@ -37,8 +36,6 @@ const it: Translation = {
|
|||||||
BRAND: 'Marca',
|
BRAND: 'Marca',
|
||||||
ENTITY_NAME: 'Nome Entità',
|
ENTITY_NAME: 'Nome Entità',
|
||||||
VALUE: '{{Valore|valore}}',
|
VALUE: '{{Valore|valore}}',
|
||||||
DEVICE_DATA: 'Device Data',
|
|
||||||
SENSOR_DATA: 'Sensor Data',
|
|
||||||
DEVICES: 'Dispositivi',
|
DEVICES: 'Dispositivi',
|
||||||
SENSORS: 'Sensori',
|
SENSORS: 'Sensori',
|
||||||
RUN_COMMAND: 'Esegui',
|
RUN_COMMAND: 'Esegui',
|
||||||
@@ -51,7 +48,6 @@ const it: Translation = {
|
|||||||
REMOVE: 'Elimina',
|
REMOVE: 'Elimina',
|
||||||
PROBLEM_UPDATING: 'Problema aggiornamento',
|
PROBLEM_UPDATING: 'Problema aggiornamento',
|
||||||
PROBLEM_LOADING: 'Problema caricamento',
|
PROBLEM_LOADING: 'Problema caricamento',
|
||||||
ACCESS_DENIED: 'Accesso Negato',
|
|
||||||
ANALOG_SENSOR: 'Sensore Analogico',
|
ANALOG_SENSOR: 'Sensore Analogico',
|
||||||
ANALOG_SENSORS: 'Sensori Analogici',
|
ANALOG_SENSORS: 'Sensori Analogici',
|
||||||
SETTINGS: 'Settings',
|
SETTINGS: 'Settings',
|
||||||
@@ -71,7 +67,6 @@ const it: Translation = {
|
|||||||
TEMP_SENSOR: 'Sensore Temperatura',
|
TEMP_SENSOR: 'Sensore Temperatura',
|
||||||
TEMP_SENSORS: 'Sensori Temperatura',
|
TEMP_SENSORS: 'Sensori Temperatura',
|
||||||
WRITE_CMD_SENT: 'Scrittura comando inviata',
|
WRITE_CMD_SENT: 'Scrittura comando inviata',
|
||||||
WRITE_CMD_FAILED: 'Scittura comando fallita',
|
|
||||||
EMS_BUS_WARNING: 'EMS bus disconnesso. Se questo avvertimento persiste dopo alcuni secondi prego verificare impostazioni scheda',
|
EMS_BUS_WARNING: 'EMS bus disconnesso. Se questo avvertimento persiste dopo alcuni secondi prego verificare impostazioni scheda',
|
||||||
EMS_BUS_SCANNING: 'Scansione dispositivi EMS ...',
|
EMS_BUS_SCANNING: 'Scansione dispositivi EMS ...',
|
||||||
CONNECTED: 'Connesso',
|
CONNECTED: 'Connesso',
|
||||||
@@ -85,7 +80,6 @@ const it: Translation = {
|
|||||||
FAIL: 'FALLITO',
|
FAIL: 'FALLITO',
|
||||||
QUALITY: 'QUALITÂ',
|
QUALITY: 'QUALITÂ',
|
||||||
SCAN_DEVICES: 'Scansione per nuovi dispositivi',
|
SCAN_DEVICES: 'Scansione per nuovi dispositivi',
|
||||||
EMS_BUS_STATUS_TITLE: 'Bus EMS & Stato Attività',
|
|
||||||
SCAN: 'Scansione',
|
SCAN: 'Scansione',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'Telegrammi EMS Ricevuti (Rx)',
|
'Telegrammi EMS Ricevuti (Rx)',
|
||||||
@@ -165,9 +159,7 @@ const it: Translation = {
|
|||||||
OPTIONS: 'Opzioni',
|
OPTIONS: 'Opzioni',
|
||||||
NAME: 'Nome',
|
NAME: 'Nome',
|
||||||
CUSTOMIZATIONS_RESET: 'Sei sicuro di voler rimuovere tutte le personalizzazioni incluse le impostazioni personalizzate dei sensori di temperatura e analogici?',
|
CUSTOMIZATIONS_RESET: 'Sei sicuro di voler rimuovere tutte le personalizzazioni incluse le impostazioni personalizzate dei sensori di temperatura e analogici?',
|
||||||
DEVICE_ENTITIES: 'Entità Dispositivo',
|
|
||||||
SUPPORT_INFORMATION: 'Informazioni di Supporto',
|
SUPPORT_INFORMATION: 'Informazioni di Supporto',
|
||||||
CLICK_HERE: 'Clicca qui',
|
|
||||||
HELP_INFORMATION_1: 'Visita il wiki online per ottenere istruzioni su come configurare EMS-ESP',
|
HELP_INFORMATION_1: 'Visita il wiki online per ottenere istruzioni su come configurare EMS-ESP',
|
||||||
HELP_INFORMATION_2: 'Per la chat della community dal vivo unisciti al nostro server Discord',
|
HELP_INFORMATION_2: 'Per la chat della community dal vivo unisciti al nostro server Discord',
|
||||||
HELP_INFORMATION_3: 'Per richiedere una funzionalità o segnalare un errore',
|
HELP_INFORMATION_3: 'Per richiedere una funzionalità o segnalare un errore',
|
||||||
@@ -183,13 +175,11 @@ const it: Translation = {
|
|||||||
STATUS_OF: 'Stato {0}',
|
STATUS_OF: 'Stato {0}',
|
||||||
UPLOAD_DOWNLOAD: 'Caricamento/Scaricamento',
|
UPLOAD_DOWNLOAD: 'Caricamento/Scaricamento',
|
||||||
VERSION_ON: 'Attualmente stai eseguendo la versione',
|
VERSION_ON: 'Attualmente stai eseguendo la versione',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'per applicare il nuovo firmware',
|
|
||||||
CLOSE: 'Chiudere',
|
CLOSE: 'Chiudere',
|
||||||
USE: 'Usa',
|
USE: 'Usa',
|
||||||
FACTORY_RESET: 'Impostazioni di fabbrica',
|
FACTORY_RESET: 'Impostazioni di fabbrica',
|
||||||
SYSTEM_FACTORY_TEXT: 'Il dispositivo è stato ripristinato alle impostazioni di fabbrica e ora verrà riavviato',
|
SYSTEM_FACTORY_TEXT: 'Il dispositivo è stato ripristinato alle impostazioni di fabbrica e ora verrà riavviato',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??',
|
||||||
VERSION_CHECK: 'Verifica Versione',
|
|
||||||
THE_LATEST: 'Ultima',
|
THE_LATEST: 'Ultima',
|
||||||
OFFICIAL: 'ufficiale',
|
OFFICIAL: 'ufficiale',
|
||||||
DEVELOPMENT: 'sviluppo',
|
DEVELOPMENT: 'sviluppo',
|
||||||
@@ -246,6 +236,7 @@ const it: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostati',
|
MQTT_INT_THERMOSTATS: 'Termostati',
|
||||||
MQTT_INT_SOLAR: 'Moduli solari',
|
MQTT_INT_SOLAR: 'Moduli solari',
|
||||||
MQTT_INT_MIXER: 'Moduli Mixer',
|
MQTT_INT_MIXER: 'Moduli Mixer',
|
||||||
|
MQTT_INT_WATER: 'Moduli Acqua',
|
||||||
MQTT_QUEUE: 'Coda MQTT',
|
MQTT_QUEUE: 'Coda MQTT',
|
||||||
DEFAULT: 'Predefinito',
|
DEFAULT: 'Predefinito',
|
||||||
MQTT_ENTITY_FORMAT: 'Formato ID entità',
|
MQTT_ENTITY_FORMAT: 'Formato ID entità',
|
||||||
@@ -331,7 +322,9 @@ const it: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Active High', // TODO translate
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Active Low', // TODO translate
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Unchanged', // TODO translate
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Always', // TODO translate
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const nl: Translation = {
|
|||||||
USERNAME: 'Gebruikersnaam',
|
USERNAME: 'Gebruikersnaam',
|
||||||
PASSWORD: 'Wachtwoord',
|
PASSWORD: 'Wachtwoord',
|
||||||
SU_PASSWORD: 'su Wachtwoord',
|
SU_PASSWORD: 'su Wachtwoord',
|
||||||
DASHBOARD: 'Dashboard',
|
|
||||||
SETTINGS_OF: '{0} Instellingen',
|
SETTINGS_OF: '{0} Instellingen',
|
||||||
HELP_OF: '{0} Help',
|
HELP_OF: '{0} Help',
|
||||||
LOGGED_IN: 'Ingelogd als {name}',
|
LOGGED_IN: 'Ingelogd als {name}',
|
||||||
@@ -37,8 +36,6 @@ const nl: Translation = {
|
|||||||
BRAND: 'Merk',
|
BRAND: 'Merk',
|
||||||
ENTITY_NAME: 'Entiteit',
|
ENTITY_NAME: 'Entiteit',
|
||||||
VALUE: '{{Waarde|waarde}}',
|
VALUE: '{{Waarde|waarde}}',
|
||||||
SENSOR_DATA: 'Sensor data',
|
|
||||||
DEVICE_DATA: 'Apparaat data',
|
|
||||||
DEVICES: 'Apparaten',
|
DEVICES: 'Apparaten',
|
||||||
SENSORS: 'Sensoren',
|
SENSORS: 'Sensoren',
|
||||||
RUN_COMMAND: 'Call commando',
|
RUN_COMMAND: 'Call commando',
|
||||||
@@ -83,7 +80,6 @@ const nl: Translation = {
|
|||||||
FAIL: 'MISLUKT',
|
FAIL: 'MISLUKT',
|
||||||
QUALITY: 'QUALITEIT',
|
QUALITY: 'QUALITEIT',
|
||||||
SCAN_DEVICES: 'Scannen naar nieuwe apparaten',
|
SCAN_DEVICES: 'Scannen naar nieuwe apparaten',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS Bus & Activiteitenstatus',
|
|
||||||
SCAN: 'Scan',
|
SCAN: 'Scan',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrammen ontvangen (Rx)',
|
'EMS Telegrammen ontvangen (Rx)',
|
||||||
@@ -126,7 +122,7 @@ const nl: Translation = {
|
|||||||
BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen',
|
BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen',
|
||||||
READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)',
|
READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)',
|
||||||
UNDERCLOCK_CPU: 'Underclock CPU snelheid',
|
UNDERCLOCK_CPU: 'Underclock CPU snelheid',
|
||||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
HEATINGOFF: 'Start ketel met geforceerde verwarming uit',
|
||||||
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
|
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
|
||||||
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
|
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
|
||||||
TRIGGER_TIME: 'Trigger tijd',
|
TRIGGER_TIME: 'Trigger tijd',
|
||||||
@@ -163,9 +159,7 @@ const nl: Translation = {
|
|||||||
OPTIONS: 'Opties',
|
OPTIONS: 'Opties',
|
||||||
NAME: 'Naam',
|
NAME: 'Naam',
|
||||||
CUSTOMIZATIONS_RESET: 'Weet je zeker dat je alle custom aanpassingen wilt verwijderen inclusief de custom instellingen voor analoge temperatuursensoren?',
|
CUSTOMIZATIONS_RESET: 'Weet je zeker dat je alle custom aanpassingen wilt verwijderen inclusief de custom instellingen voor analoge temperatuursensoren?',
|
||||||
DEVICE_ENTITIES: 'Apparaat Entiteiten',
|
|
||||||
SUPPORT_INFORMATION: 'Support Informatie',
|
SUPPORT_INFORMATION: 'Support Informatie',
|
||||||
CLICK_HERE: 'Klik Hier',
|
|
||||||
HELP_INFORMATION_1: 'Bezoek de online wiki om instructies te vinden om EMS-ESP te configureren',
|
HELP_INFORMATION_1: 'Bezoek de online wiki om instructies te vinden om EMS-ESP te configureren',
|
||||||
HELP_INFORMATION_2: 'Voor de live community ga naar de Discord server',
|
HELP_INFORMATION_2: 'Voor de live community ga naar de Discord server',
|
||||||
HELP_INFORMATION_3: 'Om een nieuwe feature te vragen of een bug te rapporteren',
|
HELP_INFORMATION_3: 'Om een nieuwe feature te vragen of een bug te rapporteren',
|
||||||
@@ -181,13 +175,11 @@ const nl: Translation = {
|
|||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||||
VERSION_ON: 'U bevindt zich momenteel op versie',
|
VERSION_ON: 'U bevindt zich momenteel op versie',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'om de nieuwe firmware te activeren',
|
|
||||||
CLOSE: 'Sluiten',
|
CLOSE: 'Sluiten',
|
||||||
USE: 'Gebruik',
|
USE: 'Gebruik',
|
||||||
FACTORY_RESET: 'Fabrieksinstellingen',
|
FACTORY_RESET: 'Fabrieksinstellingen',
|
||||||
SYSTEM_FACTORY_TEXT: 'Gateway is gereset en start nu weer op met fabrieksinstellingen',
|
SYSTEM_FACTORY_TEXT: 'Gateway is gereset en start nu weer op met fabrieksinstellingen',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Weet je zeker dat je een reset naar fabrieksinstellingen uit wilt voeren?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Weet je zeker dat je een reset naar fabrieksinstellingen uit wilt voeren?',
|
||||||
VERSION_CHECK: 'Versie Check',
|
|
||||||
THE_LATEST: 'De laatste',
|
THE_LATEST: 'De laatste',
|
||||||
OFFICIAL: 'official',
|
OFFICIAL: 'official',
|
||||||
DEVELOPMENT: 'development',
|
DEVELOPMENT: 'development',
|
||||||
@@ -244,6 +236,7 @@ const nl: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Thermostaten',
|
MQTT_INT_THERMOSTATS: 'Thermostaten',
|
||||||
MQTT_INT_SOLAR: 'Solar Modules',
|
MQTT_INT_SOLAR: 'Solar Modules',
|
||||||
MQTT_INT_MIXER: 'Mixer Modules',
|
MQTT_INT_MIXER: 'Mixer Modules',
|
||||||
|
MQTT_INT_WATER: 'Water Modules',
|
||||||
MQTT_QUEUE: 'MQTT Queue',
|
MQTT_QUEUE: 'MQTT Queue',
|
||||||
DEFAULT: 'Default',
|
DEFAULT: 'Default',
|
||||||
MQTT_ENTITY_FORMAT: 'Entity ID formaat',
|
MQTT_ENTITY_FORMAT: 'Entity ID formaat',
|
||||||
@@ -280,7 +273,7 @@ const nl: Translation = {
|
|||||||
NETWORK_SCANNER: 'Netwerk Scanner',
|
NETWORK_SCANNER: 'Netwerk Scanner',
|
||||||
NETWORK_NO_WIFI: 'Geen WiFi networken gevonden',
|
NETWORK_NO_WIFI: 'Geen WiFi networken gevonden',
|
||||||
NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen',
|
NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen',
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
||||||
TX_POWER: 'Tx Vermogen',
|
TX_POWER: 'Tx Vermogen',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten',
|
NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten',
|
||||||
@@ -316,20 +309,22 @@ const nl: Translation = {
|
|||||||
SCHEDULE_TIMER_2: 'elke minuut',
|
SCHEDULE_TIMER_2: 'elke minuut',
|
||||||
SCHEDULE_TIMER_3: 'elke huur',
|
SCHEDULE_TIMER_3: 'elke huur',
|
||||||
CUSTOM_ENTITIES: 'Aangepaste Entiteiten',
|
CUSTOM_ENTITIES: 'Aangepaste Entiteiten',
|
||||||
ENTITIES_HELP_1: 'Aangepaste entiteiten ophalen uit de EMS-bus', // TODO translate
|
ENTITIES_HELP_1: 'Aangepaste entiteiten ophalen uit de EMS-bus',
|
||||||
ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
|
ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
|
||||||
WRITEABLE: 'Beschrijfbare',
|
WRITEABLE: 'Beschrijfbare',
|
||||||
SHOWING: 'Tonen',
|
SHOWING: 'Tonen',
|
||||||
SEARCH: 'Zoek',
|
SEARCH: 'Zoek',
|
||||||
CERT: 'TLS rootcertificaat (laat leeg om TLS-insecure)', // TODO translate
|
CERT: 'TLS rootcertificaat (laat leeg om TLS-insecure)',
|
||||||
ENABLE_TLS: 'Activeer TLS',
|
ENABLE_TLS: 'Activeer TLS',
|
||||||
ON: 'On', // TODO translate
|
ON: 'Aan',
|
||||||
OFF: 'Off', // TODO translate
|
OFF: 'Uit',
|
||||||
POLARITY: 'Polarity', // TODO translate
|
POLARITY: 'Polariteit',
|
||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Actiev Hoog',
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Actiev Laag',
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Ongewijzigd',
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Altijd',
|
||||||
|
ACTIVITY: 'Activiteit',
|
||||||
|
CONFIGURE: '{0} Configureren'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const no: Translation = {
|
|||||||
USERNAME: 'Brukernavn',
|
USERNAME: 'Brukernavn',
|
||||||
PASSWORD: 'Passord',
|
PASSWORD: 'Passord',
|
||||||
SU_PASSWORD: 'su Passord',
|
SU_PASSWORD: 'su Passord',
|
||||||
DASHBOARD: 'Dashboard',
|
|
||||||
SETTINGS_OF: '{0} Innstillinger',
|
SETTINGS_OF: '{0} Innstillinger',
|
||||||
HELP_OF: '{0} Hjelp',
|
HELP_OF: '{0} Hjelp',
|
||||||
LOGGED_IN: 'Logget in som {name}',
|
LOGGED_IN: 'Logget in som {name}',
|
||||||
@@ -37,8 +36,6 @@ const no: Translation = {
|
|||||||
BRAND: 'Fabrikat',
|
BRAND: 'Fabrikat',
|
||||||
ENTITY_NAME: 'Objektsnavn',
|
ENTITY_NAME: 'Objektsnavn',
|
||||||
VALUE: '{{Verdi|verdi}}',
|
VALUE: '{{Verdi|verdi}}',
|
||||||
DEVICE_DATA: 'Enheterdata',
|
|
||||||
SENSOR_DATA: 'Sensordata',
|
|
||||||
DEVICES: 'Enheter',
|
DEVICES: 'Enheter',
|
||||||
SENSORS: 'Sensorer',
|
SENSORS: 'Sensorer',
|
||||||
RUN_COMMAND: 'Kjør kommando',
|
RUN_COMMAND: 'Kjør kommando',
|
||||||
@@ -83,7 +80,6 @@ const no: Translation = {
|
|||||||
FAIL: 'MISLYKKET',
|
FAIL: 'MISLYKKET',
|
||||||
QUALITY: 'KVALITET',
|
QUALITY: 'KVALITET',
|
||||||
SCAN_DEVICES: 'Søk etter nye enheter',
|
SCAN_DEVICES: 'Søk etter nye enheter',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS Buss & Aktivitet Status',
|
|
||||||
SCAN: 'Søk',
|
SCAN: 'Søk',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrammer Mottatt (Rx)',
|
'EMS Telegrammer Mottatt (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const no: Translation = {
|
|||||||
OPTIONS: 'Alternativ',
|
OPTIONS: 'Alternativ',
|
||||||
NAME: 'Navn',
|
NAME: 'Navn',
|
||||||
CUSTOMIZATIONS_RESET: 'Er du sikker på att du vil fjerne tilpassninger inkludert innstillinger for Temperatur og Analoge sensorer?',
|
CUSTOMIZATIONS_RESET: 'Er du sikker på att du vil fjerne tilpassninger inkludert innstillinger for Temperatur og Analoge sensorer?',
|
||||||
DEVICE_ENTITIES: 'Enhets objekter',
|
|
||||||
SUPPORT_INFORMATION: 'Supportinformasjon',
|
SUPPORT_INFORMATION: 'Supportinformasjon',
|
||||||
CLICK_HERE: 'Klikk her',
|
|
||||||
HELP_INFORMATION_1: 'Besøk wiki for instruksjoner for å konfigurere EMS-ESP',
|
HELP_INFORMATION_1: 'Besøk wiki for instruksjoner for å konfigurere EMS-ESP',
|
||||||
HELP_INFORMATION_2: 'For community-support besøk vår Discord-server',
|
HELP_INFORMATION_2: 'For community-support besøk vår Discord-server',
|
||||||
HELP_INFORMATION_3: 'For å be om en ny funksjon eller melde feil',
|
HELP_INFORMATION_3: 'For å be om en ny funksjon eller melde feil',
|
||||||
@@ -181,13 +175,11 @@ const no: Translation = {
|
|||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Opp/Nedlasting',
|
UPLOAD_DOWNLOAD: 'Opp/Nedlasting',
|
||||||
VERSION_ON: 'You are currently on', // TODO translate
|
VERSION_ON: 'You are currently on', // TODO translate
|
||||||
SYSTEM_APPLY_FIRMWARE: 'for å aktivere ny firmware',
|
|
||||||
CLOSE: 'Steng',
|
CLOSE: 'Steng',
|
||||||
USE: 'Bruk',
|
USE: 'Bruk',
|
||||||
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
|
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
|
||||||
SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte',
|
SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
|
||||||
VERSION_CHECK: 'Versjonsjekk',
|
|
||||||
THE_LATEST: 'Den nyeste',
|
THE_LATEST: 'Den nyeste',
|
||||||
OFFICIAL: 'official',
|
OFFICIAL: 'official',
|
||||||
DEVELOPMENT: 'development',
|
DEVELOPMENT: 'development',
|
||||||
@@ -244,6 +236,7 @@ const no: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostat',
|
MQTT_INT_THERMOSTATS: 'Termostat',
|
||||||
MQTT_INT_SOLAR: 'Solpaneler',
|
MQTT_INT_SOLAR: 'Solpaneler',
|
||||||
MQTT_INT_MIXER: 'Blandeventil',
|
MQTT_INT_MIXER: 'Blandeventil',
|
||||||
|
MQTT_INT_WATER: 'Water Modules', // TODO translate
|
||||||
MQTT_QUEUE: 'MQTT Queue',
|
MQTT_QUEUE: 'MQTT Queue',
|
||||||
DEFAULT: 'Standard',
|
DEFAULT: 'Standard',
|
||||||
MQTT_ENTITY_FORMAT: 'Enhets ID format',
|
MQTT_ENTITY_FORMAT: 'Enhets ID format',
|
||||||
@@ -329,7 +322,9 @@ const no: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Active High', // TODO translate
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Active Low', // TODO translate
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Unchanged', // TODO translate
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Always', // TODO translate
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const pl: BaseTranslation = {
|
|||||||
USERNAME: '{{Użytkownik|Nazwa użytkownika|}}',
|
USERNAME: '{{Użytkownik|Nazwa użytkownika|}}',
|
||||||
PASSWORD: 'Hasło',
|
PASSWORD: 'Hasło',
|
||||||
SU_PASSWORD: 'Hasło "su"',
|
SU_PASSWORD: 'Hasło "su"',
|
||||||
DASHBOARD: 'Pulpit',
|
|
||||||
SETTINGS_OF: 'Ustawienia {0}',
|
SETTINGS_OF: 'Ustawienia {0}',
|
||||||
HELP_OF: 'Pomoc {0}',
|
HELP_OF: 'Pomoc {0}',
|
||||||
LOGGED_IN: 'Zalogowano użytkownika {name}.',
|
LOGGED_IN: 'Zalogowano użytkownika {name}.',
|
||||||
@@ -37,8 +36,6 @@ const pl: BaseTranslation = {
|
|||||||
VERSION: 'Wersja',
|
VERSION: 'Wersja',
|
||||||
ENTITY_NAME: '{{N|n|}}azwa encji',
|
ENTITY_NAME: '{{N|n|}}azwa encji',
|
||||||
VALUE: '{{W|w|}}artość',
|
VALUE: '{{W|w|}}artość',
|
||||||
DEVICE_DATA: 'Dane z urządzeń',
|
|
||||||
SENSOR_DATA: 'Dane z czujników',
|
|
||||||
DEVICES: 'Urządzenia',
|
DEVICES: 'Urządzenia',
|
||||||
SENSORS: 'Czujniki',
|
SENSORS: 'Czujniki',
|
||||||
RUN_COMMAND: 'Wykonaj komendę',
|
RUN_COMMAND: 'Wykonaj komendę',
|
||||||
@@ -83,7 +80,6 @@ const pl: BaseTranslation = {
|
|||||||
FAIL: 'Nieudane',
|
FAIL: 'Nieudane',
|
||||||
QUALITY: 'Jakość',
|
QUALITY: 'Jakość',
|
||||||
SCAN_DEVICES: 'Wyszukiwanie nowych urządzeń',
|
SCAN_DEVICES: 'Wyszukiwanie nowych urządzeń',
|
||||||
EMS_BUS_STATUS_TITLE: 'Aktywność',
|
|
||||||
SCAN: 'Skanuj',
|
SCAN: 'Skanuj',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS, telegramy odebrane (Rx)',
|
'EMS, telegramy odebrane (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const pl: BaseTranslation = {
|
|||||||
OPTIONS: 'Opcje',
|
OPTIONS: 'Opcje',
|
||||||
NAME: '{{Nazwa|nazwa|}}',
|
NAME: '{{Nazwa|nazwa|}}',
|
||||||
CUSTOMIZATIONS_RESET: 'Na pewno chcesz usunąć wszystkie personalizacje łącznie z ustawieniami dla czujników temperatury 1-Wire® i urządzeń podłączonych do EMS-ESP?',
|
CUSTOMIZATIONS_RESET: 'Na pewno chcesz usunąć wszystkie personalizacje łącznie z ustawieniami dla czujników temperatury 1-Wire® i urządzeń podłączonych do EMS-ESP?',
|
||||||
DEVICE_ENTITIES: 'Encje urządzenia',
|
|
||||||
SUPPORT_INFORMATION: '{{I|i|}}nformacj{{e|i|}} o systemie',
|
SUPPORT_INFORMATION: '{{I|i|}}nformacj{{e|i|}} o systemie',
|
||||||
CLICK_HERE: 'Kliknij tu',
|
|
||||||
HELP_INFORMATION_1: 'Aby uzyskać instrukcje dotyczące konfiguracji EMS-ESP, skorzystaj z wiki w internecie',
|
HELP_INFORMATION_1: 'Aby uzyskać instrukcje dotyczące konfiguracji EMS-ESP, skorzystaj z wiki w internecie',
|
||||||
HELP_INFORMATION_2: 'Aby dołączyć do naszego serwera Discord i komunikować się na żywo ze społecznością',
|
HELP_INFORMATION_2: 'Aby dołączyć do naszego serwera Discord i komunikować się na żywo ze społecznością',
|
||||||
HELP_INFORMATION_3: 'Aby zaproponować nową funkcjonalność lub zgłosić problem',
|
HELP_INFORMATION_3: 'Aby zaproponować nową funkcjonalność lub zgłosić problem',
|
||||||
@@ -181,13 +175,11 @@ const pl: BaseTranslation = {
|
|||||||
STATUS_OF: 'Status {0}',
|
STATUS_OF: 'Status {0}',
|
||||||
UPLOAD_DOWNLOAD: 'Przesyłanie plików',
|
UPLOAD_DOWNLOAD: 'Przesyłanie plików',
|
||||||
VERSION_ON: 'Aktualnie używasz',
|
VERSION_ON: 'Aktualnie używasz',
|
||||||
SYSTEM_APPLY_FIRMWARE: '',
|
|
||||||
CLOSE: 'Zamknij',
|
CLOSE: 'Zamknij',
|
||||||
USE: 'Aby zaktualizować firmware skorzystaj z funkcji',
|
USE: 'Aby zaktualizować firmware skorzystaj z funkcji',
|
||||||
FACTORY_RESET: 'Ustawienia fabryczne',
|
FACTORY_RESET: 'Ustawienia fabryczne',
|
||||||
SYSTEM_FACTORY_TEXT: 'Interfejs EMS-ESP został przywrócony do ustawień fabrycznych i zostanie teraz ponownie uruchomiony.',
|
SYSTEM_FACTORY_TEXT: 'Interfejs EMS-ESP został przywrócony do ustawień fabrycznych i zostanie teraz ponownie uruchomiony.',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP? ',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP? ',
|
||||||
VERSION_CHECK: 'Sprawd{{ź|zanie|}} wersj{{ę|i|}}',
|
|
||||||
THE_LATEST: 'Najnowsze',
|
THE_LATEST: 'Najnowsze',
|
||||||
OFFICIAL: 'oficjalne',
|
OFFICIAL: 'oficjalne',
|
||||||
DEVELOPMENT: 'testowe',
|
DEVELOPMENT: 'testowe',
|
||||||
@@ -244,6 +236,7 @@ const pl: BaseTranslation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
MQTT_INT_THERMOSTATS: 'Termostaty',
|
||||||
MQTT_INT_SOLAR: 'Panele solarne',
|
MQTT_INT_SOLAR: 'Panele solarne',
|
||||||
MQTT_INT_MIXER: 'Mieszacze',
|
MQTT_INT_MIXER: 'Mieszacze',
|
||||||
|
MQTT_INT_WATER: 'Woda',
|
||||||
MQTT_QUEUE: 'Kolejka MQTT',
|
MQTT_QUEUE: 'Kolejka MQTT',
|
||||||
DEFAULT: '{{Pozostałe|Domyślna|}}',
|
DEFAULT: '{{Pozostałe|Domyślna|}}',
|
||||||
MQTT_ENTITY_FORMAT: 'Format "Entity ID"',
|
MQTT_ENTITY_FORMAT: 'Format "Entity ID"',
|
||||||
@@ -316,7 +309,7 @@ const pl: BaseTranslation = {
|
|||||||
SCHEDULE_TIMER_2: 'co minutę',
|
SCHEDULE_TIMER_2: 'co minutę',
|
||||||
SCHEDULE_TIMER_3: 'co godzinę',
|
SCHEDULE_TIMER_3: 'co godzinę',
|
||||||
CUSTOM_ENTITIES: '{{N|n|}}iestandardowe{{|j|}} encj{{e|i|}}',
|
CUSTOM_ENTITIES: '{{N|n|}}iestandardowe{{|j|}} encj{{e|i|}}',
|
||||||
ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS.', // TODO translate
|
ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS.',
|
||||||
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
|
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
|
||||||
WRITEABLE: 'Zapisywalna',
|
WRITEABLE: 'Zapisywalna',
|
||||||
SHOWING: 'Wyświetlane',
|
SHOWING: 'Wyświetlane',
|
||||||
@@ -329,7 +322,9 @@ const pl: BaseTranslation = {
|
|||||||
ACTIVEHIGH: 'Wyzwalany stanem wysokim',
|
ACTIVEHIGH: 'Wyzwalany stanem wysokim',
|
||||||
ACTIVELOW: 'Wyzwalany stanem niskim',
|
ACTIVELOW: 'Wyzwalany stanem niskim',
|
||||||
UNCHANGED: 'Zachowaj stan',
|
UNCHANGED: 'Zachowaj stan',
|
||||||
ALWAYS: 'Zawsze'
|
ALWAYS: 'Zawsze',
|
||||||
|
ACTIVITY: 'Aktywność',
|
||||||
|
CONFIGURE: 'Konfiguracja {0}'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const sk: Translation = {
|
|||||||
USERNAME: 'Užívateľské meno',
|
USERNAME: 'Užívateľské meno',
|
||||||
PASSWORD: 'Heslo',
|
PASSWORD: 'Heslo',
|
||||||
SU_PASSWORD: 'su heslo',
|
SU_PASSWORD: 'su heslo',
|
||||||
DASHBOARD: 'Panel',
|
|
||||||
SETTINGS_OF: '{0} Nastavenia',
|
SETTINGS_OF: '{0} Nastavenia',
|
||||||
HELP_OF: '{0} Pomoc',
|
HELP_OF: '{0} Pomoc',
|
||||||
LOGGED_IN: 'Prihlásený ako {name}',
|
LOGGED_IN: 'Prihlásený ako {name}',
|
||||||
@@ -36,9 +35,7 @@ const sk: Translation = {
|
|||||||
VERSION: 'Verzia',
|
VERSION: 'Verzia',
|
||||||
BRAND: 'Značka',
|
BRAND: 'Značka',
|
||||||
ENTITY_NAME: 'Názov entity',
|
ENTITY_NAME: 'Názov entity',
|
||||||
VALUE: '{{Value|value}}',
|
VALUE: '{{Hodnota|hodnota}}',
|
||||||
DEVICE_DATA: 'Dáta zariadenia',
|
|
||||||
SENSOR_DATA: 'Dáta snímača',
|
|
||||||
DEVICES: 'Zariadenia',
|
DEVICES: 'Zariadenia',
|
||||||
SENSORS: 'Snímače',
|
SENSORS: 'Snímače',
|
||||||
RUN_COMMAND: 'Volať príkaz',
|
RUN_COMMAND: 'Volať príkaz',
|
||||||
@@ -80,34 +77,33 @@ const sk: Translation = {
|
|||||||
ACTIVE_DEVICES: 'Aktívne zariadenia a snímače',
|
ACTIVE_DEVICES: 'Aktívne zariadenia a snímače',
|
||||||
EMS_DEVICE: 'EMS zariadenie',
|
EMS_DEVICE: 'EMS zariadenie',
|
||||||
SUCCESS: 'ÚSPEŠNÉ',
|
SUCCESS: 'ÚSPEŠNÉ',
|
||||||
FAIL: 'ZLYHANIE',
|
FAIL: 'ZLÝHANIE',
|
||||||
QUALITY: 'KVALITA',
|
QUALITY: 'KVALITA',
|
||||||
SCAN_DEVICES: 'Scan pre nové zariadenia',
|
SCAN_DEVICES: 'Scan pre nové zariadenia',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS zbernica & stav aktivity',
|
|
||||||
SCAN: 'Scan',
|
SCAN: 'Scan',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegramy prijaté (Rx)',
|
'EMS Telegramy prijaté (Rx)',
|
||||||
'EMS Čítania (Tx)',
|
'EMS Čítania (Tx)',
|
||||||
'EMS Zápisy (Tx)',
|
'EMS Zápisy (Tx)',
|
||||||
'Čítanie snímača teploty',
|
'Čítanie snímačov teploty',
|
||||||
'Analógové snímanie',
|
'Čítanie analógových snímačov',
|
||||||
'MQTT Publikovanie',
|
'MQTT Publikovanie',
|
||||||
'API volania',
|
'Externé API volania',
|
||||||
'Syslog správy'
|
'Syslog správy'
|
||||||
],
|
],
|
||||||
NUM_DEVICES: '{num} Zariadenia{{s}}',
|
NUM_DEVICES: '{num} Zariaden{{í|ie|ia|ia|í|í}}',
|
||||||
NUM_TEMP_SENSORS: '{num} Teplotné snímače{{s}}',
|
NUM_TEMP_SENSORS: '{num} Teplotn{{ých|ý|é|é|ých|ých}} sníma{{čov|č|če|če|čov|čov}}',
|
||||||
NUM_ANALOG_SENSORS: '{num} Analógové snímače{{s}}',
|
NUM_ANALOG_SENSORS: '{num} Analogov{{ých|ý|é|é|ých|ých}} sníma{{čov|č|če|če|čov|čov}}',
|
||||||
NUM_DAYS: '{num} dní{{s}}',
|
NUM_DAYS: '{num} d{{ní|eň|ní|ní|ní|ní}}',
|
||||||
NUM_SECONDS: '{num} sekúnd{{s}}',
|
NUM_SECONDS: '{num} sek{{únd|unda|undy|undy|únd|únd}}',
|
||||||
NUM_HOURS: '{num} hodín{{s}}',
|
NUM_HOURS: '{num} hod{{ín|ina|iny|iny|ín|ín}}',
|
||||||
NUM_MINUTES: '{num} minút{{s}}',
|
NUM_MINUTES: '{num} minú{{t|ta|ty|ty|t|t}}',
|
||||||
APPLICATION_SETTINGS: 'Nastavenia aplikácie',
|
APPLICATION_SETTINGS: 'Nastavenia aplikácie',
|
||||||
CUSTOMIZATIONS: 'Prispôsobenia',
|
CUSTOMIZATIONS: 'Prispôsobenia',
|
||||||
APPLICATION_RESTARTING: 'EMS-ESP sa reštartuje',
|
APPLICATION_RESTARTING: 'EMS-ESP sa reštartuje',
|
||||||
INTERFACE_BOARD_PROFILE: 'Profil boardu rozhrania',
|
INTERFACE_BOARD_PROFILE: 'Profil dosky rozhrania',
|
||||||
BOARD_PROFILE_TEXT: 'Vyberte vopred nakonfigurovaný profil dosky rozhrania zo zoznamu nižšie alebo vyberte možnosť Vlastné a nakonfigurujte svoje vlastné hardvérové nastavenia',
|
BOARD_PROFILE_TEXT: 'Vyberte vopred nakonfigurovaný profil dosky rozhrania zo zoznamu nižšie, alebo vyberte možnosť Vlastné a nakonfigurujte svoje vlastné hardvérové nastavenia',
|
||||||
BOARD_PROFILE: 'Board profil',
|
BOARD_PROFILE: 'Profil dosky',
|
||||||
CUSTOM: 'Vlastné',
|
CUSTOM: 'Vlastné',
|
||||||
GPIO_OF: '{0} GPIO',
|
GPIO_OF: '{0} GPIO',
|
||||||
BUTTON: 'Tlačidlo',
|
BUTTON: 'Tlačidlo',
|
||||||
@@ -122,11 +118,12 @@ const sk: Translation = {
|
|||||||
HIDE_LED: 'Skryť LED',
|
HIDE_LED: 'Skryť LED',
|
||||||
ENABLE_TELNET: 'Povoliť Telnet konzolu',
|
ENABLE_TELNET: 'Povoliť Telnet konzolu',
|
||||||
ENABLE_ANALOG: 'Povoliť analógové snímače',
|
ENABLE_ANALOG: 'Povoliť analógové snímače',
|
||||||
CONVERT_FAHRENHEIT: 'Previesť hodnoty teploty na fahrenheity',
|
CONVERT_FAHRENHEIT: 'Previesť hodnoty teploty na °F',
|
||||||
BYPASS_TOKEN: 'Vynechajte autorizáciu prístupového tokenu pri volaniach API',
|
BYPASS_TOKEN: 'Vynechajte autorizáciu prístupového tokenu pri volaniach API',
|
||||||
READONLY: 'Povoliť režim len na čítanie (blokuje všetky odchádzajúce príkazy EMS Tx Write)',
|
READONLY: 'Povoliť režim len na čítanie (blokuje všetky odchádzajúce príkazy EMS Tx Write)',
|
||||||
UNDERCLOCK_CPU: 'Podtaktovanie rýchlosti procesora',
|
UNDERCLOCK_CPU: 'Podtaktovanie rýchlosti procesora',
|
||||||
HEATINGOFF: 'Spustite kotol s núteným vykurovaním',
|
HEATINGOFF: 'Spustiť kotol s vynúteným vykurovaním',
|
||||||
|
|
||||||
ENABLE_SHOWER_TIMER: 'Povoliť časovač sprchovania',
|
ENABLE_SHOWER_TIMER: 'Povoliť časovač sprchovania',
|
||||||
ENABLE_SHOWER_ALERT: 'Povoliť upozornenie na sprchu',
|
ENABLE_SHOWER_ALERT: 'Povoliť upozornenie na sprchu',
|
||||||
TRIGGER_TIME: 'Čas spustenia',
|
TRIGGER_TIME: 'Čas spustenia',
|
||||||
@@ -136,7 +133,7 @@ const sk: Translation = {
|
|||||||
BOOLEAN_FORMAT_API: 'Boolean formát API/MQTT',
|
BOOLEAN_FORMAT_API: 'Boolean formát API/MQTT',
|
||||||
ENUM_FORMAT: 'Enum formát API/MQTT',
|
ENUM_FORMAT: 'Enum formát API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Povolenie parazitného napájania',
|
ENABLE_PARASITE: 'Povoliť parazité napájanie DS18B20',
|
||||||
LOGGING: 'Logovanie',
|
LOGGING: 'Logovanie',
|
||||||
LOG_HEX: 'Záznam telegramov EMS v hexadecimálnej sústave',
|
LOG_HEX: 'Záznam telegramov EMS v hexadecimálnej sústave',
|
||||||
ENABLE_SYSLOG: 'Povoliť Syslog',
|
ENABLE_SYSLOG: 'Povoliť Syslog',
|
||||||
@@ -163,9 +160,7 @@ const sk: Translation = {
|
|||||||
OPTIONS: 'Možnosti',
|
OPTIONS: 'Možnosti',
|
||||||
NAME: 'Názov',
|
NAME: 'Názov',
|
||||||
CUSTOMIZATIONS_RESET: 'Naozaj chcete odstrániť všetky prispôsobenia vrátane vlastných nastavení snímačov teploty a analógových snímačov?',
|
CUSTOMIZATIONS_RESET: 'Naozaj chcete odstrániť všetky prispôsobenia vrátane vlastných nastavení snímačov teploty a analógových snímačov?',
|
||||||
DEVICE_ENTITIES: 'Entity zariadenia',
|
SUPPORT_INFORMATION: 'Informácie pre podporu',
|
||||||
SUPPORT_INFORMATION: 'Informácie o podpore',
|
|
||||||
CLICK_HERE: 'Kliknite tu',
|
|
||||||
HELP_INFORMATION_1: 'Navštívte online wiki, kde nájdete pokyny na konfiguráciu EMS-ESP',
|
HELP_INFORMATION_1: 'Navštívte online wiki, kde nájdete pokyny na konfiguráciu EMS-ESP',
|
||||||
HELP_INFORMATION_2: 'Pre živý komunitný chat sa pripojte na náš Discord server',
|
HELP_INFORMATION_2: 'Pre živý komunitný chat sa pripojte na náš Discord server',
|
||||||
HELP_INFORMATION_3: 'Ak chcete požiadať o funkciu alebo nahlásiť chybu',
|
HELP_INFORMATION_3: 'Ak chcete požiadať o funkciu alebo nahlásiť chybu',
|
||||||
@@ -180,27 +175,25 @@ const sk: Translation = {
|
|||||||
LOG_OF: '{0} Log',
|
LOG_OF: '{0} Log',
|
||||||
STATUS_OF: '{0} Stav',
|
STATUS_OF: '{0} Stav',
|
||||||
UPLOAD_DOWNLOAD: 'Nahrať/Stiahnuť',
|
UPLOAD_DOWNLOAD: 'Nahrať/Stiahnuť',
|
||||||
VERSION_ON: 'Momentálne ste vo verzii',
|
VERSION_ON: 'Momentálne nainštalovaná verzia: ',
|
||||||
SYSTEM_APPLY_FIRMWARE: 'na použitie nového firmvéru',
|
|
||||||
CLOSE: 'Zatvoriť',
|
CLOSE: 'Zatvoriť',
|
||||||
USE: 'Použiť',
|
USE: 'Použiť',
|
||||||
FACTORY_RESET: 'Továrenské nastavenia',
|
FACTORY_RESET: 'Továrenské nastavenia',
|
||||||
SYSTEM_FACTORY_TEXT: 'Zariadenie bolo obnovené z výroby a teraz sa reštartuje',
|
SYSTEM_FACTORY_TEXT: 'Zariadenie bolo obnovené z výroby a teraz sa reštartuje',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?',
|
||||||
VERSION_CHECK: 'Kontrola verzie',
|
|
||||||
THE_LATEST: 'Posledná',
|
THE_LATEST: 'Posledná',
|
||||||
OFFICIAL: 'officiálna',
|
OFFICIAL: 'officiálna',
|
||||||
DEVELOPMENT: 'vývojárska',
|
DEVELOPMENT: 'vývojárska',
|
||||||
RELEASE_IS: 'vydanie je',
|
RELEASE_IS: 'verzia je',
|
||||||
RELEASE_NOTES: 'poznámky k vydaniu',
|
RELEASE_NOTES: 'poznámky k verzii',
|
||||||
EMS_ESP_VER: 'EMS-ESP verzia',
|
EMS_ESP_VER: 'EMS-ESP verzia',
|
||||||
UPTIME: 'Beh systému',
|
UPTIME: 'Beh systému',
|
||||||
HEAP: 'Zásobník (voľné / max pridelenie)',
|
HEAP: 'Zásobník (voľné / max pridelenie)',
|
||||||
PSRAM: 'PSRAM (Veľkosť / Voľné)',
|
PSRAM: 'PSRAM (Veľkosť / Voľné)',
|
||||||
FLASH: 'Flash chip (Veľkosť / Rýchlosť)',
|
FLASH: 'Flash chip (Veľkosť / Rýchlosť)',
|
||||||
APPSIZE: 'Applikácia (Priečka: Použité / Voľné)',
|
APPSIZE: 'Applikácia (Oddiel: Použité / Voľné)',
|
||||||
FILESYSTEM: 'Súborový systém (Použité / Voľné)',
|
FILESYSTEM: 'Súborový systém (Použité / Voľné)',
|
||||||
BUFFER_SIZE: 'Maximálna veľkosť vyrovnávacej pamäte',
|
BUFFER_SIZE: 'Buffer-max.veľkosť',
|
||||||
COMPACT: 'Kompaktné',
|
COMPACT: 'Kompaktné',
|
||||||
ENABLE_OTA: 'Povoliť OTA aktualizácie',
|
ENABLE_OTA: 'Povoliť OTA aktualizácie',
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Stiahnutie prispôsobení entity',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Stiahnutie prispôsobení entity',
|
||||||
@@ -208,7 +201,7 @@ const sk: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
||||||
UPLOAD_TEXT: 'Najskôr nahrajte nový súbor firmvéru (.bin), nastavenia alebo prispôsobenia (.json), pre voliteľné overenie nahrajte súbor (.md5)',
|
UPLOAD_TEXT: 'Najskôr nahrajte nový súbor firmvéru (.bin), nastavenia alebo prispôsobenia (.json), pre voliteľné overenie nahrajte súbor (.md5)',
|
||||||
UPLOADING: 'Nahrávanie',
|
UPLOADING: 'Nahrávanie',
|
||||||
UPLOAD_DROP_TEXT: 'Zahodiť súbor alebo kliknúť sem',
|
UPLOAD_DROP_TEXT: 'Potiahnúť a pripnúť súbor alebo kliknúť sem',
|
||||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
||||||
TIME_SET: 'Nastavený čas',
|
TIME_SET: 'Nastavený čas',
|
||||||
MANAGE_USERS: 'Správa používateľov',
|
MANAGE_USERS: 'Správa používateľov',
|
||||||
@@ -244,6 +237,7 @@ const sk: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
MQTT_INT_THERMOSTATS: 'Termostaty',
|
||||||
MQTT_INT_SOLAR: 'Solárne moduly',
|
MQTT_INT_SOLAR: 'Solárne moduly',
|
||||||
MQTT_INT_MIXER: 'Zmiešavacie moduley',
|
MQTT_INT_MIXER: 'Zmiešavacie moduley',
|
||||||
|
MQTT_INT_WATER: 'Voda moduley',
|
||||||
MQTT_QUEUE: 'Fronta MQTT',
|
MQTT_QUEUE: 'Fronta MQTT',
|
||||||
DEFAULT: 'Predvolené',
|
DEFAULT: 'Predvolené',
|
||||||
MQTT_ENTITY_FORMAT: 'ID formát entity',
|
MQTT_ENTITY_FORMAT: 'ID formát entity',
|
||||||
@@ -265,7 +259,7 @@ const sk: Translation = {
|
|||||||
ACCESS_POINT: 'Prístupový bod',
|
ACCESS_POINT: 'Prístupový bod',
|
||||||
AP_PROVIDE: 'Povoliť prístupový bod',
|
AP_PROVIDE: 'Povoliť prístupový bod',
|
||||||
AP_PROVIDE_TEXT_1: 'vždy',
|
AP_PROVIDE_TEXT_1: 'vždy',
|
||||||
AP_PROVIDE_TEXT_2: 'keď WiFi je odpojená',
|
AP_PROVIDE_TEXT_2: 'keď je WiFi odpojená',
|
||||||
AP_PROVIDE_TEXT_3: 'nikdy',
|
AP_PROVIDE_TEXT_3: 'nikdy',
|
||||||
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
||||||
AP_HIDE_SSID: 'Skryť SSID',
|
AP_HIDE_SSID: 'Skryť SSID',
|
||||||
@@ -293,11 +287,11 @@ const sk: Translation = {
|
|||||||
NETWORK_GATEWAY: 'Brána',
|
NETWORK_GATEWAY: 'Brána',
|
||||||
NETWORK_SUBNET: 'Maska podsiete',
|
NETWORK_SUBNET: 'Maska podsiete',
|
||||||
NETWORK_DNS: 'DNS servery',
|
NETWORK_DNS: 'DNS servery',
|
||||||
ADDRESS_OF: '{0} adries',
|
ADDRESS_OF: '{0} adresa',
|
||||||
ADMIN: 'Admin',
|
ADMIN: 'Admin',
|
||||||
GUEST: 'Hosť',
|
GUEST: 'Hosť',
|
||||||
NEW: 'Nová',
|
NEW: 'Nová',
|
||||||
NEW_NAME_OF: 'Nových {0} názvov',
|
NEW_NAME_OF: 'Nový názov {0}',
|
||||||
ENTITY: 'entita',
|
ENTITY: 'entita',
|
||||||
MIN: 'min',
|
MIN: 'min',
|
||||||
MAX: 'max',
|
MAX: 'max',
|
||||||
@@ -308,7 +302,7 @@ const sk: Translation = {
|
|||||||
SCHEDULER: 'Plánovač',
|
SCHEDULER: 'Plánovač',
|
||||||
SCHEDULER_HELP_1: 'Automatizujte príkazy pridaním naplánovaných udalostí nižšie. Nastavte jedinečné meno na aktiváciu/deaktiváciu cez API/MQTT.',
|
SCHEDULER_HELP_1: 'Automatizujte príkazy pridaním naplánovaných udalostí nižšie. Nastavte jedinečné meno na aktiváciu/deaktiváciu cez API/MQTT.',
|
||||||
SCHEDULER_HELP_2: 'Použite 00:00 na jednorazové spustenie pri štarte',
|
SCHEDULER_HELP_2: 'Použite 00:00 na jednorazové spustenie pri štarte',
|
||||||
SCHEDULE: 'Plánovať',
|
SCHEDULE: 'Plánovač',
|
||||||
TIME: 'Čas',
|
TIME: 'Čas',
|
||||||
TIMER: 'Časovač',
|
TIMER: 'Časovač',
|
||||||
SCHEDULE_UPDATED: 'Plánovanie aktualizované',
|
SCHEDULE_UPDATED: 'Plánovanie aktualizované',
|
||||||
@@ -316,7 +310,7 @@ const sk: Translation = {
|
|||||||
SCHEDULE_TIMER_2: 'každú minútu',
|
SCHEDULE_TIMER_2: 'každú minútu',
|
||||||
SCHEDULE_TIMER_3: 'každú hodinu',
|
SCHEDULE_TIMER_3: 'každú hodinu',
|
||||||
CUSTOM_ENTITIES: 'Vlastné entity',
|
CUSTOM_ENTITIES: 'Vlastné entity',
|
||||||
ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS', // TODO translate
|
ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS',
|
||||||
ENTITIES_UPDATED: 'Aktualizované entity',
|
ENTITIES_UPDATED: 'Aktualizované entity',
|
||||||
WRITEABLE: 'Zapísateľný',
|
WRITEABLE: 'Zapísateľný',
|
||||||
SHOWING: 'Zobrazenie',
|
SHOWING: 'Zobrazenie',
|
||||||
@@ -329,7 +323,9 @@ const sk: Translation = {
|
|||||||
ACTIVEHIGH: 'Aktívny Vysoký',
|
ACTIVEHIGH: 'Aktívny Vysoký',
|
||||||
ACTIVELOW: 'Aktívny Nízky',
|
ACTIVELOW: 'Aktívny Nízky',
|
||||||
UNCHANGED: 'Nezmenené',
|
UNCHANGED: 'Nezmenené',
|
||||||
ALWAYS: 'Vždy'
|
ALWAYS: 'Vždy',
|
||||||
|
ACTIVITY: 'Aktivita',
|
||||||
|
CONFIGURE: 'Konfiguracia {0}'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const sv: Translation = {
|
|||||||
USERNAME: 'Användarnamn',
|
USERNAME: 'Användarnamn',
|
||||||
PASSWORD: 'Lösenord',
|
PASSWORD: 'Lösenord',
|
||||||
SU_PASSWORD: 'su Lösenord',
|
SU_PASSWORD: 'su Lösenord',
|
||||||
DASHBOARD: 'Kontrollpanel',
|
|
||||||
SETTINGS_OF: '{0} Inställningar',
|
SETTINGS_OF: '{0} Inställningar',
|
||||||
HELP_OF: '{0} Hjälp',
|
HELP_OF: '{0} Hjälp',
|
||||||
LOGGED_IN: 'Inloggad som {name}',
|
LOGGED_IN: 'Inloggad som {name}',
|
||||||
@@ -37,8 +36,6 @@ const sv: Translation = {
|
|||||||
BRAND: 'Fabrikat',
|
BRAND: 'Fabrikat',
|
||||||
ENTITY_NAME: 'Entitetsnamn',
|
ENTITY_NAME: 'Entitetsnamn',
|
||||||
VALUE: '{{Värde|värde}}',
|
VALUE: '{{Värde|värde}}',
|
||||||
DEVICE_DATA: 'Enhets data',
|
|
||||||
SENSOR_DATA: 'Sensor data',
|
|
||||||
DEVICES: 'Enheter',
|
DEVICES: 'Enheter',
|
||||||
SENSORS: 'Sensorer',
|
SENSORS: 'Sensorer',
|
||||||
RUN_COMMAND: 'Kör Kommando',
|
RUN_COMMAND: 'Kör Kommando',
|
||||||
@@ -83,7 +80,6 @@ const sv: Translation = {
|
|||||||
FAIL: 'Misslyckades',
|
FAIL: 'Misslyckades',
|
||||||
QUALITY: 'Kvalitet',
|
QUALITY: 'Kvalitet',
|
||||||
SCAN_DEVICES: 'Sök efter nya enheter',
|
SCAN_DEVICES: 'Sök efter nya enheter',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS-buss & aktivitetsstatus',
|
|
||||||
SCAN: 'Sök',
|
SCAN: 'Sök',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS-telegram (Rx)',
|
'EMS-telegram (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const sv: Translation = {
|
|||||||
OPTIONS: 'Alternativ',
|
OPTIONS: 'Alternativ',
|
||||||
NAME: 'Namn',
|
NAME: 'Namn',
|
||||||
CUSTOMIZATIONS_RESET: 'Är du säker på att du vill ta bort alla anpassningar inklusive inställningar för Temperatur och Analoga sensorer?',
|
CUSTOMIZATIONS_RESET: 'Är du säker på att du vill ta bort alla anpassningar inklusive inställningar för Temperatur och Analoga sensorer?',
|
||||||
DEVICE_ENTITIES: 'Enhets-entiteter',
|
|
||||||
SUPPORT_INFORMATION: 'Supportinformation',
|
SUPPORT_INFORMATION: 'Supportinformation',
|
||||||
CLICK_HERE: 'Klicka Här',
|
|
||||||
HELP_INFORMATION_1: 'Besök Wikin för instruktioner för hur du kan konfigurera EMS-ESP',
|
HELP_INFORMATION_1: 'Besök Wikin för instruktioner för hur du kan konfigurera EMS-ESP',
|
||||||
HELP_INFORMATION_2: 'För community-support besök vår Discord-server',
|
HELP_INFORMATION_2: 'För community-support besök vår Discord-server',
|
||||||
HELP_INFORMATION_3: 'Önska en ny funktion eller rapportera en bugg',
|
HELP_INFORMATION_3: 'Önska en ny funktion eller rapportera en bugg',
|
||||||
@@ -181,13 +175,11 @@ const sv: Translation = {
|
|||||||
STATUS_OF: '{0} Status',
|
STATUS_OF: '{0} Status',
|
||||||
UPLOAD_DOWNLOAD: 'Upp/Nedladdning',
|
UPLOAD_DOWNLOAD: 'Upp/Nedladdning',
|
||||||
VERSION_ON: 'You are currently on', // TODO translate
|
VERSION_ON: 'You are currently on', // TODO translate
|
||||||
SYSTEM_APPLY_FIRMWARE: 'för att aktivera ny firmware',
|
|
||||||
CLOSE: 'Stäng',
|
CLOSE: 'Stäng',
|
||||||
USE: 'Använd',
|
USE: 'Använd',
|
||||||
FACTORY_RESET: 'Fabriksåterställning',
|
FACTORY_RESET: 'Fabriksåterställning',
|
||||||
SYSTEM_FACTORY_TEXT: 'Enheten har blivit fabriksåterställd och startar nu om',
|
SYSTEM_FACTORY_TEXT: 'Enheten har blivit fabriksåterställd och startar nu om',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Är du säker att du vill fabriksåterställa enheten?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Är du säker att du vill fabriksåterställa enheten?',
|
||||||
VERSION_CHECK: 'Senaste versioner',
|
|
||||||
THE_LATEST: 'Den senaste',
|
THE_LATEST: 'Den senaste',
|
||||||
OFFICIAL: 'officiell',
|
OFFICIAL: 'officiell',
|
||||||
DEVELOPMENT: 'utveckling',
|
DEVELOPMENT: 'utveckling',
|
||||||
@@ -244,6 +236,7 @@ const sv: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostater',
|
MQTT_INT_THERMOSTATS: 'Termostater',
|
||||||
MQTT_INT_SOLAR: 'Solpaneler',
|
MQTT_INT_SOLAR: 'Solpaneler',
|
||||||
MQTT_INT_MIXER: 'Blandningsventiler',
|
MQTT_INT_MIXER: 'Blandningsventiler',
|
||||||
|
MQTT_INT_WATER: 'Water Modules', // TODO translate
|
||||||
MQTT_QUEUE: 'MQTT-kö',
|
MQTT_QUEUE: 'MQTT-kö',
|
||||||
DEFAULT: 'Standard',
|
DEFAULT: 'Standard',
|
||||||
MQTT_ENTITY_FORMAT: 'Entitets-ID format',
|
MQTT_ENTITY_FORMAT: 'Entitets-ID format',
|
||||||
@@ -329,7 +322,9 @@ const sv: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Active High', // TODO translate
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Active Low', // TODO translate
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Unchanged', // TODO translate
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Always', // TODO translate
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const tr: Translation = {
|
|||||||
USERNAME: 'Kullanıcı Adı',
|
USERNAME: 'Kullanıcı Adı',
|
||||||
PASSWORD: 'Şifre',
|
PASSWORD: 'Şifre',
|
||||||
SU_PASSWORD: 'SK Şifresi',
|
SU_PASSWORD: 'SK Şifresi',
|
||||||
DASHBOARD: 'Gösterge Paneli',
|
|
||||||
SETTINGS_OF: '{0} Ayarlar',
|
SETTINGS_OF: '{0} Ayarlar',
|
||||||
HELP_OF: '{0} Yardım',
|
HELP_OF: '{0} Yardım',
|
||||||
LOGGED_IN: '{name} olarak giriş yapıldı',
|
LOGGED_IN: '{name} olarak giriş yapıldı',
|
||||||
@@ -37,8 +36,6 @@ const tr: Translation = {
|
|||||||
BRAND: 'Marka',
|
BRAND: 'Marka',
|
||||||
ENTITY_NAME: 'Valık Adı',
|
ENTITY_NAME: 'Valık Adı',
|
||||||
VALUE: '{{Değer|değer}}',
|
VALUE: '{{Değer|değer}}',
|
||||||
DEVICE_DATA: 'Cihaz Bilgisi',
|
|
||||||
SENSOR_DATA: 'Sensör Bilgisi',
|
|
||||||
DEVICES: 'Cihazlar',
|
DEVICES: 'Cihazlar',
|
||||||
SENSORS: 'Sensörler',
|
SENSORS: 'Sensörler',
|
||||||
RUN_COMMAND: 'Çalıştırma Komutu',
|
RUN_COMMAND: 'Çalıştırma Komutu',
|
||||||
@@ -83,7 +80,6 @@ const tr: Translation = {
|
|||||||
FAIL: 'HATA',
|
FAIL: 'HATA',
|
||||||
QUALITY: 'KALİTE',
|
QUALITY: 'KALİTE',
|
||||||
SCAN_DEVICES: 'Yeni cihaz taraması',
|
SCAN_DEVICES: 'Yeni cihaz taraması',
|
||||||
EMS_BUS_STATUS_TITLE: 'EMS Hattı ve Aktivite Durumu',
|
|
||||||
SCAN: 'Tara',
|
SCAN: 'Tara',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegramlar Alındı (Rx)',
|
'EMS Telegramlar Alındı (Rx)',
|
||||||
@@ -163,9 +159,7 @@ const tr: Translation = {
|
|||||||
OPTIONS: 'Seçenekler',
|
OPTIONS: 'Seçenekler',
|
||||||
NAME: 'İsim',
|
NAME: 'İsim',
|
||||||
CUSTOMIZATIONS_RESET: 'Sıcaklık ve Analog Sensörlerin özelleştirilmiş seçenekleri dahil bütün özelleştirmeleri kaldırmak istediğinizden emin misiniz?',
|
CUSTOMIZATIONS_RESET: 'Sıcaklık ve Analog Sensörlerin özelleştirilmiş seçenekleri dahil bütün özelleştirmeleri kaldırmak istediğinizden emin misiniz?',
|
||||||
DEVICE_ENTITIES: 'Cihaz Varlıkları',
|
|
||||||
SUPPORT_INFORMATION: 'Destek Bilgileri',
|
SUPPORT_INFORMATION: 'Destek Bilgileri',
|
||||||
CLICK_HERE: 'Buraya Tıklayın',
|
|
||||||
HELP_INFORMATION_1: 'EMS-ESPnin nasıl ayarlanacağı ile ilgili bilgileri edinmek için çevrimiçi WIKI sayfasını ziyaret edin',
|
HELP_INFORMATION_1: 'EMS-ESPnin nasıl ayarlanacağı ile ilgili bilgileri edinmek için çevrimiçi WIKI sayfasını ziyaret edin',
|
||||||
HELP_INFORMATION_2: 'Canlı topluluk sohbeti için Discord sunucumuza katılın',
|
HELP_INFORMATION_2: 'Canlı topluluk sohbeti için Discord sunucumuza katılın',
|
||||||
HELP_INFORMATION_3: 'Yeni bir özellik talep etmek yada hata bildirmek için',
|
HELP_INFORMATION_3: 'Yeni bir özellik talep etmek yada hata bildirmek için',
|
||||||
@@ -181,13 +175,11 @@ const tr: Translation = {
|
|||||||
STATUS_OF: '{0} Durumu',
|
STATUS_OF: '{0} Durumu',
|
||||||
UPLOAD_DOWNLOAD: 'Yükleme/İndirme',
|
UPLOAD_DOWNLOAD: 'Yükleme/İndirme',
|
||||||
VERSION_ON: 'You are currently on', // TODO translate
|
VERSION_ON: 'You are currently on', // TODO translate
|
||||||
SYSTEM_APPLY_FIRMWARE: 'yeni bellenimi uygulamak için',
|
|
||||||
CLOSE: 'Kapat',
|
CLOSE: 'Kapat',
|
||||||
USE: 'KUllan',
|
USE: 'KUllan',
|
||||||
FACTORY_RESET: 'Fabrika ayarına dönme',
|
FACTORY_RESET: 'Fabrika ayarına dönme',
|
||||||
SYSTEM_FACTORY_TEXT: 'Cihaz fabrika ayarlarına döndü ve şimdi yendiden başlatılacak',
|
SYSTEM_FACTORY_TEXT: 'Cihaz fabrika ayarlarına döndü ve şimdi yendiden başlatılacak',
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?',
|
||||||
VERSION_CHECK: 'Sürüm Kontrolü',
|
|
||||||
THE_LATEST: 'En son',
|
THE_LATEST: 'En son',
|
||||||
OFFICIAL: 'resmi',
|
OFFICIAL: 'resmi',
|
||||||
DEVELOPMENT: 'geliştirme',
|
DEVELOPMENT: 'geliştirme',
|
||||||
@@ -244,6 +236,7 @@ const tr: Translation = {
|
|||||||
MQTT_INT_THERMOSTATS: 'Termostatlar',
|
MQTT_INT_THERMOSTATS: 'Termostatlar',
|
||||||
MQTT_INT_SOLAR: 'Güneş Enerjisi Modülleri',
|
MQTT_INT_SOLAR: 'Güneş Enerjisi Modülleri',
|
||||||
MQTT_INT_MIXER: 'Karışım Modülleri',
|
MQTT_INT_MIXER: 'Karışım Modülleri',
|
||||||
|
MQTT_INT_WATER: 'Water Modules', // TODO translate
|
||||||
MQTT_QUEUE: 'MQTT Sırası',
|
MQTT_QUEUE: 'MQTT Sırası',
|
||||||
DEFAULT: 'Varsayılan',
|
DEFAULT: 'Varsayılan',
|
||||||
MQTT_ENTITY_FORMAT: 'Varlık Kimlik biçimi',
|
MQTT_ENTITY_FORMAT: 'Varlık Kimlik biçimi',
|
||||||
@@ -329,7 +322,9 @@ const tr: Translation = {
|
|||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
ACTIVEHIGH: 'Active High', // TODO translate
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
ACTIVELOW: 'Active Low', // TODO translate
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
UNCHANGED: 'Unchanged', // TODO translate
|
||||||
ALWAYS: 'Always' // TODO translate
|
ALWAYS: 'Always', // TODO translate
|
||||||
|
ACTIVITY: 'Activity', // TODO translate
|
||||||
|
CONFIGURE: 'Configure {0}' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ import {
|
|||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
ButtonRow,
|
ButtonRow,
|
||||||
MessageBox,
|
MessageBox,
|
||||||
BlockNavigation
|
BlockNavigation,
|
||||||
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import RestartMonitor from 'framework/system/RestartMonitor';
|
import RestartMonitor from 'framework/system/RestartMonitor';
|
||||||
@@ -36,7 +37,7 @@ export function boardProfileSelectItems() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsApplication: FC = () => {
|
const ApplicationSettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saveData,
|
saveData,
|
||||||
@@ -97,6 +98,8 @@ const SettingsApplication: FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useLayoutTitle(LL.APPLICATION_SETTINGS());
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||||
@@ -136,7 +139,7 @@ const SettingsApplication: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 1 }} variant="h6" color="primary">
|
||||||
{LL.INTERFACE_BOARD_PROFILE()}
|
{LL.INTERFACE_BOARD_PROFILE()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box color="warning.main">
|
<Box color="warning.main">
|
||||||
@@ -680,11 +683,11 @@ const SettingsApplication: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.APPLICATION_SETTINGS()} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
{restarting ? <RestartMonitor /> : content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsApplication;
|
export default ApplicationSettings;
|
||||||
@@ -13,17 +13,17 @@ import { useBlocker } from 'react-router-dom';
|
|||||||
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog';
|
import SettingsCustomEntitiesDialog from './CustomEntitiesDialog';
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
|
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
|
||||||
import { entityItemValidation } from './validators';
|
import { entityItemValidation } from './validators';
|
||||||
import type { EntityItem } from './types';
|
import type { EntityItem } from './types';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
|
import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const SettingsCustomEntities: FC = () => {
|
const CustomEntities: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [numChanges, setNumChanges] = useState<number>(0);
|
const [numChanges, setNumChanges] = useState<number>(0);
|
||||||
const blocker = useBlocker(numChanges !== 0);
|
const blocker = useBlocker(numChanges !== 0);
|
||||||
@@ -31,6 +31,8 @@ const SettingsCustomEntities: FC = () => {
|
|||||||
const [creating, setCreating] = useState<boolean>(false);
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useLayoutTitle(LL.CUSTOM_ENTITIES(0));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: entities,
|
data: entities,
|
||||||
send: fetchEntities,
|
send: fetchEntities,
|
||||||
@@ -246,7 +248,7 @@ const SettingsCustomEntities: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.CUSTOM_ENTITIES(0)} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={2} color="warning.main">
|
||||||
<Typography variant="body2">{LL.ENTITIES_HELP_1()}</Typography>
|
<Typography variant="body2">{LL.ENTITIES_HELP_1()}</Typography>
|
||||||
@@ -265,7 +267,7 @@ const SettingsCustomEntities: FC = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box mt={1} display="flex" flexWrap="wrap">
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
{numChanges > 0 && (
|
{numChanges > 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
@@ -298,4 +300,4 @@ const SettingsCustomEntities: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsCustomEntities;
|
export default CustomEntities;
|
||||||
@@ -30,7 +30,7 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
import { numberValue, updateValue } from 'utils';
|
import { numberValue, updateValue } from 'utils';
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
type SettingsCustomEntitiesDialogProps = {
|
type CustomEntitiesDialogProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
creating: boolean;
|
creating: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -39,14 +39,14 @@ type SettingsCustomEntitiesDialogProps = {
|
|||||||
validator: Schema;
|
validator: Schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsCustomEntitiesDialog = ({
|
const CustomEntitiesDialog = ({
|
||||||
open,
|
open,
|
||||||
creating,
|
creating,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
validator
|
validator
|
||||||
}: SettingsCustomEntitiesDialogProps) => {
|
}: CustomEntitiesDialogProps) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
|
const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
@@ -281,4 +281,4 @@ const SettingsCustomEntitiesDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsCustomEntitiesDialog;
|
export default CustomEntitiesDialog;
|
||||||
@@ -26,9 +26,9 @@ import { useState, useEffect, useCallback } from 'react';
|
|||||||
import { useBlocker, useLocation } from 'react-router-dom';
|
import { useBlocker, useLocation } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import SettingsCustomizationDialog from './CustomizationDialog';
|
||||||
import EntityMaskToggle from './EntityMaskToggle';
|
import EntityMaskToggle from './EntityMaskToggle';
|
||||||
import OptionIcon from './OptionIcon';
|
import OptionIcon from './OptionIcon';
|
||||||
import SettingsCustomizationDialog from './SettingsCustomizationDialog';
|
|
||||||
|
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
|
|
||||||
@@ -37,14 +37,14 @@ import type { DeviceShort, DeviceEntity } from './types';
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import { ButtonRow, SectionContent, MessageBox, BlockNavigation } from 'components';
|
import { ButtonRow, SectionContent, MessageBox, BlockNavigation, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
import RestartMonitor from 'framework/system/RestartMonitor';
|
import RestartMonitor from 'framework/system/RestartMonitor';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
export const APIURL = window.location.origin + '/api/';
|
export const APIURL = window.location.origin + '/api/';
|
||||||
|
|
||||||
const SettingsCustomization: FC = () => {
|
const Customization: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [numChanges, setNumChanges] = useState<number>(0);
|
const [numChanges, setNumChanges] = useState<number>(0);
|
||||||
const blocker = useBlocker(numChanges !== 0);
|
const blocker = useBlocker(numChanges !== 0);
|
||||||
@@ -58,6 +58,8 @@ const SettingsCustomization: FC = () => {
|
|||||||
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
|
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
|
||||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useLayoutTitle(LL.CUSTOMIZATIONS());
|
||||||
|
|
||||||
// fetch devices first
|
// fetch devices first
|
||||||
const { data: devices } = useRequest(EMSESP.readDevices);
|
const { data: devices } = useRequest(EMSESP.readDevices);
|
||||||
|
|
||||||
@@ -508,9 +510,6 @@ const SettingsCustomization: FC = () => {
|
|||||||
|
|
||||||
const renderContent = () => (
|
const renderContent = () => (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
|
||||||
{LL.DEVICE_ENTITIES()}
|
|
||||||
</Typography>
|
|
||||||
{devices && renderDeviceList()}
|
{devices && renderDeviceList()}
|
||||||
{deviceEntities && renderDeviceData()}
|
{deviceEntities && renderDeviceData()}
|
||||||
{restartNeeded && (
|
{restartNeeded && (
|
||||||
@@ -544,7 +543,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<ButtonRow>
|
<ButtonRow mt={1}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -561,7 +560,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.CUSTOMIZATIONS()} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{restarting ? <RestartMonitor /> : renderContent()}
|
{restarting ? <RestartMonitor /> : renderContent()}
|
||||||
{selectedDeviceEntity && (
|
{selectedDeviceEntity && (
|
||||||
@@ -576,4 +575,4 @@ const SettingsCustomization: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsCustomization;
|
export default Customization;
|
||||||
@@ -31,7 +31,7 @@ type SettingsCustomizationDialogProps = {
|
|||||||
selectedItem: DeviceEntity;
|
selectedItem: DeviceEntity;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsCustomizationDialog = ({ open, onClose, onSave, selectedItem }: SettingsCustomizationDialogProps) => {
|
const CustomizationDialog = ({ open, onClose, onSave, selectedItem }: SettingsCustomizationDialogProps) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [editItem, setEditItem] = useState<DeviceEntity>(selectedItem);
|
const [editItem, setEditItem] = useState<DeviceEntity>(selectedItem);
|
||||||
const [error, setError] = useState<boolean>(false);
|
const [error, setError] = useState<boolean>(false);
|
||||||
@@ -152,4 +152,4 @@ const SettingsCustomizationDialog = ({ open, onClose, onSave, selectedItem }: Se
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsCustomizationDialog;
|
export default CustomizationDialog;
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { Tab } from '@mui/material';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
|
||||||
|
|
||||||
import DashboardDevices from './DashboardDevices';
|
|
||||||
import DashboardSensors from './DashboardSensors';
|
|
||||||
import DashboardStatus from './DashboardStatus';
|
|
||||||
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
const Dashboard: FC = () => {
|
|
||||||
const { routerTab } = useRouterTab();
|
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
useLayoutTitle(LL.DASHBOARD());
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RouterTabs value={routerTab}>
|
|
||||||
<Tab value="/dashboard/devices" label={LL.DEVICES()} />
|
|
||||||
<Tab value="/dashboard/sensors" label={LL.SENSORS()} />
|
|
||||||
<Tab value="/dashboard/status" label="Status" />
|
|
||||||
</RouterTabs>
|
|
||||||
<Routes>
|
|
||||||
<Route path="devices" element={<DashboardDevices />} />
|
|
||||||
<Route path="sensors" element={<DashboardSensors />} />
|
|
||||||
<Route path="status" element={<DashboardStatus />} />
|
|
||||||
<Route path="*" element={<Navigate replace to="/dashboard/devices" />} />
|
|
||||||
</Routes>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Dashboard;
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
|
||||||
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
|
||||||
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
|
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText,
|
|
||||||
useTheme
|
|
||||||
} from '@mui/material';
|
|
||||||
import { Body, Cell, Header, HeaderCell, HeaderRow, Row, Table } from '@table-library/react-table-library/table';
|
|
||||||
import { useTheme as tableTheme } from '@table-library/react-table-library/theme';
|
|
||||||
import { useRequest } from 'alova';
|
|
||||||
import { useContext, useEffect, useState } from 'react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import * as EMSESP from './api';
|
|
||||||
import { busConnectionStatus } from './types';
|
|
||||||
import type { Stat, Status } from './types';
|
|
||||||
import type { Theme } from '@mui/material';
|
|
||||||
|
|
||||||
import type { Translation } from 'i18n/i18n-types';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
import { dialogStyle } from 'CustomTheme';
|
|
||||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
export const isConnected = ({ status }: Status) => status !== busConnectionStatus.BUS_STATUS_OFFLINE;
|
|
||||||
|
|
||||||
const busStatusHighlight = ({ status }: Status, theme: Theme) => {
|
|
||||||
switch (status) {
|
|
||||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
|
||||||
return theme.palette.success.main;
|
|
||||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
|
||||||
return theme.palette.error.main;
|
|
||||||
default:
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showQuality = (stat: Stat) => {
|
|
||||||
if (stat.q === 0 || stat.s + stat.f === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stat.q === 100) {
|
|
||||||
return <div style={{ color: '#00FF7F' }}>{stat.q}%</div>;
|
|
||||||
}
|
|
||||||
if (stat.q >= 95) {
|
|
||||||
return <div style={{ color: 'orange' }}>{stat.q}%</div>;
|
|
||||||
} else {
|
|
||||||
return <div style={{ color: 'red' }}>{stat.q}%</div>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const DashboardStatus: FC = () => {
|
|
||||||
const { data: data, send: loadData, error } = useRequest(EMSESP.readStatus);
|
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const [confirmScan, setConfirmScan] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
|
|
||||||
const { send: scanDevices } = useRequest(EMSESP.scanDevices, {
|
|
||||||
immediate: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const stats_theme = tableTheme({
|
|
||||||
Table: `
|
|
||||||
--data-table-library_grid-template-columns: repeat(1, minmax(0, 1fr)) 90px 90px 80px;
|
|
||||||
`,
|
|
||||||
BaseRow: `
|
|
||||||
font-size: 14px;
|
|
||||||
`,
|
|
||||||
HeaderRow: `
|
|
||||||
text-transform: uppercase;
|
|
||||||
background-color: black;
|
|
||||||
color: #90CAF9;
|
|
||||||
|
|
||||||
.th {
|
|
||||||
height: 36px;
|
|
||||||
border-bottom: 1px solid #565656;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
Row: `
|
|
||||||
.td {
|
|
||||||
padding: 8px;
|
|
||||||
border-top: 1px solid #565656;
|
|
||||||
border-bottom: 1px solid #565656;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-of-type(odd) .td {
|
|
||||||
background-color: #303030;
|
|
||||||
}
|
|
||||||
&:nth-of-type(even) .td {
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
BaseCell: `
|
|
||||||
&:not(:first-of-type) {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const timer = setInterval(() => loadData(), 30000);
|
|
||||||
return () => {
|
|
||||||
clearInterval(timer);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const showName = (id: any) => {
|
|
||||||
const name: keyof Translation['STATUS_NAMES'] = id;
|
|
||||||
return LL.STATUS_NAMES[name]();
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDurationSec = (duration_sec: number) => {
|
|
||||||
const days = Math.trunc((duration_sec * 1000) / 86400000);
|
|
||||||
const hours = Math.trunc((duration_sec * 1000) / 3600000) % 24;
|
|
||||||
const minutes = Math.trunc((duration_sec * 1000) / 60000) % 60;
|
|
||||||
const seconds = Math.trunc((duration_sec * 1000) / 1000) % 60;
|
|
||||||
|
|
||||||
let formatted = '';
|
|
||||||
if (days) {
|
|
||||||
formatted += LL.NUM_DAYS({ num: days }) + ' ';
|
|
||||||
}
|
|
||||||
if (hours) {
|
|
||||||
formatted += LL.NUM_HOURS({ num: hours }) + ' ';
|
|
||||||
}
|
|
||||||
if (minutes) {
|
|
||||||
formatted += LL.NUM_MINUTES({ num: minutes }) + ' ';
|
|
||||||
}
|
|
||||||
formatted += LL.NUM_SECONDS({ num: seconds });
|
|
||||||
return formatted;
|
|
||||||
};
|
|
||||||
|
|
||||||
const busStatus = () => {
|
|
||||||
if (data) {
|
|
||||||
switch (data.status) {
|
|
||||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
|
||||||
return LL.CONNECTED(0) + ' (' + formatDurationSec(data.uptime) + ')';
|
|
||||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
|
||||||
return LL.TX_ISSUES();
|
|
||||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
|
||||||
return LL.DISCONNECTED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'Unknown';
|
|
||||||
};
|
|
||||||
|
|
||||||
const scan = async () => {
|
|
||||||
await scanDevices()
|
|
||||||
.then(() => {
|
|
||||||
toast.info(LL.SCANNING() + '...');
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error(err.message);
|
|
||||||
});
|
|
||||||
setConfirmScan(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderScanDialog = () => (
|
|
||||||
<Dialog sx={dialogStyle} open={confirmScan} onClose={() => setConfirmScan(false)}>
|
|
||||||
<DialogTitle>{LL.SCAN_DEVICES()}</DialogTitle>
|
|
||||||
<DialogContent dividers>{LL.EMS_SCAN()}</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button startIcon={<CancelIcon />} variant="outlined" onClick={() => setConfirmScan(false)} color="secondary">
|
|
||||||
{LL.CANCEL()}
|
|
||||||
</Button>
|
|
||||||
<Button startIcon={<PermScanWifiIcon />} variant="outlined" onClick={scan} color="primary">
|
|
||||||
{LL.SCAN()}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = () => {
|
|
||||||
if (!data) {
|
|
||||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar sx={{ bgcolor: busStatusHighlight(data, theme) }}>
|
|
||||||
<DirectionsBusIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary={LL.EMS_BUS_STATUS()} secondary={busStatus()} />
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<DeviceHubIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={LL.ACTIVE_DEVICES()}
|
|
||||||
secondary={
|
|
||||||
LL.NUM_DEVICES({ num: data.num_devices }) +
|
|
||||||
', ' +
|
|
||||||
LL.NUM_TEMP_SENSORS({ num: data.num_sensors }) +
|
|
||||||
', ' +
|
|
||||||
LL.NUM_ANALOG_SENSORS({ num: data.num_analogs })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Box m={3} />
|
|
||||||
<Table data={{ nodes: data.stats }} theme={stats_theme} layout={{ custom: true }}>
|
|
||||||
{(tableList: any) => (
|
|
||||||
<>
|
|
||||||
<Header>
|
|
||||||
<HeaderRow>
|
|
||||||
<HeaderCell resize />
|
|
||||||
<HeaderCell stiff>{LL.SUCCESS()}</HeaderCell>
|
|
||||||
<HeaderCell stiff>{LL.FAIL()}</HeaderCell>
|
|
||||||
<HeaderCell stiff>{LL.QUALITY()}</HeaderCell>
|
|
||||||
</HeaderRow>
|
|
||||||
</Header>
|
|
||||||
<Body>
|
|
||||||
{tableList.map((stat: Stat) => (
|
|
||||||
<Row key={stat.id} item={stat}>
|
|
||||||
<Cell>{showName(stat.id)}</Cell>
|
|
||||||
<Cell stiff>{Intl.NumberFormat().format(stat.s)}</Cell>
|
|
||||||
<Cell stiff>{Intl.NumberFormat().format(stat.f)}</Cell>
|
|
||||||
<Cell stiff>{showQuality(stat)}</Cell>
|
|
||||||
</Row>
|
|
||||||
))}
|
|
||||||
</Body>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Table>
|
|
||||||
</List>
|
|
||||||
{renderScanDialog()}
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
|
||||||
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
|
||||||
{LL.REFRESH()}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
|
||||||
<ButtonRow>
|
|
||||||
<Button
|
|
||||||
startIcon={<PermScanWifiIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
disabled={!me.admin}
|
|
||||||
onClick={() => setConfirmScan(true)}
|
|
||||||
>
|
|
||||||
{LL.SCAN_DEVICES()}
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionContent title={LL.EMS_BUS_STATUS_TITLE()} titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DashboardStatus;
|
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
||||||
import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai';
|
import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai';
|
||||||
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
||||||
|
|
||||||
import { FaSolarPanel } from 'react-icons/fa';
|
import { FaSolarPanel } from 'react-icons/fa';
|
||||||
import { GiHeatHaze } from 'react-icons/gi';
|
import { GiHeatHaze, GiTap } from 'react-icons/gi';
|
||||||
import { MdThermostatAuto, MdOutlineSensors, MdOutlineExtension, MdOutlineDevices } from 'react-icons/md';
|
import { MdThermostatAuto, MdOutlineSensors, MdOutlineDevices, MdOutlinePool } from 'react-icons/md';
|
||||||
import { TiFlowSwitch } from 'react-icons/ti';
|
import { TiFlowSwitch } from 'react-icons/ti';
|
||||||
import { VscVmConnect } from 'react-icons/vsc';
|
import { VscVmConnect } from 'react-icons/vsc';
|
||||||
import { DeviceType } from './types';
|
import { DeviceType } from './types';
|
||||||
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
interface DeviceIconProps {
|
interface DeviceIconProps {
|
||||||
@@ -40,8 +41,12 @@ const DeviceIcon: FC<DeviceIconProps> = ({ type_id }) => {
|
|||||||
return <AiOutlineAlert />;
|
return <AiOutlineAlert />;
|
||||||
case DeviceType.EXTENSION:
|
case DeviceType.EXTENSION:
|
||||||
return <MdOutlineDevices />;
|
return <MdOutlineDevices />;
|
||||||
|
case DeviceType.WATER:
|
||||||
|
return <GiTap />;
|
||||||
|
case DeviceType.POOL:
|
||||||
|
return <MdOutlinePool />;
|
||||||
case DeviceType.CUSTOM:
|
case DeviceType.CUSTOM:
|
||||||
return <MdOutlineExtension />;
|
return <PlaylistAddIcon sx={{ color: 'lightblue', fontSize: 22, verticalAlign: 'middle' }} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort
|
|||||||
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
||||||
import { useTheme } from '@table-library/react-table-library/theme';
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react';
|
import { useState, useEffect, useCallback, useLayoutEffect, useContext } from 'react';
|
||||||
|
|
||||||
import { IconContext } from 'react-icons';
|
import { IconContext } from 'react-icons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import DashboardDevicesDialog from './DashboardDevicesDialog';
|
|
||||||
import DeviceIcon from './DeviceIcon';
|
import DeviceIcon from './DeviceIcon';
|
||||||
|
import DashboardDevicesDialog from './DevicesDialog';
|
||||||
|
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
import { formatValue } from './deviceValue';
|
import { formatValue } from './deviceValue';
|
||||||
@@ -49,14 +49,15 @@ import { deviceValueItemValidation } from './validators';
|
|||||||
import type { Device, DeviceValue } from './types';
|
import type { Device, DeviceValue } from './types';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { dialogStyle } from 'CustomTheme';
|
import { dialogStyle } from 'CustomTheme';
|
||||||
import { ButtonRow, SectionContent, MessageBox } from 'components';
|
import { ButtonRow, SectionContent, MessageBox, useLayoutTitle } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const DashboardDevices: FC = () => {
|
const Devices: FC = () => {
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
const [size, setSize] = useState([0, 0]);
|
const [size, setSize] = useState([0, 0]);
|
||||||
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
|
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
|
||||||
const [onlyFav, setOnlyFav] = useState(false);
|
const [onlyFav, setOnlyFav] = useState(false);
|
||||||
@@ -66,6 +67,8 @@ const DashboardDevices: FC = () => {
|
|||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useLayoutTitle(LL.DEVICES());
|
||||||
|
|
||||||
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
|
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
|
||||||
initialData: {
|
initialData: {
|
||||||
connected: true,
|
connected: true,
|
||||||
@@ -281,9 +284,9 @@ const DashboardDevices: FC = () => {
|
|||||||
|
|
||||||
const customize = () => {
|
const customize = () => {
|
||||||
if (selectedDevice == 99) {
|
if (selectedDevice == 99) {
|
||||||
navigate('/settings/customentities');
|
navigate('/customentities');
|
||||||
} else {
|
} else {
|
||||||
navigate('/settings/customization', { state: selectedDevice });
|
navigate('/customizations', { state: selectedDevice });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -420,11 +423,8 @@ const DashboardDevices: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderCoreData = () => (
|
const renderCoreData = () => (
|
||||||
<IconContext.Provider value={{ color: 'lightblue', size: '24', style: { verticalAlign: 'middle' } }}>
|
<IconContext.Provider value={{ color: 'lightblue', size: '18', style: { verticalAlign: 'middle' } }}>
|
||||||
{!coreData.connected && <MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />}
|
{!coreData.connected && <MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />}
|
||||||
{/* {coreData.connected && coreData.devices.length === 0 && (
|
|
||||||
<MessageBox my={2} level="warning" message={LL.EMS_BUS_SCANNING()} />
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
{coreData.connected && (
|
{coreData.connected && (
|
||||||
<Table data={{ nodes: coreData.devices }} select={device_select} theme={device_theme} layout={{ custom: true }}>
|
<Table data={{ nodes: coreData.devices }} select={device_select} theme={device_theme} layout={{ custom: true }}>
|
||||||
@@ -523,9 +523,11 @@ const DashboardDevices: FC = () => {
|
|||||||
<IconButton onClick={() => setShowDeviceInfo(true)}>
|
<IconButton onClick={() => setShowDeviceInfo(true)}>
|
||||||
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
{me.admin && (
|
||||||
<IconButton onClick={customize}>
|
<IconButton onClick={customize}>
|
||||||
<FormatListNumberedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
<FormatListNumberedIcon sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
)}
|
||||||
<IconButton onClick={handleDownloadCsv}>
|
<IconButton onClick={handleDownloadCsv}>
|
||||||
<DownloadIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
<DownloadIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -587,7 +589,7 @@ const DashboardDevices: FC = () => {
|
|||||||
<Cell>{renderNameCell(dv)}</Cell>
|
<Cell>{renderNameCell(dv)}</Cell>
|
||||||
<Cell>{formatValue(LL, dv.v, dv.u)}</Cell>
|
<Cell>{formatValue(LL, dv.v, dv.u)}</Cell>
|
||||||
<Cell stiff>
|
<Cell stiff>
|
||||||
{dv.c && me.admin && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && (
|
{me.admin && dv.c && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) && (
|
||||||
<IconButton size="small" onClick={() => showDeviceValue(dv)}>
|
<IconButton size="small" onClick={() => showDeviceValue(dv)}>
|
||||||
{dv.v === '' && dv.c ? (
|
{dv.v === '' && dv.c ? (
|
||||||
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
||||||
@@ -608,7 +610,7 @@ const DashboardDevices: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.DEVICE_DATA()} titleGutter id="devices-window">
|
<SectionContent id="devices-window">
|
||||||
{renderCoreData()}
|
{renderCoreData()}
|
||||||
{renderDeviceData()}
|
{renderDeviceData()}
|
||||||
{renderDeviceDetails()}
|
{renderDeviceDetails()}
|
||||||
@@ -619,15 +621,13 @@ const DashboardDevices: FC = () => {
|
|||||||
onSave={deviceValueDialogSave}
|
onSave={deviceValueDialogSave}
|
||||||
selectedItem={selectedDeviceValue}
|
selectedItem={selectedDeviceValue}
|
||||||
writeable={
|
writeable={
|
||||||
me.admin &&
|
selectedDeviceValue.c !== undefined && !hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY)
|
||||||
selectedDeviceValue.c !== undefined &&
|
|
||||||
!hasMask(selectedDeviceValue.id, DeviceEntityMask.DV_READONLY)
|
|
||||||
}
|
}
|
||||||
validator={deviceValueItemValidation(selectedDeviceValue)}
|
validator={deviceValueItemValidation(selectedDeviceValue)}
|
||||||
progress={submitting}
|
progress={submitting}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ButtonRow>
|
<ButtonRow mt={1}>
|
||||||
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={refreshData}>
|
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={refreshData}>
|
||||||
{LL.REFRESH()}
|
{LL.REFRESH()}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -636,4 +636,4 @@ const DashboardDevices: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardDevices;
|
export default Devices;
|
||||||
@@ -40,7 +40,7 @@ type DashboardDevicesDialogProps = {
|
|||||||
progress: boolean;
|
progress: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DashboardDevicesDialog = ({
|
const DevicesDialog = ({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
@@ -204,4 +204,4 @@ const DashboardDevicesDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardDevicesDialog;
|
export default DevicesDialog;
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
import CommentIcon from '@mui/icons-material/CommentTwoTone';
|
import CommentIcon from '@mui/icons-material/CommentTwoTone';
|
||||||
import EastIcon from '@mui/icons-material/East';
|
|
||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||||
import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone';
|
import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone';
|
||||||
import { Box, List, ListItem, ListItemAvatar, ListItemText, Link, Typography, Button } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
Link,
|
||||||
|
Typography,
|
||||||
|
Button,
|
||||||
|
ListItemButton,
|
||||||
|
Avatar
|
||||||
|
} from '@mui/material';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
@@ -39,59 +49,56 @@ const Help: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SUPPORT_INFORMATION(0)} titleGutter>
|
<SectionContent>
|
||||||
<List>
|
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
<ListItemButton component="a" href="https://emsesp.github.io/docs">
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<MenuBookIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||||
|
<MenuBookIcon />
|
||||||
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText>
|
<ListItemText primary={LL.HELP_INFORMATION_1()} />
|
||||||
{LL.HELP_INFORMATION_1()}
|
</ListItemButton>
|
||||||
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
|
||||||
|
|
||||||
<Link target="_blank" href="https://emsesp.github.io/docs" color="primary">
|
|
||||||
{LL.CLICK_HERE()}
|
|
||||||
</Link>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
<ListItemButton component="a" href="https://discord.gg/3J3GgnzpyT">
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<CommentIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||||
|
<CommentIcon />
|
||||||
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText>
|
<ListItemText primary={LL.HELP_INFORMATION_2()} />
|
||||||
{LL.HELP_INFORMATION_2()}
|
</ListItemButton>
|
||||||
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
|
||||||
|
|
||||||
<Link target="_blank" href="https://discord.gg/3J3GgnzpyT" color="primary">
|
|
||||||
{LL.CLICK_HERE()}
|
|
||||||
</Link>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
<ListItemButton component="a" href="https://github.com/emsesp/EMS-ESP32/issues/new/choose">
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<GitHubIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
<Avatar sx={{ bgcolor: '#72caf9' }}>
|
||||||
|
<GitHubIcon />
|
||||||
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText>
|
<ListItemText primary={LL.HELP_INFORMATION_3()} />
|
||||||
{LL.HELP_INFORMATION_3()}
|
</ListItemButton>
|
||||||
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
|
|
||||||
<Link target="_blank" href="https://github.com/emsesp/EMS-ESP32/issues/new/choose" color="primary">
|
|
||||||
{LL.CLICK_HERE()}
|
|
||||||
</Link>
|
|
||||||
<br />
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<Box color="warning.main">
|
<Box p={2} color="warning.main">
|
||||||
<Typography mb={1} variant="body2">
|
<Typography mb={1} variant="body2">
|
||||||
{LL.HELP_INFORMATION_4()}
|
{LL.HELP_INFORMATION_4()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
<Button
|
||||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={() => callAPI('system', 'info')}>
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => callAPI('system', 'info')}
|
||||||
|
>
|
||||||
{LL.SUPPORT_INFORMATION(0)}
|
{LL.SUPPORT_INFORMATION(0)}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
sx={{ ml: 2 }}
|
sx={{ ml: 2 }}
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
@@ -102,7 +109,7 @@ const Help: FC = () => {
|
|||||||
All Values
|
All Values
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Box border={1} p={1} mt={4} color="orange">
|
<Box border={1} p={1} mt={4}>
|
||||||
<Typography align="center" variant="subtitle1" color="orange">
|
<Typography align="center" variant="subtitle1" color="orange">
|
||||||
<b>{LL.HELP_INFORMATION_5()}</b>
|
<b>{LL.HELP_INFORMATION_5()}</b>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ import { updateState, useRequest } from 'alova';
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useBlocker } from 'react-router-dom';
|
import { useBlocker } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import SettingsSchedulerDialog from './SettingsSchedulerDialog';
|
import SettingsSchedulerDialog from './SchedulerDialog';
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
import { ScheduleFlag } from './types';
|
import { ScheduleFlag } from './types';
|
||||||
import { schedulerItemValidation } from './validators';
|
import { schedulerItemValidation } from './validators';
|
||||||
import type { ScheduleItem } from './types';
|
import type { ScheduleItem } from './types';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
|
import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const SettingsScheduler: FC = () => {
|
const Scheduler: FC = () => {
|
||||||
const { LL, locale } = useI18nContext();
|
const { LL, locale } = useI18nContext();
|
||||||
const [numChanges, setNumChanges] = useState<number>(0);
|
const [numChanges, setNumChanges] = useState<number>(0);
|
||||||
const blocker = useBlocker(numChanges !== 0);
|
const blocker = useBlocker(numChanges !== 0);
|
||||||
@@ -194,6 +194,8 @@ const SettingsScheduler: FC = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useLayoutTitle(LL.SCHEDULER());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
data={{ nodes: schedule.filter((si) => !si.deleted).sort((a, b) => a.time.localeCompare(b.time)) }}
|
data={{ nodes: schedule.filter((si) => !si.deleted).sort((a, b) => a.time.localeCompare(b.time)) }}
|
||||||
@@ -249,7 +251,7 @@ const SettingsScheduler: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SCHEDULER()} titleGutter>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={2} color="warning.main">
|
||||||
<Typography variant="body2">{LL.SCHEDULER_HELP_1()}</Typography>
|
<Typography variant="body2">{LL.SCHEDULER_HELP_1()}</Typography>
|
||||||
@@ -268,7 +270,7 @@ const SettingsScheduler: FC = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box mt={1} display="flex" flexWrap="wrap">
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
@@ -298,4 +300,4 @@ const SettingsScheduler: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsScheduler;
|
export default Scheduler;
|
||||||
@@ -32,7 +32,7 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
import { updateValue } from 'utils';
|
import { updateValue } from 'utils';
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
type SettingsSchedulerDialogProps = {
|
type SchedulerDialogProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
creating: boolean;
|
creating: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -42,15 +42,7 @@ type SettingsSchedulerDialogProps = {
|
|||||||
dow: string[];
|
dow: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsSchedulerDialog = ({
|
const SchedulerDialog = ({ open, creating, onClose, onSave, selectedItem, validator, dow }: SchedulerDialogProps) => {
|
||||||
open,
|
|
||||||
creating,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
selectedItem,
|
|
||||||
validator,
|
|
||||||
dow
|
|
||||||
}: SettingsSchedulerDialogProps) => {
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [editItem, setEditItem] = useState<ScheduleItem>(selectedItem);
|
const [editItem, setEditItem] = useState<ScheduleItem>(selectedItem);
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
@@ -246,4 +238,4 @@ const SettingsSchedulerDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsSchedulerDialog;
|
export default SchedulerDialog;
|
||||||
@@ -8,26 +8,27 @@ import { useSort, SortToggleType } from '@table-library/react-table-library/sort
|
|||||||
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
||||||
import { useTheme } from '@table-library/react-table-library/theme';
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useState, useContext, useEffect } from 'react';
|
import { useState, useEffect, useContext } from 'react';
|
||||||
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import DashboardSensorsAnalogDialog from './DashboardSensorsAnalogDialog';
|
import DashboardSensorsAnalogDialog from './SensorsAnalogDialog';
|
||||||
import DashboardSensorsTemperatureDialog from './DashboardSensorsTemperatureDialog';
|
import DashboardSensorsTemperatureDialog from './SensorsTemperatureDialog';
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
|
|
||||||
import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames, AnalogType } from './types';
|
import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames, AnalogType } from './types';
|
||||||
import { temperatureSensorItemValidation, analogSensorItemValidation } from './validators';
|
import { temperatureSensorItemValidation, analogSensorItemValidation } from './validators';
|
||||||
import type { TemperatureSensor, AnalogSensor } from './types';
|
import type { TemperatureSensor, AnalogSensor } from './types';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { ButtonRow, SectionContent } from 'components';
|
import { ButtonRow, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const DashboardSensors: FC = () => {
|
const Sensors: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const { me } = useContext(AuthenticatedContext);
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState<TemperatureSensor>();
|
const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState<TemperatureSensor>();
|
||||||
const [selectedAnalogSensor, setSelectedAnalogSensor] = useState<AnalogSensor>();
|
const [selectedAnalogSensor, setSelectedAnalogSensor] = useState<AnalogSensor>();
|
||||||
const [temperatureDialogOpen, setTemperatureDialogOpen] = useState<boolean>(false);
|
const [temperatureDialogOpen, setTemperatureDialogOpen] = useState<boolean>(false);
|
||||||
@@ -51,8 +52,6 @@ const DashboardSensors: FC = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const isAdmin = me.admin;
|
|
||||||
|
|
||||||
const common_theme = useTheme({
|
const common_theme = useTheme({
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -170,6 +169,8 @@ const DashboardSensors: FC = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useLayoutTitle(LL.SENSORS());
|
||||||
|
|
||||||
const formatDurationMin = (duration_min: number) => {
|
const formatDurationMin = (duration_min: number) => {
|
||||||
const days = Math.trunc((duration_min * 60000) / 86400000);
|
const days = Math.trunc((duration_min * 60000) / 86400000);
|
||||||
const hours = Math.trunc((duration_min * 60000) / 3600000) % 24;
|
const hours = Math.trunc((duration_min * 60000) / 3600000) % 24;
|
||||||
@@ -220,7 +221,7 @@ const DashboardSensors: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateTemperatureSensor = (ts: TemperatureSensor) => {
|
const updateTemperatureSensor = (ts: TemperatureSensor) => {
|
||||||
if (isAdmin) {
|
if (me.admin) {
|
||||||
setSelectedTemperatureSensor(ts);
|
setSelectedTemperatureSensor(ts);
|
||||||
setTemperatureDialogOpen(true);
|
setTemperatureDialogOpen(true);
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,7 @@ const DashboardSensors: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateAnalogSensor = (as: AnalogSensor) => {
|
const updateAnalogSensor = (as: AnalogSensor) => {
|
||||||
if (isAdmin) {
|
if (me.admin) {
|
||||||
setCreating(false);
|
setCreating(false);
|
||||||
setSelectedAnalogSensor(as);
|
setSelectedAnalogSensor(as);
|
||||||
setAnalogDialogOpen(true);
|
setAnalogDialogOpen(true);
|
||||||
@@ -406,10 +407,8 @@ const DashboardSensors: FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent title={LL.SENSOR_DATA()} titleGutter>
|
<SectionContent>
|
||||||
{sensorData.ts.length > 0 && (
|
<Typography sx={{ pb: 1 }} variant="h6" color="secondary">
|
||||||
<>
|
|
||||||
<Typography sx={{ pt: 2, pb: 1 }} variant="h6" color="secondary">
|
|
||||||
{LL.TEMP_SENSORS()}
|
{LL.TEMP_SENSORS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<RenderTemperatureSensors />
|
<RenderTemperatureSensors />
|
||||||
@@ -422,9 +421,6 @@ const DashboardSensors: FC = () => {
|
|||||||
validator={temperatureSensorItemValidation()}
|
validator={temperatureSensorItemValidation()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{sensorData?.analog_enabled === true && (
|
{sensorData?.analog_enabled === true && (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{ pt: 4, pb: 1 }} variant="h6" color="secondary">
|
<Typography sx={{ pt: 4, pb: 1 }} variant="h6" color="secondary">
|
||||||
@@ -443,15 +439,14 @@ const DashboardSensors: FC = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Box mt={2} display="flex" flexWrap="wrap">
|
<Box mt={1} display="flex" flexWrap="wrap">
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={fetchSensorData}>
|
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={fetchSensorData}>
|
||||||
{LL.REFRESH()}
|
{LL.REFRESH()}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
{sensorData?.analog_enabled === true && (
|
{sensorData?.analog_enabled === true && me.admin && (
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -467,4 +462,4 @@ const DashboardSensors: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardSensors;
|
export default Sensors;
|
||||||
@@ -38,7 +38,7 @@ type DashboardSensorsAnalogDialogProps = {
|
|||||||
validator: Schema;
|
validator: Schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DashboardSensorsAnalogDialog = ({
|
const SensorsAnalogDialog = ({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
@@ -296,4 +296,4 @@ const DashboardSensorsAnalogDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardSensorsAnalogDialog;
|
export default SensorsAnalogDialog;
|
||||||
@@ -26,7 +26,7 @@ import { numberValue, updateValue } from 'utils';
|
|||||||
|
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
type DashboardSensorsTemperatureDialogProps = {
|
type SensorsTemperatureDialogProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (ts: TemperatureSensor) => void;
|
onSave: (ts: TemperatureSensor) => void;
|
||||||
@@ -34,13 +34,13 @@ type DashboardSensorsTemperatureDialogProps = {
|
|||||||
validator: Schema;
|
validator: Schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DashboardSensorsTemperatureDialog = ({
|
const SensorsTemperatureDialog = ({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
validator
|
validator
|
||||||
}: DashboardSensorsTemperatureDialogProps) => {
|
}: SensorsTemperatureDialogProps) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
const [editItem, setEditItem] = useState<TemperatureSensor>(selectedItem);
|
const [editItem, setEditItem] = useState<TemperatureSensor>(selectedItem);
|
||||||
@@ -119,4 +119,4 @@ const DashboardSensorsTemperatureDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardSensorsTemperatureDialog;
|
export default SensorsTemperatureDialog;
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { Tab } from '@mui/material';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
|
||||||
|
|
||||||
import SettingsApplication from './SettingsApplication';
|
|
||||||
import SettingsCustomEntities from './SettingsCustomEntities';
|
|
||||||
import SettingsCustomization from './SettingsCustomization';
|
|
||||||
import SettingsScheduler from './SettingsScheduler';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
const Settings: FC = () => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
const { routerTab } = useRouterTab();
|
|
||||||
|
|
||||||
useLayoutTitle(LL.SETTINGS_OF(''));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RouterTabs value={routerTab}>
|
|
||||||
<Tab value="/settings/application" label={LL.APPLICATION_SETTINGS()} />
|
|
||||||
<Tab value="/settings/customization" label={LL.CUSTOMIZATIONS()} />
|
|
||||||
<Tab value="/settings/scheduler" label={LL.SCHEDULER()} />
|
|
||||||
<Tab value="/settings/customentities" label={LL.CUSTOM_ENTITIES(0)} />
|
|
||||||
</RouterTabs>
|
|
||||||
<Routes>
|
|
||||||
<Route path="application" element={<SettingsApplication />} />
|
|
||||||
<Route path="customization" element={<SettingsCustomization />} />
|
|
||||||
<Route path="scheduler" element={<SettingsScheduler />} />
|
|
||||||
<Route path="customentities" element={<SettingsCustomEntities />} />
|
|
||||||
<Route path="*" element={<Navigate replace to="/settings/application" />} />
|
|
||||||
</Routes>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Settings;
|
|
||||||
130
interface/src/project/SystemActivity.tsx
Normal file
130
interface/src/project/SystemActivity.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
import { Body, Cell, Header, HeaderCell, HeaderRow, Row, Table } from '@table-library/react-table-library/table';
|
||||||
|
import { useTheme as tableTheme } from '@table-library/react-table-library/theme';
|
||||||
|
import { useRequest } from 'alova';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import * as EMSESP from './api';
|
||||||
|
import type { Stat } from './types';
|
||||||
|
|
||||||
|
import type { Translation } from 'i18n/i18n-types';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
const SystemActivity: FC = () => {
|
||||||
|
const { data: data, send: loadData, error } = useRequest(EMSESP.readActivity);
|
||||||
|
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
useLayoutTitle(LL.ACTIVITY());
|
||||||
|
|
||||||
|
const stats_theme = tableTheme({
|
||||||
|
Table: `
|
||||||
|
--data-table-library_grid-template-columns: repeat(1, minmax(0, 1fr)) 90px 90px 80px;
|
||||||
|
`,
|
||||||
|
BaseRow: `
|
||||||
|
font-size: 14px;
|
||||||
|
`,
|
||||||
|
HeaderRow: `
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: black;
|
||||||
|
color: #90CAF9;
|
||||||
|
|
||||||
|
.th {
|
||||||
|
height: 36px;
|
||||||
|
border-bottom: 1px solid #565656;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Row: `
|
||||||
|
.td {
|
||||||
|
padding: 8px;
|
||||||
|
border-top: 1px solid #565656;
|
||||||
|
border-bottom: 1px solid #565656;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-of-type(odd) .td {
|
||||||
|
background-color: #303030;
|
||||||
|
}
|
||||||
|
&:nth-of-type(even) .td {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
BaseCell: `
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => loadData(), 30000);
|
||||||
|
return () => {
|
||||||
|
clearInterval(timer);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const showName = (id: any) => {
|
||||||
|
const name: keyof Translation['STATUS_NAMES'] = id;
|
||||||
|
return LL.STATUS_NAMES[name]();
|
||||||
|
};
|
||||||
|
|
||||||
|
const showQuality = (stat: Stat) => {
|
||||||
|
if (stat.q === 0 || stat.s + stat.f === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stat.q === 100) {
|
||||||
|
return <div style={{ color: '#00FF7F' }}>{stat.q}%</div>;
|
||||||
|
}
|
||||||
|
if (stat.q >= 95) {
|
||||||
|
return <div style={{ color: 'orange' }}>{stat.q}%</div>;
|
||||||
|
} else {
|
||||||
|
return <div style={{ color: 'red' }}>{stat.q}%</div>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table data={{ nodes: data.stats }} theme={stats_theme} layout={{ custom: true }}>
|
||||||
|
{(tableList: any) => (
|
||||||
|
<>
|
||||||
|
<Header>
|
||||||
|
<HeaderRow>
|
||||||
|
<HeaderCell resize />
|
||||||
|
<HeaderCell stiff>{LL.SUCCESS()}</HeaderCell>
|
||||||
|
<HeaderCell stiff>{LL.FAIL()}</HeaderCell>
|
||||||
|
<HeaderCell stiff>{LL.QUALITY()}</HeaderCell>
|
||||||
|
</HeaderRow>
|
||||||
|
</Header>
|
||||||
|
<Body>
|
||||||
|
{tableList.map((stat: Stat) => (
|
||||||
|
<Row key={stat.id} item={stat}>
|
||||||
|
<Cell>{showName(stat.id)}</Cell>
|
||||||
|
<Cell stiff>{Intl.NumberFormat().format(stat.s)}</Cell>
|
||||||
|
<Cell stiff>{Intl.NumberFormat().format(stat.f)}</Cell>
|
||||||
|
<Cell stiff>{showQuality(stat)}</Cell>
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
</Body>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
<ButtonRow mt={1}>
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={loadData}>
|
||||||
|
{LL.REFRESH()}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SectionContent>{content()}</SectionContent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemActivity;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
APIcall,
|
APIcall,
|
||||||
Settings,
|
Settings,
|
||||||
Status,
|
Activity,
|
||||||
CoreData,
|
CoreData,
|
||||||
Devices,
|
Devices,
|
||||||
DeviceEntity,
|
DeviceEntity,
|
||||||
@@ -25,7 +25,7 @@ export const readDeviceData = (id: number) =>
|
|||||||
});
|
});
|
||||||
export const writeDeviceValue = (data: any) => alovaInstance.Post('/rest/writeDeviceValue', data);
|
export const writeDeviceValue = (data: any) => alovaInstance.Post('/rest/writeDeviceValue', data);
|
||||||
|
|
||||||
// SettingsApplication
|
// Application Settings
|
||||||
export const readSettings = () => alovaInstance.Get<Settings>('/rest/settings');
|
export const readSettings = () => alovaInstance.Get<Settings>('/rest/settings');
|
||||||
export const writeSettings = (data: any) => alovaInstance.Post('/rest/settings', data);
|
export const writeSettings = (data: any) => alovaInstance.Post('/rest/settings', data);
|
||||||
export const getBoardProfile = (boardProfile: string) =>
|
export const getBoardProfile = (boardProfile: string) =>
|
||||||
@@ -33,17 +33,18 @@ export const getBoardProfile = (boardProfile: string) =>
|
|||||||
params: { boardProfile }
|
params: { boardProfile }
|
||||||
});
|
});
|
||||||
|
|
||||||
// DashboardSensors
|
// Sensors
|
||||||
export const readSensorData = () => alovaInstance.Get<SensorData>('/rest/sensorData');
|
export const readSensorData = () => alovaInstance.Get<SensorData>('/rest/sensorData');
|
||||||
export const writeTemperatureSensor = (ts: WriteTemperatureSensor) =>
|
export const writeTemperatureSensor = (ts: WriteTemperatureSensor) =>
|
||||||
alovaInstance.Post('/rest/writeTemperatureSensor', ts);
|
alovaInstance.Post('/rest/writeTemperatureSensor', ts);
|
||||||
export const writeAnalogSensor = (as: WriteAnalogSensor) => alovaInstance.Post('/rest/writeAnalogSensor', as);
|
export const writeAnalogSensor = (as: WriteAnalogSensor) => alovaInstance.Post('/rest/writeAnalogSensor', as);
|
||||||
|
|
||||||
// DashboardStatus
|
// Activity
|
||||||
export const readStatus = () => alovaInstance.Get<Status>('/rest/status');
|
export const readActivity = () => alovaInstance.Get<Activity>('/rest/activity');
|
||||||
|
|
||||||
export const scanDevices = () => alovaInstance.Post('/rest/scanDevices');
|
export const scanDevices = () => alovaInstance.Post('/rest/scanDevices');
|
||||||
|
|
||||||
// HelpInformation
|
// API, used in HelpInformation
|
||||||
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
|
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
|
||||||
|
|
||||||
// UploadFileForm
|
// UploadFileForm
|
||||||
|
|||||||
@@ -50,13 +50,7 @@ export interface Stat {
|
|||||||
q: number; // quality
|
q: number; // quality
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Status {
|
export interface Activity {
|
||||||
status: busConnectionStatus;
|
|
||||||
tx_mode: number;
|
|
||||||
uptime: number;
|
|
||||||
num_devices: number;
|
|
||||||
num_sensors: number;
|
|
||||||
num_analogs: number;
|
|
||||||
stats: Stat[];
|
stats: Stat[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +354,7 @@ export const enum DeviceType {
|
|||||||
TEMPERATURESENSOR,
|
TEMPERATURESENSOR,
|
||||||
ANALOGSENSOR,
|
ANALOGSENSOR,
|
||||||
SCHEDULER,
|
SCHEDULER,
|
||||||
|
CUSTOM,
|
||||||
BOILER,
|
BOILER,
|
||||||
THERMOSTAT,
|
THERMOSTAT,
|
||||||
MIXER,
|
MIXER,
|
||||||
@@ -373,7 +368,9 @@ export const enum DeviceType {
|
|||||||
EXTENSION,
|
EXTENSION,
|
||||||
GENERIC,
|
GENERIC,
|
||||||
HEATSOURCE,
|
HEATSOURCE,
|
||||||
CUSTOM,
|
VENTILATION,
|
||||||
|
WATER,
|
||||||
|
POOL,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,26 @@ export const GPIO_VALIDATOR = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GPIO_VALIDATORR = {
|
||||||
|
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
||||||
|
if (
|
||||||
|
value &&
|
||||||
|
(value === 1 ||
|
||||||
|
(value >= 6 && value <= 11) ||
|
||||||
|
(value >= 16 && value <= 17) ||
|
||||||
|
value === 20 ||
|
||||||
|
value === 24 ||
|
||||||
|
(value >= 28 && value <= 31) ||
|
||||||
|
value > 40 ||
|
||||||
|
value < 0)
|
||||||
|
) {
|
||||||
|
callback('Must be an valid GPIO port');
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const GPIO_VALIDATORC3 = {
|
export const GPIO_VALIDATORC3 = {
|
||||||
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
||||||
if (value && ((value >= 11 && value <= 19) || value > 21 || value < 0)) {
|
if (value && ((value >= 11 && value <= 19) || value > 21 || value < 0)) {
|
||||||
@@ -69,6 +89,14 @@ export const createSettingsValidator = (settings: Settings) =>
|
|||||||
tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATOR],
|
tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATOR],
|
||||||
rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATOR]
|
rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATOR]
|
||||||
}),
|
}),
|
||||||
|
...(settings.board_profile === 'CUSTOM' &&
|
||||||
|
settings.platform === 'ESP32R' && {
|
||||||
|
led_gpio: [{ required: true, message: 'LED GPIO is required' }, GPIO_VALIDATORR],
|
||||||
|
dallas_gpio: [{ required: true, message: 'GPIO is required' }, GPIO_VALIDATORR],
|
||||||
|
pbutton_gpio: [{ required: true, message: 'Button GPIO is required' }, GPIO_VALIDATORR],
|
||||||
|
tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATORR],
|
||||||
|
rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATORR]
|
||||||
|
}),
|
||||||
...(settings.board_profile === 'CUSTOM' &&
|
...(settings.board_profile === 'CUSTOM' &&
|
||||||
settings.platform === 'ESP32-C3' && {
|
settings.platform === 'ESP32-C3' && {
|
||||||
led_gpio: [{ required: true, message: 'LED GPIO is required' }, GPIO_VALIDATORC3],
|
led_gpio: [{ required: true, message: 'LED GPIO is required' }, GPIO_VALIDATORC3],
|
||||||
@@ -193,7 +221,15 @@ export const analogSensorItemValidation = (sensors: AnalogSensor[], creating: bo
|
|||||||
n: [{ required: true, message: 'Name is required' }],
|
n: [{ required: true, message: 'Name is required' }],
|
||||||
g: [
|
g: [
|
||||||
{ required: true, message: 'GPIO is required' },
|
{ required: true, message: 'GPIO is required' },
|
||||||
platform === 'ESP32-S3' ? GPIO_VALIDATORS3 : platform === 'ESP32-C3' ? GPIO_VALIDATORC3 : GPIO_VALIDATOR,
|
platform === 'ESP32-S3'
|
||||||
|
? GPIO_VALIDATORS3
|
||||||
|
: platform === 'ESP32-S2'
|
||||||
|
? GPIO_VALIDATORS2
|
||||||
|
: platform === 'ESP32-C3'
|
||||||
|
? GPIO_VALIDATORC3
|
||||||
|
: platform === 'ESP32R'
|
||||||
|
? GPIO_VALIDATORR
|
||||||
|
: GPIO_VALIDATOR,
|
||||||
...(creating ? [isGPIOUniqueValidator(sensors)] : [])
|
...(creating ? [isGPIOUniqueValidator(sensors)] : [])
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ export enum APNetworkStatus {
|
|||||||
LINGERING = 2
|
LINGERING = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APStatus {
|
export interface APStatusType {
|
||||||
status: APNetworkStatus;
|
status: APNetworkStatus;
|
||||||
ip_address: string;
|
ip_address: string;
|
||||||
mac_address: string;
|
mac_address: string;
|
||||||
station_num: number;
|
station_num: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APSettings {
|
export interface APSettingsType {
|
||||||
provision_mode: APProvisionMode;
|
provision_mode: APProvisionMode;
|
||||||
ssid: string;
|
ssid: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export enum MqttDisconnectReason {
|
|||||||
TCP_DISCONNECTED = 7
|
TCP_DISCONNECTED = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MqttStatus {
|
export interface MqttStatusType {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
client_id: string;
|
client_id: string;
|
||||||
@@ -19,7 +19,7 @@ export interface MqttStatus {
|
|||||||
connect_count: number;
|
connect_count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MqttSettings {
|
export interface MqttSettingsType {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
@@ -36,6 +36,7 @@ export interface MqttSettings {
|
|||||||
publish_time_thermostat: number;
|
publish_time_thermostat: number;
|
||||||
publish_time_solar: number;
|
publish_time_solar: number;
|
||||||
publish_time_mixer: number;
|
publish_time_mixer: number;
|
||||||
|
publish_time_water: number;
|
||||||
publish_time_other: number;
|
publish_time_other: number;
|
||||||
publish_time_sensor: number;
|
publish_time_sensor: number;
|
||||||
publish_time_heartbeat: number;
|
publish_time_heartbeat: number;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export enum WiFiEncryptionType {
|
|||||||
WIFI_AUTH_WPA2_WPA3_PSK = 7
|
WIFI_AUTH_WPA2_WPA3_PSK = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NetworkStatus {
|
export interface NetworkStatusType {
|
||||||
status: NetworkConnectionStatus;
|
status: NetworkConnectionStatus;
|
||||||
local_ip: string;
|
local_ip: string;
|
||||||
local_ipv6: string;
|
local_ipv6: string;
|
||||||
@@ -36,7 +36,7 @@ export interface NetworkStatus {
|
|||||||
hostname: string;
|
hostname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NetworkSettings {
|
export interface NetworkSettingsType {
|
||||||
ssid: string;
|
ssid: string;
|
||||||
bssid: string;
|
bssid: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ export enum NTPSyncStatus {
|
|||||||
NTP_ACTIVE = 2
|
NTP_ACTIVE = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NTPStatus {
|
export interface NTPStatusType {
|
||||||
status: NTPSyncStatus;
|
status: NTPSyncStatus;
|
||||||
utc_time: string;
|
utc_time: string;
|
||||||
local_time: string;
|
local_time: string;
|
||||||
server: string;
|
server: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NTPSettings {
|
export interface NTPSettingsType {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
server: string;
|
server: string;
|
||||||
tz_label: string;
|
tz_label: string;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export interface User {
|
export interface UserType {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SecuritySettings {
|
export interface SecuritySettingsType {
|
||||||
users: User[];
|
users: UserType[];
|
||||||
jwt_secret: string;
|
jwt_secret: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export interface SystemStatus {
|
import type { busConnectionStatus } from 'project/types';
|
||||||
|
|
||||||
|
export interface ESPSystemStatus {
|
||||||
emsesp_version: string;
|
emsesp_version: string;
|
||||||
esp_platform: string;
|
esp_platform: string;
|
||||||
max_alloc_heap: number;
|
max_alloc_heap: number;
|
||||||
@@ -16,14 +18,29 @@ export interface SystemStatus {
|
|||||||
app_free: number;
|
app_free: number;
|
||||||
fs_used: number;
|
fs_used: number;
|
||||||
fs_free: number;
|
fs_free: number;
|
||||||
uptime: string;
|
|
||||||
free_mem: number;
|
free_mem: number;
|
||||||
psram_size?: number;
|
psram_size?: number;
|
||||||
free_psram?: number;
|
free_psram?: number;
|
||||||
has_loader: boolean;
|
has_loader: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OTASettings {
|
export interface SystemStatus {
|
||||||
|
emsesp_version: string;
|
||||||
|
esp_platform: string;
|
||||||
|
status: busConnectionStatus;
|
||||||
|
uptime: number;
|
||||||
|
bus_uptime: number;
|
||||||
|
num_devices: number;
|
||||||
|
num_sensors: number;
|
||||||
|
num_analogs: number;
|
||||||
|
free_heap: number;
|
||||||
|
ntp_status: number;
|
||||||
|
ota_status: boolean;
|
||||||
|
mqtt_status: boolean;
|
||||||
|
ap_status: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OTASettingsType {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
port: number;
|
port: number;
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import Schema from 'async-validator';
|
import Schema from 'async-validator';
|
||||||
import { IP_ADDRESS_VALIDATOR } from './shared';
|
import { IP_ADDRESS_VALIDATOR } from './shared';
|
||||||
import type { APSettings } from 'types';
|
import type { APSettingsType } from 'types';
|
||||||
import { isAPEnabled } from 'framework/ap/APSettingsForm';
|
import { isAPEnabled } from 'framework/ap/APSettings';
|
||||||
|
|
||||||
export const createAPSettingsValidator = (apSettings: APSettings) =>
|
export const createAPSettingsValidator = (apSettings: APSettingsType) =>
|
||||||
new Schema({
|
new Schema({
|
||||||
provision_mode: { required: true, message: 'Please provide a provision mode' },
|
provision_mode: { required: true, message: 'Please provide a provision mode' },
|
||||||
...(isAPEnabled(apSettings) && {
|
...(isAPEnabled(apSettings) && {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Schema from 'async-validator';
|
import Schema from 'async-validator';
|
||||||
import { IP_OR_HOSTNAME_VALIDATOR } from './shared';
|
import { IP_OR_HOSTNAME_VALIDATOR } from './shared';
|
||||||
import type { MqttSettings } from 'types';
|
import type { MqttSettingsType } from 'types';
|
||||||
|
|
||||||
export const createMqttSettingsValidator = (mqttSettings: MqttSettings) =>
|
export const createMqttSettingsValidator = (mqttSettings: MqttSettingsType) =>
|
||||||
new Schema({
|
new Schema({
|
||||||
...(mqttSettings.enabled && {
|
...(mqttSettings.enabled && {
|
||||||
host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR],
|
host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Schema from 'async-validator';
|
import Schema from 'async-validator';
|
||||||
import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared';
|
import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared';
|
||||||
import type { NetworkSettings } from 'types';
|
import type { NetworkSettingsType } from 'types';
|
||||||
|
|
||||||
export const createNetworkSettingsValidator = (networkSettings: NetworkSettings) =>
|
export const createNetworkSettingsValidator = (networkSettings: NetworkSettingsType) =>
|
||||||
new Schema({
|
new Schema({
|
||||||
ssid: [{ type: 'string', max: 32, message: 'SSID must be 32 characters or less' }],
|
ssid: [{ type: 'string', max: 32, message: 'SSID must be 32 characters or less' }],
|
||||||
bssid: [{ type: 'string', max: 17, message: 'BSSID must be 17 characters or empty' }],
|
bssid: [{ type: 'string', max: 17, message: 'BSSID must be 17 characters or empty' }],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Schema from 'async-validator';
|
import Schema from 'async-validator';
|
||||||
import type { InternalRuleItem } from 'async-validator';
|
import type { InternalRuleItem } from 'async-validator';
|
||||||
import type { User } from 'types';
|
import type { UserType } from 'types';
|
||||||
|
|
||||||
export const SECURITY_SETTINGS_VALIDATOR = new Schema({
|
export const SECURITY_SETTINGS_VALIDATOR = new Schema({
|
||||||
jwt_secret: [
|
jwt_secret: [
|
||||||
@@ -9,7 +9,7 @@ export const SECURITY_SETTINGS_VALIDATOR = new Schema({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createUniqueUsernameValidator = (users: User[]) => ({
|
export const createUniqueUsernameValidator = (users: UserType[]) => ({
|
||||||
validator(rule: InternalRuleItem, username: string, callback: (error?: string) => void) {
|
validator(rule: InternalRuleItem, username: string, callback: (error?: string) => void) {
|
||||||
if (username && users.find((u) => u.username === username)) {
|
if (username && users.find((u) => u.username === username)) {
|
||||||
callback('Username already in use');
|
callback('Username already in use');
|
||||||
@@ -19,7 +19,7 @@ export const createUniqueUsernameValidator = (users: User[]) => ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createUserValidator = (users: User[], creating: boolean) =>
|
export const createUserValidator = (users: UserType[], creating: boolean) =>
|
||||||
new Schema({
|
new Schema({
|
||||||
username: [
|
username: [
|
||||||
{ required: true, message: 'Username is required' },
|
{ required: true, message: 'Username is required' },
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === 'build' && mode === 'hosted') {
|
if (mode === 'hosted') {
|
||||||
return {
|
return {
|
||||||
plugins: [preact(), viteTsconfigPaths()],
|
plugins: [preact(), viteTsconfigPaths()],
|
||||||
build: {
|
build: {
|
||||||
@@ -38,8 +38,6 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// production build, both for hosted and building the firmware
|
|
||||||
if (command === 'build') {
|
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
preact(),
|
preact(),
|
||||||
@@ -81,7 +79,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
open: false,
|
open: false,
|
||||||
gzipSize: true,
|
gzipSize: true,
|
||||||
brotliSize: true,
|
brotliSize: true,
|
||||||
filename: 'analyse.html' // will be saved in project's root
|
filename: '../analyse.html' // will be saved in project's root
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -130,5 +128,4 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1459,7 +1459,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react@npm:*, @types/react@npm:^18.2.69":
|
"@types/react@npm:*":
|
||||||
version: 18.2.69
|
version: 18.2.69
|
||||||
resolution: "@types/react@npm:18.2.69"
|
resolution: "@types/react@npm:18.2.69"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1470,6 +1470,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/react@npm:^18.2.72":
|
||||||
|
version: 18.2.72
|
||||||
|
resolution: "@types/react@npm:18.2.72"
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types": "npm:*"
|
||||||
|
csstype: "npm:^3.0.2"
|
||||||
|
checksum: 10/0c15461eeb8cd5153b48446d4aae681ae1d4f45fa5828fc53c12aaac9dfe426a64259a284737f6abfc3bea36df9507c129d7065ebb390b21349ad30128385ac1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/responselike@npm:^1.0.0":
|
"@types/responselike@npm:^1.0.0":
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
resolution: "@types/responselike@npm:1.0.3"
|
resolution: "@types/responselike@npm:1.0.3"
|
||||||
@@ -1502,15 +1512,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@npm:^7.3.1":
|
"@typescript-eslint/eslint-plugin@npm:^7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/eslint-plugin@npm:7.3.1"
|
resolution: "@typescript-eslint/eslint-plugin@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp": "npm:^4.5.1"
|
"@eslint-community/regexpp": "npm:^4.5.1"
|
||||||
"@typescript-eslint/scope-manager": "npm:7.3.1"
|
"@typescript-eslint/scope-manager": "npm:7.4.0"
|
||||||
"@typescript-eslint/type-utils": "npm:7.3.1"
|
"@typescript-eslint/type-utils": "npm:7.4.0"
|
||||||
"@typescript-eslint/utils": "npm:7.3.1"
|
"@typescript-eslint/utils": "npm:7.4.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:7.3.1"
|
"@typescript-eslint/visitor-keys": "npm:7.4.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
graphemer: "npm:^1.4.0"
|
graphemer: "npm:^1.4.0"
|
||||||
ignore: "npm:^5.2.4"
|
ignore: "npm:^5.2.4"
|
||||||
@@ -1523,44 +1533,44 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10/8ed276113a714d93ab3ababb1179e4785bd9378e6d97726519ea1d2ac502a94475e0be988c2ec427dcfc1e6950329d58da6e64131ee87028fce63493461cc51a
|
checksum: 10/9bd8852c7e4e9608c3fded94f7c60506cc7d2b6d8a8c1cad6d48969a7363751b20282874e55ccdf180635cf204cb10b3e1e5c3d1cff34d4fcd07762be3fc138e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/parser@npm:^7.3.1":
|
"@typescript-eslint/parser@npm:^7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/parser@npm:7.3.1"
|
resolution: "@typescript-eslint/parser@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager": "npm:7.3.1"
|
"@typescript-eslint/scope-manager": "npm:7.4.0"
|
||||||
"@typescript-eslint/types": "npm:7.3.1"
|
"@typescript-eslint/types": "npm:7.4.0"
|
||||||
"@typescript-eslint/typescript-estree": "npm:7.3.1"
|
"@typescript-eslint/typescript-estree": "npm:7.4.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:7.3.1"
|
"@typescript-eslint/visitor-keys": "npm:7.4.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.56.0
|
eslint: ^8.56.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10/018326010fec1dcefd75809ccac5102a475bf1e052d824b898d707e7c0bf3e51e101164b410d1b2a513628985c96eb412538644d2005e26b99a22db6eb9402df
|
checksum: 10/142a9e1187d305ed43b4fef659c36fa4e28359467198c986f0955c70b4067c9799f4c85d9881fbf099c55dfb265e30666e28b3ef290520e242b45ca7cb8e4ca9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@npm:7.3.1":
|
"@typescript-eslint/scope-manager@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/scope-manager@npm:7.3.1"
|
resolution: "@typescript-eslint/scope-manager@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:7.3.1"
|
"@typescript-eslint/types": "npm:7.4.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:7.3.1"
|
"@typescript-eslint/visitor-keys": "npm:7.4.0"
|
||||||
checksum: 10/7384d1f46d7f3678a1135a1ac0bd8b6dfa2f01e93b19e2510c7082766cf6983a1bf80b4ccf498651199a81d9f2bdb65101fd7a19226a723260514204d0c30b34
|
checksum: 10/8cf9292444f9731017a707cac34bef5ae0eb33b5cd42ed07fcd046e981d97889d9201d48e02f470f2315123f53771435e10b1dc81642af28a11df5352a8e8be2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@npm:7.3.1":
|
"@typescript-eslint/type-utils@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/type-utils@npm:7.3.1"
|
resolution: "@typescript-eslint/type-utils@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree": "npm:7.3.1"
|
"@typescript-eslint/typescript-estree": "npm:7.4.0"
|
||||||
"@typescript-eslint/utils": "npm:7.3.1"
|
"@typescript-eslint/utils": "npm:7.4.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
ts-api-utils: "npm:^1.0.1"
|
ts-api-utils: "npm:^1.0.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1568,23 +1578,23 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10/fae9003a76a8f2a2a4bb88dc0f82c0a1ca0688633183fac391920e7124a12807aac84bb287a21f61e99523c15223d1c08e7680685ebf21d07429604cba6c420b
|
checksum: 10/a8bd0929d8237679b2b8a7817f070a4b9658ee976882fba8ff37e4a70dd33f87793e1b157771104111fe8054eaa8ad437a010b6aa465072fbdb932647125db2d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/types@npm:7.3.1":
|
"@typescript-eslint/types@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/types@npm:7.3.1"
|
resolution: "@typescript-eslint/types@npm:7.4.0"
|
||||||
checksum: 10/c9c8eae1cf937cececd99a253bd65eb71b40206e79cf917ad9c3b3ab80cc7ce5fefb2804f9fd2a70e7438951f0d1e63df3031fc61e3a08dfef5fde208a12e0ed
|
checksum: 10/2782c5bf65cd3dfa9cd32bc3023676bbca22144987c3f6c6b67fd96c73d4a60b85a57458c49fd11b9971ac6531824bb3ae0664491e7a6de25d80c523c9be92b7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@npm:7.3.1":
|
"@typescript-eslint/typescript-estree@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/typescript-estree@npm:7.3.1"
|
resolution: "@typescript-eslint/typescript-estree@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:7.3.1"
|
"@typescript-eslint/types": "npm:7.4.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:7.3.1"
|
"@typescript-eslint/visitor-keys": "npm:7.4.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
globby: "npm:^11.1.0"
|
globby: "npm:^11.1.0"
|
||||||
is-glob: "npm:^4.0.3"
|
is-glob: "npm:^4.0.3"
|
||||||
@@ -1594,34 +1604,34 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10/363ad9864b56394b4000dff7c2b77d0ea52042c3c20e3b86c0f3c66044915632d9890255527c6f3a5ef056886dec72e38fbcfce49d4ad092c160440f54128230
|
checksum: 10/162ec9d7582f45588342e1be36fdb60e41f50bbdfbc3035c91b517ff5d45244f776921c88d88e543e1c7d0f1e6ada5474a8316b78f1b0e6d2233b101bc45b166
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/utils@npm:7.3.1":
|
"@typescript-eslint/utils@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/utils@npm:7.3.1"
|
resolution: "@typescript-eslint/utils@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||||
"@types/json-schema": "npm:^7.0.12"
|
"@types/json-schema": "npm:^7.0.12"
|
||||||
"@types/semver": "npm:^7.5.0"
|
"@types/semver": "npm:^7.5.0"
|
||||||
"@typescript-eslint/scope-manager": "npm:7.3.1"
|
"@typescript-eslint/scope-manager": "npm:7.4.0"
|
||||||
"@typescript-eslint/types": "npm:7.3.1"
|
"@typescript-eslint/types": "npm:7.4.0"
|
||||||
"@typescript-eslint/typescript-estree": "npm:7.3.1"
|
"@typescript-eslint/typescript-estree": "npm:7.4.0"
|
||||||
semver: "npm:^7.5.4"
|
semver: "npm:^7.5.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.56.0
|
eslint: ^8.56.0
|
||||||
checksum: 10/234d9d65fe5d0f4a31345bd8f5a6f2879a578b3a531a14c2b3edaa7fb587c71d26249f86c41857382c0405384dc104955c02b588b3cee6fc2734f1ae40aef07b
|
checksum: 10/ffed27e770c486cd000ff892d9049b0afe8b9d6318452a5355b78a37436cbb414bceacae413a2ac813f3e584684825d5e0baa2e6376b7ad6013a108ac91bc19d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@npm:7.3.1":
|
"@typescript-eslint/visitor-keys@npm:7.4.0":
|
||||||
version: 7.3.1
|
version: 7.4.0
|
||||||
resolution: "@typescript-eslint/visitor-keys@npm:7.3.1"
|
resolution: "@typescript-eslint/visitor-keys@npm:7.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:7.3.1"
|
"@typescript-eslint/types": "npm:7.4.0"
|
||||||
eslint-visitor-keys: "npm:^3.4.1"
|
eslint-visitor-keys: "npm:^3.4.1"
|
||||||
checksum: 10/163a93597c1d696920a19b3c1627d02368bdd52059f811c0fadd680c38034bb6418ebefe99d8ce26e0dd44ae184f18fab186af775de1a8771256be1a7905c174
|
checksum: 10/70dc99f2ad116c6e2d9e55af249e4453e06bba2ceea515adef2d2e86e97e557865bb1b1d467667462443eb0d624baba36f7442fd1082f3874339bbc381c26e93
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1648,11 +1658,11 @@ __metadata:
|
|||||||
"@types/imagemin": "npm:^8.0.5"
|
"@types/imagemin": "npm:^8.0.5"
|
||||||
"@types/lodash-es": "npm:^4.17.12"
|
"@types/lodash-es": "npm:^4.17.12"
|
||||||
"@types/node": "npm:^20.11.30"
|
"@types/node": "npm:^20.11.30"
|
||||||
"@types/react": "npm:^18.2.69"
|
"@types/react": "npm:^18.2.72"
|
||||||
"@types/react-dom": "npm:^18.2.22"
|
"@types/react-dom": "npm:^18.2.22"
|
||||||
"@types/react-router-dom": "npm:^5.3.3"
|
"@types/react-router-dom": "npm:^5.3.3"
|
||||||
"@typescript-eslint/eslint-plugin": "npm:^7.3.1"
|
"@typescript-eslint/eslint-plugin": "npm:^7.4.0"
|
||||||
"@typescript-eslint/parser": "npm:^7.3.1"
|
"@typescript-eslint/parser": "npm:^7.4.0"
|
||||||
alova: "npm:^2.18.0"
|
alova: "npm:^2.18.0"
|
||||||
async-validator: "npm:^4.2.5"
|
async-validator: "npm:^4.2.5"
|
||||||
concurrently: "npm:^8.2.2"
|
concurrently: "npm:^8.2.2"
|
||||||
@@ -1682,7 +1692,7 @@ __metadata:
|
|||||||
terser: "npm:^5.29.2"
|
terser: "npm:^5.29.2"
|
||||||
typesafe-i18n: "npm:^5.26.2"
|
typesafe-i18n: "npm:^5.26.2"
|
||||||
typescript: "npm:^5.4.3"
|
typescript: "npm:^5.4.3"
|
||||||
vite: "npm:^5.2.4"
|
vite: "npm:^5.2.6"
|
||||||
vite-plugin-imagemin: "npm:^0.6.1"
|
vite-plugin-imagemin: "npm:^0.6.1"
|
||||||
vite-tsconfig-paths: "npm:^4.3.2"
|
vite-tsconfig-paths: "npm:^4.3.2"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
@@ -8342,9 +8352,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vite@npm:^5.2.4":
|
"vite@npm:^5.2.6":
|
||||||
version: 5.2.4
|
version: 5.2.6
|
||||||
resolution: "vite@npm:5.2.4"
|
resolution: "vite@npm:5.2.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: "npm:^0.20.1"
|
esbuild: "npm:^0.20.1"
|
||||||
fsevents: "npm:~2.3.3"
|
fsevents: "npm:~2.3.3"
|
||||||
@@ -8378,7 +8388,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
vite: bin/vite.js
|
vite: bin/vite.js
|
||||||
checksum: 10/ed3d4fa2023642dd578e90eb02876e01729fda0af196304bb1c3a7f037b457f1a505bfa36c10f28d0466945b9da7d7972219716554d43ff3c025f26ed3d89e11
|
checksum: 10/0409acd4bbad1bca42b2015ac5d0f710bbc84b86f6b518add9a9c13adf1aab02fd40fcca854dc08ff2a2226c1df77d5d5b4a958c6c4c04ca27a6bfb0b4f60615
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
|
|||||||
//length() is not thread-safe, thus acquiring the lock before this call..
|
//length() is not thread-safe, thus acquiring the lock before this call..
|
||||||
_lockmq.lock();
|
_lockmq.lock();
|
||||||
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
||||||
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
// ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||||
delete dataMessage;
|
delete dataMessage;
|
||||||
} else {
|
} else {
|
||||||
_messageQueue.add(dataMessage);
|
_messageQueue.add(dataMessage);
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ void AsyncWebSocketClient::_queueMessage(std::shared_ptr<std::vector<uint8_t>> b
|
|||||||
if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES)
|
if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES)
|
||||||
{
|
{
|
||||||
l.unlock();
|
l.unlock();
|
||||||
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued, closing connection\n");
|
//ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued, closing connection\n");
|
||||||
_status = WS_DISCONNECTED;
|
_status = WS_DISCONNECTED;
|
||||||
if (_client) _client->close(true);
|
if (_client) _client->close(true);
|
||||||
return;
|
return;
|
||||||
@@ -1272,9 +1272,9 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket
|
|||||||
(String&)key += WS_STR_UUID;
|
(String&)key += WS_STR_UUID;
|
||||||
mbedtls_sha1_context ctx;
|
mbedtls_sha1_context ctx;
|
||||||
mbedtls_sha1_init(&ctx);
|
mbedtls_sha1_init(&ctx);
|
||||||
mbedtls_sha1_starts_ret(&ctx);
|
mbedtls_sha1_starts(&ctx);
|
||||||
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)key.c_str(), key.length());
|
mbedtls_sha1_update(&ctx, (const unsigned char*)key.c_str(), key.length());
|
||||||
mbedtls_sha1_finish_ret(&ctx, hash);
|
mbedtls_sha1_finish(&ctx, hash);
|
||||||
mbedtls_sha1_free(&ctx);
|
mbedtls_sha1_free(&ctx);
|
||||||
#endif
|
#endif
|
||||||
base64_encodestate _state;
|
base64_encodestate _state;
|
||||||
|
|||||||
@@ -76,10 +76,17 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
|||||||
return false;
|
return false;
|
||||||
memset(_buf, 0x00, 16);
|
memset(_buf, 0x00, 16);
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
|
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||||
mbedtls_md5_init(&_ctx);
|
mbedtls_md5_init(&_ctx);
|
||||||
mbedtls_md5_starts_ret(&_ctx);
|
mbedtls_md5_starts_ret(&_ctx);
|
||||||
mbedtls_md5_update_ret(&_ctx, data, len);
|
mbedtls_md5_update_ret(&_ctx, data, len);
|
||||||
mbedtls_md5_finish_ret(&_ctx, _buf);
|
mbedtls_md5_finish_ret(&_ctx, _buf);
|
||||||
|
#else
|
||||||
|
mbedtls_md5_init(&_ctx);
|
||||||
|
mbedtls_md5_starts(&_ctx);
|
||||||
|
mbedtls_md5_update(&_ctx, data, len);
|
||||||
|
mbedtls_md5_finish(&_ctx, _buf);
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
MD5Init(&_ctx);
|
MD5Init(&_ctx);
|
||||||
MD5Update(&_ctx, data, len);
|
MD5Update(&_ctx, data, len);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user