mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86e29515e7 | ||
|
|
46eb4185d7 | ||
|
|
8da6761a48 | ||
|
|
9233f0dfcc | ||
|
|
292f743b14 | ||
|
|
dd6dfffd57 | ||
|
|
ec705a5307 | ||
|
|
f45f071710 | ||
|
|
f3858546de | ||
|
|
d0ac0b7804 | ||
|
|
d8284ec09f | ||
|
|
6e982acde8 | ||
|
|
8c94ce99b2 | ||
|
|
fc057d18c9 | ||
|
|
18e9b99413 | ||
|
|
a47e0e8266 | ||
|
|
f412ddc716 | ||
|
|
29110e96e5 | ||
|
|
b65866217a | ||
|
|
611e3b1243 | ||
|
|
2ca0a0c634 | ||
|
|
7eb1f061b7 | ||
|
|
50459a23fe | ||
|
|
5bf53c3389 | ||
|
|
4b7aa95be3 | ||
|
|
70943f5758 | ||
|
|
3bc280b817 | ||
|
|
62b15a5319 | ||
|
|
8dd18802d6 | ||
|
|
57a516a83a | ||
|
|
a57fdaa4b3 | ||
|
|
4841e42286 | ||
|
|
8c2d2b06ed | ||
|
|
38c8b1b7f0 | ||
|
|
6fb5933a02 | ||
|
|
c0944433be | ||
|
|
478e6362c9 | ||
|
|
4d6354db78 | ||
|
|
beab0f0c77 | ||
|
|
c17749bd22 | ||
|
|
2bad769c5c | ||
|
|
8ad89ca64b | ||
|
|
9244d8daec | ||
|
|
02d01334b2 |
23
.github/workflows/github-releases-to-discord.yml
vendored
23
.github/workflows/github-releases-to-discord.yml
vendored
@@ -1,23 +0,0 @@
|
|||||||
name: 'github-releases-to-discord'
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
github-releases-to-discord:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Github Releases To Discord
|
|
||||||
uses: SethCohen/github-releases-to-discord@v1.13.1
|
|
||||||
with:
|
|
||||||
webhook_url: ${{ secrets.WEBHOOK_URL }}
|
|
||||||
color: '2105893'
|
|
||||||
username: 'Release Changelog'
|
|
||||||
avatar_url: 'https://cdn.discordapp.com/icons/816637840644505620/0b14718532d855c452903851b4f0c9a2.png'
|
|
||||||
content: '||@everyone||'
|
|
||||||
footer_title: 'Changelog'
|
|
||||||
footer_icon_url: 'https://cdn.discordapp.com/icons/816637840644505620/0b14718532d855c452903851b4f0c9a2.png'
|
|
||||||
footer_timestamp: true
|
|
||||||
19
.github/workflows/pre_release.yml
vendored
19
.github/workflows/pre_release.yml
vendored
@@ -12,13 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v4
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
node-version: '18'
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
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
|
||||||
@@ -35,10 +33,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd interface
|
cd interface
|
||||||
yarn install
|
yarn install
|
||||||
yarn typesafe-i18n --no-watch
|
yarn run typesafe-i18n --no-watch
|
||||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||||
yarn build
|
yarn run build
|
||||||
yarn webUI
|
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: |
|
run: |
|
||||||
@@ -48,10 +45,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
platformio run -e ci_s3
|
platformio run -e ci_s3
|
||||||
|
|
||||||
- name: Build E32V2 firmware
|
|
||||||
run: |
|
|
||||||
platformio run -e ci_16M
|
|
||||||
|
|
||||||
- name: Create a GH Release
|
- name: Create a GH Release
|
||||||
id: 'automatic_releases'
|
id: 'automatic_releases'
|
||||||
uses: 'marvinpinto/action-automatic-releases@latest'
|
uses: 'marvinpinto/action-automatic-releases@latest'
|
||||||
|
|||||||
4
.github/workflows/sonar_check.yml
vendored
4
.github/workflows/sonar_check.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
|||||||
# if: github.repository_owner == 'emsesp'
|
# if: github.repository_owner == 'emsesp'
|
||||||
# if: github.repository == 'emsesp/EMS-ESP32'
|
# if: github.repository == 'emsesp/EMS-ESP32'
|
||||||
env:
|
env:
|
||||||
BUILD_WRAPPER_OUT_DIR: bw-output
|
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
- name: Install sonar-scanner and build-wrapper
|
- name: Install sonar-scanner and build-wrapper
|
||||||
|
|||||||
15
.github/workflows/tagged_release.yml
vendored
15
.github/workflows/tagged_release.yml
vendored
@@ -11,13 +11,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v4
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
node-version: '18'
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
|
|
||||||
- name: Install PlatformIO
|
- name: Install PlatformIO
|
||||||
run: |
|
run: |
|
||||||
@@ -30,10 +28,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd interface
|
cd interface
|
||||||
yarn install
|
yarn install
|
||||||
yarn typesafe-i18n --no-watch
|
yarn run typesafe-i18n --no-watch
|
||||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||||
yarn build
|
yarn run build
|
||||||
yarn webUI
|
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
15
.github/workflows/test_release.yml
vendored
15
.github/workflows/test_release.yml
vendored
@@ -12,13 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v4
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
node-version: '18'
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
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
|
||||||
@@ -35,10 +33,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd interface
|
cd interface
|
||||||
yarn install
|
yarn install
|
||||||
yarn typesafe-i18n --no-watch
|
yarn run typesafe-i18n --no-watch
|
||||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||||
yarn build
|
yarn run build
|
||||||
yarn webUI
|
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -2,7 +2,7 @@
|
|||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
#.vscode/settings.json
|
# .vscode/settings.json
|
||||||
|
|
||||||
# c++ compiling
|
# c++ compiling
|
||||||
.clang_complete
|
.clang_complete
|
||||||
@@ -12,11 +12,11 @@ cppcheck.out.xml
|
|||||||
# platformio
|
# platformio
|
||||||
.pio
|
.pio
|
||||||
pio_local.ini
|
pio_local.ini
|
||||||
*_old
|
|
||||||
|
|
||||||
# OS specific
|
# OS specific
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*Thumbs.db
|
*Thumbs.db
|
||||||
|
emsesp
|
||||||
|
|
||||||
# web specfic
|
# web specfic
|
||||||
build/
|
build/
|
||||||
@@ -36,15 +36,12 @@ stats.html
|
|||||||
!.yarn/releases
|
!.yarn/releases
|
||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
yarn.lock
|
|
||||||
analyse.html
|
|
||||||
interface/vite.config.ts.timestamp*
|
|
||||||
|
|
||||||
# scripts
|
# scripts
|
||||||
test.sh
|
test.sh
|
||||||
scripts/run.sh
|
scripts/run.sh
|
||||||
scripts/__pycache__
|
scripts/__pycache__
|
||||||
scripts/stackdmp.txt
|
/scripts/stackdmp.txt
|
||||||
|
|
||||||
# i18n generated files
|
# i18n generated files
|
||||||
interface/src/i18n/i18n-react.tsx
|
interface/src/i18n/i18n-react.tsx
|
||||||
@@ -56,8 +53,8 @@ interface/src/i18n/i18n-util.async.ts
|
|||||||
# sonar
|
# sonar
|
||||||
.scannerwork/
|
.scannerwork/
|
||||||
sonar/
|
sonar/
|
||||||
bw-output/
|
build_wrapper_output_directory/
|
||||||
|
|
||||||
# testing
|
|
||||||
emsesp
|
|
||||||
|
|
||||||
|
# entity dump results
|
||||||
|
# dump_entities.csv
|
||||||
|
# dump_entities.xls*
|
||||||
|
|||||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -2,6 +2,9 @@
|
|||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
// for the documentation about the extensions.json format
|
// for the documentation about the extensions.json format
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
|
"arcanis.vscode-zipfs",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
"platformio.platformio-ide"
|
"platformio.platformio-ide"
|
||||||
],
|
],
|
||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
|
|||||||
134
.vscode/settings.json
vendored
134
.vscode/settings.json
vendored
@@ -1,91 +1,45 @@
|
|||||||
{
|
{
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/.yarn": true,
|
"**/.yarn": true,
|
||||||
"**/.pnp.*": true
|
"**/.pnp.*": true
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": "explicit"
|
"source.fixAll": true
|
||||||
},
|
// "source.organizeImports": true
|
||||||
"eslint.nodePath": "interface/.yarn/sdks",
|
},
|
||||||
"eslint.workingDirectories": ["interface"],
|
"eslint.nodePath": "interface/.yarn/sdks",
|
||||||
"prettier.prettierPath": "",
|
"eslint.workingDirectories": ["interface"],
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
"prettier.prettierPath": "",
|
||||||
"files.associations": {
|
"typescript.tsdk": "interface/.yarn/sdks/typescript/lib",
|
||||||
"*.tsx": "typescriptreact",
|
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||||
"*.tcc": "cpp",
|
"files.associations": {
|
||||||
"optional": "cpp",
|
"*.tsx": "typescriptreact",
|
||||||
"istream": "cpp",
|
"*.tcc": "cpp",
|
||||||
"ostream": "cpp",
|
"optional": "cpp",
|
||||||
"ratio": "cpp",
|
"istream": "cpp",
|
||||||
"system_error": "cpp",
|
"ostream": "cpp",
|
||||||
"array": "cpp",
|
"ratio": "cpp",
|
||||||
"functional": "cpp",
|
"system_error": "cpp",
|
||||||
"regex": "cpp",
|
"array": "cpp",
|
||||||
"tuple": "cpp",
|
"functional": "cpp",
|
||||||
"type_traits": "cpp",
|
"regex": "cpp",
|
||||||
"utility": "cpp",
|
"tuple": "cpp",
|
||||||
"string": "cpp",
|
"type_traits": "cpp",
|
||||||
"string_view": "cpp",
|
"utility": "cpp",
|
||||||
"atomic": "cpp",
|
"string": "cpp",
|
||||||
"bitset": "cpp",
|
"string_view": "cpp"
|
||||||
"cctype": "cpp",
|
},
|
||||||
"chrono": "cpp",
|
"todo-tree.filtering.excludeGlobs": [
|
||||||
"clocale": "cpp",
|
"**/vendor/**",
|
||||||
"cmath": "cpp",
|
"**/node_modules/**",
|
||||||
"condition_variable": "cpp",
|
"**/dist/**",
|
||||||
"cstdarg": "cpp",
|
"**/bower_components/**",
|
||||||
"cstddef": "cpp",
|
"**/build/**",
|
||||||
"cstdint": "cpp",
|
"**/.vscode/**",
|
||||||
"cstdio": "cpp",
|
"**/.github/**",
|
||||||
"cstdlib": "cpp",
|
"**/_output/**",
|
||||||
"cstring": "cpp",
|
"**/*.min.*",
|
||||||
"ctime": "cpp",
|
"**/*.map",
|
||||||
"cwchar": "cpp",
|
"**/ArduinoJson/**"
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
18
.vscode/tasks.json
vendored
Normal file
18
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"label": "build standalone emsesp",
|
||||||
|
"command": "make",
|
||||||
|
"args": [],
|
||||||
|
"problemMatcher": ["$gcc"],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
138
CHANGELOG.md
138
CHANGELOG.md
@@ -5,138 +5,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [3.6.5] March 23 2024
|
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
|
||||||
|
|
||||||
- The Wifi Tx Power setting in Network Settings will be reset to Auto
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- thermostat boost mode and boost time [#1446](https://github.com/emsesp/EMS-ESP32/issues/1446)
|
|
||||||
- heatpump energy meters [#1463](https://github.com/emsesp/EMS-ESP32/issues/1463)
|
|
||||||
- heatpump max power [#1475](https://github.com/emsesp/EMS-ESP32/issues/1475)
|
|
||||||
- checkbox for MQTT-TLS enable [#1474](https://github.com/emsesp/EMS-ESP32/issues/1474)
|
|
||||||
- added SK (Slovak) language. Thanks @misa1515
|
|
||||||
- CPU info [#1497](https://github.com/emsesp/EMS-ESP32/pull/1497)
|
|
||||||
- Show network hostname in Web UI under Network Status
|
|
||||||
- Improved HA Discovery so each section (EMS device, Scheduler, Analog, Temperature, Custom, Shower) have their own section
|
|
||||||
- boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536)
|
|
||||||
- mixer MM100 telegram 0x2CC [#1554](https://github.com/emsesp/EMS-ESP32/issues/1554)
|
|
||||||
- boiler hpSetDiffPressure [#1563](https://github.com/emsesp/EMS-ESP32/issues/1563)
|
|
||||||
- custom variables [#1423](https://github.com/emsesp/EMS-ESP32/issues/1423)
|
|
||||||
- weather compensation [#1642](https://github.com/emsesp/EMS-ESP32/issues/1642)
|
|
||||||
- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635)
|
|
||||||
- command `restart partitionname` and button long press to start with other partition [#1657](https://github.com/emsesp/EMS-ESP32/issues/1657)
|
|
||||||
- command `set service <mqtt|ota|ntp|ap> <enable|disable>` [#1663](https://github.com/emsesp/EMS-ESP32/issues/1663)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- exhaust temperature for some boilers
|
|
||||||
- add back boil2hyst [#1477](https://github.com/emsesp/EMS-ESP32/issues/1477)
|
|
||||||
- subscribed MQTT topics not detecting changes by EMS-ESP [#1494](https://github.com/emsesp/EMS-ESP32/issues/1494)
|
|
||||||
- changed HA name and grouping to be consistent [#1528](https://github.com/emsesp/EMS-ESP32/issues/1528)
|
|
||||||
- MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528)
|
|
||||||
- dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495)
|
|
||||||
- added writeable icon to Web's Custom Entity page for each entity shown in the table
|
|
||||||
- Wifi Tx Power not adjusted [#1614](https://github.com/emsesp/EMS-ESP32/issues/1614)
|
|
||||||
- MQTT discovery of custom entity doesn't consider type of data [#1587](https://github.com/emsesp/EMS-ESP32/issues/1587)
|
|
||||||
- WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default.
|
|
||||||
- dns w/wo IPv6 [#1644](https://github.com/emsesp/EMS-ESP32/issues/1644)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459)
|
|
||||||
- upgraded ArduinoJson to 7.0.0 #1538 and then 7.0.2
|
|
||||||
- small changes to the API for analog and temperature sensors
|
|
||||||
- Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619)
|
|
||||||
- C++ optimizations - see <https://github.com/emsesp/EMS-ESP32/pull/1615>
|
|
||||||
- Send MQTT heartbeat immediately after connection [#1628](https://github.com/emsesp/EMS-ESP32/issues/1628)
|
|
||||||
- 16MB partitions with second nvs, larger FS, Coredump, optional factory partition
|
|
||||||
- stop fetching empty telegrams after 5 min
|
|
||||||
|
|
||||||
## [3.6.4] November 24 2023
|
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
- humidity for ventilation devices
|
|
||||||
- telegrams for RC100H, hc2, etc. (seen on discord, not tested)
|
|
||||||
- names for BC400, GB192i.2, read temperatures for low loss header and heatblock [#1317](https://github.com/emsesp/EMS-ESP32/discussions/1317)
|
|
||||||
- option for `forceheatingoff` [#1262](https://github.com/emsesp/EMS-ESP32/issues/1262)
|
|
||||||
- remote thermostat emulation RC100H for RC3xx [#1278](https://github.com/emsesp/EMS-ESP32/discussions/1278)
|
|
||||||
- shower_data MQTT payload contains the timestamp [#1329](https://github.com/emsesp/EMS-ESP32/issues/1329)
|
|
||||||
- HA discovery for writeable text entities [#1337](https://github.com/emsesp/EMS-ESP32/pull/1377)
|
|
||||||
- autodetect board_profile, store in nvs, add telnet command option, add E32V2
|
|
||||||
- heat pump high res energy counters [#1348, #1349. #1350](https://github.com/emsesp/EMS-ESP32/issues/1348)
|
|
||||||
- optional bssid in network settings
|
|
||||||
- extension module EM100 [#1315](https://github.com/emsesp/EMS-ESP32/discussions/1315)
|
|
||||||
- digital_out with new options for polarity and startup state
|
|
||||||
- added 'system allvalues' command that dumps all the EMS device values, plus sensors and any custom entities
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- remove command `remoteseltemp`, thermostat accept it only from remote thermostat
|
|
||||||
- shower_data MQTT payload contains the timestamp [#1329](https://github.com/emsesp/EMS-ESP32/issues/1329)
|
|
||||||
- fixed helper text in Web Device Entity dialog box for numerical ranges
|
|
||||||
- MQTT base with paths not working in HA [#1393](https://github.com/emsesp/EMS-ESP32/issues/1393)
|
|
||||||
- set/read thermostat mode for RC100-RC300, [#1440](https://github.com/emsesp/EMS-ESP32/issues/1440) [#1442](https://github.com/emsesp/EMS-ESP32/issues/1442)
|
|
||||||
- some setting commands for ems-boiler have used wrong ems+ telegram in 3.6.3
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- update to platform 6.4.0, arduino 2.0.14 / idf 4.4.6
|
|
||||||
- small changes for arduino 3.0.0 / idf 5.1 compatibility (not backward compatible to platform 6.3.2 and before)
|
|
||||||
- AP start after 10 sec, stay until station/eth connected
|
|
||||||
- tested wifi-all-channel-scan (3.6.3-dev4 a-e), removed again because of connect issues
|
|
||||||
- mqtt disconnect stops queue
|
|
||||||
|
|
||||||
## [3.6.2] October 1 2023
|
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- Power entities
|
|
||||||
- Optional input of BSSID for AP connection
|
|
||||||
- Return empty json if no entries in scheduler/custom/analogsensor/temperaturesensor
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- Wifi full scan to get strongest AP
|
|
||||||
- Add missing dhw tags
|
|
||||||
- Sending a dash/- to the Reset command doesn't return an error [#1308](https://github.com/emsesp/EMS-ESP32/discussions/1308)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- MQTT queue max 300 messages, check heap and maxAlloc
|
|
||||||
- API call commands are logged as WARN in the log
|
|
||||||
- Reset Command renamed to 'reset' in lowercase in EN
|
|
||||||
|
|
||||||
## [3.6.1] September 9 2023
|
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
|
||||||
|
|
||||||
- `shower_data` MQTT topic shows duration is seconds (was previously a full english sentence)
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- Show WiFi rssi in Network Status Page, show quality as color
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- Issue in espMqttClient causing a memory leak when MQTT broker is disconnected due to network unavailability [#1264](https://github.com/emsesp/EMS-ESP32/issues/1264)
|
|
||||||
- Using MQTT enum values correctly formatted in MQTT Discovery [#1280](https://github.com/emsesp/EMS-ESP32/issues/1280)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- MQTT free mem check set to 60 kb
|
|
||||||
- Small cosmetic changes to Searching in Customization web page
|
|
||||||
- Updated to espressif32@6.4.0
|
|
||||||
|
|
||||||
# [3.6.0] August 13 2023
|
# [3.6.0] August 13 2023
|
||||||
|
|
||||||
## **IMPORTANT! BREAKING CHANGES**
|
## **IMPORTANT! BREAKING CHANGES**
|
||||||
@@ -147,7 +15,7 @@ There are breaking changes between 3.5.x and earlier versions of 3.6.0. Please r
|
|||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- Workaround for better Domoticz MQTT integration? [#904](https://github.com/emsesp/EMS-ESP32/issues/904)
|
- Workaround for better Domoticz MQTT intergration? [#904](https://github.com/emsesp/EMS-ESP32/issues/904)
|
||||||
- Show MAC address without connecting to network enhancement [#933](https://github.com/emsesp/EMS-ESP32/issues/933)
|
- Show MAC address without connecting to network enhancement [#933](https://github.com/emsesp/EMS-ESP32/issues/933)
|
||||||
- Warn user in WebUI of unsaved changes [#911](https://github.com/emsesp/EMS-ESP32/issues/911)
|
- Warn user in WebUI of unsaved changes [#911](https://github.com/emsesp/EMS-ESP32/issues/911)
|
||||||
- Detect old Tado thermostat, device-id 0x19, no entities
|
- Detect old Tado thermostat, device-id 0x19, no entities
|
||||||
@@ -286,7 +154,7 @@ There are breaking changes between 3.5.x and earlier versions of 3.6.0. Please r
|
|||||||
|
|
||||||
- fix Table resizing in WebUI [#519](https://github.com/emsesp/EMS-ESP32/issues/519)
|
- fix Table resizing in WebUI [#519](https://github.com/emsesp/EMS-ESP32/issues/519)
|
||||||
- allow larger customization files [#570](https://github.com/emsesp/EMS-ESP32/issues/570)
|
- allow larger customization files [#570](https://github.com/emsesp/EMS-ESP32/issues/570)
|
||||||
- losing entity wwcomfort [#581](https://github.com/emsesp/EMS-ESP32/issues/581)
|
- losing entitiy wwcomfort [#581](https://github.com/emsesp/EMS-ESP32/issues/581)
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
@@ -313,7 +181,7 @@ There are breaking changes between 3.5.x and earlier versions of 3.6.0. Please r
|
|||||||
|
|
||||||
- WebUI optimizations, updated look&feel and better performance [#124](https://github.com/emsesp/EMS-ESP32/issues/124)
|
- WebUI optimizations, updated look&feel and better performance [#124](https://github.com/emsesp/EMS-ESP32/issues/124)
|
||||||
- Auto refresh of WebUI after successful firmware upload [#178](https://github.com/emsesp/EMS-ESP32/issues/178)
|
- Auto refresh of WebUI after successful firmware upload [#178](https://github.com/emsesp/EMS-ESP32/issues/178)
|
||||||
- New Customization Service in WebUI. First feature is the ability to enable/disabled Entities (device values) from EMS devices [#206](https://github.com/emsesp/EMS-ESP32/issues/206)
|
- New Customization Service in WebUI. First feature is the ability to enable/disabled Enitites (device values) from EMS devices [#206](https://github.com/emsesp/EMS-ESP32/issues/206)
|
||||||
- Option to disable Telnet Console [#209](https://github.com/emsesp/EMS-ESP32/issues/209)
|
- Option to disable Telnet Console [#209](https://github.com/emsesp/EMS-ESP32/issues/209)
|
||||||
- Added Hide SSID, Max Clients and Preferred Channel to Access Point
|
- Added Hide SSID, Max Clients and Preferred Channel to Access Point
|
||||||
- Merged in MichaelDvP's changes like Fahrenheit conversion, publish single (for IOBroker) and a few other critical optimizations
|
- Merged in MichaelDvP's changes like Fahrenheit conversion, publish single (for IOBroker) and a few other critical optimizations
|
||||||
|
|||||||
@@ -1,23 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [3.7.0]
|
# [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)
|
|
||||||
|
|||||||
4
Makefile
4
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.7.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Sources & Files
|
# Sources & Files
|
||||||
@@ -81,7 +81,7 @@ CPPFLAGS += -g3
|
|||||||
CPPFLAGS += -Os
|
CPPFLAGS += -Os
|
||||||
|
|
||||||
CFLAGS += $(CPPFLAGS)
|
CFLAGS += $(CPPFLAGS)
|
||||||
CFLAGS += -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-unused-lambda-capture -Wno-sign-compare
|
CFLAGS += -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-unused-lambda-capture
|
||||||
|
|
||||||
CXXFLAGS += $(CFLAGS) -MMD
|
CXXFLAGS += $(CFLAGS) -MMD
|
||||||
|
|
||||||
|
|||||||
2338
dump_entities.csv
2338
dump_entities.csv
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,6 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
nvs, data, nvs, 0x9000, 0x005000,
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
otadata, data, ota, , 0x002000,
|
otadata, data, ota, , 0x2000,
|
||||||
boot, app, factory, , 0x280000,
|
app0, app, ota_0, , 0x7F0000,
|
||||||
app0, app, ota_0, , 0x590000,
|
app1, app, ota_1, , 0x7F0000,
|
||||||
app1, app, ota_1, , 0x590000,
|
spiffs, data, spiffs, , 64K,
|
||||||
nvs1, data, nvs, , 0x040000,
|
|
||||||
spiffs, data, spiffs, , 0x200000,
|
|
||||||
coredump, data, coredump,, 0x010000,
|
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
|
||||||
nvs, data, nvs, 0x9000, 0x005000,
|
|
||||||
otadata, data, ota, , 0x002000,
|
|
||||||
app0, app, ota_0, , 0xDD0000,
|
|
||||||
app1, app, ota_1, , 0xDD0000,
|
|
||||||
nvs1, data, nvs, , 0x040000,
|
|
||||||
spiffs, data, spiffs, , 0x400000,
|
|
||||||
coredump, data, coredump,, 0x010000,
|
|
||||||
|
@@ -36,6 +36,7 @@ build_flags =
|
|||||||
-D FACTORY_MQTT_PORT=1883
|
-D FACTORY_MQTT_PORT=1883
|
||||||
-D FACTORY_MQTT_USERNAME=\"\"
|
-D FACTORY_MQTT_USERNAME=\"\"
|
||||||
-D FACTORY_MQTT_PASSWORD=\"\"
|
-D FACTORY_MQTT_PASSWORD=\"\"
|
||||||
|
-D FACTORY_MQTT_CLIENT_ID=\"ems-esp\"
|
||||||
-D FACTORY_MQTT_KEEP_ALIVE=60
|
-D FACTORY_MQTT_KEEP_ALIVE=60
|
||||||
-D FACTORY_MQTT_CLEAN_SESSION=false
|
-D FACTORY_MQTT_CLEAN_SESSION=false
|
||||||
-D FACTORY_MQTT_MAX_TOPIC_LENGTH=128
|
-D FACTORY_MQTT_MAX_TOPIC_LENGTH=128
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
// "airbnb/hooks",
|
"airbnb/hooks",
|
||||||
// "airbnb-typescript",
|
"airbnb-typescript",
|
||||||
"plugin:react/recommended",
|
"plugin:react/recommended",
|
||||||
"plugin:react/jsx-runtime",
|
"plugin:react/jsx-runtime",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"adapter": "react",
|
"adapter": "react",
|
||||||
"baseLocale": "pl",
|
"baseLocale": "pl",
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json"
|
"$schema": "https://unpkg.com/typesafe-i18n@5.26.0/schema/typesafe-i18n.json"
|
||||||
}
|
}
|
||||||
9
interface/.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
9
interface/.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
873
interface/.yarn/releases/yarn-3.4.1.cjs
vendored
Executable file
873
interface/.yarn/releases/yarn-3.4.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
893
interface/.yarn/releases/yarn-4.1.1.cjs
vendored
893
interface/.yarn/releases/yarn-4.1.1.cjs
vendored
File diff suppressed because one or more lines are too long
20
interface/.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
20
interface/.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real eslint/bin/eslint.js your application uses
|
||||||
|
module.exports = absRequire(`eslint/bin/eslint.js`);
|
||||||
20
interface/.yarn/sdks/eslint/lib/api.js
vendored
Normal file
20
interface/.yarn/sdks/eslint/lib/api.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real eslint your application uses
|
||||||
|
module.exports = absRequire(`eslint`);
|
||||||
6
interface/.yarn/sdks/eslint/package.json
vendored
Normal file
6
interface/.yarn/sdks/eslint/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint",
|
||||||
|
"version": "8.36.0-sdk",
|
||||||
|
"main": "./lib/api.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
5
interface/.yarn/sdks/integrations.yml
vendored
Normal file
5
interface/.yarn/sdks/integrations.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# This file is automatically generated by @yarnpkg/sdks.
|
||||||
|
# Manual changes might be lost!
|
||||||
|
|
||||||
|
integrations:
|
||||||
|
- vscode
|
||||||
20
interface/.yarn/sdks/prettier/index.js
vendored
Executable file
20
interface/.yarn/sdks/prettier/index.js
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require prettier/index.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real prettier/index.js your application uses
|
||||||
|
module.exports = absRequire(`prettier/index.js`);
|
||||||
6
interface/.yarn/sdks/prettier/package.json
vendored
Normal file
6
interface/.yarn/sdks/prettier/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "prettier",
|
||||||
|
"version": "2.8.7-sdk",
|
||||||
|
"main": "./index.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
20
interface/.yarn/sdks/typescript/bin/tsc
vendored
Executable file
20
interface/.yarn/sdks/typescript/bin/tsc
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsc
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsc your application uses
|
||||||
|
module.exports = absRequire(`typescript/bin/tsc`);
|
||||||
20
interface/.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
20
interface/.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsserver
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsserver your application uses
|
||||||
|
module.exports = absRequire(`typescript/bin/tsserver`);
|
||||||
20
interface/.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
20
interface/.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsc.js your application uses
|
||||||
|
module.exports = absRequire(`typescript/lib/tsc.js`);
|
||||||
223
interface/.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
223
interface/.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const moduleWrapper = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
||||||
223
interface/.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
223
interface/.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const moduleWrapper = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
||||||
20
interface/.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
20
interface/.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/typescript.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/typescript.js your application uses
|
||||||
|
module.exports = absRequire(`typescript/lib/typescript.js`);
|
||||||
6
interface/.yarn/sdks/typescript/package.json
vendored
Normal file
6
interface/.yarn/sdks/typescript/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "typescript",
|
||||||
|
"version": "5.0.2-sdk",
|
||||||
|
"main": "./lib/typescript.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
compressionLevel: mixed
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||||
|
spec: '@yarnpkg/plugin-typescript'
|
||||||
|
|
||||||
enableGlobalCache: false
|
yarnPath: .yarn/releases/yarn-3.4.1.cjs
|
||||||
|
|
||||||
|
# uing pnp
|
||||||
|
# nodeLinker: pnp
|
||||||
|
|
||||||
|
# use these if not using PnP and have node_modules
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
compressionLevel: 0
|
||||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
nmMode: hardlinks-local
|
||||||
|
enableGlobalCache: true
|
||||||
|
|||||||
@@ -1,78 +1,79 @@
|
|||||||
{
|
{
|
||||||
"name": "EMS-ESP",
|
"name": "EMS-ESP",
|
||||||
"version": "3.7",
|
"version": "3.6.0",
|
||||||
"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",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
"build-hosted": "vite build --mode hosted",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build-hosted": "typesafe-i18n --no-watch && vite build --mode hosted",
|
"preview-standalone": "npm-run-all -p preview typesafe-i18n mock-api",
|
||||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"npm:mock-api\" \"vite preview\"",
|
"mock-api": "nodemon --watch ../mock-api ../mock-api/server.js",
|
||||||
"mock-api": "bun --watch ../mock-api/server.ts",
|
"standalone": "npm-run-all -p dev typesafe-i18n mock-api",
|
||||||
"old_mock-api": "bun --watch ../mock-api/server.js",
|
"typesafe-i18n": "typesafe-i18n",
|
||||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"npm:mock-api\" \"vite\"",
|
|
||||||
"old_standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"npm:old_mock-api\" \"vite\"",
|
|
||||||
"typesafe-i18n": "typesafe-i18n --no-watch",
|
|
||||||
"webUI": "node progmem-generator.js",
|
|
||||||
"format": "prettier --write '**/*.{ts,tsx,js,css,json,md}'",
|
"format": "prettier --write '**/*.{ts,tsx,js,css,json,md}'",
|
||||||
"lint": "eslint . --cache --fix"
|
"lint": "eslint . --cache --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-xhr": "^1.0.3",
|
"@alova/adapter-xhr": "^1.0.1",
|
||||||
"@babel/core": "^7.24.3",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/react": "^11.11.4",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.15.14",
|
"@mui/icons-material": "^5.14.3",
|
||||||
"@mui/material": "^5.15.14",
|
"@mui/material": "^5.14.4",
|
||||||
|
"@preact/compat": "^17.1.2",
|
||||||
|
"@prefresh/vite": "^2.4.1",
|
||||||
"@table-library/react-table-library": "4.1.7",
|
"@table-library/react-table-library": "4.1.7",
|
||||||
"@types/imagemin": "^8.0.5",
|
"@types/lodash-es": "^4.17.8",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/node": "^20.4.10",
|
||||||
"@types/node": "^20.11.30",
|
"@types/react": "^18.2.20",
|
||||||
"@types/react": "^18.2.72",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@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.10.0",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^3.1.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
|
"preact": "^10.16.0",
|
||||||
"react": "latest",
|
"react": "latest",
|
||||||
"react-dom": "latest",
|
"react-dom": "latest",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^4.10.1",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.15.0",
|
||||||
"react-toastify": "^10.0.5",
|
"react-toastify": "^9.1.3",
|
||||||
"sockette": "^2.0.6",
|
"sockette": "^2.0.6",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.26.0",
|
||||||
"typescript": "^5.4.3"
|
"typescript": "^5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/compat": "^17.1.2",
|
"@babel/core": "^7.22.10",
|
||||||
"@preact/preset-vite": "^2.8.2",
|
"@preact/preset-vite": "^2.5.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@types/babel__core": "^7",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"@typescript-eslint/parser": "^6.3.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.47.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-import-resolver-typescript": "^3.6.0",
|
||||||
"eslint-plugin-autofix": "^1.1.0",
|
"eslint-plugin-autofix": "^1.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.28.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-prettier": "alpha",
|
"eslint-plugin-prettier": "alpha",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.33.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"preact": "^10.20.1",
|
"nodemon": "^3.0.1",
|
||||||
"prettier": "^3.2.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"prettier": "^3.0.1",
|
||||||
"terser": "^5.29.2",
|
"rollup-plugin-visualizer": "^5.9.2",
|
||||||
"vite": "^5.2.6",
|
"terser": "^5.19.2",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite": "^4.4.9",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-plugin-svgr": "^3.2.0",
|
||||||
|
"vite-tsconfig-paths": "^4.2.0"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.1.1"
|
"packageManager": "yarn@3.4.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,10 @@
|
|||||||
import { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } from 'fs';
|
const { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } = require('fs');
|
||||||
import { resolve, relative, sep } from 'path';
|
const { resolve, relative, sep } = require('path');
|
||||||
import zlib from 'zlib';
|
var zlib = require('zlib');
|
||||||
import mime from 'mime-types';
|
var mime = require('mime-types');
|
||||||
import crypto from 'crypto';
|
|
||||||
|
|
||||||
const ARDUINO_INCLUDES = '#include <Arduino.h>\n\n';
|
const ARDUINO_INCLUDES = '#include <Arduino.h>\n\n';
|
||||||
const INDENT = ' ';
|
const INDENT = ' ';
|
||||||
const outputPath = '../lib/framework/WWWData.h';
|
|
||||||
const sourcePath = './dist';
|
|
||||||
const bytesPerLine = 20;
|
|
||||||
var totalSize = 0;
|
|
||||||
|
|
||||||
const generateWWWClass = () =>
|
|
||||||
`typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
|
|
||||||
// Total size is ${totalSize} bytes
|
|
||||||
|
|
||||||
class WWWData {
|
|
||||||
${indent}public:
|
|
||||||
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
|
|
||||||
${fileInfo
|
|
||||||
.map(
|
|
||||||
(file) =>
|
|
||||||
`${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size}, "${file.hash}");`
|
|
||||||
)
|
|
||||||
.join('\n')}
|
|
||||||
${indent.repeat(2)}}
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
function getFilesSync(dir, files = []) {
|
function getFilesSync(dir, files = []) {
|
||||||
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
||||||
@@ -40,6 +18,10 @@ function getFilesSync(dir, files = []) {
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function coherseToBuffer(input) {
|
||||||
|
// return Buffer.isBuffer(input) ? input : Buffer.from(input);
|
||||||
|
// }
|
||||||
|
|
||||||
function cleanAndOpen(path) {
|
function cleanAndOpen(path) {
|
||||||
if (existsSync(path)) {
|
if (existsSync(path)) {
|
||||||
unlinkSync(path);
|
unlinkSync(path);
|
||||||
@@ -47,68 +29,90 @@ function cleanAndOpen(path) {
|
|||||||
return createWriteStream(path, { flags: 'w+' });
|
return createWriteStream(path, { flags: 'w+' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeFile = (relativeFilePath, buffer) => {
|
export default function ProgmemGenerator({ outputPath = './WWWData.h', bytesPerLine = 20 }) {
|
||||||
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
|
return {
|
||||||
const mimeType = mime.lookup(relativeFilePath);
|
name: 'ProgmemGenerator',
|
||||||
var size = 0;
|
writeBundle: () => {
|
||||||
writeStream.write('const uint8_t ' + variable + '[] = {');
|
console.log('Generating ' + outputPath);
|
||||||
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
|
const includes = ARDUINO_INCLUDES;
|
||||||
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
|
const indent = INDENT;
|
||||||
|
const fileInfo = [];
|
||||||
|
const writeStream = cleanAndOpen(resolve(outputPath));
|
||||||
|
|
||||||
// create sha
|
try {
|
||||||
const hashSum = crypto.createHash('sha256');
|
const writeIncludes = () => {
|
||||||
hashSum.update(zipBuffer);
|
writeStream.write(includes);
|
||||||
const hash = hashSum.digest('hex');
|
};
|
||||||
|
|
||||||
zipBuffer.forEach((b) => {
|
const writeFile = (relativeFilePath, buffer) => {
|
||||||
if (!(size % bytesPerLine)) {
|
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
|
||||||
writeStream.write('\n');
|
const mimeType = mime.lookup(relativeFilePath);
|
||||||
writeStream.write(indent);
|
var size = 0;
|
||||||
}
|
writeStream.write('const uint8_t ' + variable + '[] = {');
|
||||||
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ',');
|
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
|
||||||
size++;
|
const zipBuffer = zlib.gzipSync(buffer);
|
||||||
});
|
zipBuffer.forEach((b) => {
|
||||||
|
if (!(size % bytesPerLine)) {
|
||||||
|
writeStream.write('\n');
|
||||||
|
writeStream.write(indent);
|
||||||
|
}
|
||||||
|
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).substr(-2) + ',');
|
||||||
|
size++;
|
||||||
|
});
|
||||||
|
if (size % bytesPerLine) {
|
||||||
|
writeStream.write('\n');
|
||||||
|
}
|
||||||
|
writeStream.write('};\n\n');
|
||||||
|
fileInfo.push({
|
||||||
|
uri: '/' + relativeFilePath.replace(sep, '/'),
|
||||||
|
mimeType,
|
||||||
|
variable,
|
||||||
|
size
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (size % bytesPerLine) {
|
const writeFiles = () => {
|
||||||
writeStream.write('\n');
|
// process static files
|
||||||
}
|
const buildPath = resolve('build');
|
||||||
|
for (const filePath of getFilesSync(buildPath)) {
|
||||||
|
const readStream = readFileSync(filePath);
|
||||||
|
const relativeFilePath = relative(buildPath, filePath);
|
||||||
|
writeFile(relativeFilePath, readStream);
|
||||||
|
}
|
||||||
|
|
||||||
writeStream.write('};\n\n');
|
// process assets
|
||||||
|
// const { assets } = compilation;
|
||||||
|
// Object.keys(assets).forEach((relativeFilePath) => {
|
||||||
|
// writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source()));
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
fileInfo.push({
|
const generateWWWClass = () =>
|
||||||
uri: '/' + relativeFilePath.replace(sep, '/'),
|
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
|
||||||
mimeType,
|
|
||||||
variable,
|
|
||||||
size,
|
|
||||||
hash
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
|
class WWWData {
|
||||||
totalSize += size;
|
${indent}public:
|
||||||
|
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
|
||||||
|
${fileInfo
|
||||||
|
.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`)
|
||||||
|
.join('\n')}
|
||||||
|
${indent.repeat(2)}}
|
||||||
};
|
};
|
||||||
|
`;
|
||||||
|
const writeWWWClass = () => {
|
||||||
|
writeStream.write(generateWWWClass());
|
||||||
|
};
|
||||||
|
|
||||||
// start
|
writeIncludes();
|
||||||
console.log('Generating ' + outputPath + ' from ' + sourcePath);
|
writeFiles();
|
||||||
const includes = ARDUINO_INCLUDES;
|
writeWWWClass();
|
||||||
const indent = INDENT;
|
|
||||||
const fileInfo = [];
|
|
||||||
const writeStream = cleanAndOpen(resolve(outputPath));
|
|
||||||
|
|
||||||
// includes
|
writeStream.on('finish', () => {
|
||||||
writeStream.write(includes);
|
// callback();
|
||||||
|
});
|
||||||
// process static files
|
} finally {
|
||||||
const buildPath = resolve(sourcePath);
|
writeStream.end();
|
||||||
for (const filePath of getFilesSync(buildPath)) {
|
}
|
||||||
const readStream = readFileSync(filePath);
|
}
|
||||||
const relativeFilePath = relative(buildPath, filePath);
|
};
|
||||||
writeFile(relativeFilePath, readStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add class
|
|
||||||
writeStream.write(generateWWWClass());
|
|
||||||
|
|
||||||
// end
|
|
||||||
writeStream.end();
|
|
||||||
|
|
||||||
console.log('Total size: ' + totalSize / 1000 + ' KB');
|
|
||||||
|
|||||||
Binary file not shown.
@@ -4,6 +4,7 @@ import { ToastContainer, Slide } from 'react-toastify';
|
|||||||
import 'react-toastify/dist/ReactToastify.min.css';
|
import 'react-toastify/dist/ReactToastify.min.css';
|
||||||
|
|
||||||
import { localStorageDetector } from 'typesafe-i18n/detectors';
|
import { localStorageDetector } from 'typesafe-i18n/detectors';
|
||||||
|
import { FeaturesLoader } from './contexts/features';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import AppRouting from 'AppRouting';
|
import AppRouting from 'AppRouting';
|
||||||
import CustomTheme from 'CustomTheme';
|
import CustomTheme from 'CustomTheme';
|
||||||
@@ -26,9 +27,11 @@ const App: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<TypesafeI18n locale={detectedLocale}>
|
<TypesafeI18n locale={detectedLocale}>
|
||||||
<CustomTheme>
|
<CustomTheme>
|
||||||
<AppRouting />
|
<FeaturesLoader>
|
||||||
|
<AppRouting />
|
||||||
|
</FeaturesLoader>
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="bottom-right"
|
position="bottom-left"
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
hideProgressBar={false}
|
hideProgressBar={false}
|
||||||
newestOnTop={false}
|
newestOnTop={false}
|
||||||
|
|||||||
@@ -1,55 +1,64 @@
|
|||||||
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 { Layout } from 'components';
|
import Settings from './project/Settings';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import type { FC } from 'react';
|
||||||
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 Network from 'framework/network/Network';
|
import NetworkConnection from 'framework/network/NetworkConnection';
|
||||||
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 { me } = useContext(AuthenticatedContext);
|
// const location = useLocation();
|
||||||
return (
|
// const navigate = useNavigate();
|
||||||
<Layout>
|
// const handleApiResponseError = useCallback(
|
||||||
<Routes>
|
// (error: AxiosError) => {
|
||||||
<Route path="/devices/*" element={<Devices />} />
|
// if (error.response && error.response.status === 401) {
|
||||||
<Route path="/sensors/*" element={<Sensors />} />
|
// AuthenticationApi.storeLoginRedirect(location);
|
||||||
<Route path="/system/*" element={<System />} />
|
// navigate('/unauthorized');
|
||||||
<Route path="/help/*" element={<Help />} />
|
// }
|
||||||
<Route path="/*" element={<Navigate to="/" />} />
|
// return Promise.reject(error);
|
||||||
{me.admin && (
|
// },
|
||||||
<>
|
// [location, navigate]
|
||||||
<Route path="/customizations/*" element={<Customization />} />
|
// );
|
||||||
<Route path="/scheduler/*" element={<Scheduler />} />
|
// useEffect(() => {
|
||||||
<Route path="/customentities/*" element={<CustomEntities />} />
|
// const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
|
||||||
<Route path="/settings/*" element={<Settings />} />
|
// return () => AXIOS.interceptors.response.eject(axiosHandlerId);
|
||||||
<Route path="/settings/network/*" element={<Network />} />
|
// }, [handleApiResponseError]);
|
||||||
<Route path="/settings/ems-esp/*" element={<ApplicationSettings />} />
|
|
||||||
<Route path="/settings/ap/*" element={<AccessPoint />} />
|
<Layout>
|
||||||
<Route path="/settings/ntp/*" element={<NetworkTime />} />
|
<Routes>
|
||||||
<Route path="/settings/mqtt/*" element={<Mqtt />} />
|
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||||
<Route path="/settings/ota/*" element={<OTASettings />} />
|
<Route
|
||||||
<Route path="/settings/security/*" element={<Security />} />
|
path="/settings/*"
|
||||||
<Route path="/settings/espsystemstatus/*" element={<ESPSystemStatus />} />
|
element={
|
||||||
<Route path="/settings/upload/*" element={<UploadDownload />} />
|
<RequireAdmin>
|
||||||
</>
|
<Settings />
|
||||||
)}
|
</RequireAdmin>
|
||||||
</Routes>
|
}
|
||||||
</Layout>
|
/>
|
||||||
);
|
<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="/*" element={<Navigate to="/" />} />
|
||||||
|
</Routes>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
|
||||||
export default AuthenticatedRouting;
|
export default AuthenticatedRouting;
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ const theme = responsiveFontSizes(
|
|||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
main: '#607d8b' // blueGrey[500]
|
main: '#607d8b' // blueGrey[500]
|
||||||
},
|
|
||||||
text: {
|
|
||||||
disabled: '#eee' // white
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Box, Paper, Typography, MenuItem, TextField, Button } from '@mui/materi
|
|||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import { FeaturesContext } from './contexts/features';
|
||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
|
|
||||||
import type { Locales } from 'i18n/i18n-types';
|
import type { Locales } from 'i18n/i18n-types';
|
||||||
@@ -14,16 +15,15 @@ import { PROJECT_NAME } from 'api/env';
|
|||||||
import { ValidatedPasswordField, ValidatedTextField } from 'components';
|
import { ValidatedPasswordField, ValidatedTextField } from 'components';
|
||||||
import { AuthenticationContext } from 'contexts/authentication';
|
import { AuthenticationContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import DEflag from 'i18n/DE.svg';
|
import { ReactComponent as DEflag } from 'i18n/DE.svg';
|
||||||
import FRflag from 'i18n/FR.svg';
|
import { ReactComponent as FRflag } from 'i18n/FR.svg';
|
||||||
import GBflag from 'i18n/GB.svg';
|
import { ReactComponent as GBflag } from 'i18n/GB.svg';
|
||||||
import ITflag from 'i18n/IT.svg';
|
import { ReactComponent as ITflag } from 'i18n/IT.svg';
|
||||||
import NLflag from 'i18n/NL.svg';
|
import { ReactComponent as NLflag } from 'i18n/NL.svg';
|
||||||
import NOflag from 'i18n/NO.svg';
|
import { ReactComponent as NOflag } from 'i18n/NO.svg';
|
||||||
import PLflag from 'i18n/PL.svg';
|
import { ReactComponent as PLflag } from 'i18n/PL.svg';
|
||||||
import SKflag from 'i18n/SK.svg';
|
import { ReactComponent as SVflag } from 'i18n/SV.svg';
|
||||||
import SVflag from 'i18n/SV.svg';
|
import { ReactComponent as TRflag } from 'i18n/TR.svg';
|
||||||
import TRflag from 'i18n/TR.svg';
|
|
||||||
import { I18nContext } from 'i18n/i18n-react';
|
import { I18nContext } from 'i18n/i18n-react';
|
||||||
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
||||||
import { onEnterCallback, updateValue } from 'utils';
|
import { onEnterCallback, updateValue } from 'utils';
|
||||||
@@ -34,6 +34,8 @@ const SignIn: FC = () => {
|
|||||||
|
|
||||||
const { LL, setLocale, locale } = useContext(I18nContext);
|
const { LL, setLocale, locale } = useContext(I18nContext);
|
||||||
|
|
||||||
|
const { features } = useContext(FeaturesContext);
|
||||||
|
|
||||||
const [signInRequest, setSignInRequest] = useState<SignInRequest>({
|
const [signInRequest, setSignInRequest] = useState<SignInRequest>({
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: ''
|
||||||
@@ -109,46 +111,43 @@ const SignIn: FC = () => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Typography variant="h4">{PROJECT_NAME}</Typography>
|
<Typography variant="h4">{PROJECT_NAME}</Typography>
|
||||||
|
<Typography variant="subtitle2">{features.version}</Typography>
|
||||||
|
|
||||||
<TextField name="locale" variant="outlined" value={locale} onChange={onLocaleSelected} size="small" select>
|
<TextField name="locale" variant="outlined" value={locale} onChange={onLocaleSelected} size="small" select>
|
||||||
<MenuItem key="de" value="de">
|
<MenuItem key="de" value="de">
|
||||||
<img src={DEflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<DEflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
DE
|
DE
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="en" value="en">
|
<MenuItem key="en" value="en">
|
||||||
<img src={GBflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<GBflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
EN
|
EN
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="fr" value="fr">
|
<MenuItem key="fr" value="fr">
|
||||||
<img src={FRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<FRflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
FR
|
FR
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="it" value="it">
|
<MenuItem key="it" value="it">
|
||||||
<img src={ITflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<ITflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
IT
|
IT
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="nl" value="nl">
|
<MenuItem key="nl" value="nl">
|
||||||
<img src={NLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<NLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
NL
|
NL
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="no" value="no">
|
<MenuItem key="no" value="no">
|
||||||
<img src={NOflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<NOflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
NO
|
NO
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="pl" value="pl">
|
<MenuItem key="pl" value="pl">
|
||||||
<img src={PLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<PLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
PL
|
PL
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="sk" value="sk">
|
|
||||||
<img src={SKflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
||||||
SK
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="sv" value="sv">
|
<MenuItem key="sv" value="sv">
|
||||||
<img src={SVflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<SVflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
SV
|
SV
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key="tr" value="tr">
|
<MenuItem key="tr" value="tr">
|
||||||
<img src={TRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
<TRflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
TR
|
TR
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { jwtDecode } from 'jwt-decode';
|
import jwtDecode from 'jwt-decode';
|
||||||
import { ACCESS_TOKEN, alovaInstance } from './endpoints';
|
import { ACCESS_TOKEN, alovaInstance } from './endpoints';
|
||||||
import type * as H from 'history';
|
import type * as H from 'history';
|
||||||
import type { Path } from 'react-router-dom';
|
import type { Path } from 'react-router-dom';
|
||||||
@@ -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 || `/devices`,
|
pathname: signInPathname || `/dashboard`,
|
||||||
search: (signInPathname && signInSearch) || undefined
|
search: (signInPathname && signInSearch) || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
5
interface/src/api/features.ts
Normal file
5
interface/src/api/features.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { alovaInstance } from './endpoints';
|
||||||
|
|
||||||
|
import type { Features } from 'types';
|
||||||
|
|
||||||
|
export const readFeatures = () => alovaInstance.Get<Features>('/rest/features');
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { alovaInstance } from './endpoints';
|
import { alovaInstance } from './endpoints';
|
||||||
import type { MqttSettingsType, MqttStatusType } from 'types';
|
import type { MqttSettings, MqttStatus } from 'types';
|
||||||
|
|
||||||
export const readMqttStatus = () => alovaInstance.Get<MqttStatusType>('/rest/mqttStatus');
|
export const readMqttStatus = () => alovaInstance.Get<MqttStatus>('/rest/mqttStatus');
|
||||||
export const readMqttSettings = () => alovaInstance.Get<MqttSettingsType>('/rest/mqttSettings');
|
export const readMqttSettings = () => alovaInstance.Get<MqttSettings>('/rest/mqttSettings');
|
||||||
export const updateMqttSettings = (data: MqttSettingsType) =>
|
export const updateMqttSettings = (data: MqttSettings) => alovaInstance.Post<MqttSettings>('/rest/mqttSettings', data);
|
||||||
alovaInstance.Post<MqttSettingsType>('/rest/mqttSettings', data);
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
||||||
import type { OTASettings, SystemStatus, LogSettings, ESPSystemStatus } from 'types';
|
import type { OTASettings, SystemStatus, LogSettings } from 'types';
|
||||||
|
|
||||||
// ESPSystemStatus - also used to ping in Restart monitor for pinging
|
// SystemStatus - 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
|
||||||
|
|||||||
@@ -498,8 +498,8 @@ function createStructureReader(structure, firstId) {
|
|||||||
key === '__proto__'
|
key === '__proto__'
|
||||||
? '__proto_:r()'
|
? '__proto_:r()'
|
||||||
: validName.test(key)
|
: validName.test(key)
|
||||||
? key + ':r()'
|
? key + ':r()'
|
||||||
: '[' + JSON.stringify(key) + ']:r()'
|
: '[' + JSON.stringify(key) + ']:r()'
|
||||||
)
|
)
|
||||||
.join(',') +
|
.join(',') +
|
||||||
'})}'
|
'})}'
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,9 +13,7 @@ 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,5 +1,6 @@
|
|||||||
import MenuIcon from '@mui/icons-material/Menu';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import { AppBar, IconButton, Toolbar, Typography } from '@mui/material';
|
import { AppBar, Box, 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;
|
||||||
@@ -26,6 +27,8 @@ 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>
|
||||||
);
|
);
|
||||||
|
|||||||
161
interface/src/components/layout/LayoutAuthMenu.tsx
Normal file
161
interface/src/components/layout/LayoutAuthMenu.tsx
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
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 { ReactComponent as DEflag } from 'i18n/DE.svg';
|
||||||
|
import { ReactComponent as FRflag } from 'i18n/FR.svg';
|
||||||
|
import { ReactComponent as GBflag } from 'i18n/GB.svg';
|
||||||
|
import { ReactComponent as ITflag } from 'i18n/IT.svg';
|
||||||
|
import { ReactComponent as NLflag } from 'i18n/NL.svg';
|
||||||
|
import { ReactComponent as NOflag } from 'i18n/NO.svg';
|
||||||
|
import { ReactComponent as PLflag } from 'i18n/PL.svg';
|
||||||
|
import { ReactComponent as SVflag } from 'i18n/SV.svg';
|
||||||
|
import { ReactComponent as 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">
|
||||||
|
<DEflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
DE
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="en" value="en">
|
||||||
|
<GBflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
EN
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="fr" value="fr">
|
||||||
|
<FRflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
FR
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="it" value="it">
|
||||||
|
<ITflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
IT
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="nl" value="nl">
|
||||||
|
<NLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="no" value="no">
|
||||||
|
<NOflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
NO
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="pl" value="pl">
|
||||||
|
<PLflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
PL
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="sv" value="sv">
|
||||||
|
<SVflag style={{ width: 16, verticalAlign: 'middle' }} />
|
||||||
|
SV
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key="tr" value="tr">
|
||||||
|
<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,8 +1,6 @@
|
|||||||
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,254 +1,53 @@
|
|||||||
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
|
||||||
import CategoryIcon from '@mui/icons-material/Category';
|
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||||
import ConstructionIcon from '@mui/icons-material/Construction';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
import LiveHelpIcon from '@mui/icons-material/LiveHelp';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
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 DEflag from 'i18n/DE.svg';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
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 { me, signOut } = useContext(AuthenticatedContext);
|
const authenticatedContext = useContext(AuthenticatedContext);
|
||||||
const { locale, LL, setLocale } = useContext(I18nContext);
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
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 component="nav">
|
<List disablePadding component="nav">
|
||||||
<LayoutMenuItem icon={CategoryIcon} label={LL.DEVICES()} to={`/devices`} />
|
<LayoutMenuItem icon={DashboardIcon} label={LL.DASHBOARD()} to={`/dashboard`} />
|
||||||
<LayoutMenuItem icon={SensorsIcon} label={LL.SENSORS()} to={`/sensors`} />
|
<LayoutMenuItem
|
||||||
|
icon={TuneIcon}
|
||||||
|
label={LL.SETTINGS_OF('')}
|
||||||
|
to={`/settings`}
|
||||||
|
disabled={!authenticatedContext.me.admin}
|
||||||
|
/>
|
||||||
|
<LayoutMenuItem icon={InfoIcon} label={LL.HELP_OF('')} to={`/help`} />
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
bgcolor: menuOpen ? 'rgba(71, 98, 130, 0.2)' : null,
|
|
||||||
pb: menuOpen ? 2 : 0
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemButton
|
|
||||||
alignItems="flex-start"
|
|
||||||
onClick={() => setMenuOpen(!menuOpen)}
|
|
||||||
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 }}
|
|
||||||
/>
|
|
||||||
<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 disablePadding component="nav">
|
||||||
<List style={{ marginTop: `auto` }}>
|
<LayoutMenuItem icon={SettingsEthernetIcon} label={LL.NETWORK(0)} to="/network" />
|
||||||
<LayoutMenuItem icon={AssessmentIcon} label={LL.SYSTEM(0)} to="/system" />
|
<LayoutMenuItem icon={SettingsInputAntennaIcon} label={LL.ACCESS_POINT(0)} to="/ap" />
|
||||||
<LayoutMenuItem icon={SettingsIcon} label={LL.SETTINGS(0)} disabled={!me.admin} to="/settings" />
|
<LayoutMenuItem icon={AccessTimeIcon} label="NTP" to="/ntp" />
|
||||||
<LayoutMenuItem icon={LiveHelpIcon} label={LL.HELP_OF('')} to={`/help`} />
|
<LayoutMenuItem icon={DeviceHubIcon} label="MQTT" to="/mqtt" />
|
||||||
|
<LayoutMenuItem
|
||||||
|
icon={LockIcon}
|
||||||
|
label={LL.SECURITY(0)}
|
||||||
|
to="/security"
|
||||||
|
disabled={!authenticatedContext.me.admin}
|
||||||
|
/>
|
||||||
|
<LayoutMenuItem icon={SettingsIcon} label={LL.SYSTEM(0)} to="/system" />
|
||||||
</List>
|
</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 { ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
import { ListItem, 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,12 +18,14 @@ const LayoutMenuItem: FC<LayoutMenuItemProps> = ({ icon: Icon, label, to, disabl
|
|||||||
const selected = routeMatches(to, pathname);
|
const selected = routeMatches(to, pathname);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
|
<ListItem disablePadding>
|
||||||
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
|
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
|
||||||
<Icon />
|
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
|
||||||
</ListItemIcon>
|
<Icon />
|
||||||
<ListItemText sx={{ color: selected ? '#90caf9' : '#f5f5f5' }}>{label}</ListItemText>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
<ListItemText sx={{ color: selected ? '#90caf9' : '#f5f5f5' }}>{label}</ListItemText>
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -14,7 +14,7 @@ const RouterTabs: FC<RouterTabsProps> = ({ value, children }) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const smallDown = useMediaQuery(theme.breakpoints.down('sm'));
|
const smallDown = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
|
||||||
const handleTabChange = (_event: any, path: string) => {
|
const handleTabChange = (event: React.ChangeEvent<HTMLInputElement>, path: string) => {
|
||||||
navigate(path);
|
navigate(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useMatch, useResolvedPath } from 'react-router-dom';
|
import { useMatch, useResolvedPath } from 'react-router-dom';
|
||||||
|
|
||||||
export const useRouterTab = () => {
|
export const useRouterTab = () => {
|
||||||
const routerTabPathMatch = useMatch(useResolvedPath(':tab').pathname);
|
const routerTabPath = useResolvedPath(':tab');
|
||||||
const routerTab = routerTabPathMatch?.params?.tab || false;
|
const routerTabPathMatch = useMatch(routerTabPath.pathname);
|
||||||
|
|
||||||
|
const routerTab = routerTabPathMatch?.params?.tab || false;
|
||||||
return { routerTab } as const;
|
return { routerTab } as const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,10 +50,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
|||||||
|
|
||||||
const progressText = () => {
|
const progressText = () => {
|
||||||
if (uploading) {
|
if (uploading) {
|
||||||
if (progress.total && progress.loaded) {
|
if (progress.total) {
|
||||||
return progress.loaded <= progress.total
|
return LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%';
|
||||||
? LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%'
|
|
||||||
: LL.UPLOADING() + ': ' + Math.round((progress.total * 100) / progress.loaded) + '%';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return LL.UPLOAD_DROP_TEXT();
|
return LL.UPLOAD_DROP_TEXT();
|
||||||
@@ -63,7 +61,7 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
|||||||
<Box
|
<Box
|
||||||
{...getRootProps({
|
{...getRootProps({
|
||||||
sx: {
|
sx: {
|
||||||
py: 4,
|
py: 8,
|
||||||
px: 2,
|
px: 2,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
@@ -85,13 +83,7 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
|||||||
<Box width="100%" p={2}>
|
<Box width="100%" p={2}>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={
|
value={progress.total === 0 ? 0 : Math.round((progress.loaded * 100) / progress.total)}
|
||||||
progress.total === 0 || progress.loaded === 0
|
|
||||||
? 0
|
|
||||||
: progress.loaded <= progress.total
|
|
||||||
? Math.round((progress.loaded * 100) / progress.total)
|
|
||||||
: Math.round((progress.total * 100) / progress.loaded)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>
|
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { redirect } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { AuthenticationContext } from './context';
|
import { AuthenticationContext } from './context';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
@@ -15,6 +15,8 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
const [me, setMe] = useState<Me>();
|
const [me, setMe] = useState<Me>();
|
||||||
|
|
||||||
@@ -34,12 +36,11 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const signOut = (doRedirect: boolean) => {
|
const signOut = (redirect: boolean) => {
|
||||||
AuthenticationApi.clearAccessToken();
|
AuthenticationApi.clearAccessToken();
|
||||||
setMe(undefined);
|
setMe(undefined);
|
||||||
if (doRedirect) {
|
if (redirect) {
|
||||||
// navigate('/');
|
navigate('/');
|
||||||
redirect('/');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
25
interface/src/contexts/features/FeaturesLoader.tsx
Normal file
25
interface/src/contexts/features/FeaturesLoader.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useRequest } from 'alova';
|
||||||
|
|
||||||
|
import { FeaturesContext } from '.';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import type { RequiredChildrenProps } from 'utils';
|
||||||
|
import * as FeaturesApi from 'api/features';
|
||||||
|
|
||||||
|
const FeaturesLoader: FC<RequiredChildrenProps> = (props) => {
|
||||||
|
const { data: features } = useRequest(FeaturesApi.readFeatures);
|
||||||
|
|
||||||
|
if (features) {
|
||||||
|
return (
|
||||||
|
<FeaturesContext.Provider
|
||||||
|
value={{
|
||||||
|
features
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</FeaturesContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeaturesLoader;
|
||||||
10
interface/src/contexts/features/context.ts
Normal file
10
interface/src/contexts/features/context.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import type { Features } from 'types';
|
||||||
|
|
||||||
|
export interface FeaturesContextValue {
|
||||||
|
features: Features;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FeaturesContextDefaultValue = {} as FeaturesContextValue;
|
||||||
|
export const FeaturesContext = createContext(FeaturesContextDefaultValue);
|
||||||
2
interface/src/contexts/features/index.ts
Normal file
2
interface/src/contexts/features/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './context';
|
||||||
|
export { default as FeaturesLoader } from './FeaturesLoader';
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
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 { APSettingsType } from 'types';
|
import type { APSettings } 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 }: APSettingsType) =>
|
export const isAPEnabled = ({ provision_mode }: APSettings) =>
|
||||||
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 APSettings: FC = () => {
|
const APSettingsForm: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -39,7 +39,7 @@ const APSettings: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<APSettingsType>({
|
} = useRest<APSettings>({
|
||||||
read: APApi.readAPSettings,
|
read: APApi.readAPSettings,
|
||||||
update: APApi.updateAPSettings
|
update: APApi.updateAPSettings
|
||||||
});
|
});
|
||||||
@@ -205,11 +205,11 @@ const APSettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default APSettings;
|
export default APSettingsForm;
|
||||||
@@ -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 { APStatusType } from 'types';
|
import type { APStatus } 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 }: APStatusType, theme: Theme) => {
|
export const apStatusHighlight = ({ status }: APStatus, 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 }: APStatusType, theme: Theme) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const APStatus: FC = () => {
|
const APStatusForm: 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 }: APStatusType) => {
|
const apStatus = ({ status }: APStatus) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case APNetworkStatus.ACTIVE:
|
case APNetworkStatus.ACTIVE:
|
||||||
return LL.ACTIVE();
|
return LL.ACTIVE();
|
||||||
@@ -99,7 +99,11 @@ const APStatus: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <SectionContent>{content()}</SectionContent>;
|
return (
|
||||||
|
<SectionContent title={LL.STATUS_OF(LL.ACCESS_POINT(1))} titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default APStatus;
|
export default APStatusForm;
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
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 APSettings from './APSettings';
|
import APSettingsForm from './APSettingsForm';
|
||||||
import APStatus from './APStatus';
|
import APStatusForm from './APStatusForm';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -13,18 +15,28 @@ 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="settings" label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} />
|
|
||||||
<Tab value="status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} />
|
<Tab value="status" label={LL.STATUS_OF(LL.ACCESS_POINT(1))} />
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} disabled={!authenticatedContext.me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<APStatus />} />
|
<Route path="status" element={<APStatusForm />} />
|
||||||
<Route path="settings" element={<APSettings />} />
|
<Route index element={<Navigate to="status" />} />
|
||||||
<Route path="*" element={<Navigate replace to="settings" />} />
|
<Route
|
||||||
|
path="settings"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<APSettingsForm />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/*" element={<Navigate replace to="status" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
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 MqttSettings from './MqttSettings';
|
import MqttSettingsForm from './MqttSettingsForm';
|
||||||
import MqttStatus from './MqttStatus';
|
import MqttStatusForm from './MqttStatusForm';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -13,18 +15,26 @@ 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="settings" label={LL.SETTINGS_OF('MQTT')} />
|
|
||||||
<Tab value="status" label={LL.STATUS_OF('MQTT')} />
|
<Tab value="status" label={LL.STATUS_OF('MQTT')} />
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF('MQTT')} disabled={!authenticatedContext.me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<MqttStatus />} />
|
<Route path="status" element={<MqttStatusForm />} />
|
||||||
<Route path="settings" element={<MqttSettings />} />
|
<Route
|
||||||
<Route path="*" element={<Navigate replace to="settings" />} />
|
path="settings"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<MqttSettingsForm />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/*" element={<Navigate replace to="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 { MqttSettingsType } from 'types';
|
import type { MqttSettings } 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 MqttSettings: FC = () => {
|
const MqttSettingsForm: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -33,7 +33,7 @@ const MqttSettings: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<MqttSettingsType>({
|
} = useRest<MqttSettings>({
|
||||||
read: MqttApi.readMqttSettings,
|
read: MqttApi.readMqttSettings,
|
||||||
update: MqttApi.updateMqttSettings
|
update: MqttApi.updateMqttSettings
|
||||||
});
|
});
|
||||||
@@ -72,7 +72,6 @@ const MqttSettings: FC = () => {
|
|||||||
name="host"
|
name="host"
|
||||||
label={LL.ADDRESS_OF(LL.BROKER())}
|
label={LL.ADDRESS_OF(LL.BROKER())}
|
||||||
fullWidth
|
fullWidth
|
||||||
multiline
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.host}
|
value={data.host}
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
@@ -169,24 +168,20 @@ const MqttSettings: FC = () => {
|
|||||||
<MenuItem value={2}>2</MenuItem>
|
<MenuItem value={2}>2</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{data.rootCA !== undefined && (
|
||||||
|
<Grid item xs={12} sm={6}>
|
||||||
|
<ValidatedPasswordField
|
||||||
|
name="rootCA"
|
||||||
|
label={LL.CERT()}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.rootCA}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
{data.enableTLS !== undefined && (
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={<Checkbox name="enableTLS" checked={data.enableTLS} onChange={updateFormValue} />}
|
|
||||||
label={LL.ENABLE_TLS()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{data.enableTLS === true && (
|
|
||||||
<ValidatedPasswordField
|
|
||||||
name="rootCA"
|
|
||||||
label={LL.CERT()}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.rootCA}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />}
|
control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />}
|
||||||
@@ -274,7 +269,6 @@ const MqttSettings: FC = () => {
|
|||||||
>
|
>
|
||||||
<MenuItem value={0}>Home Assistant</MenuItem>
|
<MenuItem value={0}>Home Assistant</MenuItem>
|
||||||
<MenuItem value={1}>Domoticz</MenuItem>
|
<MenuItem value={1}>Domoticz</MenuItem>
|
||||||
<MenuItem value={2}>Domoticz (latest)</MenuItem>
|
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={4}>
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
@@ -388,21 +382,6 @@ const MqttSettings: 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"
|
||||||
@@ -464,11 +443,11 @@ const MqttSettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF('MQTT')} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MqttSettings;
|
export default MqttSettingsForm;
|
||||||
@@ -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 { MqttStatusType } from 'types';
|
import type { MqttStatus } 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 }: MqttStatusType, theme: Theme) => {
|
export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, 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 }: MqttStatusType, them
|
|||||||
return theme.palette.error.main;
|
return theme.palette.error.main;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatusType, theme: Theme) => {
|
export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, 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 }: MqttStatusType, theme: Theme) => {
|
export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatus, 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 MqttStatus: FC = () => {
|
const MqttStatusForm: 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 }: MqttStatusType) => {
|
const mqttStatus = ({ enabled, connected, connect_count }: MqttStatus) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return LL.NOT_ENABLED();
|
return LL.NOT_ENABLED();
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ const MqttStatus: FC = () => {
|
|||||||
return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
|
return LL.DISCONNECTED() + (connect_count > 1 ? ' (' + connect_count + ')' : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const disconnectReason = ({ disconnect_reason }: MqttStatusType) => {
|
const disconnectReason = ({ disconnect_reason }: MqttStatus) => {
|
||||||
switch (disconnect_reason) {
|
switch (disconnect_reason) {
|
||||||
case MqttDisconnectReason.TCP_DISCONNECTED:
|
case MqttDisconnectReason.TCP_DISCONNECTED:
|
||||||
return 'TCP disconnected';
|
return 'TCP disconnected';
|
||||||
@@ -69,7 +69,7 @@ const MqttStatus: FC = () => {
|
|||||||
case MqttDisconnectReason.MQTT_NOT_AUTHORIZED:
|
case MqttDisconnectReason.MQTT_NOT_AUTHORIZED:
|
||||||
return 'Not authorized';
|
return 'Not authorized';
|
||||||
case MqttDisconnectReason.TLS_BAD_FINGERPRINT:
|
case MqttDisconnectReason.TLS_BAD_FINGERPRINT:
|
||||||
return 'TLS fingerprint invalid';
|
return 'TSL fingerprint invalid';
|
||||||
default:
|
default:
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,11 @@ const MqttStatus: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <SectionContent>{content()}</SectionContent>;
|
return (
|
||||||
|
<SectionContent title={LL.STATUS_OF('MQTT')} titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MqttStatus;
|
export default MqttStatusForm;
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useContext, useState } from 'react';
|
||||||
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
|
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
import NetworkSettings from './NetworkSettings';
|
import NetworkSettingsForm from './NetworkSettingsForm';
|
||||||
import NetworkStatus from './NetworkStatus';
|
import NetworkStatusForm from './NetworkStatusForm';
|
||||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||||
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import type { WiFiNetwork } from 'types';
|
import type { WiFiNetwork } from 'types';
|
||||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const Network: FC = () => {
|
const NetworkConnection: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.NETWORK(0));
|
useLayoutTitle(LL.NETWORK(0));
|
||||||
|
|
||||||
const { routerTab } = useRouterTab();
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
|
const authenticatedContext = useContext(AuthenticatedContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>();
|
const [selectedNetwork, setSelectedNetwork] = useState<WiFiNetwork>();
|
||||||
@@ -42,18 +44,32 @@ const Network: FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RouterTabs value={routerTab}>
|
<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="status" label={LL.STATUS_OF(LL.NETWORK(1))} />
|
||||||
<Tab value="scan" label={LL.NETWORK_SCAN()} />
|
<Tab value="scan" label={LL.NETWORK_SCAN()} disabled={!authenticatedContext.me.admin} />
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.NETWORK(1))} disabled={!authenticatedContext.me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<NetworkStatus />} />
|
<Route path="status" element={<NetworkStatusForm />} />
|
||||||
<Route path="scan" element={<WiFiNetworkScanner />} />
|
<Route
|
||||||
<Route path="settings" element={<NetworkSettings />} />
|
path="scan"
|
||||||
<Route path="*" element={<Navigate replace to="settings" />} />
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<WiFiNetworkScanner />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="settings"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<NetworkSettingsForm />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/*" element={<Navigate replace to="status" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</WiFiConnectionContext.Provider>
|
</WiFiConnectionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Network;
|
export default NetworkConnection;
|
||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
ListItemSecondaryAction,
|
ListItemSecondaryAction,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Typography,
|
Typography,
|
||||||
TextField,
|
InputAdornment,
|
||||||
MenuItem
|
TextField
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
// eslint-disable-next-line import/named
|
// eslint-disable-next-line import/named
|
||||||
import { updateState, useRequest } from 'alova';
|
import { updateState, useRequest } from 'alova';
|
||||||
@@ -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 { NetworkSettingsType } from 'types';
|
import type { NetworkSettings } 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 {
|
||||||
@@ -43,12 +43,12 @@ import {
|
|||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
import { updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { createNetworkSettingsValidator } from 'validators/network';
|
import { createNetworkSettingsValidator } from 'validators/network';
|
||||||
|
|
||||||
const NetworkSettings: FC = () => {
|
const WiFiSettingsForm: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
||||||
@@ -68,7 +68,7 @@ const NetworkSettings: FC = () => {
|
|||||||
saveData,
|
saveData,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
restartNeeded
|
restartNeeded
|
||||||
} = useRest<NetworkSettingsType>({
|
} = useRest<NetworkSettings>({
|
||||||
read: NetworkApi.readNetworkSettings,
|
read: NetworkApi.readNetworkSettings,
|
||||||
update: NetworkApi.updateNetworkSettings
|
update: NetworkApi.updateNetworkSettings
|
||||||
});
|
});
|
||||||
@@ -82,13 +82,12 @@ const NetworkSettings: FC = () => {
|
|||||||
if (selectedNetwork) {
|
if (selectedNetwork) {
|
||||||
updateState('networkSettings', (current_data) => ({
|
updateState('networkSettings', (current_data) => ({
|
||||||
ssid: selectedNetwork.ssid,
|
ssid: selectedNetwork.ssid,
|
||||||
bssid: selectedNetwork.bssid,
|
password: '',
|
||||||
password: current_data ? current_data.password : '',
|
|
||||||
hostname: current_data?.hostname,
|
hostname: current_data?.hostname,
|
||||||
static_ip_config: false,
|
static_ip_config: false,
|
||||||
enableIPv6: false,
|
enableIPv6: false,
|
||||||
bandwidth20: false,
|
bandwidth20: false,
|
||||||
tx_power: 0,
|
tx_power: 20,
|
||||||
nosleep: false,
|
nosleep: false,
|
||||||
enableMDNS: true,
|
enableMDNS: true,
|
||||||
enableCORS: false,
|
enableCORS: false,
|
||||||
@@ -118,12 +117,6 @@ const NetworkSettings: FC = () => {
|
|||||||
} catch (errors: any) {
|
} catch (errors: any) {
|
||||||
setFieldErrors(errors);
|
setFieldErrors(errors);
|
||||||
}
|
}
|
||||||
deselectNetwork();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCancel = async () => {
|
|
||||||
deselectNetwork();
|
|
||||||
await loadData();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
@@ -135,7 +128,7 @@ const NetworkSettings: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h6" color="primary">
|
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||||
WiFi
|
WiFi
|
||||||
</Typography>
|
</Typography>
|
||||||
{selectedNetwork ? (
|
{selectedNetwork ? (
|
||||||
@@ -146,17 +139,10 @@ const NetworkSettings: FC = () => {
|
|||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={selectedNetwork.ssid}
|
primary={selectedNetwork.ssid}
|
||||||
secondary={
|
secondary={'Security: ' + networkSecurityMode(selectedNetwork) + ', Ch: ' + selectedNetwork.channel}
|
||||||
'Security: ' +
|
|
||||||
networkSecurityMode(selectedNetwork) +
|
|
||||||
', Ch: ' +
|
|
||||||
selectedNetwork.channel +
|
|
||||||
', bssid: ' +
|
|
||||||
selectedNetwork.bssid
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<IconButton onClick={setCancel}>
|
<IconButton onClick={deselectNetwork}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
@@ -174,16 +160,6 @@ const NetworkSettings: FC = () => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="bssid"
|
|
||||||
label={'BSSID (' + LL.NETWORK_BLANK_BSSID() + ')'}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.bssid}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
{(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && (
|
{(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && (
|
||||||
<ValidatedPasswordField
|
<ValidatedPasswordField
|
||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
@@ -196,29 +172,20 @@ const NetworkSettings: FC = () => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<TextField
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors}
|
||||||
name="tx_power"
|
name="tx_power"
|
||||||
label={LL.TX_POWER()}
|
label={LL.TX_POWER()}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">dBm</InputAdornment>
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.tx_power}
|
value={numberValue(data.tx_power)}
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
type="number"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
select
|
/>
|
||||||
>
|
|
||||||
<MenuItem value={0}>Auto</MenuItem>
|
|
||||||
<MenuItem value={78}>19.5 dBm</MenuItem>
|
|
||||||
<MenuItem value={76}>19 dBm</MenuItem>
|
|
||||||
<MenuItem value={74}>18.5 dBm</MenuItem>
|
|
||||||
<MenuItem value={68}>17 dBm</MenuItem>
|
|
||||||
<MenuItem value={60}>15 dBm</MenuItem>
|
|
||||||
<MenuItem value={52}>13 dBm</MenuItem>
|
|
||||||
<MenuItem value={44}>11 dBm</MenuItem>
|
|
||||||
<MenuItem value={34}>8.5 dBm</MenuItem>
|
|
||||||
<MenuItem value={28}>7 dBm</MenuItem>
|
|
||||||
<MenuItem value={20}>5 dBm</MenuItem>
|
|
||||||
<MenuItem value={8}>2 dBm</MenuItem>
|
|
||||||
</TextField>
|
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={<Checkbox name="nosleep" checked={data.nosleep} onChange={updateFormValue} />}
|
control={<Checkbox name="nosleep" checked={data.nosleep} onChange={updateFormValue} />}
|
||||||
label={LL.NETWORK_DISABLE_SLEEP()}
|
label={LL.NETWORK_DISABLE_SLEEP()}
|
||||||
@@ -259,12 +226,10 @@ const NetworkSettings: FC = () => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{data.enableIPv6 !== undefined && (
|
<BlockFormControlLabel
|
||||||
<BlockFormControlLabel
|
control={<Checkbox name="enableIPv6" checked={data.enableIPv6} onChange={updateFormValue} />}
|
||||||
control={<Checkbox name="enableIPv6" checked={data.enableIPv6} onChange={updateFormValue} />}
|
label={LL.NETWORK_ENABLE_IPV6()}
|
||||||
label={LL.NETWORK_ENABLE_IPV6()}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={<Checkbox name="static_ip_config" checked={data.static_ip_config} onChange={updateFormValue} />}
|
control={<Checkbox name="static_ip_config" checked={data.static_ip_config} onChange={updateFormValue} />}
|
||||||
label={LL.NETWORK_FIXED_IP()}
|
label={LL.NETWORK_FIXED_IP()}
|
||||||
@@ -324,14 +289,14 @@ const NetworkSettings: FC = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{restartNeeded && (
|
{restartNeeded && (
|
||||||
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT(0)}>
|
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
|
||||||
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
|
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
|
||||||
{LL.RESTART()}
|
{LL.RESTART()}
|
||||||
</Button>
|
</Button>
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!restartNeeded && (selectedNetwork || (dirtyFlags && dirtyFlags.length !== 0)) && (
|
{!restartNeeded && dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<CancelIcon />}
|
startIcon={<CancelIcon />}
|
||||||
@@ -360,11 +325,11 @@ const NetworkSettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF(LL.NETWORK(1))} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{restarting ? <RestartMonitor /> : content()}
|
{restarting ? <RestartMonitor /> : content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NetworkSettings;
|
export default WiFiSettingsForm;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import DnsIcon from '@mui/icons-material/Dns';
|
import DnsIcon from '@mui/icons-material/Dns';
|
||||||
import GiteIcon from '@mui/icons-material/Gite';
|
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import RouterIcon from '@mui/icons-material/Router';
|
import RouterIcon from '@mui/icons-material/Router';
|
||||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||||
@@ -11,18 +10,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 { NetworkStatusType } from 'types';
|
import type { NetworkStatus } 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 }: NetworkStatusType) =>
|
const isConnected = ({ status }: NetworkStatus) =>
|
||||||
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
||||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||||
|
|
||||||
const networkStatusHighlight = ({ status }: NetworkStatusType, theme: Theme) => {
|
const networkStatusHighlight = ({ status }: NetworkStatus, 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,27 +38,17 @@ const networkStatusHighlight = ({ status }: NetworkStatusType, theme: Theme) =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const networkQualityHighlight = ({ rssi }: NetworkStatusType, theme: Theme) => {
|
export const isWiFi = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
||||||
if (rssi <= -85) {
|
export const isEthernet = ({ status }: NetworkStatus) => status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||||
return theme.palette.error.main;
|
|
||||||
} else if (rssi <= -75) {
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
}
|
|
||||||
return theme.palette.success.main;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isWiFi = ({ status }: NetworkStatusType) => status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatus) => {
|
||||||
export const isEthernet = ({ status }: NetworkStatusType) =>
|
|
||||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
|
||||||
|
|
||||||
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: NetworkStatusType) => {
|
const IPs = (status: NetworkStatus) => {
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -69,14 +58,14 @@ const IPs = (status: NetworkStatusType) => {
|
|||||||
return status.local_ip + ', ' + status.local_ipv6;
|
return status.local_ip + ', ' + status.local_ipv6;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NetworkStatus: FC = () => {
|
const NetworkStatusForm: 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 }: NetworkStatusType) => {
|
const networkStatus = ({ status }: NetworkStatus) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
||||||
return LL.INACTIVE(1);
|
return LL.INACTIVE(1);
|
||||||
@@ -117,24 +106,15 @@ const NetworkStatus: FC = () => {
|
|||||||
<ListItemText primary="Status" secondary={networkStatus(data)} />
|
<ListItemText primary="Status" secondary={networkStatus(data)} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar sx={{ bgcolor: networkStatusHighlight(data, theme) }}>
|
|
||||||
<GiteIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="Hostname" secondary={data.hostname} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
{isWiFi(data) && (
|
{isWiFi(data) && (
|
||||||
<>
|
<>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar sx={{ bgcolor: networkQualityHighlight(data, theme) }}>
|
<Avatar>
|
||||||
<SettingsInputAntennaIcon />
|
<SettingsInputAntennaIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="SSID (RSSI)" secondary={data.ssid + ' (' + data.rssi + ' dBm)'} />
|
<ListItemText primary="SSID" secondary={data.ssid} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
</>
|
</>
|
||||||
@@ -194,7 +174,11 @@ const NetworkStatus: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <SectionContent>{content()}</SectionContent>;
|
return (
|
||||||
|
<SectionContent title={LL.STATUS_OF(LL.NETWORK(1))} titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NetworkStatus;
|
export default NetworkStatusForm;
|
||||||
@@ -56,7 +56,7 @@ const WiFiNetworkScanner: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.NETWORK_SCANNER()}>
|
||||||
{renderNetworkScanner()}
|
{renderNetworkScanner()}
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import LockOpenIcon from '@mui/icons-material/LockOpen';
|
import LockOpenIcon from '@mui/icons-material/LockOpen';
|
||||||
import WifiIcon from '@mui/icons-material/Wifi';
|
import WifiIcon from '@mui/icons-material/Wifi';
|
||||||
import { Avatar, Badge, List, ListItem, ListItemAvatar, ListItemIcon, ListItemText, useTheme } from '@mui/material';
|
import { Avatar, Badge, List, ListItem, ListItemAvatar, ListItemIcon, ListItemText } from '@mui/material';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||||
import type { Theme } from '@mui/material';
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { WiFiNetwork, WiFiNetworkList } from 'types';
|
import type { WiFiNetwork, WiFiNetworkList } from 'types';
|
||||||
import { MessageBox } from 'components';
|
import { MessageBox } from 'components';
|
||||||
@@ -43,18 +42,8 @@ export const networkSecurityMode = ({ encryption_type }: WiFiNetwork) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const networkQualityHighlight = ({ rssi }: WiFiNetwork, theme: Theme) => {
|
|
||||||
if (rssi <= -85) {
|
|
||||||
return theme.palette.error.main;
|
|
||||||
} else if (rssi <= -75) {
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
}
|
|
||||||
return theme.palette.success.main;
|
|
||||||
};
|
|
||||||
|
|
||||||
const WiFiNetworkSelector: FC<WiFiNetworkSelectorProps> = ({ networkList }) => {
|
const WiFiNetworkSelector: FC<WiFiNetworkSelectorProps> = ({ networkList }) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const wifiConnectionContext = useContext(WiFiConnectionContext);
|
const wifiConnectionContext = useContext(WiFiConnectionContext);
|
||||||
|
|
||||||
@@ -65,13 +54,11 @@ const WiFiNetworkSelector: FC<WiFiNetworkSelectorProps> = ({ networkList }) => {
|
|||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={network.ssid}
|
primary={network.ssid}
|
||||||
secondary={
|
secondary={'Security: ' + networkSecurityMode(network) + ', Ch: ' + network.channel}
|
||||||
'Security: ' + networkSecurityMode(network) + ', Ch: ' + network.channel + ', bssid: ' + network.bssid
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Badge badgeContent={network.rssi + 'dBm'}>
|
<Badge badgeContent={network.rssi + 'db'}>
|
||||||
<WifiIcon sx={{ color: networkQualityHighlight(network, theme) }} />
|
<WifiIcon />
|
||||||
</Badge>
|
</Badge>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|||||||
@@ -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 { NTPSettingsType } from 'types';
|
import type { NTPSettings } 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 NTPSettings: FC = () => {
|
const NTPSettingsForm: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saving,
|
saving,
|
||||||
@@ -35,7 +35,7 @@ const NTPSettings: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<NTPSettingsType>({
|
} = useRest<NTPSettings>({
|
||||||
read: NTPApi.readNTPSettings,
|
read: NTPApi.readNTPSettings,
|
||||||
update: NTPApi.updateNTPSettings
|
update: NTPApi.updateNTPSettings
|
||||||
});
|
});
|
||||||
@@ -130,11 +130,11 @@ const NTPSettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF('NTP')} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NTPSettings;
|
export default NTPSettingsForm;
|
||||||
@@ -22,26 +22,44 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useRequest } from 'alova';
|
import { useRequest } from 'alova';
|
||||||
import { useState } from 'react';
|
import { useContext, 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 { NTPStatusType } from 'types';
|
import type { NTPStatus } 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';
|
||||||
|
|
||||||
const NTPStatus: FC = () => {
|
export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE;
|
||||||
|
export const isNtpEnabled = ({ status }: NTPStatus) => status !== NTPSyncStatus.NTP_DISABLED;
|
||||||
|
|
||||||
|
export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => {
|
||||||
|
switch (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 NTPStatusForm: FC = () => {
|
||||||
const { data: data, send: loadData, error } = useRequest(NTPApi.readNTPStatus);
|
const { data: data, send: loadData, error } = useRequest(NTPApi.readNTPStatus);
|
||||||
|
|
||||||
const [localTime, setLocalTime] = useState<string>('');
|
const [localTime, setLocalTime] = useState<string>('');
|
||||||
const [settingTime, setSettingTime] = useState<boolean>(false);
|
const [settingTime, setSettingTime] = useState<boolean>(false);
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
@@ -51,22 +69,6 @@ const NTPStatus: FC = () => {
|
|||||||
|
|
||||||
NTPApi.updateTime;
|
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) {
|
|
||||||
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 updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => setLocalTime(event.target.value);
|
const updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => setLocalTime(event.target.value);
|
||||||
|
|
||||||
const openSetTime = () => {
|
const openSetTime = () => {
|
||||||
@@ -76,7 +78,7 @@ const NTPStatus: FC = () => {
|
|||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const ntpStatus = ({ status }: NTPStatusType) => {
|
const ntpStatus = ({ status }: NTPStatus) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NTPSyncStatus.NTP_DISABLED:
|
case NTPSyncStatus.NTP_DISABLED:
|
||||||
return LL.NOT_ENABLED();
|
return LL.NOT_ENABLED();
|
||||||
@@ -199,7 +201,7 @@ const NTPStatus: FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Box>
|
</Box>
|
||||||
{data && !isNtpActive(data) && (
|
{me.admin && 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 />}>
|
||||||
@@ -214,7 +216,11 @@ const NTPStatus: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <SectionContent>{content()}</SectionContent>;
|
return (
|
||||||
|
<SectionContent title={LL.STATUS_OF('NTP')} titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NTPStatus;
|
export default NTPStatusForm;
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
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 NTPSettings from './NTPSettings';
|
import NTPSettingsForm from './NTPSettingsForm';
|
||||||
import NTPStatus from './NTPStatus';
|
import NTPStatusForm from './NTPStatusForm';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
@@ -12,18 +14,26 @@ 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="settings" label={LL.SETTINGS_OF('NTP')} />
|
|
||||||
<Tab value="status" label={LL.STATUS_OF('NTP')} />
|
<Tab value="status" label={LL.STATUS_OF('NTP')} />
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF('NTP')} disabled={!authenticatedContext.me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<NTPStatus />} />
|
<Route path="status" element={<NTPStatusForm />} />
|
||||||
<Route path="settings" element={<NTPSettings />} />
|
<Route
|
||||||
<Route path="*" element={<Navigate replace to="settings" />} />
|
path="settings"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<NTPSettingsForm />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/*" element={<Navigate replace to="status" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
|
|||||||
if (open) {
|
if (open) {
|
||||||
void generateToken();
|
void generateToken();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [open, generateToken]);
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog sx={dialogStyle} onClose={onClose} open={!!username} fullWidth maxWidth="sm">
|
<Dialog sx={dialogStyle} onClose={onClose} open={!!username} fullWidth maxWidth="sm">
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-li
|
|||||||
import { useTheme } from '@table-library/react-table-library/theme';
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
|
|
||||||
import { useBlocker } from 'react-router-dom';
|
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
||||||
import GenerateToken from './GenerateToken';
|
import GenerateToken from './GenerateToken';
|
||||||
import User from './User';
|
import UserForm from './UserForm';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { SecuritySettingsType, UserType } from 'types';
|
import type { SecuritySettings, User } 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 ManageUsers: FC = () => {
|
const ManageUsersForm: FC = () => {
|
||||||
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettingsType>({
|
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettings>({
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
|
|
||||||
const [user, setUser] = useState<UserType>();
|
const [user, setUser] = useState<User>();
|
||||||
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 ManageUsers: FC = () => {
|
|||||||
|
|
||||||
const noAdminConfigured = () => !data.users.find((u) => u.admin);
|
const noAdminConfigured = () => !data.users.find((u) => u.admin);
|
||||||
|
|
||||||
const removeUser = (toRemove: UserType) => {
|
const removeUser = (toRemove: User) => {
|
||||||
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 ManageUsers: FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const editUser = (toEdit: UserType) => {
|
const editUser = (toEdit: User) => {
|
||||||
setCreating(false);
|
setCreating(false);
|
||||||
setUser({ ...toEdit });
|
setUser({ ...toEdit });
|
||||||
};
|
};
|
||||||
@@ -219,7 +219,7 @@ const ManageUsers: FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
|
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
|
||||||
<User
|
<UserForm
|
||||||
user={user}
|
user={user}
|
||||||
setUser={setUser}
|
setUser={setUser}
|
||||||
creating={creating}
|
creating={creating}
|
||||||
@@ -232,11 +232,11 @@ const ManageUsers: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.MANAGE_USERS()} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ManageUsers;
|
export default ManageUsersForm;
|
||||||
@@ -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 ManageUsers from './ManageUsers';
|
import ManageUsersForm from './ManageUsersForm';
|
||||||
import SecuritySettings from './SecuritySettings';
|
import SecuritySettingsForm from './SecuritySettingsForm';
|
||||||
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="settings" label={LL.SETTINGS_OF(LL.SECURITY(1))} />
|
|
||||||
<Tab value="users" label={LL.MANAGE_USERS()} />
|
<Tab value="users" label={LL.MANAGE_USERS()} />
|
||||||
|
<Tab value="settings" label={LL.SETTINGS_OF(LL.SECURITY(1))} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="users" element={<ManageUsers />} />
|
<Route path="users" element={<ManageUsersForm />} />
|
||||||
<Route path="settings" element={<SecuritySettings />} />
|
<Route path="settings" element={<SecuritySettingsForm />} />
|
||||||
<Route path="*" element={<Navigate replace to="settings" />} />
|
<Route path="/*" element={<Navigate replace to="users" />} />
|
||||||
</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 { SecuritySettingsType } from 'types';
|
import type { SecuritySettings } 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 SecuritySettings: FC = () => {
|
const SecuritySettingsForm: FC = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
@@ -29,7 +29,7 @@ const SecuritySettings: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<SecuritySettingsType>({
|
} = useRest<SecuritySettings>({
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
@@ -96,11 +96,11 @@ const SecuritySettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF(LL.SECURITY(1))} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SecuritySettings;
|
export default SecuritySettingsForm;
|
||||||
@@ -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 { UserType } from 'types';
|
import type { User } 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?: UserType;
|
user?: User;
|
||||||
setUser: React.Dispatch<React.SetStateAction<UserType | undefined>>;
|
setUser: React.Dispatch<React.SetStateAction<User | undefined>>;
|
||||||
|
|
||||||
onDoneEditing: () => void;
|
onDoneEditing: () => void;
|
||||||
onCancelEditing: () => void;
|
onCancelEditing: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const User: FC<UserFormProps> = ({ creating, validator, user, setUser, onDoneEditing, onCancelEditing }) => {
|
const UserForm: 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 User: FC<UserFormProps> = ({ creating, validator, user, setUser, onDoneEdi
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default User;
|
export default UserForm;
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -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 { OTASettingsType } from 'types';
|
import type { OTASettings } from 'types';
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -14,8 +14,7 @@ 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';
|
||||||
@@ -24,7 +23,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 OTASettings: FC = () => {
|
const OTASettingsForm: FC = () => {
|
||||||
const {
|
const {
|
||||||
loadData,
|
loadData,
|
||||||
saveData,
|
saveData,
|
||||||
@@ -36,7 +35,7 @@ const OTASettings: FC = () => {
|
|||||||
setDirtyFlags,
|
setDirtyFlags,
|
||||||
blocker,
|
blocker,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest<OTASettingsType>({
|
} = useRest<OTASettings>({
|
||||||
read: SystemApi.readOTASettings,
|
read: SystemApi.readOTASettings,
|
||||||
update: SystemApi.updateOTASettings
|
update: SystemApi.updateOTASettings
|
||||||
});
|
});
|
||||||
@@ -62,8 +61,6 @@ const OTASettings: FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useLayoutTitle('OTA');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
@@ -120,11 +117,11 @@ const OTASettings: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent title={LL.SETTINGS_OF('OTA')} titleGutter>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OTASettings;
|
export default OTASettingsForm;
|
||||||
@@ -1,42 +1,53 @@
|
|||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
import { useContext, type FC } from 'react';
|
import { useContext } 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 SystemStatus from './SystemStatus';
|
import SystemStatusForm from './SystemStatusForm';
|
||||||
|
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 { routerTab } = useRouterTab();
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
const { me } = useContext(AuthenticatedContext);
|
||||||
|
const { routerTab } = useRouterTab();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="status" label={LL.STATUS_OF('')} />
|
<Tab value="status" label={LL.STATUS_OF(LL.SYSTEM(1))} />
|
||||||
<Tab value="activity" label={LL.ACTIVITY()} />
|
<Tab value="log" label={LL.LOG_OF(LL.SYSTEM(2))} />
|
||||||
<Tab disabled={!me.admin} value="log" label={me.admin ? LL.LOG_OF('') : ''} />
|
<Tab value="ota" label={LL.SETTINGS_OF('OTA')} disabled={!me.admin} />
|
||||||
|
<Tab value="upload" label={LL.UPLOAD_DOWNLOAD()} disabled={!me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<SystemStatus />} />
|
<Route path="status" element={<SystemStatusForm />} />
|
||||||
<Route path="activity" element={<SystemActivity />} />
|
<Route path="log" element={<SystemLog />} />
|
||||||
<Route
|
<Route
|
||||||
path="log"
|
path="ota"
|
||||||
element={
|
element={
|
||||||
<RequireAdmin>
|
<RequireAdmin>
|
||||||
<SystemLog />
|
<OTASettingsForm />
|
||||||
</RequireAdmin>
|
</RequireAdmin>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="*" element={<Navigate replace to="status" />} />
|
<Route
|
||||||
|
path="upload"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<UploadFileForm />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/*" element={<Navigate replace to="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, useLayoutTitle } from 'components';
|
import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { LogLevel } from 'types';
|
import { LogLevel } from 'types';
|
||||||
@@ -50,8 +50,6 @@ 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,
|
||||||
@@ -234,7 +232,7 @@ const SystemLog: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent id="log-window">
|
<SectionContent title={LL.LOG_OF(LL.SYSTEM(2))} titleGutter id="log-window">
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
{content()}
|
{content()}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
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;
|
|
||||||
349
interface/src/framework/system/SystemStatusForm.tsx
Normal file
349
interface/src/framework/system/SystemStatusForm.tsx
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
import AppsIcon from '@mui/icons-material/Apps';
|
||||||
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
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 ShowChartIcon from '@mui/icons-material/ShowChart';
|
||||||
|
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 { FeaturesContext } from '../../contexts/features';
|
||||||
|
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 { features } = useContext(FeaturesContext);
|
||||||
|
|
||||||
|
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>
|
||||||
|
<DevicesIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary={LL.PLATFORM()} secondary={data.esp_platform + ' / ' + data.sdk_version} />
|
||||||
|
</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>
|
||||||
|
<ShowChartIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary={LL.CPU_FREQ()} secondary={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={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={features.platform}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemStatusForm;
|
||||||
112
interface/src/framework/system/SystemStatusVersionDialog.tsx
Normal file
112
interface/src/framework/system/SystemStatusVersionDialog.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
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;
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
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;
|
|
||||||
177
interface/src/framework/system/UploadFileForm.tsx
Normal file
177
interface/src/framework/system/UploadFileForm.tsx
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
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>(false);
|
||||||
|
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 {
|
||||||
|
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 + '.json';
|
||||||
|
anchor.click();
|
||||||
|
URL.revokeObjectURL(anchor.href);
|
||||||
|
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||||
|
};
|
||||||
|
|
||||||
|
onSuccessGetSettings((event) => {
|
||||||
|
saveFile(event.data, 'settings');
|
||||||
|
});
|
||||||
|
onSuccessgetCustomizations((event) => {
|
||||||
|
saveFile(event.data, 'customizations');
|
||||||
|
});
|
||||||
|
onSuccessGetEntities((event) => {
|
||||||
|
saveFile(event.data, 'entities');
|
||||||
|
});
|
||||||
|
onSuccessGetSchedule((event) => {
|
||||||
|
saveFile(event.data, 'schedule');
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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()} </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: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
|
{LL.DOWNLOAD(0)}
|
||||||
|
</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;
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 85.333 512 341.333"><path fill="#FFF" d="M0 85.337h512v341.326H0z"/><path fill="#0052B4" d="M0 196.641h512v118.717H0z"/><path fill="#D80027" d="M0 315.359h512v111.304H0z"/><path fill="#FFF" d="M129.468 181.799v85.136c0 48.429 63.267 63.267 63.267 63.267S256 315.362 256 266.935v-85.136H129.468z"/><path fill="#D80027" d="M146.126 184.294v81.941c0 5.472 1.215 10.64 3.623 15.485h85.97c2.408-4.844 3.623-10.012 3.623-15.485v-81.941h-93.216z"/><path fill="#FFF" d="M221.301 241.427h-21.425v-14.283h14.284v-14.283h-14.284v-14.284h-14.283v14.284h-14.282v14.283h14.282v14.283h-21.426v14.284h21.426v14.283h14.283v-14.283h21.425z"/><path fill="#0052B4" d="M169.232 301.658c9.204 5.783 18.66 9.143 23.502 10.636 4.842-1.494 14.298-4.852 23.502-10.636 9.282-5.833 15.79-12.506 19.484-19.939a24.878 24.878 0 0 0-14.418-4.583c-1.956 0-3.856.232-5.682.657-3.871-8.796-12.658-14.94-22.884-14.94-10.227 0-19.013 6.144-22.884 14.94a25.048 25.048 0 0 0-5.682-.657 24.88 24.88 0 0 0-14.418 4.583c3.691 7.433 10.198 14.106 19.48 19.939z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const de: Translation = {
|
|||||||
BYPASS_TOKEN: 'Zugriffstoken-Autorisierung bei API-Aufrufen umgehen',
|
BYPASS_TOKEN: 'Zugriffstoken-Autorisierung bei API-Aufrufen umgehen',
|
||||||
READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)',
|
READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)',
|
||||||
UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten',
|
UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten',
|
||||||
HEATINGOFF: 'Heizen ausschalten beim EMS-ESP Start',
|
|
||||||
ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren',
|
ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren',
|
||||||
ENABLE_SHOWER_ALERT: 'Duschalarm aktivieren',
|
ENABLE_SHOWER_ALERT: 'Duschalarm aktivieren',
|
||||||
TRIGGER_TIME: 'Auslösezeit',
|
TRIGGER_TIME: 'Auslösezeit',
|
||||||
@@ -159,12 +162,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'Bitte laden Sie die System-Details und hängen Sie sie an das Support-Issue an. ',
|
HELP_INFORMATION_4: 'Bitte laden Sie die System-Details und hängen Sie sie an das Support-Issue an. ',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP ist ein freies Open-Source Projekt. Bitte unterstützen Sie die zukünftige Entwicklung mit einem "Star" auf Github!',
|
HELP_INFORMATION_5: 'EMS-ESP ist ein freies Open-Source Projekt. Bitte unterstützen Sie die zukünftige Entwicklung mit einem "Star" auf Github!',
|
||||||
|
SUPPORT_INFO: 'Support Info',
|
||||||
UPLOAD: 'Hochladen',
|
UPLOAD: 'Hochladen',
|
||||||
DOWNLOAD: '{{H|h|h}}erunterladen',
|
DOWNLOAD: '{{H|h|h}}erunterladen',
|
||||||
ABORTED: 'abgebrochen',
|
ABORTED: 'abgebrochen',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
RELEASE_IS: 'Release ist',
|
RELEASE_IS: 'release ist', // TODO translate
|
||||||
RELEASE_NOTES: 'Versionshinweise',
|
RELEASE_NOTES: 'Versionshinweise',
|
||||||
EMS_ESP_VER: 'EMS-ESP Version',
|
EMS_ESP_VER: 'EMS-ESP Version',
|
||||||
|
PLATFORM: 'Platform (Platform / SDK)',
|
||||||
UPTIME: 'System Betriebszeit',
|
UPTIME: 'System Betriebszeit',
|
||||||
|
CPU_FREQ: 'CPU Frequenz',
|
||||||
HEAP: 'freier RAM Speicher (Gesamt / max. Block)',
|
HEAP: 'freier RAM Speicher (Gesamt / max. Block)',
|
||||||
PSRAM: 'PSRAM (Größe / Frei)',
|
PSRAM: 'PSRAM (Größe / Frei)',
|
||||||
FLASH: 'Flash Speicher (Größe / Geschwindigkeit)',
|
FLASH: 'Flash Speicher (Größe / Geschwindigkeit)',
|
||||||
APPSIZE: 'Programm (Partition: Genutzt / Frei)',
|
APPSIZE: 'Programm (Genutzt / Frei)',
|
||||||
FILESYSTEM: 'Dateisystem (Genutzt / Frei)',
|
FILESYSTEM: 'Dateisystem (Genutzt / Frei)',
|
||||||
BUFFER_SIZE: 'max. Puffergröße',
|
BUFFER_SIZE: 'max. Puffergröße',
|
||||||
COMPACT: 'Kompakte Darstellung',
|
COMPACT: 'Kompakte Darstellung',
|
||||||
@@ -236,7 +246,6 @@ 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',
|
||||||
@@ -273,7 +282,6 @@ const de: Translation = {
|
|||||||
NETWORK_SCANNER: 'Netzwerk Suche',
|
NETWORK_SCANNER: 'Netzwerk Suche',
|
||||||
NETWORK_NO_WIFI: 'Keine WiFi Netzwerke gefunden',
|
NETWORK_NO_WIFI: 'Keine WiFi Netzwerke gefunden',
|
||||||
NETWORK_BLANK_SSID: 'Freilassen um WiFi zu deaktivieren und ETH zu aktivieren',
|
NETWORK_BLANK_SSID: 'Freilassen um WiFi zu deaktivieren und ETH zu aktivieren',
|
||||||
NETWORK_BLANK_BSSID: 'Freilassen um nur SSID für die Verbindung zu nutzen',
|
|
||||||
TX_POWER: 'Tx Leistung',
|
TX_POWER: 'Tx Leistung',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus',
|
NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus',
|
||||||
@@ -300,7 +308,7 @@ const de: Translation = {
|
|||||||
LEAVE: 'Verlassen',
|
LEAVE: 'Verlassen',
|
||||||
SCHEDULER: 'Planer',
|
SCHEDULER: 'Planer',
|
||||||
SCHEDULER_HELP_1: 'Fügen Sie eigene, geplante Befehle zur Automatisierung hinzu. Vergeben Sie einen Entitätsnamen um die Aktivierung über API/Mqtt zu steuern',
|
SCHEDULER_HELP_1: 'Fügen Sie eigene, geplante Befehle zur Automatisierung hinzu. Vergeben Sie einen Entitätsnamen um die Aktivierung über API/Mqtt zu steuern',
|
||||||
SCHEDULER_HELP_2: '00:00 aktiviert einmalige Ausführung am Start',
|
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
|
||||||
SCHEDULE: 'Zeitplan',
|
SCHEDULE: 'Zeitplan',
|
||||||
TIME: 'Zeit',
|
TIME: 'Zeit',
|
||||||
TIMER: 'Timer',
|
TIMER: 'Timer',
|
||||||
@@ -309,22 +317,12 @@ const de: Translation = {
|
|||||||
SCHEDULE_TIMER_2: 'jede Minute',
|
SCHEDULE_TIMER_2: 'jede Minute',
|
||||||
SCHEDULE_TIMER_3: 'jede Stunde',
|
SCHEDULE_TIMER_3: 'jede Stunde',
|
||||||
CUSTOM_ENTITIES: 'Individuelle Entitäten',
|
CUSTOM_ENTITIES: 'Individuelle Entitäten',
|
||||||
ENTITIES_HELP_1: 'Definition eigener EMS-Werte oder dynamischer Variablen',
|
ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus',
|
||||||
ENTITIES_UPDATED: 'Entitäten gespeichert',
|
ENTITIES_UPDATED: 'Entitäten gespeichert',
|
||||||
WRITEABLE: 'Schreibbar',
|
WRITEABLE: 'Schreibbar',
|
||||||
SHOWING: 'Anzeigen von',
|
SHOWING: 'Anzeigen von',
|
||||||
SEARCH: 'Suche',
|
SEARCH: 'Suche',
|
||||||
CERT: 'TLS Zertifikat (Freilassen für unsichere Verbindung)',
|
CERT: 'TSL Zertifikat (Freilassen um TSL zu deaktivieren)'
|
||||||
ENABLE_TLS: 'Aktiviere TLS',
|
|
||||||
ON: 'An',
|
|
||||||
OFF: 'Aus',
|
|
||||||
POLARITY: 'Polarität',
|
|
||||||
ACTIVEHIGH: 'Aktiv Positiv',
|
|
||||||
ACTIVELOW: 'Aktiv Negativ',
|
|
||||||
UNCHANGED: 'Unverändert',
|
|
||||||
ALWAYS: 'Immer',
|
|
||||||
ACTIVITY: 'Activity', // TODO translate
|
|
||||||
CONFIGURE: 'Configure {0}' // TODO translate
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -60,7 +63,7 @@ const en: Translation = {
|
|||||||
FREQ: 'Frequency',
|
FREQ: 'Frequency',
|
||||||
DUTY_CYCLE: 'Duty Cycle',
|
DUTY_CYCLE: 'Duty Cycle',
|
||||||
UNIT: 'UoM',
|
UNIT: 'UoM',
|
||||||
STARTVALUE: 'Start Value',
|
STARTVALUE: 'Start value',
|
||||||
WARN_GPIO: 'Warning: be careful when assigning a GPIO!',
|
WARN_GPIO: 'Warning: be careful when assigning a GPIO!',
|
||||||
EDIT: 'Edit',
|
EDIT: 'Edit',
|
||||||
SENSOR: 'Sensor',
|
SENSOR: 'Sensor',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const en: Translation = {
|
|||||||
BYPASS_TOKEN: 'Bypass Access Token authorization on API calls',
|
BYPASS_TOKEN: 'Bypass Access Token authorization on API calls',
|
||||||
READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)',
|
READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)',
|
||||||
UNDERCLOCK_CPU: 'Underclock CPU speed',
|
UNDERCLOCK_CPU: 'Underclock CPU speed',
|
||||||
HEATINGOFF: 'Start boiler with forced heating off',
|
|
||||||
ENABLE_SHOWER_TIMER: 'Enable Shower Timer',
|
ENABLE_SHOWER_TIMER: 'Enable Shower Timer',
|
||||||
ENABLE_SHOWER_ALERT: 'Enable Shower Alert',
|
ENABLE_SHOWER_ALERT: 'Enable Shower Alert',
|
||||||
TRIGGER_TIME: 'Trigger Time',
|
TRIGGER_TIME: 'Trigger Time',
|
||||||
@@ -154,17 +157,20 @@ 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: 'Download and attach your support information for a faster response when reporting an issue',
|
HELP_INFORMATION_4: 'remember to download and attach your system 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!',
|
||||||
|
SUPPORT_INFO: 'Support Info',
|
||||||
UPLOAD: 'Upload',
|
UPLOAD: 'Upload',
|
||||||
DOWNLOAD: '{{D|d|d}}ownload',
|
DOWNLOAD: '{{D|d|d}}ownload',
|
||||||
ABORTED: 'aborted',
|
ABORTED: 'aborted',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
|
CPU_FREQ: 'CPU Frequency',
|
||||||
HEAP: 'Heap (Free / Max Alloc)',
|
HEAP: 'Heap (Free / Max Alloc)',
|
||||||
PSRAM: 'PSRAM (Size / Free)',
|
PSRAM: 'PSRAM (Size / Free)',
|
||||||
FLASH: 'Flash Chip (Size / Speed)',
|
FLASH: 'Flash Chip (Size / Speed)',
|
||||||
APPSIZE: 'Application (Partition: Used / Free)',
|
APPSIZE: 'Application (Used / Free)',
|
||||||
FILESYSTEM: 'File System (Used / Free)',
|
FILESYSTEM: 'File System (Used / Free)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
@@ -220,7 +230,7 @@ const en: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Client',
|
CLIENT: 'Client',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'optional',
|
OPTIONAL: 'Optional',
|
||||||
FORMATTING: 'Formatting',
|
FORMATTING: 'Formatting',
|
||||||
MQTT_FORMAT: 'Topic/Payload Format',
|
MQTT_FORMAT: 'Topic/Payload Format',
|
||||||
MQTT_NEST_1: 'Nested in a single topic',
|
MQTT_NEST_1: 'Nested in a single topic',
|
||||||
@@ -236,7 +246,6 @@ 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',
|
||||||
@@ -273,7 +282,6 @@ const en: Translation = {
|
|||||||
NETWORK_SCANNER: 'Network Scanner',
|
NETWORK_SCANNER: 'Network Scanner',
|
||||||
NETWORK_NO_WIFI: 'No WiFi networks found',
|
NETWORK_NO_WIFI: 'No WiFi networks found',
|
||||||
NETWORK_BLANK_SSID: 'leave blank to disable WiFi and enable ETH',
|
NETWORK_BLANK_SSID: 'leave blank to disable WiFi and enable ETH',
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID',
|
|
||||||
TX_POWER: 'Tx Power',
|
TX_POWER: 'Tx Power',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
|
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
|
||||||
@@ -309,22 +317,12 @@ const en: Translation = {
|
|||||||
SCHEDULE_TIMER_2: 'every minute',
|
SCHEDULE_TIMER_2: 'every minute',
|
||||||
SCHEDULE_TIMER_3: 'every hour',
|
SCHEDULE_TIMER_3: 'every hour',
|
||||||
CUSTOM_ENTITIES: 'Custom Entities',
|
CUSTOM_ENTITIES: 'Custom Entities',
|
||||||
ENTITIES_HELP_1: 'Define custom EMS entities or dynamic user variables',
|
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus',
|
||||||
ENTITIES_UPDATED: 'Entities Updated',
|
ENTITIES_UPDATED: 'Entities Updated',
|
||||||
WRITEABLE: 'Writeable',
|
WRITEABLE: 'Writeable',
|
||||||
SHOWING: 'Showing',
|
SHOWING: 'Showing',
|
||||||
SEARCH: 'Search',
|
SEARCH: 'Search',
|
||||||
CERT: 'TLS root certificate (leave blank for insecure)',
|
CERT: 'TSL root certificate (leave blank to disable TSL)'
|
||||||
ENABLE_TLS: 'Enable TLS',
|
|
||||||
ON: 'On',
|
|
||||||
OFF: 'Off',
|
|
||||||
POLARITY: 'Polarity',
|
|
||||||
ACTIVEHIGH: 'Active High',
|
|
||||||
ACTIVELOW: 'Active Low',
|
|
||||||
UNCHANGED: 'Unchanged',
|
|
||||||
ALWAYS: 'Always',
|
|
||||||
ACTIVITY: 'Activity',
|
|
||||||
CONFIGURE: 'Configure {0}'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const fr: Translation = {
|
|||||||
BYPASS_TOKEN: 'Contourner l\'autorisation du jeton d\'accès sur les appels API',
|
BYPASS_TOKEN: 'Contourner l\'autorisation du jeton d\'accès sur les appels API',
|
||||||
READONLY: 'Activer le mode lecture uniquement (bloque toutes les commandes EMS sortantes en écriture Tx)',
|
READONLY: 'Activer le mode lecture uniquement (bloque toutes les commandes EMS sortantes en écriture Tx)',
|
||||||
UNDERCLOCK_CPU: 'Underclock du CPU',
|
UNDERCLOCK_CPU: 'Underclock du CPU',
|
||||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
|
||||||
ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche',
|
ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche',
|
||||||
ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche',
|
ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche',
|
||||||
TRIGGER_TIME: 'Durée avant déclenchement',
|
TRIGGER_TIME: 'Durée avant déclenchement',
|
||||||
@@ -159,12 +162,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'N\'oubliez pas de télécharger et de joindre les informations relatives à votre système pour obtenir une réponse plus rapide lorsque vous signalez un problème',
|
HELP_INFORMATION_4: 'n\'oubliez pas de télécharger et de joindre les informations relatives à votre système pour obtenir une réponse plus rapide lorsque vous signalez un problème',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP est un projet libre et open-source. Merci de soutenir son développement futur en lui donnant une étoile sur Github !',
|
HELP_INFORMATION_5: 'EMS-ESP est un projet libre et open-source. Merci de soutenir son développement futur en lui donnant une étoile sur Github !',
|
||||||
|
SUPPORT_INFO: 'Information de support',
|
||||||
UPLOAD: 'Upload',
|
UPLOAD: 'Upload',
|
||||||
DOWNLOAD: '{{D|d|d}}ownload',
|
DOWNLOAD: '{{D|d|d}}ownload',
|
||||||
ABORTED: 'annulé',
|
ABORTED: 'annulé',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
RELEASE_IS: 'release est', // TODO translate
|
RELEASE_IS: 'release est', // TODO translate
|
||||||
RELEASE_NOTES: 'notes de version',
|
RELEASE_NOTES: 'notes de version',
|
||||||
EMS_ESP_VER: 'Version EMS-ESP',
|
EMS_ESP_VER: 'Version EMS-ESP',
|
||||||
|
PLATFORM: 'Appareil (Plateforme / SDK)',
|
||||||
UPTIME: 'Durée de fonctionnement du système',
|
UPTIME: 'Durée de fonctionnement du système',
|
||||||
|
CPU_FREQ: 'Fréquence du CPU',
|
||||||
HEAP: 'Heap (Libre / Max Allouée)',
|
HEAP: 'Heap (Libre / Max Allouée)',
|
||||||
PSRAM: 'PSRAM (Taille / Libre)',
|
PSRAM: 'PSRAM (Taille / Libre)',
|
||||||
FLASH: 'Flash Chip (Taille / Vitesse)',
|
FLASH: 'Flash Chip (Taille / Vitesse)',
|
||||||
APPSIZE: 'Application (Partition: Utilisée / Libre)',
|
APPSIZE: 'Application (Utilisée / Libre)',
|
||||||
FILESYSTEM: 'File System (Utilisée / Libre)',
|
FILESYSTEM: 'File System (Utilisée / Libre)',
|
||||||
BUFFER_SIZE: 'Max taille du buffer',
|
BUFFER_SIZE: 'Max taille du buffer',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
@@ -220,7 +230,7 @@ const fr: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Client',
|
CLIENT: 'Client',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'optionnel',
|
OPTIONAL: 'Optionnel',
|
||||||
FORMATTING: 'Mise en forme',
|
FORMATTING: 'Mise en forme',
|
||||||
MQTT_FORMAT: 'Format du Topic/Payload',
|
MQTT_FORMAT: 'Format du Topic/Payload',
|
||||||
MQTT_NEST_1: 'Englobé dans un topic unique',
|
MQTT_NEST_1: 'Englobé dans un topic unique',
|
||||||
@@ -236,7 +246,6 @@ 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
|
||||||
@@ -273,7 +282,6 @@ const fr: Translation = {
|
|||||||
NETWORK_SCANNER: 'Scan réseau',
|
NETWORK_SCANNER: 'Scan réseau',
|
||||||
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
||||||
NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi', // and enable ETH // TODO translate
|
NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi', // and enable ETH // TODO translate
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
|
||||||
TX_POWER: 'Puissance Tx',
|
TX_POWER: 'Puissance Tx',
|
||||||
HOSTNAME: 'Nom d\'hôte',
|
HOSTNAME: 'Nom d\'hôte',
|
||||||
NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi',
|
NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi',
|
||||||
@@ -314,17 +322,7 @@ const fr: Translation = {
|
|||||||
WRITEABLE: 'Writeable', // TODO translate
|
WRITEABLE: 'Writeable', // TODO translate
|
||||||
SHOWING: 'Showing', // TODO translate
|
SHOWING: 'Showing', // TODO translate
|
||||||
SEARCH: 'Search', // TODO translate
|
SEARCH: 'Search', // TODO translate
|
||||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
|
||||||
ENABLE_TLS: 'Activer TLS',
|
|
||||||
ON: 'On', // TODO translate
|
|
||||||
OFF: 'Off', // TODO translate
|
|
||||||
POLARITY: 'Polarity', // TODO translate
|
|
||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
|
||||||
ALWAYS: 'Always', // TODO translate
|
|
||||||
ACTIVITY: 'Activity', // TODO translate
|
|
||||||
CONFIGURE: 'Configure {0}' // TODO translate
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -48,6 +51,7 @@ 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',
|
||||||
@@ -67,6 +71,7 @@ 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',
|
||||||
@@ -80,6 +85,7 @@ 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)',
|
||||||
@@ -122,7 +128,6 @@ const it: Translation = {
|
|||||||
BYPASS_TOKEN: 'Ignora autorizzazione del token di accesso sulle chiamate API',
|
BYPASS_TOKEN: 'Ignora autorizzazione del token di accesso sulle chiamate API',
|
||||||
READONLY: 'Abilita modalità sola-lettura (blocca tutti i comandi di scrittura EMS Tx in uscita)',
|
READONLY: 'Abilita modalità sola-lettura (blocca tutti i comandi di scrittura EMS Tx in uscita)',
|
||||||
UNDERCLOCK_CPU: 'Abbassa velocità della CPU',
|
UNDERCLOCK_CPU: 'Abbassa velocità della CPU',
|
||||||
HEATINGOFF: 'Avviamento caldaia con riscaldamento forzato spento',
|
|
||||||
ENABLE_SHOWER_TIMER: 'Abilita timer doccia',
|
ENABLE_SHOWER_TIMER: 'Abilita timer doccia',
|
||||||
ENABLE_SHOWER_ALERT: 'Abilita avviso doccia',
|
ENABLE_SHOWER_ALERT: 'Abilita avviso doccia',
|
||||||
TRIGGER_TIME: 'Tempo di avvio',
|
TRIGGER_TIME: 'Tempo di avvio',
|
||||||
@@ -159,12 +164,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'Ricordati di scaricare e allegare le informazioni del tuo sistema per una risposta più rapida quando segnali un problema',
|
HELP_INFORMATION_4: 'ricordati di scaricare e allegare le informazioni del tuo sistema per una risposta più rapida quando segnali un problema',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP è un progetto gratuito e open-source. Supporta il suo sviluppo futuro assegnandogli una stella su Github!',
|
HELP_INFORMATION_5: 'EMS-ESP è un progetto gratuito e open-source. Supporta il suo sviluppo futuro assegnandogli una stella su Github!',
|
||||||
|
SUPPORT_INFO: 'Info Supporto',
|
||||||
UPLOAD: 'Carica',
|
UPLOAD: 'Carica',
|
||||||
DOWNLOAD: 'Scarica',
|
DOWNLOAD: 'Scarica',
|
||||||
ABORTED: 'Annullato',
|
ABORTED: 'Annullato',
|
||||||
@@ -175,22 +183,26 @@ 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',
|
||||||
RELEASE_IS: 'rilascio é',
|
RELEASE_IS: 'rilascio é',
|
||||||
RELEASE_NOTES: 'note rilascio',
|
RELEASE_NOTES: 'note rilascio',
|
||||||
EMS_ESP_VER: 'Versione EMS-ESP',
|
EMS_ESP_VER: 'Versione EMS-ESP',
|
||||||
|
PLATFORM: 'Dispositivo (Piattaforma / SDK)',
|
||||||
UPTIME: 'Tempo di attività del sistema',
|
UPTIME: 'Tempo di attività del sistema',
|
||||||
|
CPU_FREQ: 'Frequenza CPU ',
|
||||||
HEAP: 'Heap (Free / Max Alloc)',
|
HEAP: 'Heap (Free / Max Alloc)',
|
||||||
PSRAM: 'PSRAM (Size / Free)',
|
PSRAM: 'PSRAM (Size / Free)',
|
||||||
FLASH: 'Flash Chip (Size / Speed)',
|
FLASH: 'Flash Chip (Size / Speed)',
|
||||||
APPSIZE: 'Applicazione (Partizione: Usata / Libera)',
|
APPSIZE: 'Applicazione (Usata / Libera)',
|
||||||
FILESYSTEM: 'Memoria Sistema (Usata / Libera)',
|
FILESYSTEM: 'Memoria Sistema (Usata / Libera)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
@@ -220,7 +232,7 @@ const it: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Cliente',
|
CLIENT: 'Cliente',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'opzionale',
|
OPTIONAL: 'Opzionale',
|
||||||
FORMATTING: 'Formattazione',
|
FORMATTING: 'Formattazione',
|
||||||
MQTT_FORMAT: 'Formato Topic/Payload ',
|
MQTT_FORMAT: 'Formato Topic/Payload ',
|
||||||
MQTT_NEST_1: 'Inserito in un singolo argomento',
|
MQTT_NEST_1: 'Inserito in un singolo argomento',
|
||||||
@@ -236,7 +248,6 @@ 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à',
|
||||||
@@ -273,7 +284,6 @@ const it: Translation = {
|
|||||||
NETWORK_SCANNER: 'Scansione Rete',
|
NETWORK_SCANNER: 'Scansione Rete',
|
||||||
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
||||||
NETWORK_BLANK_SSID: 'lasciare vuoto per disattivare WiFi',
|
NETWORK_BLANK_SSID: 'lasciare vuoto per disattivare WiFi',
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
|
||||||
TX_POWER: 'Potenza Tx',
|
TX_POWER: 'Potenza Tx',
|
||||||
HOSTNAME: 'Nome ospite',
|
HOSTNAME: 'Nome ospite',
|
||||||
NETWORK_DISABLE_SLEEP: 'Disabilita la modalità sospensione Wi-Fi',
|
NETWORK_DISABLE_SLEEP: 'Disabilita la modalità sospensione Wi-Fi',
|
||||||
@@ -309,22 +319,12 @@ const it: Translation = {
|
|||||||
SCHEDULE_TIMER_2: 'Ogni minuto',
|
SCHEDULE_TIMER_2: 'Ogni minuto',
|
||||||
SCHEDULE_TIMER_3: 'Ogni ora',
|
SCHEDULE_TIMER_3: 'Ogni ora',
|
||||||
CUSTOM_ENTITIES: 'Entità personalizzate',
|
CUSTOM_ENTITIES: 'Entità personalizzate',
|
||||||
ENTITIES_HELP_1: 'Recupera entità personalizzate dal BUS EMS', // TODO translate
|
ENTITIES_HELP_1: 'Recupera entità personalizzate dal BUS EMS',
|
||||||
ENTITIES_UPDATED: 'Entità aggiornate',
|
ENTITIES_UPDATED: 'Entità aggiornate',
|
||||||
WRITEABLE: 'Scrivibile',
|
WRITEABLE: 'Scrivibile',
|
||||||
SHOWING: 'Visualizza',
|
SHOWING: 'Visualizza',
|
||||||
SEARCH: 'Ricerca',
|
SEARCH: 'Ricerca',
|
||||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
|
||||||
ENABLE_TLS: 'Abilita TLS',
|
|
||||||
ON: 'On', // TODO translate
|
|
||||||
OFF: 'Off', // TODO translate
|
|
||||||
POLARITY: 'Polarity', // TODO translate
|
|
||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
|
||||||
ALWAYS: 'Always', // TODO translate
|
|
||||||
ACTIVITY: 'Activity', // TODO translate
|
|
||||||
CONFIGURE: 'Configure {0}' // TODO translate
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ 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 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',
|
||||||
@@ -159,12 +162,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'Zorg dat je ook je systeem details zijn toevoeged voor een sneller antwoord',
|
HELP_INFORMATION_4: 'zorg dat je ook je systeem details zijn toevoeged voor een sneller antwoord',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP is een gratis en open source project. Steun ons met een Star op Github!',
|
HELP_INFORMATION_5: 'EMS-ESP is een gratis en open source project. Steun ons met een Star op Github!',
|
||||||
|
SUPPORT_INFO: 'Support Info',
|
||||||
UPLOAD: 'Upload',
|
UPLOAD: 'Upload',
|
||||||
DOWNLOAD: '{{D|d|d}}ownload',
|
DOWNLOAD: '{{D|d|d}}ownload',
|
||||||
ABORTED: 'afgebroken',
|
ABORTED: 'afgebroken',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
RELEASE_IS: 'release is',
|
RELEASE_IS: 'release is',
|
||||||
RELEASE_NOTES: 'release notes',
|
RELEASE_NOTES: 'release notes',
|
||||||
EMS_ESP_VER: 'EMS-ESP Versie',
|
EMS_ESP_VER: 'EMS-ESP Versie',
|
||||||
|
PLATFORM: 'Apparaat (Platform / SDK)',
|
||||||
UPTIME: 'Systeem Uptime',
|
UPTIME: 'Systeem Uptime',
|
||||||
|
CPU_FREQ: 'CPU Frequency',
|
||||||
HEAP: 'Heap (Free / Max Alloc)',
|
HEAP: 'Heap (Free / Max Alloc)',
|
||||||
PSRAM: 'PSRAM (Size / Free)',
|
PSRAM: 'PSRAM (Size / Free)',
|
||||||
FLASH: 'Flash Chip (Size / Speed)',
|
FLASH: 'Flash Chip (Size / Speed)',
|
||||||
APPSIZE: 'Application (Partition: Used / Free)',
|
APPSIZE: 'Application (Used / Free)',
|
||||||
FILESYSTEM: 'File System (Used / Free)',
|
FILESYSTEM: 'File System (Used / Free)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
@@ -220,7 +230,7 @@ const nl: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Client',
|
CLIENT: 'Client',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'optioneel',
|
OPTIONAL: 'Optioneel',
|
||||||
FORMATTING: 'Formatteren',
|
FORMATTING: 'Formatteren',
|
||||||
MQTT_FORMAT: 'Topic/Payload Formattering',
|
MQTT_FORMAT: 'Topic/Payload Formattering',
|
||||||
MQTT_NEST_1: 'Genest in 1 topic',
|
MQTT_NEST_1: 'Genest in 1 topic',
|
||||||
@@ -236,7 +246,6 @@ 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',
|
||||||
@@ -273,7 +282,6 @@ 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: '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',
|
||||||
@@ -314,17 +322,7 @@ const nl: Translation = {
|
|||||||
WRITEABLE: 'Beschrijfbare',
|
WRITEABLE: 'Beschrijfbare',
|
||||||
SHOWING: 'Tonen',
|
SHOWING: 'Tonen',
|
||||||
SEARCH: 'Zoek',
|
SEARCH: 'Zoek',
|
||||||
CERT: 'TLS rootcertificaat (laat leeg om TLS-insecure)',
|
CERT: 'TSL rootcertificaat (laat leeg om TSL uit te schakelen)'
|
||||||
ENABLE_TLS: 'Activeer TLS',
|
|
||||||
ON: 'Aan',
|
|
||||||
OFF: 'Uit',
|
|
||||||
POLARITY: 'Polariteit',
|
|
||||||
ACTIVEHIGH: 'Actiev Hoog',
|
|
||||||
ACTIVELOW: 'Actiev Laag',
|
|
||||||
UNCHANGED: 'Ongewijzigd',
|
|
||||||
ALWAYS: 'Altijd',
|
|
||||||
ACTIVITY: 'Activiteit',
|
|
||||||
CONFIGURE: '{0} Configureren'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const no: Translation = {
|
|||||||
BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall',
|
BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall',
|
||||||
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
|
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
|
||||||
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
|
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
|
||||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
|
||||||
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
|
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
|
||||||
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
|
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
|
||||||
TRIGGER_TIME: 'Aktiveringstid',
|
TRIGGER_TIME: 'Aktiveringstid',
|
||||||
@@ -159,12 +162,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'Husk å laste ned og legg ved din systeminformasjon for en raskere respons når du rapporterer et problem',
|
HELP_INFORMATION_4: 'husk å laste ned og legg ved din systeminformasjon for en raskere respons når du rapporterer et problem',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP er gratis og åpen kildekode. Bidra til utviklingen ved å gi oss en stjerne på GitHub!',
|
HELP_INFORMATION_5: 'EMS-ESP er gratis og åpen kildekode. Bidra til utviklingen ved å gi oss en stjerne på GitHub!',
|
||||||
|
SUPPORT_INFO: 'Supportinfo',
|
||||||
UPLOAD: 'Opplasning',
|
UPLOAD: 'Opplasning',
|
||||||
DOWNLOAD: '{{N|n|n}}edlasting',
|
DOWNLOAD: '{{N|n|n}}edlasting',
|
||||||
ABORTED: 'avbrutt',
|
ABORTED: 'avbrutt',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
RELEASE_IS: 'release er',
|
RELEASE_IS: 'release er',
|
||||||
RELEASE_NOTES: 'release notes',
|
RELEASE_NOTES: 'release notes',
|
||||||
EMS_ESP_VER: 'EMS-ESP Version',
|
EMS_ESP_VER: 'EMS-ESP Version',
|
||||||
|
PLATFORM: 'Enhet (Platform / SDK)',
|
||||||
UPTIME: 'System Oppetid',
|
UPTIME: 'System Oppetid',
|
||||||
|
CPU_FREQ: 'CPU Frekvens',
|
||||||
HEAP: 'Heap (Ledig / Max Allokert)',
|
HEAP: 'Heap (Ledig / Max Allokert)',
|
||||||
PSRAM: 'PSRAM (Størrelse / Ledig)',
|
PSRAM: 'PSRAM (Størrelse / Ledig)',
|
||||||
FLASH: 'Flash Chip (Størrelse / Hastighet)',
|
FLASH: 'Flash Chip (Størrelse / Hastighet)',
|
||||||
APPSIZE: 'Applikasjon (Partition: Brukt / Ledig)',
|
APPSIZE: 'Applikasjon (Brukt / Ledig)',
|
||||||
FILESYSTEM: 'File System (Brukt / Ledig)',
|
FILESYSTEM: 'File System (Brukt / Ledig)',
|
||||||
BUFFER_SIZE: 'Max Buffer Størrelse',
|
BUFFER_SIZE: 'Max Buffer Størrelse',
|
||||||
COMPACT: 'Komprimere',
|
COMPACT: 'Komprimere',
|
||||||
@@ -220,7 +230,7 @@ const no: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Client',
|
CLIENT: 'Client',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'valgfritt',
|
OPTIONAL: 'Valgfritt',
|
||||||
FORMATTING: 'Formatering',
|
FORMATTING: 'Formatering',
|
||||||
MQTT_FORMAT: 'Topic/Payload Format',
|
MQTT_FORMAT: 'Topic/Payload Format',
|
||||||
MQTT_NEST_1: 'Nestet i en topic',
|
MQTT_NEST_1: 'Nestet i en topic',
|
||||||
@@ -236,7 +246,6 @@ 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',
|
||||||
@@ -273,7 +282,6 @@ const no: Translation = {
|
|||||||
NETWORK_SCANNER: 'Nettverk Scanner',
|
NETWORK_SCANNER: 'Nettverk Scanner',
|
||||||
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
||||||
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk', // TODO translate
|
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk', // TODO translate
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
|
||||||
TX_POWER: 'Tx Effekt',
|
TX_POWER: 'Tx Effekt',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
|
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
|
||||||
@@ -314,17 +322,7 @@ const no: Translation = {
|
|||||||
WRITEABLE: 'Writeable', // TODO translate
|
WRITEABLE: 'Writeable', // TODO translate
|
||||||
SHOWING: 'Showing', // TODO translate
|
SHOWING: 'Showing', // TODO translate
|
||||||
SEARCH: 'Search', // TODO translate
|
SEARCH: 'Search', // TODO translate
|
||||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
|
||||||
ENABLE_TLS: 'Aktiviser TLS',
|
|
||||||
ON: 'On', // TODO translate
|
|
||||||
OFF: 'Off', // TODO translate
|
|
||||||
POLARITY: 'Polarity', // TODO translate
|
|
||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
|
||||||
ALWAYS: 'Always', // TODO translate
|
|
||||||
ACTIVITY: 'Activity', // TODO translate
|
|
||||||
CONFIGURE: 'Configure {0}' // TODO translate
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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}.',
|
||||||
@@ -36,6 +37,8 @@ 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ę',
|
||||||
@@ -50,7 +53,7 @@ const pl: BaseTranslation = {
|
|||||||
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
||||||
ANALOG_SENSOR: '{{u|u||ustawienia u|ustawień u}}rządzeni{{a podłączonego do EMS-ESP|e||a podłączonego do EMS-ESP|a podłączonego do EMS-ESP}}',
|
ANALOG_SENSOR: '{{u|u||ustawienia u|ustawień u}}rządzeni{{a podłączonego do EMS-ESP|e||a podłączonego do EMS-ESP|a podłączonego do EMS-ESP}}',
|
||||||
ANALOG_SENSORS: 'Urządzenia podłączone do EMS-ESP',
|
ANALOG_SENSORS: 'Urządzenia podłączone do EMS-ESP',
|
||||||
SETTINGS: 'ustawie{{nia|ń|}}',
|
SETTINGS: 'ustawienia',
|
||||||
UPDATED_OF: 'Zaktualizowano {0}.',
|
UPDATED_OF: 'Zaktualizowano {0}.',
|
||||||
UPDATE_OF: 'Aktualizacja {0}',
|
UPDATE_OF: 'Aktualizacja {0}',
|
||||||
REMOVED_OF: 'Usunięto ustawienia {0}.',
|
REMOVED_OF: 'Usunięto ustawienia {0}.',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const pl: BaseTranslation = {
|
|||||||
BYPASS_TOKEN: 'Pomiń autoryzację tokenem w wywołaniach API',
|
BYPASS_TOKEN: 'Pomiń autoryzację tokenem w wywołaniach API',
|
||||||
READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)',
|
READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)',
|
||||||
UNDERCLOCK_CPU: 'Obniż taktowanie CPU',
|
UNDERCLOCK_CPU: 'Obniż taktowanie CPU',
|
||||||
HEATINGOFF: 'Uruchom kocioł z wymuszonym wyłączonym grzaniem',
|
|
||||||
ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica',
|
ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica',
|
||||||
ENABLE_SHOWER_ALERT: 'Aktywuj alarm prysznica',
|
ENABLE_SHOWER_ALERT: 'Aktywuj alarm prysznica',
|
||||||
TRIGGER_TIME: 'Wyzwalaj po czasie',
|
TRIGGER_TIME: 'Wyzwalaj po czasie',
|
||||||
@@ -142,13 +145,13 @@ const pl: BaseTranslation = {
|
|||||||
MINUTES: 'minut',
|
MINUTES: 'minut',
|
||||||
HOURS: 'godzin',
|
HOURS: 'godzin',
|
||||||
RESTART: 'Restart',
|
RESTART: 'Restart',
|
||||||
RESTART_TEXT: 'Aby zastosować wprowadzone zmiany, interfejs EMS-ESP {{musi zostać|zostanie|}} uruchomiony ponowni{{e.|e|}}',
|
RESTART_TEXT: 'Aby zastosować wprowadzone zmiany interfejs EMS-ESP musi zostać zrestartowany.',
|
||||||
RESTART_CONFIRM: 'Na pewno chcesz zrestartować interfejs EMS-ESP?',
|
RESTART_CONFIRM: 'Na pewno chcesz zrestartować interfejs EMS-ESP?',
|
||||||
COMMAND: '{{Komenda|KOMENDA|}}',
|
COMMAND: '{{Komenda|KOMENDA|}}',
|
||||||
CUSTOMIZATIONS_RESTART: 'Wszystkie personalizacje zostały usunięte. Restartuję...',
|
CUSTOMIZATIONS_RESTART: 'Wszystkie personalizacje zostały usunięte. Restartuję...',
|
||||||
CUSTOMIZATIONS_FULL: 'Wybrano za dużo obiektów. Wprowadź zmiany w mniejszych partiach.',
|
CUSTOMIZATIONS_FULL: 'Wybrano za dużo obiektów. Wprowadź zmiany w mniejszych partiach.',
|
||||||
CUSTOMIZATIONS_SAVED: 'Personalizacje zostały zapisane.',
|
CUSTOMIZATIONS_SAVED: 'Personalizacje zostały zapisane.',
|
||||||
CUSTOMIZATIONS_HELP_1: 'Wybierz urządzenie EMS, a następnie dostosuj opcje lub kliknij na nazwie encji by tę nazwę zmienić',
|
CUSTOMIZATIONS_HELP_1: 'Wybierz urządzenie EMS, dostosuj opcje lub kliknij by zmienić nazwę encji.',
|
||||||
CUSTOMIZATIONS_HELP_2: 'oznacz jako ulubioną',
|
CUSTOMIZATIONS_HELP_2: 'oznacz jako ulubioną',
|
||||||
CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu',
|
CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu',
|
||||||
CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API',
|
CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API',
|
||||||
@@ -159,12 +162,15 @@ 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?',
|
||||||
SUPPORT_INFORMATION: '{{I|i|}}nformacj{{e|i|}} o systemie',
|
DEVICE_ENTITIES: 'Encje urządzenia',
|
||||||
HELP_INFORMATION_1: 'Aby uzyskać instrukcje dotyczące konfiguracji EMS-ESP, skorzystaj z wiki w internecie',
|
SUPPORT_INFORMATION: 'Informacje dotyczące wsparcia',
|
||||||
|
CLICK_HERE: 'Kliknij tu',
|
||||||
|
HELP_INFORMATION_1: 'Aby uzyskać instrukcje dotyczące konfiguracji EMS-ESP skorzystaj z wiki w internecie',
|
||||||
HELP_INFORMATION_2: 'Aby dołączyć do naszego serwera Discord i komunikować się na żywo ze społecznością',
|
HELP_INFORMATION_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',
|
||||||
HELP_INFORMATION_4: 'Zgłaszając problem, nie zapomnij pobrać i dołączyć informacji o swoim systemie!',
|
HELP_INFORMATION_4: 'Zgłaszając problem, nie zapomnij dołączyć informacji o swoim systemie!',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP jest darmowym projektem typu open-source. Aby go wesprzeć, rozważ przyznanie nam gwiazdki na Github!',
|
HELP_INFORMATION_5: 'EMS-ESP jest darmowym projektem typu open-source. Aby go wesprzeć, rozważ przyznanie nam gwiazdki na Github!',
|
||||||
|
SUPPORT_INFO: 'Pobierz informacje',
|
||||||
UPLOAD: 'Wysyłanie',
|
UPLOAD: 'Wysyłanie',
|
||||||
DOWNLOAD: '{{P|p||P}}obier{{anie|z||z}}',
|
DOWNLOAD: '{{P|p||P}}obier{{anie|z||z}}',
|
||||||
ABORTED: 'zostało przerwane!',
|
ABORTED: 'zostało przerwane!',
|
||||||
@@ -175,30 +181,34 @@ 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',
|
||||||
RELEASE_IS: 'wydanie to',
|
RELEASE_IS: 'wydanie to',
|
||||||
RELEASE_NOTES: 'lista zmian',
|
RELEASE_NOTES: 'lista zmian',
|
||||||
EMS_ESP_VER: 'Wersja EMS-ESP',
|
EMS_ESP_VER: 'Wersja EMS-ESP',
|
||||||
|
PLATFORM: 'Urządzenie (platforma / SDK)',
|
||||||
UPTIME: 'Czas działania systemu',
|
UPTIME: 'Czas działania systemu',
|
||||||
|
CPU_FREQ: 'Taktowanie CPU',
|
||||||
HEAP: 'HEAP (wolne / maksymalny przydział)',
|
HEAP: 'HEAP (wolne / maksymalny przydział)',
|
||||||
PSRAM: 'PSRAM (rozmiar / wolne)',
|
PSRAM: 'PSRAM (rozmiar / wolne)',
|
||||||
FLASH: 'FLASH (rozmiar / taktowanie)',
|
FLASH: 'FLASH (rozmiar / taktowanie)',
|
||||||
APPSIZE: 'Aplikacja (partycja: wykorzystane / wolne)',
|
APPSIZE: 'Aplikacja (wykorzystane / wolne)',
|
||||||
FILESYSTEM: 'System plików (wykorzystane / wolne)',
|
FILESYSTEM: 'System plików (wykorzystane / wolne)',
|
||||||
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
||||||
COMPACT: 'Kompaktowy',
|
COMPACT: 'Kompaktowy',
|
||||||
ENABLE_OTA: 'Aktywuj aktualizację OTA',
|
ENABLE_OTA: 'Aktywuj aktualizację OTA',
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Pobierz personalizacje.',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Pobierz personalizacje.',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uwaga! Plik z ustawieniami zawiera hasła oraz inne wrażliwe informacje systemowe! Nie udostepniaj go pochopnie!',
|
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uważaj jeśli udostępniasz plik z ustawieniami, ponieważ zawiera on hasła oraz inne wrażliwe informacje!',
|
||||||
UPLOAD_TEXT: 'Wyślij firmware (.bin), ustawienia lub personalizacje (.json). Opcjonalnie, wyślij wcześniej plik walidacji z sumą kontrolną (.md5).',
|
UPLOAD_TEXT: 'Wyślij firmware (.bin), ustawienia lub personalizacje (.json). Opcjonalnie, wyślij wcześniej plik walidacji (.md5).',
|
||||||
UPLOADING: 'Wysłano',
|
UPLOADING: 'Wysłano',
|
||||||
UPLOAD_DROP_TEXT: 'Przeciągnij tutaj plik lub kliknij',
|
UPLOAD_DROP_TEXT: 'Przeciągnij tutaj plik lub kliknij',
|
||||||
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
||||||
@@ -236,7 +246,6 @@ 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"',
|
||||||
@@ -272,15 +281,14 @@ const pl: BaseTranslation = {
|
|||||||
SCAN_AGAIN: 'Skanuj ponownie',
|
SCAN_AGAIN: 'Skanuj ponownie',
|
||||||
NETWORK_SCANNER: 'Skaner sieci WiFi',
|
NETWORK_SCANNER: 'Skaner sieci WiFi',
|
||||||
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
||||||
NETWORK_BLANK_SSID: 'pozostaw puste aby wyłączyć WiFi i włączyć ETH',
|
NETWORK_BLANK_SSID: 'pozostaw puste aby wyłączyć WiFi', // and enable ETH // TODO translate
|
||||||
NETWORK_BLANK_BSSID: 'pozostaw puste aby używać tylko SSID',
|
|
||||||
TX_POWER: 'Moc nadawania',
|
TX_POWER: 'Moc nadawania',
|
||||||
HOSTNAME: 'Nazwa w sieci',
|
HOSTNAME: 'Nazwa w sieci',
|
||||||
NETWORK_DISABLE_SLEEP: 'Wyłącz tryb uśpienia WiFi',
|
NETWORK_DISABLE_SLEEP: 'Wyłącz tryb uśpienia WiFi',
|
||||||
NETWORK_LOW_BAND: 'Używaj mniejszej szerokości pasma WiFi (20MHz)',
|
NETWORK_LOW_BAND: 'Używaj mniejszej szerokości pasma WiFi (20MHz)',
|
||||||
NETWORK_USE_DNS: 'Włącz wsparcie dla mDNS',
|
NETWORK_USE_DNS: 'Włącz wsparcie dla mDNS',
|
||||||
NETWORK_ENABLE_CORS: 'Włącz wsparcie dla CORS',
|
NETWORK_ENABLE_CORS: 'Włącz wsparcie dla CORS',
|
||||||
NETWORK_CORS_ORIGIN: 'CORS Origin',
|
NETWORK_CORS_ORIGIN: 'CORS origin',
|
||||||
NETWORK_ENABLE_IPV6: 'Włącz wsparcie dla IPv6',
|
NETWORK_ENABLE_IPV6: 'Włącz wsparcie dla IPv6',
|
||||||
NETWORK_FIXED_IP: 'Użyj stałego adresu IP',
|
NETWORK_FIXED_IP: 'Użyj stałego adresu IP',
|
||||||
NETWORK_GATEWAY: 'Brama',
|
NETWORK_GATEWAY: 'Brama',
|
||||||
@@ -311,20 +319,10 @@ const pl: BaseTranslation = {
|
|||||||
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.',
|
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',
|
||||||
SEARCH: 'Szukaj',
|
SEARCH: 'Szukaj',
|
||||||
CERT: 'Certyfikat główny TLS (pozostaw puste dla TLS-insecure)',
|
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
|
||||||
ENABLE_TLS: 'Włącz wsparcie dla TLS',
|
|
||||||
ON: 'włączony',
|
|
||||||
OFF: 'wyłączony',
|
|
||||||
POLARITY: 'Typ przekaźnika',
|
|
||||||
ACTIVEHIGH: 'Wyzwalany stanem wysokim',
|
|
||||||
ACTIVELOW: 'Wyzwalany stanem niskim',
|
|
||||||
UNCHANGED: 'Zachowaj stan',
|
|
||||||
ALWAYS: 'Zawsze',
|
|
||||||
ACTIVITY: 'Aktywność',
|
|
||||||
CONFIGURE: 'Konfiguracja {0}'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -1,331 +0,0 @@
|
|||||||
import type { Translation } from '../i18n-types';
|
|
||||||
/* prettier-ignore */
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
const sk: Translation = {
|
|
||||||
LANGUAGE: 'Jazyk',
|
|
||||||
RETRY: 'Opakovať',
|
|
||||||
LOADING: 'Načítanie',
|
|
||||||
IS_REQUIRED: '{0} je požadovaných',
|
|
||||||
SIGN_IN: 'Prihlásiť sa',
|
|
||||||
SIGN_OUT: 'Odhlásiť sa',
|
|
||||||
USERNAME: 'Užívateľské meno',
|
|
||||||
PASSWORD: 'Heslo',
|
|
||||||
SU_PASSWORD: 'su heslo',
|
|
||||||
SETTINGS_OF: '{0} Nastavenia',
|
|
||||||
HELP_OF: '{0} Pomoc',
|
|
||||||
LOGGED_IN: 'Prihlásený ako {name}',
|
|
||||||
PLEASE_SIGNIN: 'Ak chcete pokračovať, prihláste sa',
|
|
||||||
UPLOAD_SUCCESSFUL: 'Nahratie úspešné',
|
|
||||||
DOWNLOAD_SUCCESSFUL: 'Stiahnutie úspešné',
|
|
||||||
INVALID_LOGIN: 'Nesprávne prihlasovacie údaje',
|
|
||||||
NETWORK: 'Sieť',
|
|
||||||
SECURITY: 'Zabezpečenie',
|
|
||||||
ONOFF_CAP: 'ZAP/VYP',
|
|
||||||
ONOFF: 'zap/vyp',
|
|
||||||
TYPE: 'Typ',
|
|
||||||
DESCRIPTION: 'Popis',
|
|
||||||
ENTITIES: 'Entity',
|
|
||||||
REFRESH: 'Obnoviť',
|
|
||||||
EXPORT: 'Export',
|
|
||||||
DEVICE_DETAILS: 'Detaily zariadenia',
|
|
||||||
ID_OF: '{0} ID',
|
|
||||||
DEVICE: 'Zariadenie',
|
|
||||||
PRODUCT: 'Produkt',
|
|
||||||
VERSION: 'Verzia',
|
|
||||||
BRAND: 'Značka',
|
|
||||||
ENTITY_NAME: 'Názov entity',
|
|
||||||
VALUE: '{{Hodnota|hodnota}}',
|
|
||||||
DEVICES: 'Zariadenia',
|
|
||||||
SENSORS: 'Snímače',
|
|
||||||
RUN_COMMAND: 'Volať príkaz',
|
|
||||||
CHANGE_VALUE: 'Zmena hodnoty',
|
|
||||||
CANCEL: 'Zrušiť',
|
|
||||||
RESET: 'Reset',
|
|
||||||
APPLY_CHANGES: 'Aplikovať zmeny ({0})',
|
|
||||||
UPDATE: 'Aktualizovať',
|
|
||||||
EXECUTE: 'Spustiť',
|
|
||||||
REMOVE: 'Odstrániť',
|
|
||||||
PROBLEM_UPDATING: 'Problém s aktualizáciou',
|
|
||||||
PROBLEM_LOADING: 'Problém s načítaním',
|
|
||||||
ANALOG_SENSOR: 'Analógový snímač',
|
|
||||||
ANALOG_SENSORS: 'Analógové snímače',
|
|
||||||
SETTINGS: 'Nastavenia',
|
|
||||||
UPDATED_OF: '{0} aktualizovaných',
|
|
||||||
UPDATE_OF: '{0} aktualizované',
|
|
||||||
REMOVED_OF: '{0} odstránených',
|
|
||||||
DELETION_OF: '{0} zmazaných',
|
|
||||||
OFFSET: 'Ofset',
|
|
||||||
FACTOR: 'Faktor',
|
|
||||||
FREQ: 'Frekvencia',
|
|
||||||
DUTY_CYCLE: 'Duty Cycle',
|
|
||||||
UNIT: 'UoM',
|
|
||||||
STARTVALUE: 'Počiatočná hodnota',
|
|
||||||
WARN_GPIO: 'Upozornenie: Buďte opatrní pri priraďovaní GPIO!',
|
|
||||||
EDIT: 'Editovať',
|
|
||||||
SENSOR: 'Snímač',
|
|
||||||
TEMP_SENSOR: 'Snímač teploty',
|
|
||||||
TEMP_SENSORS: 'Snímače teploty',
|
|
||||||
WRITE_CMD_SENT: 'Príkaz zápisu bol odoslaný',
|
|
||||||
EMS_BUS_WARNING: 'Zbernica EMS odpojená. Ak toto upozornenie pretrváva aj po niekoľkých sekundách, skontrolujte nastavenia a profil dosky',
|
|
||||||
EMS_BUS_SCANNING: 'Zisťovanie EMS zariadení...',
|
|
||||||
CONNECTED: 'Pripojené',
|
|
||||||
TX_ISSUES: 'Problémy s Tx – skontrolujte Tx režim',
|
|
||||||
DISCONNECTED: 'Odpojené',
|
|
||||||
EMS_SCAN: 'Naozaj chcete spustiť úplnú kontrolu zariadenia zbernice EMS?',
|
|
||||||
EMS_BUS_STATUS: 'Stav zbernice EMS',
|
|
||||||
ACTIVE_DEVICES: 'Aktívne zariadenia a snímače',
|
|
||||||
EMS_DEVICE: 'EMS zariadenie',
|
|
||||||
SUCCESS: 'ÚSPEŠNÉ',
|
|
||||||
FAIL: 'ZLÝHANIE',
|
|
||||||
QUALITY: 'KVALITA',
|
|
||||||
SCAN_DEVICES: 'Scan pre nové zariadenia',
|
|
||||||
SCAN: 'Scan',
|
|
||||||
STATUS_NAMES: [
|
|
||||||
'EMS Telegramy prijaté (Rx)',
|
|
||||||
'EMS Čítania (Tx)',
|
|
||||||
'EMS Zápisy (Tx)',
|
|
||||||
'Čítanie snímačov teploty',
|
|
||||||
'Čítanie analógových snímačov',
|
|
||||||
'MQTT Publikovanie',
|
|
||||||
'Externé API volania',
|
|
||||||
'Syslog správy'
|
|
||||||
],
|
|
||||||
NUM_DEVICES: '{num} Zariaden{{í|ie|ia|ia|í|í}}',
|
|
||||||
NUM_TEMP_SENSORS: '{num} Teplotn{{ých|ý|é|é|ých|ých}} sníma{{čov|č|če|če|čov|čov}}',
|
|
||||||
NUM_ANALOG_SENSORS: '{num} Analogov{{ých|ý|é|é|ých|ých}} sníma{{čov|č|če|če|čov|čov}}',
|
|
||||||
NUM_DAYS: '{num} d{{ní|eň|ní|ní|ní|ní}}',
|
|
||||||
NUM_SECONDS: '{num} sek{{únd|unda|undy|undy|únd|únd}}',
|
|
||||||
NUM_HOURS: '{num} hod{{ín|ina|iny|iny|ín|ín}}',
|
|
||||||
NUM_MINUTES: '{num} minú{{t|ta|ty|ty|t|t}}',
|
|
||||||
APPLICATION_SETTINGS: 'Nastavenia aplikácie',
|
|
||||||
CUSTOMIZATIONS: 'Prispôsobenia',
|
|
||||||
APPLICATION_RESTARTING: 'EMS-ESP sa reštartuje',
|
|
||||||
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: 'Profil dosky',
|
|
||||||
CUSTOM: 'Vlastné',
|
|
||||||
GPIO_OF: '{0} GPIO',
|
|
||||||
BUTTON: 'Tlačidlo',
|
|
||||||
TEMPERATURE: 'Teplota',
|
|
||||||
PHY_TYPE: 'Eth PHY Typ',
|
|
||||||
DISABLED: 'zakázané',
|
|
||||||
TX_MODE: 'Tx režim',
|
|
||||||
HARDWARE: 'Hardware',
|
|
||||||
EMS_BUS: '{{BUS|EMS BUS}}',
|
|
||||||
GENERAL_OPTIONS: 'Všeobecné možnosti',
|
|
||||||
LANGUAGE_ENTITIES: 'Jazyk (pre entity zariadenia)',
|
|
||||||
HIDE_LED: 'Skryť LED',
|
|
||||||
ENABLE_TELNET: 'Povoliť Telnet konzolu',
|
|
||||||
ENABLE_ANALOG: 'Povoliť analógové snímače',
|
|
||||||
CONVERT_FAHRENHEIT: 'Previesť hodnoty teploty na °F',
|
|
||||||
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)',
|
|
||||||
UNDERCLOCK_CPU: 'Podtaktovanie rýchlosti procesora',
|
|
||||||
HEATINGOFF: 'Spustiť kotol s vynúteným vykurovaním',
|
|
||||||
|
|
||||||
ENABLE_SHOWER_TIMER: 'Povoliť časovač sprchovania',
|
|
||||||
ENABLE_SHOWER_ALERT: 'Povoliť upozornenie na sprchu',
|
|
||||||
TRIGGER_TIME: 'Čas spustenia',
|
|
||||||
COLD_SHOT_DURATION: 'Trvanie studeného záberu',
|
|
||||||
FORMATTING_OPTIONS: 'Možnosti formátovania',
|
|
||||||
BOOLEAN_FORMAT_DASHBOARD: 'Panel Boolean formát',
|
|
||||||
BOOLEAN_FORMAT_API: 'Boolean formát API/MQTT',
|
|
||||||
ENUM_FORMAT: 'Enum formát API/MQTT',
|
|
||||||
INDEX: 'Index',
|
|
||||||
ENABLE_PARASITE: 'Povoliť parazité napájanie DS18B20',
|
|
||||||
LOGGING: 'Logovanie',
|
|
||||||
LOG_HEX: 'Záznam telegramov EMS v hexadecimálnej sústave',
|
|
||||||
ENABLE_SYSLOG: 'Povoliť Syslog',
|
|
||||||
LOG_LEVEL: 'Log úroveň',
|
|
||||||
MARK_INTERVAL: 'Označenie intervalu',
|
|
||||||
SECONDS: 'sekundy',
|
|
||||||
MINUTES: 'minúty',
|
|
||||||
HOURS: 'hodiny',
|
|
||||||
RESTART: 'Reštart',
|
|
||||||
RESTART_TEXT: 'EMS-ESP sa musí reštartovať, aby sa použili zmenené systémové nastavenia',
|
|
||||||
RESTART_CONFIRM: 'Ste si istí, že chcete reštartovať EMS-ESP?',
|
|
||||||
COMMAND: 'Príkaz',
|
|
||||||
CUSTOMIZATIONS_RESTART: 'Ste si istí, že chcete reštartovať EMS-ESP?',
|
|
||||||
CUSTOMIZATIONS_FULL: 'Vybrané subjekty prekročili limit. Prosím, ukladajte v dávkach',
|
|
||||||
CUSTOMIZATIONS_SAVED: 'Uložené prispôsobenia',
|
|
||||||
CUSTOMIZATIONS_HELP_1: 'Vyberte zariadenie a prispôsobte možnosti entít alebo kliknutím premenujte',
|
|
||||||
CUSTOMIZATIONS_HELP_2: 'označiť ako obľúbené',
|
|
||||||
CUSTOMIZATIONS_HELP_3: 'zakázať akciu zápisu',
|
|
||||||
CUSTOMIZATIONS_HELP_4: 'vylúčiť z MQTT a API',
|
|
||||||
CUSTOMIZATIONS_HELP_5: 'skryť z panela',
|
|
||||||
CUSTOMIZATIONS_HELP_6: 'odstrániť z pamäte',
|
|
||||||
SELECT_DEVICE: 'Zvoliť zariadenie',
|
|
||||||
SET_ALL: 'nastaviť všetko',
|
|
||||||
OPTIONS: 'Možnosti',
|
|
||||||
NAME: 'Názov',
|
|
||||||
CUSTOMIZATIONS_RESET: 'Naozaj chcete odstrániť všetky prispôsobenia vrátane vlastných nastavení snímačov teploty a analógových snímačov?',
|
|
||||||
SUPPORT_INFORMATION: 'Informácie pre podporu',
|
|
||||||
HELP_INFORMATION_1: 'Navštívte online wiki, kde nájdete pokyny na konfiguráciu EMS-ESP',
|
|
||||||
HELP_INFORMATION_2: 'Pre živý komunitný chat sa pripojte na náš Discord server',
|
|
||||||
HELP_INFORMATION_3: 'Ak chcete požiadať o funkciu alebo nahlásiť chybu',
|
|
||||||
HELP_INFORMATION_4: 'nezabudnite si stiahnuť a pripojiť informácie o vašom systéme, aby ste mohli rýchlejšie reagovať pri nahlasovaní problému',
|
|
||||||
HELP_INFORMATION_5: 'EMS-ESP je bezplatný a open source projekt. Podporte jeho budúci vývoj tým, že mu dáte hviezdičku na Github!',
|
|
||||||
UPLOAD: 'Nahrať',
|
|
||||||
DOWNLOAD: '{{S|s|s}}tiahnuť',
|
|
||||||
ABORTED: 'zrušené',
|
|
||||||
FAILED: 'chybné',
|
|
||||||
SUCCESSFUL: 'úspešné',
|
|
||||||
SYSTEM: 'Systém',
|
|
||||||
LOG_OF: '{0} Log',
|
|
||||||
STATUS_OF: '{0} Stav',
|
|
||||||
UPLOAD_DOWNLOAD: 'Nahrať/Stiahnuť',
|
|
||||||
VERSION_ON: 'Momentálne nainštalovaná verzia: ',
|
|
||||||
CLOSE: 'Zatvoriť',
|
|
||||||
USE: 'Použiť',
|
|
||||||
FACTORY_RESET: 'Továrenské nastavenia',
|
|
||||||
SYSTEM_FACTORY_TEXT: 'Zariadenie bolo obnovené z výroby a teraz sa reštartuje',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?',
|
|
||||||
THE_LATEST: 'Posledná',
|
|
||||||
OFFICIAL: 'officiálna',
|
|
||||||
DEVELOPMENT: 'vývojárska',
|
|
||||||
RELEASE_IS: 'verzia je',
|
|
||||||
RELEASE_NOTES: 'poznámky k verzii',
|
|
||||||
EMS_ESP_VER: 'EMS-ESP verzia',
|
|
||||||
UPTIME: 'Beh systému',
|
|
||||||
HEAP: 'Zásobník (voľné / max pridelenie)',
|
|
||||||
PSRAM: 'PSRAM (Veľkosť / Voľné)',
|
|
||||||
FLASH: 'Flash chip (Veľkosť / Rýchlosť)',
|
|
||||||
APPSIZE: 'Applikácia (Oddiel: Použité / Voľné)',
|
|
||||||
FILESYSTEM: 'Súborový systém (Použité / Voľné)',
|
|
||||||
BUFFER_SIZE: 'Buffer-max.veľkosť',
|
|
||||||
COMPACT: 'Kompaktné',
|
|
||||||
ENABLE_OTA: 'Povoliť OTA aktualizácie',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Stiahnutie prispôsobení entity',
|
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Stiahnutie plánovača udalostí',
|
|
||||||
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)',
|
|
||||||
UPLOADING: 'Nahrávanie',
|
|
||||||
UPLOAD_DROP_TEXT: 'Potiahnúť a pripnúť súbor alebo kliknúť sem',
|
|
||||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
|
||||||
TIME_SET: 'Nastavený čas',
|
|
||||||
MANAGE_USERS: 'Správa používateľov',
|
|
||||||
IS_ADMIN: 'je Admin',
|
|
||||||
USER_WARNING: 'Musíte mať nakonfigurovaného aspoň jedného používateľa administrátora',
|
|
||||||
ADD: 'Pridať',
|
|
||||||
ACCESS_TOKEN_FOR: 'Prístupový token pre',
|
|
||||||
ACCESS_TOKEN_TEXT: 'Nižšie uvedený token sa používa pri volaniach REST API, ktoré vyžadujú autorizáciu. Môže byť odovzdaný buď ako token Bearer v hlavičke Authorization (Autorizácia), alebo v parametri dotazu URL access_token.',
|
|
||||||
GENERATING_TOKEN: 'Generovanie tokenu',
|
|
||||||
USER: 'Užívateľ',
|
|
||||||
MODIFY: 'Upraviť',
|
|
||||||
SU_TEXT: 'Heslo su (superužívateľ) sa používa na podpisovanie autentifikačných tokenov a tiež na povolenie oprávnení správcu v rámci konzoly.',
|
|
||||||
NOT_ENABLED: 'Nie je povolené',
|
|
||||||
ERRORS_OF: '{0} errory',
|
|
||||||
DISCONNECT_REASON: 'Dôvod odpojenia',
|
|
||||||
ENABLE_MQTT: 'Povoliť MQTT',
|
|
||||||
BROKER: 'Broker',
|
|
||||||
CLIENT: 'Klient',
|
|
||||||
BASE_TOPIC: 'Base',
|
|
||||||
OPTIONAL: 'voliteľné',
|
|
||||||
FORMATTING: 'Formátovanie',
|
|
||||||
MQTT_FORMAT: 'Formát témy/záťaže',
|
|
||||||
MQTT_NEST_1: 'Vnorené do jednej témy',
|
|
||||||
MQTT_NEST_2: 'Ako jednotlivé témy',
|
|
||||||
MQTT_RESPONSE: 'Publikovanie výstupu príkazu do témy `response`',
|
|
||||||
MQTT_PUBLISH_TEXT_1: 'Zverejňovanie tém jednotlivých hodnôt pri zmene',
|
|
||||||
MQTT_PUBLISH_TEXT_2: 'Publikovanie do tém príkazov (ioBroker)',
|
|
||||||
MQTT_PUBLISH_TEXT_3: 'Povolenie zisťovania MQTT',
|
|
||||||
MQTT_PUBLISH_TEXT_4: 'Predpona tém Discovery',
|
|
||||||
MQTT_PUBLISH_TEXT_5: 'Typ zistenia',
|
|
||||||
MQTT_PUBLISH_INTERVALS: 'Intervaly zverejňovania',
|
|
||||||
MQTT_INT_BOILER: 'Kotly a tepelné čerpadlá',
|
|
||||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
|
||||||
MQTT_INT_SOLAR: 'Solárne moduly',
|
|
||||||
MQTT_INT_MIXER: 'Zmiešavacie moduley',
|
|
||||||
MQTT_INT_WATER: 'Voda moduley',
|
|
||||||
MQTT_QUEUE: 'Fronta MQTT',
|
|
||||||
DEFAULT: 'Predvolené',
|
|
||||||
MQTT_ENTITY_FORMAT: 'ID formát entity',
|
|
||||||
MQTT_ENTITY_FORMAT_0: 'Jedna inštancia, dlhý názov (v3.4)',
|
|
||||||
MQTT_ENTITY_FORMAT_1: 'Jedna inštancia, krátky názov',
|
|
||||||
MQTT_ENTITY_FORMAT_2: 'Viacero inštancií, krátky názov',
|
|
||||||
MQTT_CLEAN_SESSION: 'Nastavenie čistej relácie',
|
|
||||||
MQTT_RETAIN_FLAG: 'Vždy nastaviť príznak Retain',
|
|
||||||
INACTIVE: 'Neaktívne',
|
|
||||||
ACTIVE: 'Aktívne',
|
|
||||||
UNKNOWN: 'Neznáme',
|
|
||||||
SET_TIME: 'Nastavený čas',
|
|
||||||
SET_TIME_TEXT: 'Na nastavenie času zadajte miestny dátum a čas nižšie',
|
|
||||||
LOCAL_TIME: 'Lokálny čas',
|
|
||||||
UTC_TIME: 'UTC čas',
|
|
||||||
ENABLE_NTP: 'Povoliť NTP',
|
|
||||||
NTP_SERVER: 'NTP Server',
|
|
||||||
TIME_ZONE: 'Časová zóna',
|
|
||||||
ACCESS_POINT: 'Prístupový bod',
|
|
||||||
AP_PROVIDE: 'Povoliť prístupový bod',
|
|
||||||
AP_PROVIDE_TEXT_1: 'vždy',
|
|
||||||
AP_PROVIDE_TEXT_2: 'keď je WiFi odpojená',
|
|
||||||
AP_PROVIDE_TEXT_3: 'nikdy',
|
|
||||||
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
|
||||||
AP_HIDE_SSID: 'Skryť SSID',
|
|
||||||
AP_CLIENTS: 'AP klienti',
|
|
||||||
AP_MAX_CLIENTS: 'Max klientov',
|
|
||||||
AP_LOCAL_IP: 'Lokálna IP',
|
|
||||||
NETWORK_SCAN: 'Scan WiFi siete',
|
|
||||||
IDLE: 'Nečinné',
|
|
||||||
LOST: 'Stratené',
|
|
||||||
SCANNING: 'Scanovanie',
|
|
||||||
SCAN_AGAIN: 'Scanovať znova',
|
|
||||||
NETWORK_SCANNER: 'Sieťový scanner',
|
|
||||||
NETWORK_NO_WIFI: 'WiFi siete nenájdené',
|
|
||||||
NETWORK_BLANK_SSID: 'nechajte prázdne, ak chcete zakázať WiFi a povoliť ETH',
|
|
||||||
NETWORK_BLANK_BSSID: 'ponechajte prázdne, ak chcete používať iba SSID',
|
|
||||||
TX_POWER: 'Tx výkon',
|
|
||||||
HOSTNAME: 'Hostname',
|
|
||||||
NETWORK_DISABLE_SLEEP: 'Zakázanie režimu spánku WiFi',
|
|
||||||
NETWORK_LOW_BAND: 'Používanie menšej šírky pásma WiFi',
|
|
||||||
NETWORK_USE_DNS: 'Povoliť mDNS službu',
|
|
||||||
NETWORK_ENABLE_CORS: 'Povoliť CORS',
|
|
||||||
NETWORK_CORS_ORIGIN: 'CORS origin',
|
|
||||||
NETWORK_ENABLE_IPV6: 'Povoliť podporu IPv6',
|
|
||||||
NETWORK_FIXED_IP: 'Použiť fixnú IP adresu',
|
|
||||||
NETWORK_GATEWAY: 'Brána',
|
|
||||||
NETWORK_SUBNET: 'Maska podsiete',
|
|
||||||
NETWORK_DNS: 'DNS servery',
|
|
||||||
ADDRESS_OF: '{0} adresa',
|
|
||||||
ADMIN: 'Admin',
|
|
||||||
GUEST: 'Hosť',
|
|
||||||
NEW: 'Nová',
|
|
||||||
NEW_NAME_OF: 'Nový názov {0}',
|
|
||||||
ENTITY: 'entita',
|
|
||||||
MIN: 'min',
|
|
||||||
MAX: 'max',
|
|
||||||
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
|
|
||||||
BLOCK_NAVIGATE_2: 'Ak prejdete na inú stránku, neuložené zmeny sa stratia. Ste si istí, že chcete opustiť túto stránku?',
|
|
||||||
STAY: 'Zostať',
|
|
||||||
LEAVE: 'Opustiť',
|
|
||||||
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_2: 'Použite 00:00 na jednorazové spustenie pri štarte',
|
|
||||||
SCHEDULE: 'Plánovač',
|
|
||||||
TIME: 'Čas',
|
|
||||||
TIMER: 'Časovač',
|
|
||||||
SCHEDULE_UPDATED: 'Plánovanie aktualizované',
|
|
||||||
SCHEDULE_TIMER_1: 'pri spustení',
|
|
||||||
SCHEDULE_TIMER_2: 'každú minútu',
|
|
||||||
SCHEDULE_TIMER_3: 'každú hodinu',
|
|
||||||
CUSTOM_ENTITIES: 'Vlastné entity',
|
|
||||||
ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS',
|
|
||||||
ENTITIES_UPDATED: 'Aktualizované entity',
|
|
||||||
WRITEABLE: 'Zapísateľný',
|
|
||||||
SHOWING: 'Zobrazenie',
|
|
||||||
SEARCH: 'Vyhľadať',
|
|
||||||
CERT: 'Koreňový certifikát TLS (ak chcete vypnúť TLS, nechajte prázdne)',
|
|
||||||
ENABLE_TLS: 'Povoliť TLS',
|
|
||||||
ON: 'Zap',
|
|
||||||
OFF: 'Vyp',
|
|
||||||
POLARITY: 'Polarita',
|
|
||||||
ACTIVEHIGH: 'Aktívny Vysoký',
|
|
||||||
ACTIVELOW: 'Aktívny Nízky',
|
|
||||||
UNCHANGED: 'Nezmenené',
|
|
||||||
ALWAYS: 'Vždy',
|
|
||||||
ACTIVITY: 'Aktivita',
|
|
||||||
CONFIGURE: 'Konfiguracia {0}'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default sk;
|
|
||||||
@@ -12,6 +12,7 @@ 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}',
|
||||||
@@ -36,6 +37,8 @@ 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',
|
||||||
@@ -80,6 +83,7 @@ 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)',
|
||||||
@@ -122,7 +126,6 @@ const sv: Translation = {
|
|||||||
BYPASS_TOKEN: 'Inaktivera Token-autensiering för API-anrop',
|
BYPASS_TOKEN: 'Inaktivera Token-autensiering för API-anrop',
|
||||||
READONLY: 'Aktivera read-only (blockerar alla utgående skrivkommandon mot EMS-bussen)',
|
READONLY: 'Aktivera read-only (blockerar alla utgående skrivkommandon mot EMS-bussen)',
|
||||||
UNDERCLOCK_CPU: 'Nedklocka Processorhastighet',
|
UNDERCLOCK_CPU: 'Nedklocka Processorhastighet',
|
||||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
|
||||||
ENABLE_SHOWER_TIMER: 'Aktivera Dusch-timer',
|
ENABLE_SHOWER_TIMER: 'Aktivera Dusch-timer',
|
||||||
ENABLE_SHOWER_ALERT: 'Aktivera Dusch-varning',
|
ENABLE_SHOWER_ALERT: 'Aktivera Dusch-varning',
|
||||||
TRIGGER_TIME: 'Aktiveringstid',
|
TRIGGER_TIME: 'Aktiveringstid',
|
||||||
@@ -159,12 +162,15 @@ 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',
|
||||||
HELP_INFORMATION_4: 'Bifoga din systeminformation för snabbare hantering när du rapporterar ett problem',
|
HELP_INFORMATION_4: 'Bifoga din systeminformation för snabbare hantering när du rapporterar ett problem',
|
||||||
HELP_INFORMATION_5: 'EMS-ESP är gratis och är öppen källkod. Bidra till utvecklingen genom att ge oss en stjärna på GitHub!',
|
HELP_INFORMATION_5: 'EMS-ESP är gratis och är öppen källkod. Bidra till utvecklingen genom att ge oss en stjärna på GitHub!',
|
||||||
|
SUPPORT_INFO: 'Supportinfo',
|
||||||
UPLOAD: 'Uppladdning',
|
UPLOAD: 'Uppladdning',
|
||||||
DOWNLOAD: '{{N|n|n}}edladdning',
|
DOWNLOAD: '{{N|n|n}}edladdning',
|
||||||
ABORTED: 'Avbruten',
|
ABORTED: 'Avbruten',
|
||||||
@@ -175,22 +181,26 @@ 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',
|
||||||
RELEASE_IS: 'release är', // TODO translate
|
RELEASE_IS: 'release är', // TODO translate
|
||||||
RELEASE_NOTES: 'release-logg',
|
RELEASE_NOTES: 'release-logg',
|
||||||
EMS_ESP_VER: 'EMS-ESP Version',
|
EMS_ESP_VER: 'EMS-ESP Version',
|
||||||
|
PLATFORM: 'Enhet (Plattform / SDK)',
|
||||||
UPTIME: 'Systemets Upptid',
|
UPTIME: 'Systemets Upptid',
|
||||||
|
CPU_FREQ: 'CPU-frekvens',
|
||||||
HEAP: 'Heap (Ledigt / Max allokerat)',
|
HEAP: 'Heap (Ledigt / Max allokerat)',
|
||||||
PSRAM: 'PSRAM (Storlek / Ledigt)',
|
PSRAM: 'PSRAM (Storlek / Ledigt)',
|
||||||
FLASH: 'Flashminne (Storlek / Hastighet)',
|
FLASH: 'Flashminne (Storlek / Hastighet)',
|
||||||
APPSIZE: 'Applikationer (Partition: Använt / Ledigt)',
|
APPSIZE: 'Applikationer (Använt / Ledigt)',
|
||||||
FILESYSTEM: 'Filsystem (Använt / Ledigt)',
|
FILESYSTEM: 'Filsystem (Använt / Ledigt)',
|
||||||
BUFFER_SIZE: 'Max Bufferstorlek',
|
BUFFER_SIZE: 'Max Bufferstorlek',
|
||||||
COMPACT: 'Komprimera',
|
COMPACT: 'Komprimera',
|
||||||
@@ -220,7 +230,7 @@ const sv: Translation = {
|
|||||||
BROKER: 'Broker',
|
BROKER: 'Broker',
|
||||||
CLIENT: 'Client',
|
CLIENT: 'Client',
|
||||||
BASE_TOPIC: 'Base',
|
BASE_TOPIC: 'Base',
|
||||||
OPTIONAL: 'valfritt',
|
OPTIONAL: 'Valfritt',
|
||||||
FORMATTING: 'Formatering',
|
FORMATTING: 'Formatering',
|
||||||
MQTT_FORMAT: 'Topic/Payload Format',
|
MQTT_FORMAT: 'Topic/Payload Format',
|
||||||
MQTT_NEST_1: 'Nestlat i en topic.',
|
MQTT_NEST_1: 'Nestlat i en topic.',
|
||||||
@@ -236,7 +246,6 @@ 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',
|
||||||
@@ -273,7 +282,6 @@ const sv: Translation = {
|
|||||||
NETWORK_SCANNER: 'Hittade nätverk',
|
NETWORK_SCANNER: 'Hittade nätverk',
|
||||||
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
||||||
NETWORK_BLANK_SSID: 'lämna blankt för att inaktivera WiFi', // and enable ETH // TODO translate
|
NETWORK_BLANK_SSID: 'lämna blankt för att inaktivera WiFi', // and enable ETH // TODO translate
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
|
||||||
TX_POWER: 'Tx Effekt',
|
TX_POWER: 'Tx Effekt',
|
||||||
HOSTNAME: 'Värdnamn',
|
HOSTNAME: 'Värdnamn',
|
||||||
NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge',
|
NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge',
|
||||||
@@ -314,17 +322,7 @@ const sv: Translation = {
|
|||||||
WRITEABLE: 'Writeable', // TODO translate
|
WRITEABLE: 'Writeable', // TODO translate
|
||||||
SHOWING: 'Showing', // TODO translate
|
SHOWING: 'Showing', // TODO translate
|
||||||
SEARCH: 'Search', // TODO translate
|
SEARCH: 'Search', // TODO translate
|
||||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
|
||||||
ENABLE_TLS: 'Aktivera TLS',
|
|
||||||
ON: 'On', // TODO translate
|
|
||||||
OFF: 'Off', // TODO translate
|
|
||||||
POLARITY: 'Polarity', // TODO translate
|
|
||||||
ACTIVEHIGH: 'Active High', // TODO translate
|
|
||||||
ACTIVELOW: 'Active Low', // TODO translate
|
|
||||||
UNCHANGED: 'Unchanged', // TODO translate
|
|
||||||
ALWAYS: 'Always', // TODO translate
|
|
||||||
ACTIVITY: 'Activity', // TODO translate
|
|
||||||
CONFIGURE: 'Configure {0}' // TODO translate
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user