mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 17:29:50 +03:00
Compare commits
66 Commits
2b2c86ba5a
...
v3.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaa277fef0 | ||
|
|
e418b7d8e7 | ||
|
|
a4e3be6a69 | ||
|
|
a1bc5bb055 | ||
|
|
f444ca31e0 | ||
|
|
1b70b55989 | ||
|
|
1487f30c43 | ||
|
|
e00eb8e64f | ||
|
|
f41bb3671c | ||
|
|
22c75e6df3 | ||
|
|
5ab10b7aa6 | ||
|
|
ee5fd4d0eb | ||
|
|
46f35bc67c | ||
|
|
ec85a7ec24 | ||
|
|
02f2389587 | ||
|
|
7f140021aa | ||
|
|
6796962c1e | ||
|
|
df6de21cf4 | ||
|
|
df9f75a5c9 | ||
|
|
7bd8710eb6 | ||
|
|
32f2c6d341 | ||
|
|
86919c1684 | ||
|
|
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 |
@@ -1,6 +1,7 @@
|
||||
name: 'Publish releases to discord'
|
||||
name: 'github-releases-to-discord'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
|
||||
2
.github/workflows/pr_check.yml
vendored
2
.github/workflows/pr_check.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 'Pre-check on PR'
|
||||
name: 'pr_check'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,60 +1,53 @@
|
||||
name: 'Build dev release'
|
||||
name: 'pre-release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'src/emsesp_version.h'
|
||||
branches:
|
||||
- 'dev'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
pre-release:
|
||||
name: 'Build Dev Release'
|
||||
name: 'Automatic pre-release build'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Install python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- name: Install Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable pnpm
|
||||
|
||||
- name: Get the EMS-ESP version
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Install python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Get EMS-ESP version
|
||||
id: build_info
|
||||
run: |
|
||||
version=`grep -E '^#define EMSESP_APP_VERSION' ./src/emsesp_version.h | awk -F'"' '{print $2}'`
|
||||
version=`grep -E '^#define EMSESP_APP_VERSION' ./src/version.h | awk -F'"' '{print $2}'`
|
||||
echo "VERSION=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
python -m pip install intelhex
|
||||
|
||||
- name: Build the WebUI
|
||||
- name: Build WebUI
|
||||
run: |
|
||||
cd interface
|
||||
pnpm install
|
||||
pnpm typesafe-i18n --no-watch
|
||||
yarn install
|
||||
yarn typesafe-i18n --no-watch
|
||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||
pnpm build
|
||||
pnpm webUI
|
||||
yarn build
|
||||
yarn webUI
|
||||
|
||||
- name: Build all PIO target environments
|
||||
- name: Build all PIO target environments from default_envs
|
||||
run: |
|
||||
platformio run
|
||||
env:
|
||||
17
.github/workflows/sonar_check.yml
vendored
17
.github/workflows/sonar_check.yml
vendored
@@ -20,12 +20,15 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Build Wrapper
|
||||
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@master
|
||||
- name: Run Build Wrapper
|
||||
run: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make all
|
||||
- name: SonarQube Scan
|
||||
uses: SonarSource/sonarqube-scan-action@master
|
||||
|
||||
- name: Install sonar-scanner and build-wrapper
|
||||
uses: SonarSource/sonarcloud-github-c-cpp@v2
|
||||
|
||||
- name: Run build-wrapper
|
||||
run: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make all
|
||||
|
||||
- name: Run sonar-scanner
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
||||
run: sonar-scanner --define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"
|
||||
|
||||
23
.github/workflows/stale_issues.yml
vendored
23
.github/workflows/stale_issues.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: "Mark or close stale issues and PRs"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Remove stale label or comment otherwise this will be closed in 5 days."
|
||||
stale-pr-message: "This PR has been automatically marked as stale because there has been no activity in last 30 days. It will be closed if no further activity occurs. Thank you for your contributions."
|
||||
close-issue-message: "This issue was closed because it has been stalled for 5 days with no activity."
|
||||
close-pr-message: "This PR was automatically closed because of being stale."
|
||||
stale-pr-label: "stale"
|
||||
stale-issue-label: "stale"
|
||||
exempt-issue-labels: "bug,enhancement,pinned,security"
|
||||
exempt-pr-labels: "bug,enhancement,pinned,security"
|
||||
@@ -1,7 +1,4 @@
|
||||
name: 'Build stable release'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
name: 'tagged-release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -11,42 +8,40 @@ on:
|
||||
|
||||
jobs:
|
||||
tagged-release:
|
||||
name: 'Build Stable Release'
|
||||
name: 'Tagged Release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Install python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- name: Install Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable pnpm
|
||||
run: corepack enable
|
||||
|
||||
- name: Install python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
python -m pip install intelhex
|
||||
|
||||
- name: Build the WebUI
|
||||
- name: Build WebUI
|
||||
run: |
|
||||
cd interface
|
||||
pnpm install
|
||||
pnpm typesafe-i18n --no-watch
|
||||
yarn install
|
||||
yarn typesafe-i18n --no-watch
|
||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||
pnpm build
|
||||
pnpm webUI
|
||||
yarn build
|
||||
yarn webUI
|
||||
|
||||
- name: Build all PIO target environments
|
||||
- name: Build all PIO target environments from default_envs
|
||||
run: |
|
||||
platformio run
|
||||
env:
|
||||
51
.github/workflows/test_release.yml
vendored
51
.github/workflows/test_release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 'Build test release'
|
||||
name: 'test-release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -6,55 +6,46 @@ on:
|
||||
branches:
|
||||
- 'dev2'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pre-release:
|
||||
name: 'Build Test Release'
|
||||
name: 'Automatic test-release build'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
||||
- name: Install python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- name: Install Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable pnpm
|
||||
run: corepack enable
|
||||
|
||||
- name: Get the EMS-ESP version
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Use Node.js 20.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Get EMS-ESP source code and version
|
||||
id: build_info
|
||||
run: |
|
||||
version=`grep -E '^#define EMSESP_APP_VERSION' ./src/emsesp_version.h | awk -F'"' '{print $2}'`
|
||||
version=`grep -E '^#define EMSESP_APP_VERSION' ./src/version.h | awk -F'"' '{print $2}'`
|
||||
echo "VERSION=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
python -m pip install intelhex
|
||||
|
||||
- name: Build the WebUI
|
||||
- name: Build WebUI
|
||||
run: |
|
||||
cd interface
|
||||
pnpm install
|
||||
pnpm typesafe-i18n --no-watch
|
||||
yarn install
|
||||
yarn typesafe-i18n --no-watch
|
||||
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
|
||||
pnpm build
|
||||
pnpm webUI
|
||||
yarn build
|
||||
yarn webUI
|
||||
|
||||
- name: Build all target environments
|
||||
- name: Build all target environments from default_envs
|
||||
run: |
|
||||
platformio run
|
||||
env:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -28,10 +28,14 @@ stats.html
|
||||
*.sln
|
||||
*.sw?
|
||||
.pnp.*
|
||||
*/.yarn/cache/*
|
||||
*/.yarn/install-state.gz
|
||||
analyse.html
|
||||
interface/vite.config.ts.timestamp*
|
||||
*.local
|
||||
src/ESP32React/WWWData.h
|
||||
.yarn/*
|
||||
.yarnrc.yml
|
||||
|
||||
# i18n generated files
|
||||
interface/src/i18n/i18n-react.tsx
|
||||
|
||||
@@ -217,7 +217,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
||||
|
||||
## **IMPORTANT! BREAKING CHANGES**
|
||||
|
||||
Writeable Text entities have moved from type `sensor` to `text` in Home Assistant to make them also editable within an HA dashboard. Examples are `datetime`, `holidays`, `switchtime`, `vacations`, `maintenancedate`... You will need to manually remove any old discovery topics from your MQTT broker using an application like MQTT Explorer.
|
||||
Writeable Text entities have moved from type `sensor` to `text` in Home Assistant to make them also editable within an HA dashboard. Examples are `datetime`, `holidays`, `switchtime`, `vacations`, `maintenancedate`. You will need to manually remove any old discovery topics from your MQTT broker using an application like MQTT Explorer.
|
||||
|
||||
## Added
|
||||
|
||||
|
||||
@@ -1,56 +1 @@
|
||||
# Changelog
|
||||
|
||||
For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
||||
|
||||
## [3.7.3]
|
||||
|
||||
## Added
|
||||
|
||||
- analogsensor types: NTC and RGB-Led
|
||||
- Flag for HMC310 [#2465](https://github.com/emsesp/EMS-ESP32/issues/2465)
|
||||
- boiler auxheatersource [#2489](https://github.com/emsesp/EMS-ESP32/discussions/2489)
|
||||
- thermostat last error for RC100/300 [#2501](https://github.com/emsesp/EMS-ESP32/issues/2501)
|
||||
- boiler 0xC6 telegram [#1963](https://github.com/emsesp/EMS-ESP32/issues/1963)
|
||||
- CS6800i changes [#2448](https://github.com/emsesp/EMS-ESP32/issues/2448), [#2449](https://github.com/emsesp/EMS-ESP32/issues/2449)
|
||||
- charging pump [#2544](https://github.com/emsesp/EMS-ESP32/issues/2544)
|
||||
- hybrid CSH5800iG [#2569](https://github.com/emsesp/EMS-ESP32/issues/2569)
|
||||
- add EMS Device details to Home Assistant MQTT Discovery
|
||||
- disinfection command [#2601](https://github.com/emsesp/EMS-ESP32/issues/2601)
|
||||
- added new board profile for upcoming BBQKees E32V2.2
|
||||
- set differential pressure entity in Mixer device
|
||||
- set set climate action cooling/heating in HA [#2583](https://github.com/emsesp/EMS-ESP32/issues/2583)
|
||||
- Internal sensors of E32V2_2
|
||||
- FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610)
|
||||
- CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437)
|
||||
- Fuse settings for BBQKees boards
|
||||
- Analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
|
||||
- Analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
|
||||
- SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636)
|
||||
- Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645)
|
||||
- RC3xx `dhw modetype` [#2659](https://github.com/emsesp/EMS-ESP32/discussions/2659)
|
||||
|
||||
## Fixed
|
||||
|
||||
- dhw/switchtime [#2490](https://github.com/emsesp/EMS-ESP32/issues/2490)
|
||||
- switch to secure mqtt [#2492](https://github.com/emsesp/EMS-ESP32/issues/2492)
|
||||
- update link buttons [#2497](https://github.com/emsesp/EMS-ESP32/issues/2497)
|
||||
- refresh scheduler states [#2502](https://github.com/emsesp/EMS-ESP32/discussions/2502)
|
||||
- also rebuild HA config on mqtt connect for scheduler, custom and shower
|
||||
- FB100 controls the hc, not the master [#2510](https://github.com/emsesp/EMS-ESP32/issues/2510)
|
||||
- IPM DHW module, [#2524](https://github.com/emsesp/EMS-ESP32/issues/2524)
|
||||
- charge optimization [#2543](https://github.com/emsesp/EMS-ESP32/issues/2543)
|
||||
- shower active state retained, shows correctly in HA
|
||||
- MQTT Command Topic with slashes [#2571](https://github.com/emsesp/EMS-ESP32/issues/2571)
|
||||
- Add pulsed water meter input to V1.3 gateway with Lilygo S3 [#2550](https://github.com/emsesp/EMS-ESP32/issues/2550)
|
||||
- fix missing long 10-second press of Button to perform a factory reset
|
||||
- fix wwMaxPower on Junkers ZBS14 [#2609](https://github.com/emsesp/EMS-ESP32/issues/2609)
|
||||
- ventilation bypass state from telegram 0x55C [#1197](https://github.com/emsesp/EMS-ESP32/issues/1197)
|
||||
- set selflowtemp for ems+ boilers [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
|
||||
|
||||
## Changed
|
||||
|
||||
- show console log with ISO date/time [#2533](https://github.com/emsesp/EMS-ESP32/discussions/2533)
|
||||
- remove ESP32 CPU temperature
|
||||
- updated core libraries like AsyncTCP, AsyncWebServer and Modbus
|
||||
- remove command `scan deep`
|
||||
- ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
|
||||
|
||||
@@ -69,7 +69,7 @@ Format: `<type>(<scope>): <subject>`
|
||||
|
||||
## Example
|
||||
|
||||
```text
|
||||
```
|
||||
feat: add hat wobble
|
||||
^--^ ^------------^
|
||||
| |
|
||||
@@ -96,7 +96,7 @@ References:
|
||||
|
||||
## Contributor License Agreement (CLA)
|
||||
|
||||
```text
|
||||
```
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
|
||||
9
Makefile
9
Makefile
@@ -98,10 +98,13 @@ CXX := /usr/bin/g++
|
||||
# LDFLAGS Linker Flags
|
||||
#----------------------------------------------------------------------
|
||||
CPPFLAGS += $(DEFINES) $(DEFAULTS) $(INCLUDE)
|
||||
CPPFLAGS += -ggdb -g3 -MMD
|
||||
CPPFLAGS += -ggdb -g3 -O3
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -flto=auto -fno-lto
|
||||
CPPFLAGS += -Wall -Wextra -Werror -Wswitch-enum
|
||||
CPPFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-vla-cxx-extension
|
||||
CPPFLAGS += -Wall -Wextra -Werror
|
||||
CPPFLAGS += -Wswitch-enum
|
||||
CPPFLAGS += -Wno-unused-parameter
|
||||
CPPFLAGS += -Wno-missing-braces
|
||||
|
||||
CPPFLAGS += $(EXTRA_CPPFLAGS)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
[](https://discord.gg/3J3GgnzpyT)
|
||||
|
||||
[](https://github.com/emsesp/EMS-ESP32/stargazers)
|
||||
[](https://github.com/emsesp/EMS-ESP32/network)
|
||||
[](https://github.com/emsesp/EMS-ES32P/network)
|
||||
[](https://www.paypal.com/paypalme/prderbyshire/2)
|
||||
|
||||
**EMS-ESP** is an open-source firmware for the Espressif ESP32 microcontroller to communicate with **EMS** (Energy Management System) compatible equipment from manufacturers such as Bosch, Buderus, Nefit, Junkers, Worcester, Sieger, elm.leblanc and iVT.
|
||||
@@ -60,7 +60,7 @@ It requires a small circuit to interface with the EMS bus which can be purchased
|
||||
|
||||
## 🚀 **Installing**
|
||||
|
||||
Head over to [download.emsesp.org](https://download.emsesp.org) for instructions on how to install EMS-ESP. There is also further details on which boards are supported in [this section](https://docs.emsesp.org/Installing/) of the documentation.
|
||||
Head over to [download.emsesp.org](https://download.emsesp.org) for instructions on how to install EMS-ESP. There is also further details on which boards are supported in [this section](https://docs.emsesp.org/Getting-Started/#first-time-install) of the documentation.
|
||||
|
||||
## 📋 **Documentation**
|
||||
|
||||
@@ -88,7 +88,7 @@ If you like **EMS-ESP**, please give it a ✨ on GitHub, or even better fork it
|
||||
- [uuid-\*](https://github.com/nomis/mcu-uuid-console) from @nomis. The console, syslog, telnet and logging are based off these awesome open source libraries
|
||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson) for all the JSON processing
|
||||
- [espMqttClient](https://github.com/bertmelis/espMqttClient) for the MQTT client
|
||||
- [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) and [AsyncTCP](https://github.com/ESP32Async/AsyncTCP) for the Web server and TCP backends
|
||||
- ESPAsyncWebServer and AsyncTCP for the Web server and TCP backends, with custom modifications for performance
|
||||
|
||||
## 📜 **License**
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"build": {
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_XIAO_ESP32C6",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||
],
|
||||
"f_cpu": "160000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2886",
|
||||
"0x0046"
|
||||
],
|
||||
[
|
||||
"0x303a",
|
||||
"0x1001"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32c6",
|
||||
"variant": "XIAO_ESP32C6"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"zigbee",
|
||||
"thread"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32c6.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Seeed Studio XIAO ESP32C6",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://wiki.seeedstudio.com/XIAO_ESP32C6_Getting_Started/",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
@@ -32,8 +32,6 @@
|
||||
"**/*.json",
|
||||
"src/core/modbus_entity_parameters.hpp",
|
||||
"sdkconfig.*",
|
||||
"managed_components/**",
|
||||
"pnpm-*.yaml",
|
||||
"vite.config.ts"
|
||||
"managed_components/**"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
11214
docs/dump_entities.csv
11214
docs/dump_entities.csv
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
telegram_type_id,name,is_fetched
|
||||
0x04,UBAFactory,fetched
|
||||
0x06,RCTime,
|
||||
0x0A,EasyMonitor,fetched
|
||||
0x10,UBAErrorMessage1,
|
||||
0x11,UBAErrorMessage2,
|
||||
0x12,RCErrorMessage,
|
||||
0x13,RCErrorMessage2,
|
||||
0x14,UBATotalUptime,fetched
|
||||
0x15,UBAMaintenanceData,
|
||||
0x16,UBAParameters,fetched
|
||||
0x18,UBAMonitorFast,
|
||||
0x19,UBAMonitorSlow,
|
||||
0x1A,UBASetPoints,
|
||||
0x1C,UBAMaintenanceStatus,
|
||||
0x1E,WM10TempMessage,
|
||||
0x23,JunkersSetMixer,fetched
|
||||
0x26,UBASettingsWW,fetched
|
||||
0x28,WeatherComp,fetched
|
||||
0x2A,MC110Status,
|
||||
0x2E,Meters,
|
||||
0x33,UBAParameterWW,fetched
|
||||
0x34,UBAMonitorWW,
|
||||
0x35,UBAFlags,
|
||||
0x37,WWSettings,fetched
|
||||
0x38,WWTimer,fetched
|
||||
0x39,WWCircTimer,fetched
|
||||
0x3A,RC30WWSettings,fetched
|
||||
0x3B,Energy,
|
||||
0x3D,RC35Set,
|
||||
0x3E,RC35Monitor,
|
||||
0x3F,RC35Timer,
|
||||
0x40,RC30Temp,
|
||||
0x41,RC30Monitor,
|
||||
0x42,RC35Timer2,
|
||||
0x47,RC35Set,
|
||||
0x48,RC35Monitor,
|
||||
0x49,RC35Timer,
|
||||
0x4C,RC35Timer2,
|
||||
0x51,RC35Set,
|
||||
0x52,RC35Monitor,
|
||||
0x53,RC35Timer,
|
||||
0x56,RC35Timer2,
|
||||
0x5B,RC35Set,
|
||||
0x5C,RC35Monitor,
|
||||
0x5D,RC35Timer,
|
||||
0x60,RC35Timer2,
|
||||
0x96,SM10Config,fetched
|
||||
0x97,SM10Monitor,
|
||||
0x9C,WM10MonitorMessage,
|
||||
0x9D,WM10SetMessage,
|
||||
0xA2,RCError,
|
||||
0xA3,RCOutdoorTemp,
|
||||
0xA5,IBASettings,fetched
|
||||
0xA7,RC30Set,
|
||||
0xA9,RC30Vacation,fetched
|
||||
0xAA,MMConfigMessage,fetched
|
||||
0xAB,MMStatusMessage,
|
||||
0xAC,MMSetMessage,
|
||||
0xAF,RC20Remote,
|
||||
0xB0,RC10Set,
|
||||
0xB1,RC10Monitor,
|
||||
0xBB,HybridSettings,fetched
|
||||
0xBF,ErrorMessage,
|
||||
0xC2,UBAErrorMessage3,
|
||||
0xD1,UBAOutdoorTemp,
|
||||
0xE3,UBAMonitorSlowPlus2,
|
||||
0xE4,UBAMonitorFastPlus,
|
||||
0xE5,UBAMonitorSlowPlus,
|
||||
0xE6,UBAParametersPlus,fetched
|
||||
0xE9,UBAMonitorWWPlus,
|
||||
0xEA,UBAParameterWWPlus,fetched
|
||||
0x0101,ISM1Set,fetched
|
||||
0x0103,ISM1StatusMessage,fetched
|
||||
0x0104,ISM2StatusMessage,
|
||||
0x010C,IPMStatusMessage,
|
||||
0x011E,IPMTempMessage,
|
||||
0x012E,HPEnergy1,
|
||||
0x013B,HPEnergy2,
|
||||
0x0165,JunkersSet,
|
||||
0x0166,JunkersSet,
|
||||
0x0167,JunkersSet,
|
||||
0x0168,JunkersSet,
|
||||
0x016E,Absent,fetched
|
||||
0x016F,JunkersMonitor,
|
||||
0x0170,JunkersMonitor,
|
||||
0x0171,JunkersMonitor,
|
||||
0x0172,JunkersMonitor,
|
||||
0x0179,JunkersSet,
|
||||
0x017A,JunkersSet,
|
||||
0x017B,JunkersSet,
|
||||
0x017C,JunkersSet,
|
||||
0x01D3,JunkersDhw,fetched
|
||||
0x023A,RC300OutdoorTemp,fetched
|
||||
0x023E,PVSettings,fetched
|
||||
0x0240,RC300Settings,fetched
|
||||
0x0241,RC300Settings,fetched
|
||||
0x0267,RC300Floordry,
|
||||
0x0269,RC300Holiday,fetched
|
||||
0x0291,HPMode,fetched
|
||||
0x0292,HPMode,fetched
|
||||
0x0293,HPMode,fetched
|
||||
0x0294,HPMode,fetched
|
||||
0x029B,RC300Curves,
|
||||
0x029C,RC300Curves,
|
||||
0x029D,RC300Curves,
|
||||
0x029E,RC300Curves,
|
||||
0x029F,RC300Curves,
|
||||
0x02A0,RC300Curves,
|
||||
0x02A1,RC300Curves,
|
||||
0x02A2,RC300Curves,
|
||||
0x02A5,RC300Monitor,fetched
|
||||
0x02A6,RC300Monitor,
|
||||
0x02A7,CRFMonitor,
|
||||
0x02A8,RC300Monitor,
|
||||
0x02A9,RC300Monitor,
|
||||
0x02AA,RC300Monitor,
|
||||
0x02AB,RC300Monitor,
|
||||
0x02AC,RC300Monitor,
|
||||
0x02AF,RC300Summer,
|
||||
0x02B0,RC300Summer,
|
||||
0x02B1,RC300Summer,
|
||||
0x02B2,RC300Summer,
|
||||
0x02B3,RC300Summer,
|
||||
0x02B4,RC300Summer,
|
||||
0x02B5,RC300Summer,
|
||||
0x02B6,RC300Summer,
|
||||
0x02B9,RC300Set,
|
||||
0x02BA,RC300Set,
|
||||
0x02BB,RC300Set,
|
||||
0x02BC,RC300Set,
|
||||
0x02BD,RC300Set,
|
||||
0x02BE,RC300Set,
|
||||
0x02BF,RC300Set,
|
||||
0x02C0,RC300Set,
|
||||
0x02CC,HPPressure,fetched
|
||||
0x02CD,MMPLUSConfigMessage,fetched
|
||||
0x02CE,RC300Set2,
|
||||
0x02D0,RC300Set2,
|
||||
0x02D2,RC300Set2,
|
||||
0x02D6,HPPump2,fetched
|
||||
0x02D7,MMPLUSStatusMessage,
|
||||
0x02F5,RC300WWmode,fetched
|
||||
0x02F6,RC300WW2mode,fetched
|
||||
0x0313,MMPLUSConfigMessage_WWC,fetched
|
||||
0x031B,RC300WWtemp,fetched
|
||||
0x031D,RC300WWmode2,
|
||||
0x031E,RC300WWmode2,
|
||||
0x0331,MMPLUSStatusMessage_WWC,
|
||||
0x0358,SM100SystemConfig,fetched
|
||||
0x035A,SM100CircuitConfig,fetched
|
||||
0x035C,SM100HeatAssist,fetched
|
||||
0x035D,SM100Circuit2Config,fetched
|
||||
0x035F,SM100Config1,fetched
|
||||
0x0361,SM100Differential,fetched
|
||||
0x0362,SM100Monitor,
|
||||
0x0363,SM100Monitor2,
|
||||
0x0364,SM100Status,
|
||||
0x0366,SM100Config,
|
||||
0x036A,SM100Status2,
|
||||
0x0380,SM100CollectorConfig,fetched
|
||||
0x038E,SM100Energy,fetched
|
||||
0x0391,SM100Time,fetched
|
||||
0x043F,CRHolidays,fetched
|
||||
0x0467,HPSet,
|
||||
0x0468,HPSet,
|
||||
0x0469,HPSet,
|
||||
0x046A,HPSet,
|
||||
0x0471,RC300Summer2,
|
||||
0x0472,RC300Summer2,
|
||||
0x0473,RC300Summer2,
|
||||
0x0474,RC300Summer2,
|
||||
0x0475,RC300Summer2,
|
||||
0x0476,RC300Summer2,
|
||||
0x0477,RC300Summer2,
|
||||
0x0478,RC300Summer2,
|
||||
0x047B,HP2,
|
||||
0x0484,HPSilentMode,fetched
|
||||
0x0485,HpCooling,fetched
|
||||
0x0486,HpInConfig,fetched
|
||||
0x0488,HPValve,fetched
|
||||
0x048A,HpPool,fetched
|
||||
0x048B,HPPumps,fetched
|
||||
0x048D,HpPower,fetched
|
||||
0x048F,HpTemperatures,
|
||||
0x0491,HPAdditionalHeater,fetched
|
||||
0x0492,HpHeaterConfig,fetched
|
||||
0x0494,UBAEnergySupplied,
|
||||
0x0495,UBAInformation,
|
||||
0x0499,HPDhwSettings,fetched
|
||||
0x049C,HPSettings2,fetched
|
||||
0x049D,HPSettings3,fetched
|
||||
0x04A2,HpInput,fetched
|
||||
0x04A5,HPFan,fetched
|
||||
0x04A7,HPPowerLimit,fetched
|
||||
0x04AA,HPPower2,fetched
|
||||
0x04AE,HPEnergy,fetched
|
||||
0x04AF,HPMeters,fetched
|
||||
0x056B,VentilationMode,fetched
|
||||
0x0583,VentilationMonitor,
|
||||
0x0585,Blowerspeed,
|
||||
0x0587,Bypass,
|
||||
0x05BA,HpPoolStatus,fetched
|
||||
0x05D9,Airquality,
|
||||
0x0772,HIUSettings,
|
||||
0x0779,HIUMonitor,
|
||||
0x07A5,SM100wwCirc,fetched
|
||||
0x07A6,SM100wwParam,fetched
|
||||
0x07AA,SM100wwStatus,
|
||||
0x07AB,SM100wwCommand,
|
||||
0x07AC,SM100wwParam1,
|
||||
0x07AD,SM100ValveStatus,
|
||||
0x07AE,SM100wwKeepWarm,fetched
|
||||
0x07D6,SM100wwTemperature,
|
||||
0x07E0,SM100wwStatus2,fetched
|
||||
0x0935,EM100SetMessage,fetched
|
||||
0x0936,EM100OutMessage,
|
||||
0x0937,EM100TempMessage,
|
||||
0x0938,EM100InputMessage,
|
||||
0x0939,EM100MonitorMessage,
|
||||
0x093A,EM100ConfigMessage,
|
||||
0x0998,HPSettings,fetched
|
||||
0x0999,HPFunctionTest,fetched
|
||||
0x099A,HPStarts,
|
||||
0x099B,HPFlowTemp,
|
||||
0x099C,HPComp,
|
||||
0x09A0,HPTemperature,
|
||||
|
||||
|
4
interface/.gitattributes
vendored
Normal file
4
interface/.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/.yarn/** linguist-vendored
|
||||
/.yarn/releases/* binary
|
||||
/.yarn/plugins/**/* binary
|
||||
/.pnp.* binary linguist-generated
|
||||
@@ -4,4 +4,5 @@ dist/
|
||||
src/i18n/*
|
||||
|
||||
.prettierrc
|
||||
.typesafe-i18n.json
|
||||
.yarn/
|
||||
.typesafe-i18n.json
|
||||
935
interface/.yarn/releases/yarn-4.7.0.cjs
vendored
Executable file
935
interface/.yarn/releases/yarn-4.7.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
3
interface/.yarnrc.yml
Normal file
3
interface/.yarnrc.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.7.0.cjs
|
||||
@@ -10,7 +10,8 @@ export default tseslint.config(
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: true
|
||||
project: true,
|
||||
tsconfigRootDir: import.meta.dirname
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "EMS-ESP",
|
||||
"version": "3.7.3",
|
||||
"version": "3.7.2",
|
||||
"description": "EMS-ESP WebUI",
|
||||
"homepage": "https://emsesp.org",
|
||||
"author": "proddy, emsesp.org",
|
||||
@@ -8,60 +8,59 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build-hosted": "typesafe-i18n && vite build --mode hosted",
|
||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
||||
"mock-rest": "bun --watch ../mock-api/restServer.ts",
|
||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite\"",
|
||||
"build-hosted": "typesafe-i18n --no-watch && vite build --mode hosted",
|
||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"yarn:mock-rest\" \"vite preview\"",
|
||||
"mock-rest": "bun --watch ../mock-api/rest_server.ts",
|
||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"yarn:mock-rest\" \"vite\"",
|
||||
"typesafe-i18n": "typesafe-i18n --no-watch",
|
||||
"webUI": "node progmem-generator.js",
|
||||
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/adapter-xhr": "2.2.1",
|
||||
"@alova/adapter-xhr": "2.1.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.4",
|
||||
"@mui/material": "^7.3.4",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.3.4",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^6.4.8",
|
||||
"@mui/material": "^6.4.8",
|
||||
"@table-library/react-table-library": "4.1.12",
|
||||
"alova": "3.2.10",
|
||||
"async-validator": "^4.2.5",
|
||||
"formidable": "^3.5.4",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"magic-string": "^0.30.19",
|
||||
"mime-types": "^3.0.1",
|
||||
"preact": "^10.27.2",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"preact": "^10.26.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.9.4",
|
||||
"react-router": "^7.4.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"typesafe-i18n": "^5.26.2",
|
||||
"typescript": "^5.9.3"
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.28.4",
|
||||
"@eslint/js": "^9.38.0",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@preact/preset-vite": "^2.10.2",
|
||||
"@preact/preset-vite": "^2.10.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/node": "^24.9.1",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.6.2",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"terser": "^5.44.0",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"vite": "^7.1.11",
|
||||
"@types/formidable": "^3",
|
||||
"@types/node": "^22.13.11",
|
||||
"@types/react": "^19.0.12",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"concurrently": "^9.1.2",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"formidable": "^3.5.2",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"terser": "^5.39.0",
|
||||
"typescript-eslint": "8.27.0",
|
||||
"vite": "^6.2.2",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8"
|
||||
"packageManager": "yarn@4.7.0"
|
||||
}
|
||||
|
||||
6056
interface/pnpm-lock.yaml
generated
6056
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
onlyBuiltDependencies:
|
||||
- cwebp-bin
|
||||
- esbuild
|
||||
- gifsicle
|
||||
- jpegtran-bin
|
||||
- mozjpeg
|
||||
- optipng-bin
|
||||
- pngquant-bin
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
existsSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
statSync,
|
||||
unlinkSync
|
||||
} from 'fs';
|
||||
import mime from 'mime-types';
|
||||
@@ -16,78 +15,66 @@ const INDENT = ' ';
|
||||
const outputPath = '../src/ESP32React/WWWData.h';
|
||||
const sourcePath = './dist';
|
||||
const bytesPerLine = 20;
|
||||
let totalSize = 0;
|
||||
let bundleStats = {
|
||||
js: { count: 0, uncompressed: 0, compressed: 0 },
|
||||
css: { count: 0, uncompressed: 0, compressed: 0 },
|
||||
html: { count: 0, uncompressed: 0, compressed: 0 },
|
||||
svg: { count: 0, uncompressed: 0, compressed: 0 },
|
||||
other: { count: 0, uncompressed: 0, compressed: 0 }
|
||||
};
|
||||
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;
|
||||
// Bundle Statistics:
|
||||
// - Total compressed size: ${(totalSize / 1000).toFixed(1)} KB
|
||||
// - Total uncompressed size: ${(Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) / 1000).toFixed(1)} KB
|
||||
// - Compression ratio: ${(((Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) - totalSize) / Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0)) * 100).toFixed(1)}%
|
||||
// - Generated on: ${new Date().toISOString()}
|
||||
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((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, "${f.hash}");`).join('\n')}
|
||||
${INDENT.repeat(2)}}
|
||||
${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)}}
|
||||
};
|
||||
`;
|
||||
|
||||
const getFilesSync = (dir, files = []) => {
|
||||
function getFilesSync(dir, files = []) {
|
||||
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
||||
const entryPath = resolve(dir, entry.name);
|
||||
entry.isDirectory() ? getFilesSync(entryPath, files) : files.push(entryPath);
|
||||
if (entry.isDirectory()) {
|
||||
getFilesSync(entryPath, files);
|
||||
} else {
|
||||
files.push(entryPath);
|
||||
}
|
||||
});
|
||||
return files;
|
||||
};
|
||||
}
|
||||
|
||||
const cleanAndOpen = (path) => {
|
||||
existsSync(path) && unlinkSync(path);
|
||||
function cleanAndOpen(path) {
|
||||
if (existsSync(path)) {
|
||||
unlinkSync(path);
|
||||
}
|
||||
return createWriteStream(path, { flags: 'w+' });
|
||||
};
|
||||
|
||||
const getFileType = (filePath) => {
|
||||
const ext = filePath.split('.').pop().toLowerCase();
|
||||
if (ext === 'js') return 'js';
|
||||
if (ext === 'css') return 'css';
|
||||
if (ext === 'html') return 'html';
|
||||
if (ext === 'svg') return 'svg';
|
||||
return 'other';
|
||||
};
|
||||
}
|
||||
|
||||
const writeFile = (relativeFilePath, buffer) => {
|
||||
const variable = `ESP_REACT_DATA_${fileInfo.length}`;
|
||||
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
|
||||
const mimeType = mime.lookup(relativeFilePath);
|
||||
const fileType = getFileType(relativeFilePath);
|
||||
let size = 0;
|
||||
writeStream.write(`const uint8_t ${variable}[] = {`);
|
||||
|
||||
var size = 0;
|
||||
writeStream.write('const uint8_t ' + variable + '[] = {');
|
||||
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
|
||||
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
|
||||
const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
|
||||
|
||||
// create sha
|
||||
const hashSum = crypto.createHash('sha256');
|
||||
hashSum.update(zipBuffer);
|
||||
const hash = hashSum.digest('hex');
|
||||
|
||||
zipBuffer.forEach((b) => {
|
||||
if (!(size % bytesPerLine)) {
|
||||
writeStream.write('\n' + INDENT);
|
||||
writeStream.write('\n');
|
||||
writeStream.write(indent);
|
||||
}
|
||||
writeStream.write('0x' + b.toString(16).toUpperCase().padStart(2, '0') + ',');
|
||||
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ',');
|
||||
size++;
|
||||
});
|
||||
|
||||
size % bytesPerLine && writeStream.write('\n');
|
||||
writeStream.write('};\n\n');
|
||||
if (size % bytesPerLine) {
|
||||
writeStream.write('\n');
|
||||
}
|
||||
|
||||
// Update bundle statistics
|
||||
bundleStats[fileType].count++;
|
||||
bundleStats[fileType].uncompressed += buffer.length;
|
||||
bundleStats[fileType].compressed += zipBuffer.length;
|
||||
writeStream.write('};\n\n');
|
||||
|
||||
fileInfo.push({
|
||||
uri: '/' + relativeFilePath.replace(sep, '/'),
|
||||
@@ -97,52 +84,32 @@ const writeFile = (relativeFilePath, buffer) => {
|
||||
hash
|
||||
});
|
||||
|
||||
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
|
||||
totalSize += size;
|
||||
};
|
||||
|
||||
console.log(`Generating ${outputPath} from ${sourcePath}`);
|
||||
// start
|
||||
console.log('Generating ' + outputPath + ' from ' + sourcePath);
|
||||
const includes = ARDUINO_INCLUDES;
|
||||
const indent = INDENT;
|
||||
const fileInfo = [];
|
||||
const writeStream = cleanAndOpen(resolve(outputPath));
|
||||
|
||||
writeStream.write(ARDUINO_INCLUDES);
|
||||
// includes
|
||||
writeStream.write(includes);
|
||||
|
||||
// process static files
|
||||
const buildPath = resolve(sourcePath);
|
||||
for (const filePath of getFilesSync(buildPath)) {
|
||||
writeFile(relative(buildPath, filePath), readFileSync(filePath));
|
||||
const readStream = readFileSync(filePath);
|
||||
const relativeFilePath = relative(buildPath, filePath);
|
||||
writeFile(relativeFilePath, readStream);
|
||||
}
|
||||
|
||||
// add class
|
||||
writeStream.write(generateWWWClass());
|
||||
|
||||
// end
|
||||
writeStream.end();
|
||||
|
||||
// Calculate and display bundle statistics
|
||||
const totalUncompressed = Object.values(bundleStats).reduce(
|
||||
(sum, stat) => sum + stat.uncompressed,
|
||||
0
|
||||
);
|
||||
const totalCompressed = Object.values(bundleStats).reduce(
|
||||
(sum, stat) => sum + stat.compressed,
|
||||
0
|
||||
);
|
||||
const compressionRatio = (
|
||||
((totalUncompressed - totalCompressed) / totalUncompressed) *
|
||||
100
|
||||
).toFixed(1);
|
||||
|
||||
console.log('\n📊 Bundle Size Analysis:');
|
||||
console.log('='.repeat(50));
|
||||
console.log(`Total compressed size: ${(totalSize / 1000).toFixed(1)} KB`);
|
||||
console.log(`Total uncompressed size: ${(totalUncompressed / 1000).toFixed(1)} KB`);
|
||||
console.log(`Compression ratio: ${compressionRatio}%`);
|
||||
console.log('\n📁 File Type Breakdown:');
|
||||
Object.entries(bundleStats).forEach(([type, stats]) => {
|
||||
if (stats.count > 0) {
|
||||
const ratio = (
|
||||
((stats.uncompressed - stats.compressed) / stats.uncompressed) *
|
||||
100
|
||||
).toFixed(1);
|
||||
console.log(
|
||||
`${type.toUpperCase().padEnd(4)}: ${stats.count} files, ${(stats.uncompressed / 1000).toFixed(1)} KB → ${(stats.compressed / 1000).toFixed(1)} KB (${ratio}% compression)`
|
||||
);
|
||||
}
|
||||
});
|
||||
console.log('='.repeat(50));
|
||||
console.log('Total size: ' + totalSize / 1000 + ' KB');
|
||||
|
||||
@@ -37,9 +37,10 @@ const AuthenticatedRouting = () => {
|
||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||
<Route path="/devices/*" element={<Devices />} />
|
||||
<Route path="/sensors/*" element={<Sensors />} />
|
||||
<Route path="/help/*" element={<Help />} />
|
||||
|
||||
<Route path="/status/*" element={<Status />} />
|
||||
<Route path="/help/*" element={<Help />} />
|
||||
<Route path="/*" element={<Navigate to="/" />} />
|
||||
|
||||
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
|
||||
<Route path="/status/activity" element={<Activity />} />
|
||||
<Route path="/status/log" element={<SystemLog />} />
|
||||
@@ -67,8 +68,6 @@ const AuthenticatedRouting = () => {
|
||||
<Route path="/customentities" element={<CustomEntities />} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Route path="/*" element={<Navigate to="/" />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -98,7 +98,7 @@ const SignIn = () => {
|
||||
|
||||
<Box display="flex" flexDirection="column" alignItems="center">
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
disabled={processing}
|
||||
sx={{
|
||||
width: 240
|
||||
@@ -117,7 +117,7 @@ const SignIn = () => {
|
||||
}}
|
||||
/>
|
||||
<ValidatedPasswordField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
disabled={processing}
|
||||
sx={{
|
||||
width: 240
|
||||
|
||||
@@ -70,7 +70,6 @@ export const readDeviceEntities = (id: number) =>
|
||||
alovaInstance.Get<DeviceEntity[]>(`/rest/deviceEntities`, {
|
||||
params: { id },
|
||||
responseType: 'arraybuffer',
|
||||
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
|
||||
transform(data) {
|
||||
return (data as DeviceEntity[]).map((de: DeviceEntity) => ({
|
||||
...de,
|
||||
@@ -93,7 +92,6 @@ export const writeDeviceName = (data: { id: number; name: string }) =>
|
||||
// SettingsScheduler
|
||||
export const readSchedule = () =>
|
||||
alovaInstance.Get<ScheduleItem[]>('/rest/schedule', {
|
||||
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
|
||||
transform(data) {
|
||||
return (data as Schedule).schedule.map((si: ScheduleItem) => ({
|
||||
...si,
|
||||
@@ -131,7 +129,6 @@ export const writeModules = (data: {
|
||||
// CustomEntities
|
||||
export const readCustomEntities = () =>
|
||||
alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
|
||||
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
|
||||
transform(data) {
|
||||
return (data as Entities).entities.map((ei: EntityItem) => ({
|
||||
...ei,
|
||||
@@ -146,8 +143,7 @@ export const readCustomEntities = () =>
|
||||
o_name: ei.name,
|
||||
o_writeable: ei.writeable,
|
||||
o_value: ei.value,
|
||||
o_deleted: ei.deleted,
|
||||
o_hide: ei.hide
|
||||
o_deleted: ei.deleted
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ export const getDevVersion = () =>
|
||||
cacheFor: 60 * 10 * 1000,
|
||||
transform(response: { data: { name: string; published_at: string } }) {
|
||||
return {
|
||||
name: response.data.name.split(/\s+/).splice(-1)[0]?.substring(1) || '',
|
||||
name: response.data.name.split(/\s+/).splice(-1)[0].substring(1),
|
||||
published_at: response.data.published_at
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,37 +1,40 @@
|
||||
// @ts-nocheck - Optimized MessagePack unpacking library for EMS-ESP32
|
||||
let decoder,
|
||||
src,
|
||||
srcEnd,
|
||||
position = 0,
|
||||
strings = [],
|
||||
stringPosition = 0,
|
||||
currentUnpackr = {},
|
||||
currentStructures,
|
||||
srcString,
|
||||
srcStringStart = 0,
|
||||
srcStringEnd = 0,
|
||||
bundledStrings,
|
||||
referenceMap,
|
||||
dataView;
|
||||
const EMPTY_ARRAY = [],
|
||||
currentExtensions = [];
|
||||
const defaultOptions = { useRecords: false, mapsAsObjects: true };
|
||||
let decoder;
|
||||
try {
|
||||
decoder = new TextDecoder();
|
||||
} catch (error) {}
|
||||
class C1Type {}
|
||||
const C1 = new C1Type();
|
||||
let src;
|
||||
let srcEnd;
|
||||
let position = 0;
|
||||
const EMPTY_ARRAY = [];
|
||||
let strings = EMPTY_ARRAY;
|
||||
let stringPosition = 0;
|
||||
let currentUnpackr = {};
|
||||
let currentStructures;
|
||||
let srcString;
|
||||
let srcStringStart = 0;
|
||||
let srcStringEnd = 0;
|
||||
let bundledStrings;
|
||||
let referenceMap;
|
||||
const currentExtensions = [];
|
||||
let dataView;
|
||||
const defaultOptions = {
|
||||
useRecords: false,
|
||||
mapsAsObjects: true
|
||||
};
|
||||
export class C1Type {}
|
||||
export const C1 = new C1Type();
|
||||
C1.name = 'MessagePack 0xC1';
|
||||
let sequentialMode = false,
|
||||
inlineObjectReadThreshold = 2,
|
||||
readStruct,
|
||||
onLoadedStructures,
|
||||
onSaveState;
|
||||
let sequentialMode = false;
|
||||
let inlineObjectReadThreshold = 2;
|
||||
let readStruct, onLoadedStructures, onSaveState;
|
||||
// no-eval build
|
||||
try {
|
||||
new Function('');
|
||||
} catch (error) {
|
||||
// if eval variants are not supported, do not create inline object readers ever
|
||||
inlineObjectReadThreshold = Infinity;
|
||||
}
|
||||
|
||||
export class Unpackr {
|
||||
constructor(options) {
|
||||
if (options) {
|
||||
@@ -47,15 +50,19 @@ export class Unpackr {
|
||||
if (options.structures)
|
||||
options.structures.sharedLength = options.structures.length;
|
||||
else if (options.getStructures) {
|
||||
(options.structures = []).uninitialized = true;
|
||||
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
|
||||
options.structures.sharedLength = 0;
|
||||
}
|
||||
if (options.int64AsNumber) options.int64AsType = 'number';
|
||||
if (options.int64AsNumber) {
|
||||
options.int64AsType = 'number';
|
||||
}
|
||||
}
|
||||
Object.assign(this, options);
|
||||
}
|
||||
|
||||
unpack(source, options?: any) {
|
||||
if (src) {
|
||||
// re-entrant execution, save the state and restore it after we do this unpack
|
||||
return saveState(() => {
|
||||
clearSource();
|
||||
return this
|
||||
@@ -79,6 +86,9 @@ export class Unpackr {
|
||||
strings = EMPTY_ARRAY;
|
||||
bundledStrings = null;
|
||||
src = source;
|
||||
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
|
||||
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
|
||||
// new ones
|
||||
try {
|
||||
dataView =
|
||||
source.dataView ||
|
||||
@@ -181,10 +191,10 @@ export class Unpackr {
|
||||
return this.unpack(source, end);
|
||||
}
|
||||
}
|
||||
function getPosition() {
|
||||
export function getPosition() {
|
||||
return position;
|
||||
}
|
||||
function checkedRead(options: any) {
|
||||
export function checkedRead(options: any) {
|
||||
try {
|
||||
if (!currentUnpackr.trusted && !sequentialMode) {
|
||||
const sharedLength = currentStructures.sharedLength || 0;
|
||||
@@ -254,7 +264,7 @@ function restoreStructures() {
|
||||
currentStructures.restoreStructures = null;
|
||||
}
|
||||
|
||||
function read() {
|
||||
export function read() {
|
||||
let token = src[position++];
|
||||
if (token < 0xa0) {
|
||||
if (token < 0x80) {
|
||||
@@ -579,7 +589,7 @@ const createSecondByteReader = (firstId, read0) =>
|
||||
return structure.read();
|
||||
};
|
||||
|
||||
function loadStructures() {
|
||||
export function loadStructures() {
|
||||
const loadedStructures = saveState(() => {
|
||||
// save the state in case getStructures modifies our buffer
|
||||
src = null;
|
||||
@@ -595,8 +605,9 @@ var readFixedString = readStringJS;
|
||||
var readString8 = readStringJS;
|
||||
var readString16 = readStringJS;
|
||||
var readString32 = readStringJS;
|
||||
let isNativeAccelerationEnabled = false;
|
||||
function setExtractor(extractStrings) {
|
||||
export let isNativeAccelerationEnabled = false;
|
||||
|
||||
export function setExtractor(extractStrings) {
|
||||
isNativeAccelerationEnabled = true;
|
||||
readFixedString = readString(1);
|
||||
readString8 = readString(2);
|
||||
@@ -690,7 +701,7 @@ function readStringJS(length) {
|
||||
|
||||
return result;
|
||||
}
|
||||
function readString(source, start, length) {
|
||||
export function readString(source, start, length) {
|
||||
const existingSrc = src;
|
||||
src = source;
|
||||
position = start;
|
||||
@@ -1054,7 +1065,7 @@ currentExtensions[0x70] = (data) => {
|
||||
|
||||
currentExtensions[0x73] = () => new Set(read());
|
||||
|
||||
const typedArrays = [
|
||||
export const typedArrays = [
|
||||
'Int8',
|
||||
'Uint8',
|
||||
'Uint8Clamped',
|
||||
@@ -1166,20 +1177,44 @@ function saveState(callback) {
|
||||
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
||||
return value;
|
||||
}
|
||||
function clearSource() {
|
||||
export function clearSource() {
|
||||
src = null;
|
||||
referenceMap = null;
|
||||
currentStructures = null;
|
||||
}
|
||||
|
||||
function addExtension(extension) {
|
||||
export function addExtension(extension) {
|
||||
if (extension.unpack) currentExtensions[extension.type] = extension.unpack;
|
||||
else currentExtensions[extension.type] = extension;
|
||||
}
|
||||
|
||||
const mult10 = new Array(147);
|
||||
export const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
|
||||
for (let i = 0; i < 256; i++) {
|
||||
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103));
|
||||
}
|
||||
const defaultUnpackr = new Unpackr({ useRecords: false });
|
||||
export const Decoder = Unpackr;
|
||||
var defaultUnpackr = new Unpackr({ useRecords: false });
|
||||
export const unpack = defaultUnpackr.unpack;
|
||||
export const unpackMultiple = defaultUnpackr.unpackMultiple;
|
||||
export const decode = defaultUnpackr.unpack;
|
||||
export const FLOAT32_OPTIONS = {
|
||||
NEVER: 0,
|
||||
ALWAYS: 1,
|
||||
DECIMAL_ROUND: 3,
|
||||
DECIMAL_FIT: 4
|
||||
};
|
||||
const f32Array = new Float32Array(1);
|
||||
const u8Array = new Uint8Array(f32Array.buffer, 0, 4);
|
||||
export function roundFloat32(float32Number) {
|
||||
f32Array[0] = float32Number;
|
||||
const multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)];
|
||||
return (
|
||||
((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) /
|
||||
multiplier
|
||||
);
|
||||
}
|
||||
export function setReadStruct(updatedReadStruct, loadedStructs, saveState) {
|
||||
readStruct = updatedReadStruct;
|
||||
onLoadedStructures = loadedStructs;
|
||||
onSaveState = saveState;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ const CustomEntities = () => {
|
||||
ei.factor !== ei.o_factor ||
|
||||
ei.value_type !== ei.o_value_type ||
|
||||
ei.writeable !== ei.o_writeable ||
|
||||
ei.hide !== ei.o_hide ||
|
||||
ei.deleted !== ei.o_deleted ||
|
||||
(ei.value || '') !== (ei.o_value || '')
|
||||
);
|
||||
@@ -137,8 +136,8 @@ const CustomEntities = () => {
|
||||
const saveEntities = async () => {
|
||||
await writeEntities({
|
||||
entities: entities
|
||||
.filter((ei: EntityItem) => !ei.deleted)
|
||||
.map((condensed_ei: EntityItem) => ({
|
||||
.filter((ei) => !ei.deleted)
|
||||
.map((condensed_ei) => ({
|
||||
id: condensed_ei.id,
|
||||
ram: condensed_ei.ram,
|
||||
name: condensed_ei.name,
|
||||
@@ -148,7 +147,6 @@ const CustomEntities = () => {
|
||||
factor: condensed_ei.factor,
|
||||
uom: condensed_ei.uom,
|
||||
writeable: condensed_ei.writeable,
|
||||
hide: condensed_ei.hide,
|
||||
value_type: condensed_ei.value_type,
|
||||
value: condensed_ei.value
|
||||
}))
|
||||
@@ -211,7 +209,6 @@ const CustomEntities = () => {
|
||||
value_type: item.value_type,
|
||||
writeable: item.writeable,
|
||||
deleted: false,
|
||||
hide: item.hide,
|
||||
value: item.value
|
||||
});
|
||||
setDialogOpen(true);
|
||||
@@ -231,7 +228,6 @@ const CustomEntities = () => {
|
||||
value_type: 0,
|
||||
writeable: false,
|
||||
deleted: false,
|
||||
hide: false,
|
||||
value: ''
|
||||
});
|
||||
setDialogOpen(true);
|
||||
@@ -252,17 +248,15 @@ const CustomEntities = () => {
|
||||
|
||||
const renderEntity = () => {
|
||||
if (!entities) {
|
||||
return (
|
||||
<FormLoader onRetry={fetchEntities} errorMessage={error?.message || ''} />
|
||||
);
|
||||
return <FormLoader onRetry={fetchEntities} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
data={{
|
||||
nodes: entities
|
||||
.filter((ei: EntityItem) => !ei.deleted)
|
||||
.sort((a: EntityItem, b: EntityItem) => a.name.localeCompare(b.name))
|
||||
.filter((ei) => !ei.deleted)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
}}
|
||||
theme={entity_theme}
|
||||
layout={{ custom: true }}
|
||||
|
||||
@@ -2,11 +2,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||
import DoneIcon from '@mui/icons-material/Done';
|
||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
||||
import {
|
||||
Box,
|
||||
@@ -16,7 +12,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
TextField
|
||||
@@ -74,10 +70,7 @@ const CustomEntitiesDialog = ({
|
||||
}
|
||||
}, [open, selectedItem]);
|
||||
|
||||
const handleClose = (
|
||||
_event: React.SyntheticEvent,
|
||||
reason: 'backdropClick' | 'escapeKeyDown'
|
||||
) => {
|
||||
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
|
||||
if (reason !== 'backdropClick') {
|
||||
onClose();
|
||||
}
|
||||
@@ -126,7 +119,7 @@ const CustomEntitiesDialog = ({
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid size={12}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="name"
|
||||
label={LL.NAME(0)}
|
||||
value={editItem.name}
|
||||
@@ -135,20 +128,6 @@ const CustomEntitiesDialog = ({
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid mt={3}>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
icon={<InsertCommentOutlinedIcon htmlColor="white" />}
|
||||
checkedIcon={<CommentsDisabledOutlinedIcon color="primary" />}
|
||||
checked={editItem.hide}
|
||||
onChange={updateFormValue}
|
||||
name="hide"
|
||||
/>
|
||||
}
|
||||
label="API/MQTT"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="ram"
|
||||
@@ -198,12 +177,10 @@ const CustomEntitiesDialog = ({
|
||||
)}
|
||||
{editItem.ram === 0 && (
|
||||
<>
|
||||
<Grid mt={3}>
|
||||
<Grid mt={3} size={9}>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
icon={<EditOffOutlinedIcon color="primary" />}
|
||||
checkedIcon={<EditOutlinedIcon htmlColor="white" />}
|
||||
checked={editItem.writeable}
|
||||
onChange={updateFormValue}
|
||||
name="writeable"
|
||||
@@ -214,7 +191,7 @@ const CustomEntitiesDialog = ({
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="device_id"
|
||||
label={LL.ID_OF(LL.DEVICE())}
|
||||
margin="normal"
|
||||
@@ -234,7 +211,7 @@ const CustomEntitiesDialog = ({
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="type_id"
|
||||
label={LL.ID_OF(LL.TYPE(1))}
|
||||
margin="normal"
|
||||
@@ -254,7 +231,7 @@ const CustomEntitiesDialog = ({
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="offset"
|
||||
label={LL.OFFSET()}
|
||||
margin="normal"
|
||||
@@ -346,7 +323,7 @@ const CustomEntitiesDialog = ({
|
||||
editItem.device_id !== '0' && (
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="factor"
|
||||
label={LL.BYTES()}
|
||||
value={numberValue(editItem.factor as number)}
|
||||
@@ -364,7 +341,7 @@ const CustomEntitiesDialog = ({
|
||||
{editItem.value_type === DeviceValueType.BOOL && (
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="factor"
|
||||
label={LL.BITMASK()}
|
||||
value={editItem.factor as string}
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
Link,
|
||||
MenuItem,
|
||||
@@ -125,22 +125,13 @@ const Customizations = () => {
|
||||
|
||||
const setOriginalSettings = (data: DeviceEntity[]) => {
|
||||
setDeviceEntities(
|
||||
data.map((de) => {
|
||||
const result: DeviceEntity = {
|
||||
...de,
|
||||
o_m: de.m
|
||||
};
|
||||
if (de.cn !== undefined) {
|
||||
result.o_cn = de.cn;
|
||||
}
|
||||
if (de.mi !== undefined) {
|
||||
result.o_mi = de.mi;
|
||||
}
|
||||
if (de.ma !== undefined) {
|
||||
result.o_ma = de.ma;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
data.map((de) => ({
|
||||
...de,
|
||||
o_m: de.m,
|
||||
o_cn: de.cn,
|
||||
o_mi: de.mi,
|
||||
o_ma: de.ma
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -253,11 +244,8 @@ const Customizations = () => {
|
||||
setSelectedDevice(-1);
|
||||
setSelectedDeviceTypeNameURL('');
|
||||
} else {
|
||||
const device = devices.devices[index];
|
||||
if (device) {
|
||||
setSelectedDeviceTypeNameURL(device.url || '');
|
||||
setSelectedDeviceName(device.n);
|
||||
}
|
||||
setSelectedDeviceTypeNameURL(devices.devices[index].url || '');
|
||||
setSelectedDeviceName(devices.devices[index].n);
|
||||
setNumChanges(0);
|
||||
setRestartNeeded(false);
|
||||
}
|
||||
@@ -318,7 +306,7 @@ const Customizations = () => {
|
||||
|
||||
const filter_entity = (de: DeviceEntity) =>
|
||||
(de.m & selectedFilters || !selectedFilters) &&
|
||||
formatName(de, true).toLowerCase().includes(search.toLowerCase());
|
||||
formatName(de, true).includes(search);
|
||||
|
||||
const maskDisabled = (set: boolean) => {
|
||||
setDeviceEntities(
|
||||
@@ -408,20 +396,14 @@ const Customizations = () => {
|
||||
await sendCustomizationEntities({
|
||||
id: selectedDevice,
|
||||
entity_ids: masked_entities
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(LL.CUSTOMIZATIONS_SAVED());
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
if (error.message === 'Reboot required') {
|
||||
setRestartNeeded(true);
|
||||
} else {
|
||||
toast.error(error.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setOriginalSettings(deviceEntities);
|
||||
});
|
||||
}).catch((error: Error) => {
|
||||
if (error.message === 'Reboot required') {
|
||||
setRestartNeeded(true);
|
||||
} else {
|
||||
toast.error(error.message);
|
||||
}
|
||||
});
|
||||
setOriginalSettings(deviceEntities);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -563,7 +545,7 @@ const Customizations = () => {
|
||||
size="small"
|
||||
color="secondary"
|
||||
value={getMaskString(selectedFilters)}
|
||||
onChange={(_, mask: string[]) => {
|
||||
onChange={(event, mask: string[]) => {
|
||||
setSelectedFilters(getMaskNumber(mask));
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
TextField,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
@@ -54,10 +54,7 @@ const CustomizationsDialog = ({
|
||||
}
|
||||
}, [open, selectedItem]);
|
||||
|
||||
const handleClose = (
|
||||
_event: React.SyntheticEvent,
|
||||
reason: 'backdropClick' | 'escapeKeyDown'
|
||||
) => {
|
||||
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
|
||||
if (reason !== 'backdropClick') {
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { IconContext } from 'react-icons/lib';
|
||||
import { Link } from 'react-router';
|
||||
import { toast } from 'react-toastify';
|
||||
@@ -76,40 +76,35 @@ const Dashboard = () => {
|
||||
}
|
||||
);
|
||||
|
||||
const deviceValueDialogSave = useCallback(
|
||||
async (devicevalue: DeviceValue) => {
|
||||
if (!selectedDashboardItem) {
|
||||
return;
|
||||
}
|
||||
const id = selectedDashboardItem.id; // this is the parent ID
|
||||
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
|
||||
.then(() => {
|
||||
toast.success(LL.WRITE_CMD_SENT());
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeviceValueDialogOpen(false);
|
||||
setSelectedDashboardItem(undefined);
|
||||
});
|
||||
},
|
||||
[selectedDashboardItem, sendDeviceValue, LL]
|
||||
);
|
||||
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
|
||||
if (!selectedDashboardItem) {
|
||||
return;
|
||||
}
|
||||
const id = selectedDashboardItem.parentNode.id; // this is the parent ID
|
||||
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
|
||||
.then(() => {
|
||||
toast.success(LL.WRITE_CMD_SENT());
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeviceValueDialogOpen(false);
|
||||
setSelectedDashboardItem(undefined);
|
||||
});
|
||||
};
|
||||
|
||||
const dashboard_theme = useMemo(
|
||||
() =>
|
||||
useTheme({
|
||||
Table: `
|
||||
const dashboard_theme = useTheme({
|
||||
Table: `
|
||||
--data-table-library_grid-template-columns: minmax(80px, auto) 120px 32px;
|
||||
`,
|
||||
BaseRow: `
|
||||
BaseRow: `
|
||||
font-size: 14px;
|
||||
.td {
|
||||
height: 28px;
|
||||
}
|
||||
`,
|
||||
Row: `
|
||||
Row: `
|
||||
cursor: pointer;
|
||||
background-color: #1e1e1e;
|
||||
&:nth-of-type(odd) .td {
|
||||
@@ -119,7 +114,7 @@ const Dashboard = () => {
|
||||
background-color: #177ac9;
|
||||
},
|
||||
`,
|
||||
BaseCell: `
|
||||
BaseCell: `
|
||||
&:nth-of-type(2) {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -127,14 +122,12 @@ const Dashboard = () => {
|
||||
text-align: right;
|
||||
}
|
||||
`
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
const tree = useTree(
|
||||
{ nodes: data.nodes },
|
||||
{
|
||||
onChange: () => {} // not used but needed
|
||||
onChange: undefined // not used but needed
|
||||
},
|
||||
{
|
||||
treeIcon: {
|
||||
@@ -169,31 +162,28 @@ const Dashboard = () => {
|
||||
: tree.fns.onRemoveAll(); // collapse tree
|
||||
}, [parentNodes]);
|
||||
|
||||
const showType = useCallback(
|
||||
(n?: string, t?: number) => {
|
||||
// if we have a name show it
|
||||
if (n) {
|
||||
return n;
|
||||
const showType = (n?: string, t?: number) => {
|
||||
// if we have a name show it
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
if (t) {
|
||||
// otherwise pick translation based on type
|
||||
switch (t) {
|
||||
case DeviceType.CUSTOM:
|
||||
return LL.CUSTOM_ENTITIES(0);
|
||||
case DeviceType.ANALOGSENSOR:
|
||||
return LL.ANALOG_SENSORS();
|
||||
case DeviceType.TEMPERATURESENSOR:
|
||||
return LL.TEMP_SENSORS();
|
||||
case DeviceType.SCHEDULER:
|
||||
return LL.SCHEDULER();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (t) {
|
||||
// otherwise pick translation based on type
|
||||
switch (t) {
|
||||
case DeviceType.CUSTOM:
|
||||
return LL.CUSTOM_ENTITIES(0);
|
||||
case DeviceType.ANALOGSENSOR:
|
||||
return LL.ANALOG_SENSORS();
|
||||
case DeviceType.TEMPERATURESENSOR:
|
||||
return LL.TEMP_SENSORS();
|
||||
case DeviceType.SCHEDULER:
|
||||
return LL.SCHEDULER();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
[LL]
|
||||
);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const showName = (di: DashboardItem) => {
|
||||
if (di.id < 100) {
|
||||
@@ -211,24 +201,20 @@ const Dashboard = () => {
|
||||
if (di.dv) {
|
||||
return <span>{di.dv.id.slice(2)}</span>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const hasMask = (id: string, mask: number) =>
|
||||
(parseInt(id.slice(0, 2), 16) & mask) === mask;
|
||||
|
||||
const editDashboardValue = useCallback(
|
||||
(di: DashboardItem) => {
|
||||
if (me.admin && di.dv?.c) {
|
||||
setSelectedDashboardItem(di);
|
||||
setDeviceValueDialogOpen(true);
|
||||
}
|
||||
},
|
||||
[me.admin]
|
||||
);
|
||||
const editDashboardValue = (di: DashboardItem) => {
|
||||
if (me.admin && di.dv?.c) {
|
||||
setSelectedDashboardItem(di);
|
||||
setDeviceValueDialogOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleShowAll = (
|
||||
_event: React.MouseEvent<HTMLElement>,
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
toggle: boolean | null
|
||||
) => {
|
||||
if (toggle !== null) {
|
||||
@@ -239,9 +225,7 @@ const Dashboard = () => {
|
||||
|
||||
const renderContent = () => {
|
||||
if (!data) {
|
||||
return (
|
||||
<FormLoader onRetry={fetchDashboard} errorMessage={error?.message || ''} />
|
||||
);
|
||||
return <FormLoader onRetry={fetchDashboard} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
const hasFavEntities = data.nodes.filter(
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
List,
|
||||
@@ -329,16 +329,13 @@ const Devices = () => {
|
||||
|
||||
const handleDownloadCsv = () => {
|
||||
const deviceIndex = coreData.devices.findIndex(
|
||||
(d: Device) => d.id === device_select.state.id
|
||||
(d) => d.id === device_select.state.id
|
||||
);
|
||||
if (deviceIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const selectedDevice = coreData.devices[deviceIndex];
|
||||
if (!selectedDevice) {
|
||||
return;
|
||||
}
|
||||
const filename = selectedDevice.tn + '_' + selectedDevice.n;
|
||||
const filename =
|
||||
coreData.devices[deviceIndex].tn + '_' + coreData.devices[deviceIndex].n;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -353,7 +350,7 @@ const Devices = () => {
|
||||
{
|
||||
accessor: (dv: DeviceValue) =>
|
||||
dv.u !== undefined && DeviceValueUOM_s[dv.u]
|
||||
? DeviceValueUOM_s[dv.u]?.replace(/[^a-zA-Z0-9]/g, '')
|
||||
? DeviceValueUOM_s[dv.u].replace(/[^a-zA-Z0-9]/g, '')
|
||||
: '',
|
||||
name: 'UoM'
|
||||
},
|
||||
@@ -376,9 +373,7 @@ const Devices = () => {
|
||||
];
|
||||
|
||||
const data = onlyFav
|
||||
? deviceData.nodes.filter((dv: DeviceValue) =>
|
||||
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE)
|
||||
)
|
||||
? deviceData.nodes.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
|
||||
: deviceData.nodes;
|
||||
|
||||
const csvData = data.reduce(
|
||||
@@ -438,14 +433,10 @@ const Devices = () => {
|
||||
const renderDeviceDetails = () => {
|
||||
if (showDeviceInfo) {
|
||||
const deviceIndex = coreData.devices.findIndex(
|
||||
(d: Device) => d.id === device_select.state.id
|
||||
(d) => d.id === device_select.state.id
|
||||
);
|
||||
if (deviceIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
const deviceDetails = coreData.devices[deviceIndex];
|
||||
if (!deviceDetails) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -458,35 +449,47 @@ const Devices = () => {
|
||||
<DialogContent dividers>
|
||||
<List dense={true}>
|
||||
<ListItem>
|
||||
<ListItemText primary={LL.TYPE(0)} secondary={deviceDetails.tn} />
|
||||
<ListItemText
|
||||
primary={LL.TYPE(0)}
|
||||
secondary={coreData.devices[deviceIndex].tn}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText primary={LL.NAME(0)} secondary={deviceDetails.n} />
|
||||
<ListItemText
|
||||
primary={LL.NAME(0)}
|
||||
secondary={coreData.devices[deviceIndex].n}
|
||||
/>
|
||||
</ListItem>
|
||||
{deviceDetails.t !== DeviceType.CUSTOM && (
|
||||
{coreData.devices[deviceIndex].t !== DeviceType.CUSTOM && (
|
||||
<>
|
||||
<ListItem>
|
||||
<ListItemText primary={LL.BRAND()} secondary={deviceDetails.b} />
|
||||
<ListItemText
|
||||
primary={LL.BRAND()}
|
||||
secondary={coreData.devices[deviceIndex].b}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={LL.ID_OF(LL.DEVICE())}
|
||||
secondary={
|
||||
'0x' +
|
||||
('00' + deviceDetails.d.toString(16).toUpperCase()).slice(-2)
|
||||
(
|
||||
'00' +
|
||||
coreData.devices[deviceIndex].d.toString(16).toUpperCase()
|
||||
).slice(-2)
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={LL.ID_OF(LL.PRODUCT())}
|
||||
secondary={deviceDetails.p}
|
||||
secondary={coreData.devices[deviceIndex].p}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={LL.VERSION()}
|
||||
secondary={deviceDetails.v}
|
||||
secondary={coreData.devices[deviceIndex].v}
|
||||
/>
|
||||
</ListItem>
|
||||
</>
|
||||
@@ -505,7 +508,6 @@ const Devices = () => {
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderCoreData = () => (
|
||||
@@ -596,26 +598,19 @@ const Devices = () => {
|
||||
|
||||
const shown_data = onlyFav
|
||||
? deviceData.nodes.filter(
|
||||
(dv: DeviceValue) =>
|
||||
(dv) =>
|
||||
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE) &&
|
||||
dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
|
||||
dv.id.slice(2).includes(search)
|
||||
)
|
||||
: deviceData.nodes.filter((dv: DeviceValue) =>
|
||||
dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
: deviceData.nodes.filter((dv) => dv.id.slice(2).includes(search));
|
||||
|
||||
const deviceIndex = coreData.devices.findIndex(
|
||||
(d: Device) => d.id === device_select.state.id
|
||||
(d) => d.id === device_select.state.id
|
||||
);
|
||||
if (deviceIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const deviceInfo = coreData.devices[deviceIndex];
|
||||
if (!deviceInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [, height] = size;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -626,15 +621,15 @@ const Devices = () => {
|
||||
bottom: 0,
|
||||
top: 64,
|
||||
zIndex: 'modal',
|
||||
maxHeight: () => (height || 0) - 126,
|
||||
maxHeight: () => size[1] - 126,
|
||||
border: '1px solid #177ac9'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 1 }}>
|
||||
<Grid container justifyContent="space-between">
|
||||
<Typography noWrap variant="subtitle1" color="warning.main">
|
||||
{deviceInfo.n} (
|
||||
{deviceInfo.tn})
|
||||
{coreData.devices[deviceIndex].n} (
|
||||
{coreData.devices[deviceIndex].tn})
|
||||
</Typography>
|
||||
<Grid justifyContent="flex-end">
|
||||
<ButtonTooltip title={LL.CLOSE()}>
|
||||
@@ -704,7 +699,7 @@ const Devices = () => {
|
||||
' ' +
|
||||
shown_data.length +
|
||||
'/' +
|
||||
deviceInfo.e +
|
||||
coreData.devices[deviceIndex].e +
|
||||
' ' +
|
||||
LL.ENTITIES(shown_data.length)}
|
||||
</span>
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormHelperText,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
TextField,
|
||||
@@ -120,7 +120,7 @@ const DevicesDialog = ({
|
||||
{editItem.l ? (
|
||||
<TextField
|
||||
name="v"
|
||||
// label={LL.VALUE(0)}
|
||||
label={LL.VALUE(0)}
|
||||
value={editItem.v}
|
||||
disabled={!writeable}
|
||||
sx={{ width: '30ch' }}
|
||||
@@ -135,7 +135,7 @@ const DevicesDialog = ({
|
||||
</TextField>
|
||||
) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? (
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="v"
|
||||
label={LL.VALUE(0)}
|
||||
value={numberValue(Math.round((editItem.v as number) * 10) / 10)}
|
||||
@@ -159,7 +159,7 @@ const DevicesDialog = ({
|
||||
/>
|
||||
) : (
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="v"
|
||||
label={LL.VALUE(0)}
|
||||
value={editItem.v}
|
||||
|
||||
@@ -43,7 +43,7 @@ const EntityMaskToggle = ({ onUpdate, de }: EntityMaskToggleProps) => {
|
||||
size="small"
|
||||
color="secondary"
|
||||
value={getMaskString(de.m)}
|
||||
onChange={(_event, mask: string[]) => {
|
||||
onChange={(event, mask: string[]) => {
|
||||
de.m = getMaskNumber(mask);
|
||||
if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) {
|
||||
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;
|
||||
|
||||
@@ -41,8 +41,7 @@ const Help = () => {
|
||||
|
||||
useRequest(() => callAction({ action: 'getCustomSupport' })).onSuccess((event) => {
|
||||
if (event && event.data && Object.keys(event.data).length !== 0) {
|
||||
const data = (event.data as { Support: { img_url?: string; html?: string[] } })
|
||||
.Support;
|
||||
const data = event.data.Support;
|
||||
if (data.img_url) {
|
||||
setCustomSupportIMG(data.img_url);
|
||||
}
|
||||
@@ -60,7 +59,7 @@ const Help = () => {
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
})
|
||||
.onError((error) => {
|
||||
toast.error(String(error.error?.message || 'An error occurred'));
|
||||
toast.error(error.message);
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -133,15 +133,13 @@ const Modules = () => {
|
||||
};
|
||||
|
||||
const saveModules = async () => {
|
||||
await Promise.all(
|
||||
modules.map((condensed_mi: ModuleItem) =>
|
||||
updateModules({
|
||||
key: condensed_mi.key,
|
||||
enabled: condensed_mi.enabled,
|
||||
license: condensed_mi.license
|
||||
})
|
||||
)
|
||||
)
|
||||
await updateModules({
|
||||
modules: modules.map((condensed_mi) => ({
|
||||
key: condensed_mi.key,
|
||||
enabled: condensed_mi.enabled,
|
||||
license: condensed_mi.license
|
||||
}))
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(LL.MODULES_UPDATED());
|
||||
})
|
||||
@@ -156,9 +154,7 @@ const Modules = () => {
|
||||
|
||||
const renderContent = () => {
|
||||
if (!modules) {
|
||||
return (
|
||||
<FormLoader onRetry={fetchModules} errorMessage={error?.message || ''} />
|
||||
);
|
||||
return <FormLoader onRetry={fetchModules} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
if (modules.length === 0) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
TextField
|
||||
} from '@mui/material';
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
useLayoutTitle
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { useInterval } from 'utils';
|
||||
|
||||
import { readSchedule, writeSchedule } from '../../api/app';
|
||||
import SettingsSchedulerDialog from './SchedulerDialog';
|
||||
@@ -74,12 +73,6 @@ const Scheduler = () => {
|
||||
);
|
||||
}
|
||||
|
||||
useInterval(() => {
|
||||
if (numChanges === 0) {
|
||||
void fetchSchedule();
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const formatter = new Intl.DateTimeFormat(locale, {
|
||||
weekday: 'short',
|
||||
@@ -135,8 +128,8 @@ const Scheduler = () => {
|
||||
const saveSchedule = async () => {
|
||||
await updateSchedule({
|
||||
schedule: schedule
|
||||
.filter((si: ScheduleItem) => !si.deleted)
|
||||
.map((condensed_si: ScheduleItem) => ({
|
||||
.filter((si) => !si.deleted)
|
||||
.map((condensed_si) => ({
|
||||
id: condensed_si.id,
|
||||
active: condensed_si.active,
|
||||
flags: condensed_si.flags,
|
||||
@@ -212,9 +205,7 @@ const Scheduler = () => {
|
||||
|
||||
const renderSchedule = () => {
|
||||
if (!schedule) {
|
||||
return (
|
||||
<FormLoader onRetry={fetchSchedule} errorMessage={error?.message || ''} />
|
||||
);
|
||||
return <FormLoader onRetry={fetchSchedule} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
const dayBox = (si: ScheduleItem, flag: number) => (
|
||||
@@ -253,8 +244,8 @@ const Scheduler = () => {
|
||||
<Table
|
||||
data={{
|
||||
nodes: schedule
|
||||
.filter((si: ScheduleItem) => !si.deleted)
|
||||
.sort((a: ScheduleItem, b: ScheduleItem) => a.flags - b.flags)
|
||||
.filter((si) => !si.deleted)
|
||||
.sort((a, b) => a.flags - b.flags)
|
||||
}}
|
||||
theme={schedule_theme}
|
||||
layout={{ custom: true }}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
TextField,
|
||||
ToggleButton,
|
||||
ToggleButtonGroup,
|
||||
@@ -144,10 +144,7 @@ const SchedulerDialog = ({
|
||||
</Typography>
|
||||
);
|
||||
|
||||
const handleClose = (
|
||||
_event: React.SyntheticEvent,
|
||||
reason: 'backdropClick' | 'escapeKeyDown'
|
||||
) => {
|
||||
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
|
||||
if (reason !== 'backdropClick') {
|
||||
onClose();
|
||||
}
|
||||
@@ -328,7 +325,7 @@ const SchedulerDialog = ({
|
||||
</>
|
||||
)}
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="cmd"
|
||||
label={LL.COMMAND(0)}
|
||||
multiline
|
||||
@@ -347,7 +344,7 @@ const SchedulerDialog = ({
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="name"
|
||||
label={LL.NAME(0) + ' (' + LL.OPTIONAL() + ')'}
|
||||
value={editItem.name}
|
||||
|
||||
@@ -439,8 +439,7 @@ const Sensors = () => {
|
||||
<Cell>{a.n}</Cell>
|
||||
<Cell stiff>{AnalogTypeNames[a.t]} </Cell>
|
||||
{(a.t === AnalogType.DIGITAL_OUT && a.g !== 25 && a.g !== 26) ||
|
||||
a.t === AnalogType.DIGITAL_IN ||
|
||||
a.t === AnalogType.PULSE ? (
|
||||
a.t === AnalogType.DIGITAL_IN ? (
|
||||
<Cell stiff>{a.v ? LL.ON() : LL.OFF()}</Cell>
|
||||
) : (
|
||||
<Cell stiff>{a.t ? formatValue(a.v, a.u) : ''}</Cell>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
TextField,
|
||||
@@ -57,10 +57,7 @@ const SensorsAnalogDialog = ({
|
||||
}
|
||||
}, [open, selectedItem]);
|
||||
|
||||
const handleClose = (
|
||||
_event: React.SyntheticEvent,
|
||||
reason: 'backdropClick' | 'escapeKeyDown'
|
||||
) => {
|
||||
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
|
||||
if (reason !== 'backdropClick') {
|
||||
onClose();
|
||||
}
|
||||
@@ -91,7 +88,7 @@ const SensorsAnalogDialog = ({
|
||||
<Grid container spacing={2}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="g"
|
||||
label="GPIO"
|
||||
sx={{ width: '11ch' }}
|
||||
@@ -110,7 +107,7 @@ const SensorsAnalogDialog = ({
|
||||
)}
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="n"
|
||||
label={LL.NAME(0)}
|
||||
value={editItem.n}
|
||||
@@ -135,9 +132,7 @@ const SensorsAnalogDialog = ({
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
{((editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE) ||
|
||||
(editItem.t >= AnalogType.FREQ_0 &&
|
||||
editItem.t <= AnalogType.FREQ_2)) && (
|
||||
{editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && (
|
||||
<Grid>
|
||||
<TextField
|
||||
name="u"
|
||||
@@ -176,27 +171,6 @@ const SensorsAnalogDialog = ({
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{editItem.t === AnalogType.NTC && (
|
||||
<Grid>
|
||||
<TextField
|
||||
name="o"
|
||||
label={LL.OFFSET()}
|
||||
value={numberValue(editItem.o)}
|
||||
sx={{ width: '11ch' }}
|
||||
type="number"
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">°C</InputAdornment>
|
||||
)
|
||||
},
|
||||
htmlInput: { min: '-20', max: '20', step: '0.1' }
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{editItem.t === AnalogType.COUNTER && (
|
||||
<Grid>
|
||||
<TextField
|
||||
@@ -213,19 +187,6 @@ const SensorsAnalogDialog = ({
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{editItem.t === AnalogType.RGB && (
|
||||
<Grid>
|
||||
<TextField
|
||||
name="o"
|
||||
label={'RGB ' + LL.VALUE(0)}
|
||||
value={numberValue(editItem.o)}
|
||||
type="number"
|
||||
sx={{ width: '11ch' }}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && (
|
||||
<Grid>
|
||||
<TextField
|
||||
@@ -353,42 +314,6 @@ const SensorsAnalogDialog = ({
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
{editItem.t === AnalogType.PULSE && (
|
||||
<>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="o"
|
||||
label={LL.POLARITY()}
|
||||
value={editItem.o}
|
||||
sx={{ width: '11ch' }}
|
||||
select
|
||||
onChange={updateFormValue}
|
||||
>
|
||||
<MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
|
||||
<MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="f"
|
||||
label="Pulse"
|
||||
value={numberValue(editItem.f)}
|
||||
type="number"
|
||||
sx={{ width: '15ch' }}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">s</InputAdornment>
|
||||
)
|
||||
},
|
||||
htmlInput: { min: '0', max: '10000', step: '0.1' }
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
Typography
|
||||
@@ -52,10 +52,7 @@ const SensorsTemperatureDialog = ({
|
||||
}
|
||||
}, [open, selectedItem]);
|
||||
|
||||
const handleClose = (
|
||||
_event: React.SyntheticEvent,
|
||||
reason: 'backdropClick' | 'escapeKeyDown'
|
||||
) => {
|
||||
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
|
||||
if (reason !== 'backdropClick') {
|
||||
onClose();
|
||||
}
|
||||
@@ -85,7 +82,7 @@ const SensorsTemperatureDialog = ({
|
||||
<Grid container spacing={2}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="n"
|
||||
label={LL.NAME(0)}
|
||||
value={editItem.n}
|
||||
|
||||
@@ -188,8 +188,7 @@ export enum DeviceValueUOM {
|
||||
VOLTS,
|
||||
MBAR,
|
||||
LH,
|
||||
CTKWH,
|
||||
HZ
|
||||
CTKWH
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
@@ -219,8 +218,7 @@ export const DeviceValueUOM_s = [
|
||||
'V',
|
||||
'mbar',
|
||||
'l/h',
|
||||
'ct/kWh',
|
||||
'Hz'
|
||||
'ct/kWh'
|
||||
];
|
||||
|
||||
export enum AnalogType {
|
||||
@@ -234,32 +232,20 @@ export enum AnalogType {
|
||||
DIGITAL_OUT = 6,
|
||||
PWM_0 = 7,
|
||||
PWM_1 = 8,
|
||||
PWM_2 = 9,
|
||||
NTC = 10,
|
||||
RGB = 11,
|
||||
PULSE = 12,
|
||||
FREQ_0 = 13,
|
||||
FREQ_1 = 14,
|
||||
FREQ_2 = 15
|
||||
PWM_2 = 9
|
||||
}
|
||||
|
||||
export const AnalogTypeNames = [
|
||||
'(disabled)',
|
||||
'Digital In',
|
||||
'Counter',
|
||||
'ADC In',
|
||||
'ADC',
|
||||
'Timer',
|
||||
'Rate',
|
||||
'Digital Out',
|
||||
'PWM 0',
|
||||
'PWM 1',
|
||||
'PWM 2',
|
||||
'NTC Temp.',
|
||||
'RGB Led',
|
||||
'Pulse',
|
||||
'Freq 0',
|
||||
'Freq 1',
|
||||
'Freq 2'
|
||||
'PWM 2'
|
||||
];
|
||||
|
||||
type BoardProfiles = Record<string, string>;
|
||||
@@ -269,7 +255,6 @@ export const BOARD_PROFILES: BoardProfiles = {
|
||||
S32S3: 'BBQKees Gateway S3',
|
||||
E32: 'BBQKees Gateway E32',
|
||||
E32V2: 'BBQKees Gateway E32 V2',
|
||||
E32V2_2: 'BBQKees Gateway E32 V2.2',
|
||||
NODEMCU: 'NodeMCU 32S',
|
||||
'MH-ET': 'MH-ET Live D1 Mini',
|
||||
LOLIN: 'Lolin D32',
|
||||
@@ -395,7 +380,6 @@ export interface EntityItem {
|
||||
value_type: number;
|
||||
value?: unknown;
|
||||
writeable: boolean;
|
||||
hide: boolean;
|
||||
deleted?: boolean;
|
||||
o_id?: number;
|
||||
o_ram?: number;
|
||||
@@ -409,7 +393,6 @@ export interface EntityItem {
|
||||
o_deleted?: boolean;
|
||||
o_writeable?: boolean;
|
||||
o_value?: unknown;
|
||||
o_hide?: boolean;
|
||||
}
|
||||
|
||||
export interface Entities {
|
||||
|
||||
@@ -13,7 +13,7 @@ import type {
|
||||
|
||||
export const GPIO_VALIDATOR = {
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -36,7 +36,7 @@ export const GPIO_VALIDATOR = {
|
||||
|
||||
export const GPIO_VALIDATORR = {
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -60,7 +60,7 @@ export const GPIO_VALIDATORR = {
|
||||
|
||||
export const GPIO_VALIDATORC3 = {
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -74,7 +74,7 @@ export const GPIO_VALIDATORC3 = {
|
||||
|
||||
export const GPIO_VALIDATORS2 = {
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -94,7 +94,7 @@ export const GPIO_VALIDATORS2 = {
|
||||
|
||||
export const GPIO_VALIDATORS3 = {
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -279,7 +279,7 @@ export const createSettingsValidator = (settings: Settings) =>
|
||||
|
||||
export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
name: string,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -324,7 +324,7 @@ export const uniqueCustomNameValidator = (
|
||||
o_name?: string
|
||||
) => ({
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
name: string,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -353,7 +353,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
|
||||
device_id: [
|
||||
{
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: string,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -367,7 +367,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
|
||||
type_id: [
|
||||
{
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: string,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -389,7 +389,7 @@ export const uniqueTemperatureNameValidator = (
|
||||
sensors: TemperatureSensor[],
|
||||
o_name?: string
|
||||
) => ({
|
||||
validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
|
||||
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
|
||||
if (
|
||||
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
|
||||
n !== '' &&
|
||||
@@ -419,7 +419,7 @@ export const temperatureSensorItemValidation = (
|
||||
|
||||
export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
gpio: number,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
@@ -435,7 +435,7 @@ export const uniqueAnalogNameValidator = (
|
||||
sensors: AnalogSensor[],
|
||||
o_name?: string
|
||||
) => ({
|
||||
validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
|
||||
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
|
||||
if (
|
||||
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
|
||||
n !== '' &&
|
||||
@@ -482,7 +482,7 @@ export const deviceValueItemValidation = (dv: DeviceValue) =>
|
||||
{ required: true, message: 'Value is required' },
|
||||
{
|
||||
validator(
|
||||
_rule: InternalRuleItem,
|
||||
rule: InternalRuleItem,
|
||||
value: unknown,
|
||||
callback: (error?: string) => void
|
||||
) {
|
||||
|
||||
@@ -54,12 +54,12 @@ const APSettings = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -80,7 +80,7 @@ const APSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="provision_mode"
|
||||
label={LL.AP_PROVIDE() + '...'}
|
||||
value={data.provision_mode}
|
||||
@@ -103,7 +103,7 @@ const APSettings = () => {
|
||||
{isAPEnabled(data) && (
|
||||
<>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="ssid"
|
||||
label={LL.ACCESS_POINT(2) + ' SSID'}
|
||||
fullWidth
|
||||
@@ -113,7 +113,7 @@ const APSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedPasswordField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="password"
|
||||
label={LL.ACCESS_POINT(2) + ' ' + LL.PASSWORD()}
|
||||
fullWidth
|
||||
@@ -123,7 +123,7 @@ const APSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="channel"
|
||||
label={LL.AP_PREFERRED_CHANNEL()}
|
||||
value={numberValue(data.channel)}
|
||||
@@ -151,7 +151,7 @@ const APSettings = () => {
|
||||
label={LL.AP_HIDE_SSID()}
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="max_clients"
|
||||
label={LL.AP_MAX_CLIENTS()}
|
||||
value={numberValue(data.max_clients)}
|
||||
@@ -169,7 +169,7 @@ const APSettings = () => {
|
||||
))}
|
||||
</ValidatedTextField>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="local_ip"
|
||||
label={LL.AP_LOCAL_IP()}
|
||||
fullWidth
|
||||
@@ -179,7 +179,7 @@ const APSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="gateway_ip"
|
||||
label={LL.NETWORK_GATEWAY()}
|
||||
fullWidth
|
||||
@@ -189,7 +189,7 @@ const APSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="subnet_mask"
|
||||
label={LL.NETWORK_SUBNET()}
|
||||
fullWidth
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
TextField,
|
||||
@@ -75,7 +75,7 @@ const ApplicationSettings = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
@@ -135,7 +135,7 @@ const ApplicationSettings = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data || !hardwareData) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -219,7 +219,7 @@ const ApplicationSettings = () => {
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="modbus_max_clients"
|
||||
label={LL.AP_MAX_CLIENTS()}
|
||||
variant="outlined"
|
||||
@@ -231,7 +231,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="modbus_port"
|
||||
label="Port"
|
||||
variant="outlined"
|
||||
@@ -243,7 +243,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="modbus_timeout"
|
||||
label="Timeout"
|
||||
slotProps={{
|
||||
@@ -273,7 +273,7 @@ const ApplicationSettings = () => {
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="syslog_host"
|
||||
label="Host"
|
||||
variant="outlined"
|
||||
@@ -284,7 +284,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="syslog_port"
|
||||
label="Port"
|
||||
variant="outlined"
|
||||
@@ -315,7 +315,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="syslog_mark_interval"
|
||||
label={LL.MARK_INTERVAL()}
|
||||
slotProps={{
|
||||
@@ -485,7 +485,7 @@ const ApplicationSettings = () => {
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="rx_gpio"
|
||||
label={LL.GPIO_OF('Rx')}
|
||||
fullWidth
|
||||
@@ -498,7 +498,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="tx_gpio"
|
||||
label={LL.GPIO_OF('Tx')}
|
||||
fullWidth
|
||||
@@ -511,7 +511,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="pbutton_gpio"
|
||||
label={LL.GPIO_OF(LL.BUTTON())}
|
||||
fullWidth
|
||||
@@ -524,7 +524,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="dallas_gpio"
|
||||
label={
|
||||
LL.GPIO_OF(LL.TEMPERATURE()) + ' (0=' + LL.DISABLED(1) + ')'
|
||||
@@ -539,7 +539,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="led_gpio"
|
||||
label={LL.GPIO_OF('LED') + ' (0=' + LL.DISABLED(1) + ')'}
|
||||
fullWidth
|
||||
@@ -554,7 +554,7 @@ const ApplicationSettings = () => {
|
||||
<Grid>
|
||||
<TextField
|
||||
name="led_type"
|
||||
label={'LED ' + LL.TYPE(0)}
|
||||
label={'LED ' + LL.TYPE()}
|
||||
value={data.led_type}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@@ -743,7 +743,7 @@ const ApplicationSettings = () => {
|
||||
{data.remote_timeout_en && (
|
||||
<Box mt={2}>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="remote_timeout"
|
||||
label={LL.REMOTE_TIMEOUT()}
|
||||
slotProps={{
|
||||
@@ -783,7 +783,7 @@ const ApplicationSettings = () => {
|
||||
{data.shower_timer && (
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="shower_min_duration"
|
||||
label={LL.MIN_DURATION()}
|
||||
slotProps={{
|
||||
@@ -801,7 +801,7 @@ const ApplicationSettings = () => {
|
||||
<>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="shower_alert_trigger"
|
||||
label={LL.TRIGGER_TIME()}
|
||||
slotProps={{
|
||||
@@ -817,7 +817,7 @@ const ApplicationSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="shower_alert_coldshot"
|
||||
label={LL.COLD_SHOT_DURATION()}
|
||||
slotProps={{
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import { Box, Button, Grid, Typography } from '@mui/material';
|
||||
import { Box, Button, Grid2 as Grid, Typography } from '@mui/material';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { API, callAction } from 'api/app';
|
||||
@@ -35,7 +35,7 @@ const DownloadUpload = () => {
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
})
|
||||
.onError((error) => {
|
||||
toast.error(String(error.error?.message || 'An error occurred'));
|
||||
toast.error(error.message);
|
||||
});
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
@@ -57,7 +57,7 @@ const DownloadUpload = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ import WarningIcon from '@mui/icons-material/Warning';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
TextField,
|
||||
@@ -56,7 +56,7 @@ const MqttSettings = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const SecondsInputProps = {
|
||||
@@ -65,7 +65,7 @@ const MqttSettings = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -93,7 +93,7 @@ const MqttSettings = () => {
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="host"
|
||||
label={LL.ADDRESS_OF(LL.BROKER())}
|
||||
multiline
|
||||
@@ -105,7 +105,7 @@ const MqttSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="port"
|
||||
label="Port"
|
||||
variant="outlined"
|
||||
@@ -117,7 +117,7 @@ const MqttSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="base"
|
||||
label={LL.BASE_TOPIC()}
|
||||
variant="outlined"
|
||||
@@ -158,7 +158,7 @@ const MqttSettings = () => {
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="keep_alive"
|
||||
label="Keep Alive"
|
||||
slotProps={{
|
||||
@@ -254,107 +254,109 @@ const MqttSettings = () => {
|
||||
}
|
||||
label={LL.MQTT_RESPONSE()}
|
||||
/>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="publish_single"
|
||||
checked={data.publish_single}
|
||||
onChange={updateFormValue}
|
||||
disabled={data.ha_enabled}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_1()}
|
||||
/>
|
||||
</Grid>
|
||||
{data.publish_single && (
|
||||
{!data.ha_enabled && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="publish_single2cmd"
|
||||
checked={data.publish_single2cmd}
|
||||
name="publish_single"
|
||||
checked={data.publish_single}
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_2()}
|
||||
label={LL.MQTT_PUBLISH_TEXT_1()}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="ha_enabled"
|
||||
checked={data.ha_enabled}
|
||||
onChange={updateFormValue}
|
||||
disabled={data.publish_single}
|
||||
{data.publish_single && (
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="publish_single2cmd"
|
||||
checked={data.publish_single2cmd}
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_2()}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_3()}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
{data.ha_enabled && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_type"
|
||||
label={LL.MQTT_PUBLISH_TEXT_5()}
|
||||
value={data.discovery_type}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Home Assistant</MenuItem>
|
||||
<MenuItem value={1}>Domoticz</MenuItem>
|
||||
<MenuItem value={2}>Domoticz (latest)</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_prefix"
|
||||
label={LL.MQTT_PUBLISH_TEXT_4()}
|
||||
variant="outlined"
|
||||
value={data.discovery_prefix}
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="entity_format"
|
||||
label={LL.MQTT_ENTITY_FORMAT()}
|
||||
value={data.entity_format}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
|
||||
<MenuItem value={3}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (v3.6)
|
||||
</MenuItem>
|
||||
<MenuItem value={4}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (v3.6)
|
||||
</MenuItem>
|
||||
<MenuItem value={1}>{LL.MQTT_ENTITY_FORMAT_1()}</MenuItem>
|
||||
<MenuItem value={2}>{LL.MQTT_ENTITY_FORMAT_2()}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
)}
|
||||
{!data.publish_single && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="ha_enabled"
|
||||
checked={data.ha_enabled}
|
||||
onChange={updateFormValue}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_3()}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
{data.ha_enabled && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_type"
|
||||
label={LL.MQTT_PUBLISH_TEXT_5()}
|
||||
value={data.discovery_type}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Home Assistant</MenuItem>
|
||||
<MenuItem value={1}>Domoticz</MenuItem>
|
||||
<MenuItem value={2}>Domoticz (latest)</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_prefix"
|
||||
label={LL.MQTT_PUBLISH_TEXT_4()}
|
||||
variant="outlined"
|
||||
value={data.discovery_prefix}
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="entity_format"
|
||||
label={LL.MQTT_ENTITY_FORMAT()}
|
||||
value={data.entity_format}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
|
||||
<MenuItem value={3}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (v3.6)
|
||||
</MenuItem>
|
||||
<MenuItem value={4}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (v3.6)
|
||||
</MenuItem>
|
||||
<MenuItem value={1}>{LL.MQTT_ENTITY_FORMAT_1()}</MenuItem>
|
||||
<MenuItem value={2}>{LL.MQTT_ENTITY_FORMAT_2()}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)}
|
||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||
{LL.MQTT_PUBLISH_INTERVALS()} (0=auto)
|
||||
</Typography>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="publish_time_heartbeat"
|
||||
label="Heartbeat"
|
||||
slotProps={{
|
||||
@@ -440,7 +442,7 @@ const MqttSettings = () => {
|
||||
<Grid>
|
||||
<TextField
|
||||
name="publish_time_sensor"
|
||||
label={LL.SENSORS()}
|
||||
label={LL.TEMP_SENSORS()}
|
||||
variant="outlined"
|
||||
value={numberValue(data.publish_time_sensor)}
|
||||
type="number"
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
MenuItem,
|
||||
TextField,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||
|
||||
import * as NTPApi from 'api/ntp';
|
||||
import { readNTPSettings } from 'api/ntp';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova/client';
|
||||
import { updateState } from 'alova/client';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
@@ -34,8 +19,8 @@ import {
|
||||
useLayoutTitle
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NTPSettingsType, Time } from 'types';
|
||||
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
|
||||
import type { NTPSettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||
|
||||
@@ -61,101 +46,18 @@ const NTPSettings = () => {
|
||||
const { LL } = useI18nContext();
|
||||
useLayoutTitle('NTP');
|
||||
|
||||
const [localTime, setLocalTime] = useState<string>('');
|
||||
const [settingTime, setSettingTime] = useState<boolean>(false);
|
||||
const [processing, setProcessing] = useState<boolean>(false);
|
||||
|
||||
const { send: updateTime } = useRequest(
|
||||
(local_time: Time) => NTPApi.updateTime(local_time),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
);
|
||||
|
||||
const updateFormValue = updateValueDirty(
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
|
||||
const updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLocalTime(event.target.value);
|
||||
|
||||
const openSetTime = () => {
|
||||
setLocalTime(formatLocalDateTime(new Date()));
|
||||
setSettingTime(true);
|
||||
};
|
||||
|
||||
const configureTime = async () => {
|
||||
setProcessing(true);
|
||||
|
||||
await updateTime({ local_time: formatLocalDateTime(new Date(localTime)) })
|
||||
.then(async () => {
|
||||
toast.success(LL.TIME_SET());
|
||||
setSettingTime(false);
|
||||
await loadData();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(LL.PROBLEM_UPDATING());
|
||||
})
|
||||
.finally(() => {
|
||||
setProcessing(false);
|
||||
});
|
||||
};
|
||||
|
||||
const renderSetTimeDialog = () => (
|
||||
<Dialog
|
||||
sx={dialogStyle}
|
||||
open={settingTime}
|
||||
onClose={() => setSettingTime(false)}
|
||||
>
|
||||
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}>
|
||||
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography>
|
||||
</Box>
|
||||
<TextField
|
||||
label={LL.LOCAL_TIME(0)}
|
||||
type="datetime-local"
|
||||
value={localTime}
|
||||
onChange={updateLocalTime}
|
||||
disabled={processing}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
inputLabel: {
|
||||
shrink: true
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => setSettingTime(false)}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<AccessTimeIcon />}
|
||||
variant="outlined"
|
||||
onClick={configureTime}
|
||||
disabled={processing}
|
||||
color="primary"
|
||||
>
|
||||
{LL.UPDATE()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -190,7 +92,7 @@ const NTPSettings = () => {
|
||||
label={LL.ENABLE_NTP()}
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="server"
|
||||
label={LL.NTP_SERVER()}
|
||||
fullWidth
|
||||
@@ -200,7 +102,7 @@ const NTPSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="tz_label"
|
||||
label={LL.TIME_ZONE()}
|
||||
fullWidth
|
||||
@@ -213,25 +115,6 @@ const NTPSettings = () => {
|
||||
<MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem>
|
||||
{timeZoneSelectItems()}
|
||||
</ValidatedTextField>
|
||||
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
{!data.enabled && !dirtyFlags.length && (
|
||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||
<ButtonRow>
|
||||
<Button
|
||||
onClick={openSetTime}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
startIcon={<AccessTimeIcon />}
|
||||
>
|
||||
{LL.SET_TIME(0)}
|
||||
</Button>
|
||||
</ButtonRow>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{renderSetTimeDialog()}
|
||||
|
||||
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||
<ButtonRow>
|
||||
<Button
|
||||
|
||||
@@ -35,7 +35,7 @@ const Network = () => {
|
||||
],
|
||||
useLocation()
|
||||
);
|
||||
const routerTab = matchedRoutes?.[0]?.route.path || false;
|
||||
const routerTab = matchedRoutes?.[0].route.path || false;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -56,7 +56,7 @@ const Network = () => {
|
||||
return (
|
||||
<WiFiConnectionContext.Provider
|
||||
value={{
|
||||
...(selectedNetwork && { selectedNetwork }),
|
||||
selectedNetwork,
|
||||
selectNetwork,
|
||||
deselectNetwork
|
||||
}}
|
||||
|
||||
@@ -104,7 +104,7 @@ const NetworkSettings = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||
@@ -113,7 +113,7 @@ const NetworkSettings = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -172,7 +172,7 @@ const NetworkSettings = () => {
|
||||
</List>
|
||||
) : (
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="ssid"
|
||||
label={'SSID (' + LL.NETWORK_BLANK_SSID() + ')'}
|
||||
fullWidth
|
||||
@@ -183,7 +183,7 @@ const NetworkSettings = () => {
|
||||
/>
|
||||
)}
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="bssid"
|
||||
label={'BSSID (' + LL.NETWORK_BLANK_BSSID() + ')'}
|
||||
fullWidth
|
||||
@@ -194,7 +194,7 @@ const NetworkSettings = () => {
|
||||
/>
|
||||
{(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && (
|
||||
<ValidatedPasswordField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="password"
|
||||
label={LL.PASSWORD()}
|
||||
fullWidth
|
||||
@@ -251,7 +251,7 @@ const NetworkSettings = () => {
|
||||
{LL.GENERAL_OPTIONS()}
|
||||
</Typography>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="hostname"
|
||||
label={LL.HOSTNAME()}
|
||||
fullWidth
|
||||
@@ -304,7 +304,7 @@ const NetworkSettings = () => {
|
||||
{data.static_ip_config && (
|
||||
<>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="local_ip"
|
||||
label={LL.AP_LOCAL_IP()}
|
||||
fullWidth
|
||||
@@ -314,7 +314,7 @@ const NetworkSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="gateway_ip"
|
||||
label={LL.NETWORK_GATEWAY()}
|
||||
fullWidth
|
||||
@@ -324,7 +324,7 @@ const NetworkSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="subnet_mask"
|
||||
label={LL.NETWORK_SUBNET()}
|
||||
fullWidth
|
||||
@@ -334,7 +334,7 @@ const NetworkSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="dns_ip_1"
|
||||
label="DNS #1"
|
||||
fullWidth
|
||||
@@ -344,7 +344,7 @@ const NetworkSettings = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="dns_ip_2"
|
||||
label="DNS #2"
|
||||
fullWidth
|
||||
|
||||
@@ -50,7 +50,9 @@ const WiFiNetworkScanner = () => {
|
||||
|
||||
const renderNetworkScanner = () => {
|
||||
if (!networkList) {
|
||||
return <FormLoader errorMessage={errorMessage || ''} />;
|
||||
return (
|
||||
<FormLoader message={LL.SCANNING() + '...'} errorMessage={errorMessage} />
|
||||
);
|
||||
}
|
||||
return <WiFiNetworkSelector networkList={networkList} />;
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ const ManageUsers = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const noAdminConfigured = () => !data.users.find((u) => u.admin);
|
||||
@@ -260,20 +260,15 @@ const ManageUsers = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<GenerateToken
|
||||
username={generatingToken || ''}
|
||||
onClose={closeGenerateToken}
|
||||
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
|
||||
<User
|
||||
user={user}
|
||||
setUser={setUser}
|
||||
creating={creating}
|
||||
onDoneEditing={doneEditingUser}
|
||||
onCancelEditing={cancelEditingUser}
|
||||
validator={createUserValidator(data.users, creating)}
|
||||
/>
|
||||
{user && (
|
||||
<User
|
||||
user={user}
|
||||
setUser={setUser}
|
||||
creating={creating}
|
||||
onDoneEditing={doneEditingUser}
|
||||
onCancelEditing={cancelEditingUser}
|
||||
validator={createUserValidator(data.users, creating)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ const Security = () => {
|
||||
],
|
||||
useLocation()
|
||||
);
|
||||
const routerTab = matchedRoutes?.[0]?.route.path || false;
|
||||
const routerTab = matchedRoutes?.[0].route.path || false;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -47,12 +47,12 @@ const SecuritySettings = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
const validateAndSubmit = async () => {
|
||||
@@ -69,7 +69,7 @@ const SecuritySettings = () => {
|
||||
return (
|
||||
<>
|
||||
<ValidatedPasswordField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="jwt_secret"
|
||||
label={LL.SU_PASSWORD()}
|
||||
fullWidth
|
||||
|
||||
@@ -82,7 +82,7 @@ const User: FC<UserFormProps> = ({
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="username"
|
||||
label={LL.USERNAME(1)}
|
||||
fullWidth
|
||||
@@ -93,7 +93,7 @@ const User: FC<UserFormProps> = ({
|
||||
margin="normal"
|
||||
/>
|
||||
<ValidatedPasswordField
|
||||
fieldErrors={fieldErrors || {}}
|
||||
fieldErrors={fieldErrors}
|
||||
name="password"
|
||||
label={LL.PASSWORD()}
|
||||
fullWidth
|
||||
|
||||
@@ -61,7 +61,7 @@ const APStatus = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -67,8 +67,7 @@ const SystemActivity = () => {
|
||||
});
|
||||
|
||||
const showName = (id: number) => {
|
||||
const name: keyof Translation['STATUS_NAMES'] =
|
||||
id.toString() as keyof Translation['STATUS_NAMES'];
|
||||
const name: keyof Translation['STATUS_NAMES'] = id;
|
||||
return LL.STATUS_NAMES[name]();
|
||||
};
|
||||
|
||||
@@ -88,7 +87,7 @@ const SystemActivity = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,7 +41,7 @@ const HardwareStatus = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -99,7 +99,7 @@ const MqttStatus = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
const renderConnectionStatus = () => (
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import DnsIcon from '@mui/icons-material/Dns';
|
||||
import SwapVerticalCircleIcon from '@mui/icons-material/SwapVerticalCircle';
|
||||
import UpdateIcon from '@mui/icons-material/Update';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import type { Theme } from '@mui/material';
|
||||
|
||||
import * as NTPApi from 'api/ntp';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova/client';
|
||||
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NTPStatusType } from 'types';
|
||||
import type { NTPStatusType, Time } from 'types';
|
||||
import { NTPSyncStatus } from 'types';
|
||||
import { useInterval } from 'utils';
|
||||
import { formatDateTime } from 'utils';
|
||||
import { formatDateTime, formatLocalDateTime } from 'utils';
|
||||
|
||||
const NTPStatus = () => {
|
||||
const { data, send: loadData, error } = useRequest(NTPApi.readNTPStatus);
|
||||
@@ -30,11 +43,24 @@ const NTPStatus = () => {
|
||||
void loadData();
|
||||
});
|
||||
|
||||
const [localTime, setLocalTime] = useState<string>('');
|
||||
const [settingTime, setSettingTime] = useState<boolean>(false);
|
||||
const [processing, setProcessing] = useState<boolean>(false);
|
||||
|
||||
const { LL } = useI18nContext();
|
||||
useLayoutTitle('NTP');
|
||||
|
||||
const { send: updateTime } = useRequest(
|
||||
(local_time: Time) => NTPApi.updateTime(local_time),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
);
|
||||
|
||||
NTPApi.updateTime;
|
||||
|
||||
const isNtpActive = ({ status }: NTPStatusType) =>
|
||||
status === NTPSyncStatus.NTP_ACTIVE;
|
||||
const isNtpEnabled = ({ status }: NTPStatusType) =>
|
||||
status !== NTPSyncStatus.NTP_DISABLED;
|
||||
|
||||
@@ -51,6 +77,14 @@ const NTPStatus = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLocalTime(event.target.value);
|
||||
|
||||
const openSetTime = () => {
|
||||
setLocalTime(formatLocalDateTime(new Date()));
|
||||
setSettingTime(true);
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const ntpStatus = ({ status }: NTPStatusType) => {
|
||||
@@ -66,9 +100,73 @@ const NTPStatus = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const configureTime = async () => {
|
||||
setProcessing(true);
|
||||
|
||||
await updateTime({ local_time: formatLocalDateTime(new Date(localTime)) })
|
||||
.then(async () => {
|
||||
toast.success(LL.TIME_SET());
|
||||
setSettingTime(false);
|
||||
await loadData();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(LL.PROBLEM_UPDATING());
|
||||
})
|
||||
.finally(() => {
|
||||
setProcessing(false);
|
||||
});
|
||||
};
|
||||
|
||||
const renderSetTimeDialog = () => (
|
||||
<Dialog
|
||||
sx={dialogStyle}
|
||||
open={settingTime}
|
||||
onClose={() => setSettingTime(false)}
|
||||
>
|
||||
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}>
|
||||
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography>
|
||||
</Box>
|
||||
<TextField
|
||||
label={LL.LOCAL_TIME(0)}
|
||||
type="datetime-local"
|
||||
value={localTime}
|
||||
onChange={updateLocalTime}
|
||||
disabled={processing}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
inputLabel: {
|
||||
shrink: true
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => setSettingTime(false)}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<AccessTimeIcon />}
|
||||
variant="outlined"
|
||||
onClick={configureTime}
|
||||
disabled={processing}
|
||||
color="primary"
|
||||
>
|
||||
{LL.UPDATE()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -121,6 +219,23 @@ const NTPStatus = () => {
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
</List>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
{data && !isNtpActive(data) && (
|
||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||
<ButtonRow>
|
||||
<Button
|
||||
onClick={openSetTime}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
startIcon={<AccessTimeIcon />}
|
||||
>
|
||||
{LL.SET_TIME(0)}
|
||||
</Button>
|
||||
</ButtonRow>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{renderSetTimeDialog()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ const NetworkStatus = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -248,7 +248,7 @@ const SystemStatus = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data || !LL) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
TextField,
|
||||
@@ -31,14 +31,13 @@ import type { LogEntry, LogSettings } from 'types';
|
||||
import { LogLevel } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
|
||||
const TextColors: Record<LogLevel, string> = {
|
||||
const TextColors = {
|
||||
[LogLevel.ERROR]: '#ff0000', // red
|
||||
[LogLevel.WARNING]: '#ff0000', // red
|
||||
[LogLevel.NOTICE]: '#ffffff', // white
|
||||
[LogLevel.INFO]: '#ffcc00', // yellow
|
||||
[LogLevel.DEBUG]: '#00ffff', // cyan
|
||||
[LogLevel.TRACE]: '#00ffff', // cyan
|
||||
[LogLevel.ALL]: '#ffffff' // white
|
||||
[LogLevel.TRACE]: '#00ffff' // cyan
|
||||
};
|
||||
|
||||
const LogEntryLine = styled('span')(
|
||||
@@ -110,7 +109,7 @@ const SystemLog = () => {
|
||||
origData,
|
||||
dirtyFlags,
|
||||
setDirtyFlags,
|
||||
updateDataValue as (value: unknown) => void
|
||||
updateDataValue
|
||||
);
|
||||
|
||||
useSSE(fetchLogES, {
|
||||
@@ -191,7 +190,7 @@ const SystemLog = () => {
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -51,7 +51,7 @@ const SystemMonitor = () => {
|
||||
}
|
||||
})
|
||||
.onError((error) => {
|
||||
setErrorMessage(String(error.error?.message || 'An error occurred'));
|
||||
setErrorMessage(error.message);
|
||||
});
|
||||
|
||||
useInterval(() => {
|
||||
@@ -97,7 +97,7 @@ const SystemMonitor = () => {
|
||||
color="error"
|
||||
onClick={onCancel}
|
||||
>
|
||||
{LL.RESTART()}
|
||||
{LL.RESET(0)}
|
||||
</Button>
|
||||
</MessageBox>
|
||||
) : (
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useContext, useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import CheckIcon from '@mui/icons-material/Done';
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import {
|
||||
Box,
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
Grid2 as Grid,
|
||||
Link,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
@@ -44,10 +44,7 @@ const Version = () => {
|
||||
const [restarting, setRestarting] = useState<boolean>(false);
|
||||
const [openInstallDialog, setOpenInstallDialog] = useState<boolean>(false);
|
||||
const [usingDevVersion, setUsingDevVersion] = useState<boolean>(false);
|
||||
const [fetchDevVersion, setFetchDevVersion] = useState<boolean>(false);
|
||||
const [devUpgradeAvailable, setDevUpgradeAvailable] = useState<boolean>(false);
|
||||
const [stableUpgradeAvailable, setStableUpgradeAvailable] =
|
||||
useState<boolean>(false);
|
||||
const [upgradeAvailable, setUpgradeAvailable] = useState<boolean>(false);
|
||||
const [internetLive, setInternetLive] = useState<boolean>(false);
|
||||
const [downloadOnly, setDownloadOnly] = useState<boolean>(false);
|
||||
|
||||
@@ -65,13 +62,8 @@ const Version = () => {
|
||||
immediate: false
|
||||
}
|
||||
).onSuccess((event) => {
|
||||
const data = event.data as {
|
||||
emsesp_version: string;
|
||||
dev_upgradeable: boolean;
|
||||
stable_upgradeable: boolean;
|
||||
};
|
||||
setDevUpgradeAvailable(data.dev_upgradeable);
|
||||
setStableUpgradeAvailable(data.stable_upgradeable);
|
||||
const data = event.data as { emsesp_version: string; upgradeable: boolean };
|
||||
setUpgradeAvailable(data.upgradeable);
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -79,7 +71,7 @@ const Version = () => {
|
||||
send: loadData,
|
||||
error
|
||||
} = useRequest(SystemApi.readSystemStatus).onSuccess((event) => {
|
||||
// older version of EMS-ESP using ESP32 (not S3) and no PSRAM, can't use OTA because of SSL support in HttpClient
|
||||
// older version of EMS-ESP on 4MB boards, can't use OTA because of SSL support in HttpClient
|
||||
if (event.data.arduino_version.startsWith('Tasmota')) {
|
||||
setDownloadOnly(true);
|
||||
}
|
||||
@@ -110,7 +102,7 @@ const Version = () => {
|
||||
}, [latestVersion, latestDevVersion]);
|
||||
|
||||
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
|
||||
const DIVISIONS: Array<{ amount: number; name: string }> = [
|
||||
const DIVISIONS = [
|
||||
{ amount: 60, name: 'seconds' },
|
||||
{ amount: 60, name: 'minutes' },
|
||||
{ amount: 24, name: 'hours' },
|
||||
@@ -119,21 +111,18 @@ const Version = () => {
|
||||
{ amount: 12, name: 'months' },
|
||||
{ amount: Number.POSITIVE_INFINITY, name: 'years' }
|
||||
];
|
||||
function formatTimeAgo(date: Date) {
|
||||
function formatTimeAgo(date) {
|
||||
let duration = (date.getTime() - new Date().getTime()) / 1000;
|
||||
for (let i = 0; i < DIVISIONS.length; i++) {
|
||||
const division = DIVISIONS[i];
|
||||
if (division && Math.abs(duration) < division.amount) {
|
||||
if (Math.abs(duration) < division.amount) {
|
||||
return rtf.format(
|
||||
Math.round(duration),
|
||||
division.name as Intl.RelativeTimeFormatUnit
|
||||
);
|
||||
}
|
||||
if (division) {
|
||||
duration /= division.amount;
|
||||
}
|
||||
duration /= division.amount;
|
||||
}
|
||||
return rtf.format(0, 'seconds');
|
||||
}
|
||||
|
||||
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
|
||||
@@ -149,21 +138,20 @@ const Version = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const getBinURL = (showingDev: boolean) => {
|
||||
const getBinURL = () => {
|
||||
if (!internetLive) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const filename =
|
||||
'EMS-ESP-' +
|
||||
(showingDev ? latestDevVersion.name : latestVersion.name).replaceAll(
|
||||
(usingDevVersion ? latestDevVersion.name : latestVersion.name).replaceAll(
|
||||
'.',
|
||||
'_'
|
||||
) +
|
||||
'-' +
|
||||
getPlatform() +
|
||||
'.bin';
|
||||
return showingDev
|
||||
return usingDevVersion
|
||||
? DEV_URL + filename
|
||||
: STABLE_URL + 'v' + latestVersion.name + '/' + filename;
|
||||
};
|
||||
@@ -184,127 +172,107 @@ const Version = () => {
|
||||
|
||||
useLayoutTitle('EMS-ESP Firmware');
|
||||
|
||||
const renderInstallDialog = () => {
|
||||
const binURL = getBinURL(fetchDevVersion);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
sx={dialogStyle}
|
||||
open={openInstallDialog}
|
||||
onClose={() => closeInstallDialog()}
|
||||
>
|
||||
<DialogTitle>
|
||||
{LL.UPDATE() +
|
||||
' ' +
|
||||
(fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()) +
|
||||
' Firmware'}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Typography mb={2}>
|
||||
{LL.INSTALL_VERSION(
|
||||
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
|
||||
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||
)}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => closeInstallDialog()}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => closeInstallDialog()}
|
||||
color="primary"
|
||||
>
|
||||
<Link underline="none" target="_blank" href={binURL} color="primary">
|
||||
{LL.DOWNLOAD(0)}
|
||||
</Link>
|
||||
</Button>
|
||||
{!downloadOnly && (
|
||||
<Button
|
||||
startIcon={<WarningIcon color="warning" />}
|
||||
variant="outlined"
|
||||
onClick={() => installFirmwareURL(binURL)}
|
||||
color="primary"
|
||||
>
|
||||
{LL.INSTALL()}
|
||||
</Button>
|
||||
const renderInstallDialog = () => (
|
||||
<Dialog
|
||||
sx={dialogStyle}
|
||||
open={openInstallDialog}
|
||||
onClose={() => closeInstallDialog()}
|
||||
>
|
||||
<DialogTitle>
|
||||
{LL.INSTALL() +
|
||||
' ' +
|
||||
(usingDevVersion ? LL.DEVELOPMENT() : LL.STABLE()) +
|
||||
' Firmware'}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Typography mb={2}>
|
||||
{LL.INSTALL_VERSION(
|
||||
usingDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||
)}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => closeInstallDialog()}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => closeInstallDialog()}
|
||||
color="primary"
|
||||
>
|
||||
<Link underline="none" target="_blank" href={getBinURL()} color="primary">
|
||||
{LL.DOWNLOAD(1)}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<WarningIcon color="warning" />}
|
||||
variant="outlined"
|
||||
onClick={() => installFirmwareURL(getBinURL())}
|
||||
color="primary"
|
||||
>
|
||||
{LL.INSTALL()}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const showFirmwareDialog = (useDevVersion: boolean) => {
|
||||
setFetchDevVersion(useDevVersion);
|
||||
const showFirmwareDialog = (useDevVersion?: boolean) => {
|
||||
setUsingDevVersion(useDevVersion || usingDevVersion);
|
||||
setOpenInstallDialog(true);
|
||||
};
|
||||
|
||||
const closeInstallDialog = () => {
|
||||
setOpenInstallDialog(false);
|
||||
setUsingDevVersion(data.emsesp_version.includes('dev'));
|
||||
};
|
||||
|
||||
const showButtons = (showingDev: boolean) => {
|
||||
const choice = showingDev
|
||||
? !usingDevVersion
|
||||
? LL.SWITCH_RELEASE_TYPE(LL.DEVELOPMENT())
|
||||
: devUpgradeAvailable
|
||||
? LL.UPDATE_AVAILABLE()
|
||||
: undefined
|
||||
: usingDevVersion
|
||||
? LL.SWITCH_RELEASE_TYPE(LL.STABLE())
|
||||
: stableUpgradeAvailable
|
||||
? LL.UPDATE_AVAILABLE()
|
||||
: undefined;
|
||||
|
||||
if (!choice) {
|
||||
return (
|
||||
<>
|
||||
<CheckIcon
|
||||
color="success"
|
||||
sx={{ verticalAlign: 'middle', ml: 0.5, mr: 0.5 }}
|
||||
/>
|
||||
<span style={{ color: '#66bb6a', fontSize: '0.8em' }}>
|
||||
{LL.LATEST_VERSION(usingDevVersion ? LL.DEVELOPMENT() : LL.STABLE())}
|
||||
</span>
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => showFirmwareDialog(showingDev)}
|
||||
>
|
||||
{LL.REINSTALL()}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const showButtons = (showDev?: boolean) => {
|
||||
if (!me.admin) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (downloadOnly) {
|
||||
return (
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => setOpenInstallDialog(false)}
|
||||
color="warning"
|
||||
size="small"
|
||||
>
|
||||
<Link underline="none" target="_blank" href={getBinURL()} color="warning">
|
||||
{LL.DOWNLOAD(1)}
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
variant="outlined"
|
||||
color={choice === LL.UPDATE_AVAILABLE() ? 'success' : 'warning'}
|
||||
color="warning"
|
||||
size="small"
|
||||
onClick={() => showFirmwareDialog(showingDev)}
|
||||
onClick={() => showFirmwareDialog()}
|
||||
>
|
||||
{choice}
|
||||
{upgradeAvailable || (!usingDevVersion && showDev)
|
||||
? LL.UPGRADE()
|
||||
: LL.REINSTALL()}
|
||||
…
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
|
||||
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
||||
}
|
||||
|
||||
const isDev = data.emsesp_version.includes('dev');
|
||||
@@ -346,25 +314,7 @@ const Version = () => {
|
||||
<Typography>
|
||||
{getPlatform()}
|
||||
<Typography variant="caption">
|
||||
(
|
||||
{data.psram ? (
|
||||
<CheckIcon
|
||||
color="success"
|
||||
sx={{
|
||||
fontSize: '1.5em',
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CloseIcon
|
||||
color="error"
|
||||
sx={{
|
||||
fontSize: '1.5em',
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
PSRAM)
|
||||
({data.psram ? '+PSRAM' : '-PSRAM'})
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -445,7 +395,7 @@ const Version = () => {
|
||||
{formatTimeAgo(new Date(latestVersion.published_at))})
|
||||
</Typography>
|
||||
)}
|
||||
{showButtons(false)}
|
||||
{!usingDevVersion && showButtons(false)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
@@ -467,6 +417,24 @@ const Version = () => {
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{upgradeAvailable ? (
|
||||
<Typography mt={2} color="warning">
|
||||
<InfoOutlinedIcon
|
||||
color="warning"
|
||||
sx={{ verticalAlign: 'middle', mr: 2 }}
|
||||
/>
|
||||
{LL.UPGRADE_AVAILABLE()}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography mt={2} color="success">
|
||||
<CheckIcon
|
||||
color="success"
|
||||
sx={{ verticalAlign: 'middle', mr: 2 }}
|
||||
/>
|
||||
{LL.LATEST_VERSION()}
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Typography mt={2} color="warning">
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { memo } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import type { BoxProps } from '@mui/material';
|
||||
|
||||
const ButtonRow = memo<BoxProps>(({ children, ...rest }) => (
|
||||
const ButtonRow: FC<BoxProps> = ({ children, ...rest }) => (
|
||||
<Box
|
||||
sx={{
|
||||
'& button, & a, & .MuiCard-root': {
|
||||
mt: 2,
|
||||
mx: 0.6,
|
||||
'&:last-child': { mr: 0 },
|
||||
'&:first-of-type': { ml: 0 }
|
||||
'&:last-child': {
|
||||
mr: 0
|
||||
},
|
||||
'&:first-of-type': {
|
||||
ml: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
));
|
||||
|
||||
ButtonRow.displayName = 'ButtonRow';
|
||||
);
|
||||
|
||||
export default ButtonRow;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material';
|
||||
|
||||
export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip
|
||||
{...props}
|
||||
placement="top"
|
||||
arrow
|
||||
classes={{ ...(className && { popper: className }) }}
|
||||
/>
|
||||
<Tooltip {...props} placement="top" arrow classes={{ popper: className }} />
|
||||
))(({ theme }) => ({
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: theme.palette.success.main
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
// Optimized exports - use direct exports to reduce bundle size
|
||||
export { default as SectionContent } from './SectionContent';
|
||||
export { default as ButtonRow } from './ButtonRow';
|
||||
export { default as MessageBox } from './MessageBox';
|
||||
export { default as ButtonTooltip } from './ButtonTooltip';
|
||||
|
||||
// Re-export sub-modules
|
||||
export * from './inputs';
|
||||
export * from './layout';
|
||||
export * from './loading';
|
||||
export * from './routing';
|
||||
export * from './upload';
|
||||
|
||||
// Specific routing exports
|
||||
export { default as SectionContent } from './SectionContent';
|
||||
export { default as ButtonRow } from './ButtonRow';
|
||||
export { default as MessageBox } from './MessageBox';
|
||||
export { default as BlockNavigation } from './routing/BlockNavigation';
|
||||
export { default as ButtonTooltip } from './ButtonTooltip';
|
||||
|
||||
@@ -16,14 +16,14 @@ const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
|
||||
fieldErrors,
|
||||
...rest
|
||||
}) => {
|
||||
const errors = fieldErrors?.[rest.name];
|
||||
|
||||
const errors = fieldErrors && fieldErrors[rest.name];
|
||||
const renderErrors = () =>
|
||||
errors &&
|
||||
errors.map((e) => <FormHelperText key={e.message}>{e.message}</FormHelperText>);
|
||||
return (
|
||||
<>
|
||||
<TextField error={!!errors} {...rest} />
|
||||
{errors?.map((e) => (
|
||||
<FormHelperText key={e.message}>{e.message}</FormHelperText>
|
||||
))}
|
||||
{renderErrors()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,12 +23,7 @@ const LayoutMenuItem = ({
|
||||
const selected = routeMatches(to, pathname);
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
to={to}
|
||||
disabled={disabled || false}
|
||||
selected={selected}
|
||||
>
|
||||
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
|
||||
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
|
||||
@@ -58,22 +58,12 @@ const LayoutMenuItem = ({
|
||||
}
|
||||
>
|
||||
<ListItemButton component={Link} to={to}>
|
||||
<RenderIcon
|
||||
icon={icon}
|
||||
{...(bgcolor && { bgcolor })}
|
||||
label={label}
|
||||
text={text}
|
||||
/>
|
||||
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
) : (
|
||||
<ListItem>
|
||||
<RenderIcon
|
||||
icon={icon}
|
||||
{...(bgcolor && { bgcolor })}
|
||||
label={label}
|
||||
text={text}
|
||||
/>
|
||||
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} />
|
||||
</ListItem>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Box, CircularProgress } from '@mui/material';
|
||||
|
||||
const LazyLoader = () => (
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight="200px"
|
||||
sx={{
|
||||
backgroundColor: 'background.default',
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider'
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={40} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default LazyLoader;
|
||||
@@ -1,3 +1,2 @@
|
||||
export { default as LoadingSpinner } from './LoadingSpinner';
|
||||
export { default as FormLoader } from './FormLoader';
|
||||
export { default as LazyLoader } from './LazyLoader';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Path } from 'react-router';
|
||||
|
||||
import type * as H from 'history';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import type { Me, SignInRequest, SignInResponse } from 'types';
|
||||
|
||||
@@ -17,7 +18,7 @@ export function getStorage() {
|
||||
return localStorage || sessionStorage;
|
||||
}
|
||||
|
||||
export function storeLoginRedirect(location?: { pathname: string; search: string }) {
|
||||
export function storeLoginRedirect(location?: H.Location) {
|
||||
if (location) {
|
||||
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
|
||||
getStorage().setItem(SIGN_IN_SEARCH, location.search);
|
||||
@@ -35,7 +36,7 @@ export function fetchLoginRedirect(): Partial<Path> {
|
||||
clearLoginRedirect();
|
||||
return {
|
||||
pathname: signInPathname || `/dashboard`,
|
||||
...(signInPathname && signInSearch && { search: signInSearch })
|
||||
search: (signInPathname && signInSearch) || undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,59 +1,16 @@
|
||||
// Code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
|
||||
import {
|
||||
type ChangeEvent,
|
||||
type DragEvent,
|
||||
type MouseEvent,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import { type ChangeEvent, useRef, useState } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import { Box, Button, Typography, styled } from '@mui/material';
|
||||
import { Box, Button } from '@mui/material';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
|
||||
border: `2px dashed ${active ? '#6dc24b' : '#4282fe'}`,
|
||||
backgroundColor: '#2e3339',
|
||||
padding: theme.spacing(1.25),
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
borderRadius: theme.spacing(1),
|
||||
cursor: 'pointer',
|
||||
minHeight: '120px',
|
||||
transition: 'border-color 0.2s ease-in-out'
|
||||
}));
|
||||
import './dragNdrop.css';
|
||||
|
||||
const UploadInfo = styled(Box)({
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
});
|
||||
|
||||
const FileInfo = styled(Box)({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
});
|
||||
|
||||
const FileName = styled(Typography)(({ theme }) => ({
|
||||
fontSize: '14px',
|
||||
color: '#6dc24b',
|
||||
margin: theme.spacing(1, 0)
|
||||
}));
|
||||
|
||||
interface DragNdropProps {
|
||||
text: string;
|
||||
onFileSelected: (file: File) => void;
|
||||
}
|
||||
|
||||
const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
||||
const DragNdrop = ({ text, onFileSelected }) => {
|
||||
const [file, setFile] = useState<File>();
|
||||
const [dragged, setDragged] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
@@ -71,17 +28,14 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
||||
};
|
||||
|
||||
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target.files || e.target.files.length === 0) {
|
||||
if (!e.target.files) {
|
||||
return;
|
||||
}
|
||||
const selectedFile = e.target.files[0];
|
||||
if (selectedFile) {
|
||||
checkFileExtension(selectedFile);
|
||||
}
|
||||
checkFileExtension(e.target.files[0]);
|
||||
e.target.value = ''; // this is to allow the same file to be selected again
|
||||
};
|
||||
|
||||
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
||||
const handleDrop = (event) => {
|
||||
event.preventDefault();
|
||||
const droppedFiles = event.dataTransfer.files;
|
||||
if (droppedFiles.length > 0) {
|
||||
@@ -89,40 +43,38 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveFile = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
const handleRemoveFile = (event) => {
|
||||
event.stopPropagation();
|
||||
setFile(undefined);
|
||||
setDragged(false);
|
||||
};
|
||||
|
||||
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
const handleUploadClick = (event) => {
|
||||
event.stopPropagation();
|
||||
if (file) {
|
||||
onFileSelected(file);
|
||||
}
|
||||
onFileSelected(file);
|
||||
};
|
||||
|
||||
const handleBrowseClick = () => {
|
||||
inputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
|
||||
const handleDragOver = (event) => {
|
||||
event.preventDefault(); // prevent file from being opened
|
||||
setDragged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<DocumentUploader
|
||||
active={!!(file || dragged)}
|
||||
<div
|
||||
className={`document-uploader ${file || dragged ? 'active' : ''}`}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={() => setDragged(false)}
|
||||
onClick={handleBrowseClick}
|
||||
>
|
||||
<UploadInfo>
|
||||
<div className="upload-info">
|
||||
<CloudUploadIcon sx={{ marginRight: 4 }} color="primary" fontSize="large" />
|
||||
<Typography>{text}</Typography>
|
||||
</UploadInfo>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
@@ -136,9 +88,9 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
||||
|
||||
{file && (
|
||||
<>
|
||||
<FileInfo>
|
||||
<FileName>{file.name}</FileName>
|
||||
</FileInfo>
|
||||
<div className="file-info">
|
||||
<p>{file.name}</p>
|
||||
</div>
|
||||
<Box>
|
||||
<Button
|
||||
startIcon={<CancelIcon />}
|
||||
@@ -160,7 +112,7 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</DocumentUploader>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,12 +12,7 @@ import { useI18nContext } from 'i18n/i18n-react';
|
||||
import DragNdrop from './DragNdrop';
|
||||
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
||||
|
||||
interface SingleUploadProps {
|
||||
text: string;
|
||||
doRestart: () => void;
|
||||
}
|
||||
|
||||
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
||||
const SingleUpload = ({ text, doRestart }) => {
|
||||
const [md5, setMd5] = useState<string>();
|
||||
const [file, setFile] = useState<File>();
|
||||
const { LL } = useI18nContext();
|
||||
@@ -30,8 +25,8 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
||||
} = useRequest(SystemApi.uploadFile, {
|
||||
immediate: false
|
||||
}).onSuccess(({ data }) => {
|
||||
if (data && typeof data === 'object' && 'md5' in data) {
|
||||
setMd5((data as { md5: string }).md5);
|
||||
if (data) {
|
||||
setMd5(data.md5 as string);
|
||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||
setFile(undefined);
|
||||
} else {
|
||||
@@ -39,19 +34,16 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const uploadFile = async () => {
|
||||
if (file) {
|
||||
await sendUpload(file).catch((error: Error) => {
|
||||
if (error.message.includes('The user aborted a request')) {
|
||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||
} else {
|
||||
toast.warning('Invalid file extension or incompatible bin file');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
void uploadFile();
|
||||
useEffect(async () => {
|
||||
if (file) {
|
||||
await sendUpload(file).catch((error: Error) => {
|
||||
if (error.message === 'The user aborted a request') {
|
||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||
} else {
|
||||
toast.warning('Invalid file extension or incompatible bin file');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [file]);
|
||||
|
||||
return (
|
||||
|
||||
33
interface/src/components/upload/dragNdrop.css
Normal file
33
interface/src/components/upload/dragNdrop.css
Normal file
@@ -0,0 +1,33 @@
|
||||
.document-uploader {
|
||||
border: 2px dashed #4282fe;
|
||||
background-color: #2e3339;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-color: #6dc24b;
|
||||
}
|
||||
|
||||
.upload-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #6dc24b;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,12 +69,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
||||
|
||||
// cache object to prevent re-renders
|
||||
const obj = useMemo(
|
||||
() => ({
|
||||
signIn,
|
||||
signOut,
|
||||
refresh,
|
||||
...(me && { me })
|
||||
}),
|
||||
() => ({ signIn, signOut, me, refresh }),
|
||||
[signIn, signOut, me, refresh]
|
||||
);
|
||||
|
||||
|
||||
@@ -162,7 +162,6 @@ const cz: Translation = {
|
||||
UPLOAD: 'Nahrát',
|
||||
DOWNLOAD: '{{S|s|s}}táhnout',
|
||||
INSTALL: 'Instalovat',
|
||||
REINSTALL: 'Znovu instalovat',
|
||||
ABORTED: 'přerušeno',
|
||||
FAILED: 'neúspěšné',
|
||||
SUCCESSFUL: 'úspěšné',
|
||||
@@ -188,7 +187,7 @@ const cz: Translation = {
|
||||
COMPACT: 'Kompaktní',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
|
||||
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr',
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
|
||||
TIME_SET: 'Čas nastaven',
|
||||
MANAGE_USERS: 'Spravovat uživatele',
|
||||
@@ -332,27 +331,28 @@ const cz: Translation = {
|
||||
ALLVALUES: 'Všechny hodnoty',
|
||||
SPECIAL_FUNCTIONS: 'Speciální funkce',
|
||||
WAIT_FIRMWARE: 'Firmware se nahrává a instaluje',
|
||||
INSTALL_VERSION: 'Tímto se {0} verze {1}. Jste si jistí?',
|
||||
UPDATE_AVAILABLE: 'aktualizace dostupná',
|
||||
LATEST_VERSION: 'Používáte nejnovější verzi {0}firmwaru',
|
||||
INSTALL_VERSION: 'Tímto se instalovat verze {0}. Jste si jistí?',
|
||||
UPGRADE_AVAILABLE: 'Je k dispozici aktualizace firmwaru!',
|
||||
LATEST_VERSION: 'Používáte nejnovější verzi firmwaru',
|
||||
PLEASE_WAIT: 'Prosím čekejte',
|
||||
RESTARTING_PRE: 'Inicializace',
|
||||
RESTARTING_POST: 'Příprava',
|
||||
AUTO_SCROLL: 'Automatické rolování',
|
||||
DASHBOARD: 'Dashboard',
|
||||
DEVELOPER_MODE: 'Režim vývojáře',
|
||||
BYTES: 'Bajty',
|
||||
BITMASK: 'Bit Mask',
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplikát',
|
||||
UPGRADE: 'Upgrade',
|
||||
DASHBOARD_1: 'Všechny aktivní entity EMS jsou označené jako oblíbené. Všechny vlastní entity, harmonogramy a externí sensory jsou zobrazeny níže.',
|
||||
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
|
||||
NO_DATA_2: 'pro jejich výběr.',
|
||||
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku',
|
||||
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte',
|
||||
NO_DATA_2: 'modul sloužící k jejich výběru.',
|
||||
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte',
|
||||
THIS_VERSION: 'Tato verze',
|
||||
PLATFORM: 'Platforma',
|
||||
RELEASE_TYPE: 'Typ sestavení',
|
||||
REINSTALL: 'Přeinstalovat',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
|
||||
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi'
|
||||
};
|
||||
|
||||
export default cz;
|
||||
|
||||
@@ -42,7 +42,7 @@ const de: Translation = {
|
||||
CANCEL: 'Abbrechen',
|
||||
RESET: 'Zurücksetzen',
|
||||
APPLY_CHANGES: 'Änderungen anwenden ({0})',
|
||||
UPDATE: 'Aktualisieren',
|
||||
UPDATE: 'Update',
|
||||
EXECUTE: 'Ausführen',
|
||||
REMOVE: 'Entfernen',
|
||||
PROBLEM_UPDATING: 'Problem beim Aktualisieren',
|
||||
@@ -160,9 +160,8 @@ const de: Translation = {
|
||||
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 Systemdetails und hängen Sie sie an das Support-Issue an',
|
||||
UPLOAD: 'Hochladen',
|
||||
DOWNLOAD: '{{Herunterladen|heruntergeladen|}}',
|
||||
DOWNLOAD: '{{H|h|h}}erunterladen',
|
||||
INSTALL: 'Installieren',
|
||||
REINSTALL: 'Neu installieren',
|
||||
ABORTED: 'abgebrochen',
|
||||
FAILED: 'gescheitert',
|
||||
SUCCESSFUL: 'erfolgreich',
|
||||
@@ -332,9 +331,9 @@ const de: Translation = {
|
||||
ALLVALUES: 'Alle Werte',
|
||||
SPECIAL_FUNCTIONS: 'Sonderfunktionen',
|
||||
WAIT_FIRMWARE: 'Die Firmware wird hochgeladen und installiert.',
|
||||
INSTALL_VERSION: 'Dadurch wird die Version {1} {0}. Sind Sie sicher?',
|
||||
UPDATE_AVAILABLE: 'Firmware-Update verfügbar',
|
||||
LATEST_VERSION: 'Sie verwenden die neueste {0} Firmware-Version',
|
||||
INSTALL_VERSION: 'Dadurch wird die Version {0} heruntergeladen. Sind Sie sicher?',
|
||||
UPGRADE_AVAILABLE: 'Es ist ein Firmware-Upgrade verfügbar.',
|
||||
LATEST_VERSION: 'Sie verwenden die neueste Firmware-Version',
|
||||
PLEASE_WAIT: 'Bitte warten',
|
||||
RESTARTING_PRE: 'Initialisierung',
|
||||
RESTARTING_POST: 'Vorbereitung',
|
||||
@@ -344,6 +343,7 @@ const de: Translation = {
|
||||
BYTES: 'Bytes',
|
||||
BITMASK: 'Bit Maske',
|
||||
DUPLICATE: 'Kopieren',
|
||||
UPGRADE: 'Aktualisieren',
|
||||
DASHBOARD_1: 'Alle EMS-Entitäten, die aktiv und als Favorit markiert sind, sowie alle benutzerdefinierten Entitäten, Zeitpläne und externen Sensordaten werden unten angezeigt.',
|
||||
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
|
||||
NO_DATA_2: ', um sie zu markieren.',
|
||||
@@ -351,8 +351,8 @@ const de: Translation = {
|
||||
THIS_VERSION: 'Diese Version',
|
||||
PLATFORM: 'Plattform',
|
||||
RELEASE_TYPE: 'Release Typ',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Für die automatische Versionsprüfung und Aktualisierung ist eine Internetverbindung erforderlich',
|
||||
SWITCH_RELEASE_TYPE: 'Zum {0}-Release wechseln'
|
||||
REINSTALL: 'Neu installieren',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internetverbindung erforderlich für automatische Version-Überprüfung und -Aktualisierung',
|
||||
};
|
||||
|
||||
export default de;
|
||||
|
||||
@@ -162,7 +162,6 @@ const en: Translation = {
|
||||
UPLOAD: 'Upload',
|
||||
DOWNLOAD: '{{D|d|d}}ownload',
|
||||
INSTALL: 'Install',
|
||||
REINSTALL: 'Reinstall',
|
||||
ABORTED: 'aborted',
|
||||
FAILED: 'failed',
|
||||
SUCCESSFUL: 'successful',
|
||||
@@ -332,9 +331,9 @@ const en: Translation = {
|
||||
ALLVALUES: 'All Values',
|
||||
SPECIAL_FUNCTIONS: 'Special Functions',
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing',
|
||||
INSTALL_VERSION: 'This will {0} version {1}. Are you sure?',
|
||||
UPDATE_AVAILABLE: 'update available',
|
||||
LATEST_VERSION: 'You are using the latest {0} firmware version',
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?',
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!',
|
||||
LATEST_VERSION: 'You are using the latest firmware version',
|
||||
PLEASE_WAIT: 'Please wait',
|
||||
RESTARTING_PRE: 'Initializing',
|
||||
RESTARTING_POST: 'Preparing',
|
||||
@@ -345,14 +344,15 @@ const en: Translation = {
|
||||
BYTES: 'Bytes',
|
||||
BITMASK: 'Bit Mask',
|
||||
DUPLICATE: 'Duplicate',
|
||||
UPGRADE: 'Upgrade',
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the',
|
||||
NO_DATA_2: 'module to mark them.',
|
||||
NO_DATA_3: 'To see all available entities go to',
|
||||
THIS_VERSION: 'This Version',
|
||||
PLATFORM: 'Platform',
|
||||
RELEASE_TYPE: 'Release Type',
|
||||
REINSTALL: 'Re-install',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading',
|
||||
SWITCH_RELEASE_TYPE: 'Switch to {0} release'
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
||||
@@ -41,9 +41,9 @@ const fr: Translation = {
|
||||
CHANGE_VALUE: 'Changer la valeur',
|
||||
CANCEL: 'Annuler',
|
||||
RESET: 'Réinitialiser',
|
||||
APPLY_CHANGES: 'Appliquer les changements ({0})',
|
||||
UPDATE: 'Update',
|
||||
EXECUTE: 'Execute',
|
||||
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||
UPDATE: 'Update', // TODO translate
|
||||
EXECUTE: 'Execute', // TODO translate
|
||||
REMOVE: 'Enlever',
|
||||
PROBLEM_UPDATING: 'Problème lors de la mise à jour',
|
||||
PROBLEM_LOADING: 'Problème lors du chargement',
|
||||
@@ -66,13 +66,13 @@ const fr: Translation = {
|
||||
TEMP_SENSOR: 'Capteur de température',
|
||||
TEMP_SENSORS: 'Capteurs de température',
|
||||
WRITE_CMD_SENT: 'Envoyer la commande sent',
|
||||
EMS_BUS_WARNING: 'Bus EMS déconnecté. Si ce message persiste après quelques secondes, vérifiez les paramètres et la configuration de la carte.',
|
||||
EMS_BUS_WARNING: 'Bus EMS déconnecté. Si ce message persiste après quelques secondes, vérifiez les paramètres et la configuration de la carte.',
|
||||
EMS_BUS_SCANNING: 'Scan des appareils EMS...',
|
||||
CONNECTED: 'Connecté',
|
||||
TX_ISSUES: 'Problèmes de transmission (Tx) - Essayez un autre mode Tx',
|
||||
DISCONNECTED: 'Déconnecté',
|
||||
EMS_SCAN: 'Etes-vous sûr de vouloir lancer un scan complet du bus EMS ?',
|
||||
DATA_TRAFFIC: 'Traffic des données',
|
||||
DATA_TRAFFIC: 'Data Traffic', // TODO translate
|
||||
EMS_DEVICE: 'Appareils EMS',
|
||||
SUCCESS: 'SUCCÈS',
|
||||
FAIL: 'ÉCHEC',
|
||||
@@ -114,9 +114,9 @@ const fr: Translation = {
|
||||
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)',
|
||||
UNDERCLOCK_CPU: 'Underclock du CPU',
|
||||
HEATINGOFF: 'Démarrer le chauffage avec le chauffage forcé éteint',
|
||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
||||
REMOTE_TIMEOUT: 'Remote timeout',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote on missing room temperature',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote on missing room temperature', // TODO translate
|
||||
MIN_DURATION: 'Wait time',
|
||||
ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche',
|
||||
ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche',
|
||||
@@ -148,7 +148,7 @@ const fr: Translation = {
|
||||
CUSTOMIZATIONS_HELP_3: "désactiver l'action d'écriture",
|
||||
CUSTOMIZATIONS_HELP_4: "exclure de MQTT et de l'API",
|
||||
CUSTOMIZATIONS_HELP_5: 'cacher des appareils',
|
||||
CUSTOMIZATIONS_HELP_6: 'supprimer de la mémoire',
|
||||
CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate
|
||||
SELECT_DEVICE: 'Sélectionnez un appareil',
|
||||
SET_ALL: 'tout régler',
|
||||
OPTIONS: 'Options',
|
||||
@@ -162,21 +162,20 @@ const fr: Translation = {
|
||||
UPLOAD: 'Upload',
|
||||
DOWNLOAD: '{{D|d|d}}ownload',
|
||||
INSTALL: 'Installer',
|
||||
REINSTALL: 'Réinstaller',
|
||||
ABORTED: 'annulé',
|
||||
FAILED: 'échoué',
|
||||
SUCCESSFUL: 'réussi',
|
||||
SYSTEM: 'Système',
|
||||
LOG_OF: '{0} Log',
|
||||
STATUS_OF: 'Statut {0}',
|
||||
DOWNLOAD_UPLOAD: 'Télécharger/Mettre à jour',
|
||||
DOWNLOAD_UPLOAD: 'Download/Upload', // TODO translate
|
||||
CLOSE: 'Fermer',
|
||||
USE: 'Utiliser',
|
||||
FACTORY_RESET: 'Réinitialisation',
|
||||
SYSTEM_FACTORY_TEXT: "L'appareil a été réinitialisé et va maintenant redémarrer",
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: "Êtes-vous sûr de vouloir réinitialiser l'appareil à ses paramètres d'usine ?",
|
||||
AVAILABLE_VERSION: 'Versions disponibles',
|
||||
STABLE: 'Stable',
|
||||
AVAILABLE_VERSION: 'Latest Available Versions', // TODO translate
|
||||
STABLE: 'Stable', // TODO translate
|
||||
DEVELOPMENT: 'Développement',
|
||||
UPTIME: 'Durée de fonctionnement du système',
|
||||
FREE_MEMORY: 'Libre Memory',
|
||||
@@ -186,9 +185,9 @@ const fr: Translation = {
|
||||
FILESYSTEM: 'File System (Utilisée / Libre)',
|
||||
BUFFER_SIZE: 'Max taille du buffer',
|
||||
COMPACT: 'Compact',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
|
||||
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', // TODO translate
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', // TODO translate
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Erreur inattendue, veuillez réessayer',
|
||||
TIME_SET: 'Time set',
|
||||
MANAGE_USERS: 'Gérer les utilisateurs',
|
||||
@@ -218,7 +217,7 @@ const fr: Translation = {
|
||||
MQTT_PUBLISH_TEXT_2: 'Publier vers des topics de commande (ioBroker)',
|
||||
MQTT_PUBLISH_TEXT_3: 'Activer la découverte MQTT',
|
||||
MQTT_PUBLISH_TEXT_4: 'Préfixe pour les topics découverte',
|
||||
MQTT_PUBLISH_TEXT_5: 'Type de découverte',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery type', // TODO translate
|
||||
MQTT_PUBLISH_INTERVALS: 'Intervalles de publication',
|
||||
MQTT_INT_BOILER: 'Chaudières et pompes à chaleur',
|
||||
MQTT_INT_THERMOSTATS: 'Thermostats',
|
||||
@@ -227,10 +226,10 @@ const fr: Translation = {
|
||||
MQTT_INT_WATER: 'Modules eau',
|
||||
MQTT_QUEUE: 'Queue MQTT',
|
||||
DEFAULT: 'Défaut',
|
||||
MQTT_ENTITY_FORMAT: 'Format de l\'ID de l\'entité',
|
||||
MQTT_ENTITY_FORMAT_0: 'Instance unique, nom long (v3.4)',
|
||||
MQTT_ENTITY_FORMAT_1: 'Instance unique, nom court',
|
||||
MQTT_ENTITY_FORMAT_2: 'Instances multiples, nom court',
|
||||
MQTT_ENTITY_FORMAT: 'Entity ID format', // TODO translate
|
||||
MQTT_ENTITY_FORMAT_0: 'Single instance, long name (v3.4)', // TODO translate
|
||||
MQTT_ENTITY_FORMAT_1: 'Single instance, short name', // TODO translate
|
||||
MQTT_ENTITY_FORMAT_2: 'Multiple instances, short name', // TODO translate
|
||||
MQTT_CLEAN_SESSION: 'Flag Clean Session',
|
||||
MQTT_RETAIN_FLAG: 'Toujours activer le Retain Flag',
|
||||
INACTIVE: 'Inactif',
|
||||
@@ -261,7 +260,7 @@ const fr: Translation = {
|
||||
NETWORK_SCANNER: 'Scan réseau',
|
||||
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
||||
NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi',
|
||||
NETWORK_BLANK_BSSID: 'laisser vide pour utiliser uniquement le SSID',
|
||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
||||
TX_POWER: 'Puissance Tx',
|
||||
HOSTNAME: "Nom d'hôte",
|
||||
NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi',
|
||||
@@ -281,78 +280,79 @@ const fr: Translation = {
|
||||
ENTITY: 'entité',
|
||||
MIN: 'min',
|
||||
MAX: 'max',
|
||||
BLOCK_NAVIGATE_1: 'Vous avez des modifications non enregistrées',
|
||||
BLOCK_NAVIGATE_2: 'Si vous naviguez vers une autre page, vos modifications non enregistrées seront perdues. Êtes-vous sûr de vouloir quitter cette page ?',
|
||||
STAY: 'Rester',
|
||||
LEAVE: 'Quitter',
|
||||
SCHEDULER: 'Scheduler',
|
||||
SCHEDULER_HELP_1: 'Automatiser les commandes en ajoutant des événements programmés ci-dessous. Définissez un nom unique pour activer/désactiver l\'activation via API/MQTT',
|
||||
SCHEDULER_HELP_2: 'Utiliser 00:00 pour déclencher une fois au démarrage',
|
||||
SCHEDULE: 'Programme',
|
||||
TIME: 'Temps',
|
||||
TIMER: 'Minuteur',
|
||||
ONCHANGE: 'Sur le changement',
|
||||
CONDITION: 'Condition',
|
||||
IMMEDIATE: 'Immédiat',
|
||||
SCHEDULE_UPDATED: 'Programme mis à jour',
|
||||
SCHEDULE_TIMER_1: 'au démarrage',
|
||||
SCHEDULE_TIMER_2: 'toutes les minutes',
|
||||
SCHEDULE_TIMER_3: 'toutes les heures',
|
||||
CUSTOM_ENTITIES: 'Entités personnalisées',
|
||||
ENTITIES_HELP_1: 'Récupérer les entités personnalisées du bus EMS',
|
||||
ENTITIES_UPDATED: 'Entités mises à jour',
|
||||
WRITEABLE: 'Écriture',
|
||||
SHOWING: 'Affichage',
|
||||
SEARCH: 'Rechercher',
|
||||
CERT: 'Certificat racine TLS (laisser vide pour l\'insecurité)',
|
||||
BLOCK_NAVIGATE_1: 'You have unsaved changes', // TODO translate
|
||||
BLOCK_NAVIGATE_2: 'If you navigate to a different page, your unsaved changes will be lost. Are you sure you want to leave this page?', // TODO translate
|
||||
STAY: 'Stay', // TODO translate
|
||||
LEAVE: 'Leave', // TODO translate
|
||||
SCHEDULER: 'Scheduler', // TODO translate
|
||||
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT', // TODO translate
|
||||
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
|
||||
SCHEDULE: 'Schedule', // TODO translate
|
||||
TIME: 'Time', // TODO translate
|
||||
TIMER: 'Timer', // TODO translate
|
||||
ONCHANGE: 'Sur le changement', // TODO translate
|
||||
CONDITION: 'Condition', // TODO translate
|
||||
IMMEDIATE: 'Immédiate', // TODO translate
|
||||
SCHEDULE_UPDATED: 'Schedule updated', // TODO translate
|
||||
SCHEDULE_TIMER_1: 'on startup', // TODO translate
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
|
||||
WRITEABLE: 'Writeable', // TODO translate
|
||||
SHOWING: 'Showing', // TODO translate
|
||||
SEARCH: 'Search', // TODO translate
|
||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
||||
ENABLE_TLS: 'Activer TLS',
|
||||
ON: 'On',
|
||||
OFF: 'Off',
|
||||
POLARITY: 'Polarity',
|
||||
ACTIVEHIGH: 'Actif haut',
|
||||
ACTIVELOW: 'Actif bas',
|
||||
UNCHANGED: 'Inchangé',
|
||||
ALWAYS: 'Toujours',
|
||||
ACTIVITY: 'Activité',
|
||||
CONFIGURE: 'Configurer {0}',
|
||||
SYSTEM_MEMORY: 'Mémoire système',
|
||||
APPLICATION_SETTINGS_1: 'Modifier les paramètres de l\'application EMS-ESP',
|
||||
SECURITY_1: 'Ajouter ou supprimer des utilisateurs',
|
||||
DOWNLOAD_UPLOAD_1: 'Télécharger et mettre à jour les paramètres et le firmware',
|
||||
MODULES: 'Module',
|
||||
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
|
||||
SYSTEM_MEMORY: 'System Memory', // TODO translate
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
|
||||
SECURITY_1: 'Add or remove users', // TODO translate
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware', // TODO translate
|
||||
MODULES: 'Module', // TODO translate
|
||||
MODULES_1: 'Activer ou désactiver les modules externes',
|
||||
MODULES_UPDATED: 'Modules mis à jour',
|
||||
MODULES_DESCRIPTION: 'Cliquer sur le module pour activer ou désactiver les modules EMS-ESP',
|
||||
MODULES_NONE: 'Aucun module externe détecté',
|
||||
RENAME: 'Renommer',
|
||||
MODULES_UPDATED: 'Modules updated', // TODO translate
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules', // TODO translate
|
||||
MODULES_NONE: 'No external modules detected', // TODO translate
|
||||
RENAME: 'Rename', // TODO translate
|
||||
ENABLE_MODBUS: 'Activer Modbus',
|
||||
VIEW_LOG: 'Voir le journal pour diagnostiquer les problèmes',
|
||||
UPLOAD_DRAG: 'glisser-déposer un fichier ici ou cliquer pour en sélectionner un',
|
||||
SERVICES: 'Services',
|
||||
ALLVALUES: 'Toutes les valeurs',
|
||||
SPECIAL_FUNCTIONS: 'Fonctions spéciales',
|
||||
WAIT_FIRMWARE: 'Firmware en cours de téléchargement et d\'installation',
|
||||
INSTALL_VERSION: 'Cela va {0} la version {1}. Êtes-vous sûr ?',
|
||||
UPDATE_AVAILABLE: 'mise à jour disponible',
|
||||
LATEST_VERSION: 'Vous utilisez la dernière version {0} du firmware',
|
||||
PLEASE_WAIT: 'Veuillez patienter',
|
||||
RESTARTING_PRE: 'Initialisation',
|
||||
RESTARTING_POST: 'Préparation',
|
||||
AUTO_SCROLL: 'Défilement automatique',
|
||||
DASHBOARD: 'Tableau de bord',
|
||||
DEVELOPER_MODE: 'Mode développeur',
|
||||
BYTES: 'Octets',
|
||||
BITMASK: 'Masque de bits',
|
||||
DUPLICATE: 'Dupliquer',
|
||||
DASHBOARD_1: 'Toutes les entités EMS actives et marquées comme favoris, plus toutes les entités personnalisées, les programmes et les données des capteurs externes sont affichées ci-dessous.',
|
||||
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
|
||||
NO_DATA_2: 'module pour les marquer.',
|
||||
NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à',
|
||||
THIS_VERSION: 'Cette version',
|
||||
PLATFORM: 'Plateforme',
|
||||
RELEASE_TYPE: 'Type de version',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Connexion Internet requise pour la vérification automatique des versions et la mise à niveau',
|
||||
SWITCH_RELEASE_TYPE: 'Passer à la version {0}'
|
||||
VIEW_LOG: 'View log to diagnose issues', // TODO translate
|
||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one', // TODO translate
|
||||
SERVICES: 'Services', // TODO translate
|
||||
ALLVALUES: 'All Values', // TODO translate
|
||||
SPECIAL_FUNCTIONS: 'Special Functions',
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing', // TODO translate
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?', // TODO translate
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!', // TODO translate
|
||||
LATEST_VERSION: 'You are using the latest firmware version', // TODO translate
|
||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||
RESTARTING_POST: 'Preparing', // TODO translate
|
||||
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||
DASHBOARD: 'Dashboard', // TODO translate
|
||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicate', // TODO translate
|
||||
UPGRADE: 'Upgrade', // TODO translate
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading',
|
||||
};
|
||||
|
||||
export default fr;
|
||||
|
||||
@@ -72,7 +72,7 @@ const it: Translation = {
|
||||
TX_ISSUES: 'Problema di Tx - prova una modalità differente',
|
||||
DISCONNECTED: 'Disconnesso',
|
||||
EMS_SCAN: 'Sei sicuro di voler iniziare una scansione completa del bus EMS ?',
|
||||
DATA_TRAFFIC: 'Traffico dati',
|
||||
DATA_TRAFFIC: 'Data Traffic', // TODO translate
|
||||
EMS_DEVICE: 'Dispositivo EMS ',
|
||||
SUCCESS: 'SUCCESSO',
|
||||
FAIL: 'FALLITO',
|
||||
@@ -115,7 +115,7 @@ const it: Translation = {
|
||||
READONLY: 'Abilita modalità sola-lettura (blocca tutti i comandi di scrittura EMS Tx in uscita)',
|
||||
UNDERCLOCK_CPU: 'Abbassa velocità della CPU',
|
||||
REMOTE_TIMEOUT: 'Remote timeout',
|
||||
REMOTE_TIMEOUT_EN: 'Disabilitare il telecomando in caso di temperatura ambiente mancante',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote on missing room temperature', // TODO translate
|
||||
HEATINGOFF: 'Avviamento caldaia con riscaldamento forzato spento',
|
||||
MIN_DURATION: 'Wait time',
|
||||
ENABLE_SHOWER_TIMER: 'Abilita timer doccia',
|
||||
@@ -162,7 +162,6 @@ const it: Translation = {
|
||||
UPLOAD: 'Carica',
|
||||
DOWNLOAD: 'Scarica',
|
||||
INSTALL: 'Installare {0}',
|
||||
REINSTALL: 'Riavviare',
|
||||
ABORTED: 'Annullato',
|
||||
FAILED: 'Fallito',
|
||||
SUCCESSFUL: 'Riuscito',
|
||||
@@ -175,8 +174,8 @@ const it: Translation = {
|
||||
FACTORY_RESET: 'Impostazioni di fabbrica',
|
||||
SYSTEM_FACTORY_TEXT: 'Il dispositivo è stato ripristinato alle impostazioni di fabbrica e ora verrà riavviato',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??',
|
||||
AVAILABLE_VERSION: 'Versioni disponibili',
|
||||
STABLE: 'Stabile',
|
||||
AVAILABLE_VERSION: 'Latest Available Versions', // TODO translate
|
||||
STABLE: 'Stable', // TODO translate
|
||||
DEVELOPMENT: 'Sviluppo',
|
||||
UPTIME: 'Tempo di attività del sistema',
|
||||
FREE_MEMORY: 'Free Memory',
|
||||
@@ -185,10 +184,10 @@ const it: Translation = {
|
||||
APPSIZE: 'Applicazione (Partizione: Usata / Libera)',
|
||||
FILESYSTEM: 'Memoria Sistema (Usata / Libera)',
|
||||
BUFFER_SIZE: 'Max Buffer Size',
|
||||
COMPACT: 'Compatto',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
||||
COMPACT: 'Compact',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', // TODO translate
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', // TODO translate
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
||||
TIME_SET: 'Imposta Ora',
|
||||
MANAGE_USERS: 'Gestione Utenti',
|
||||
@@ -261,7 +260,7 @@ const it: Translation = {
|
||||
NETWORK_SCANNER: 'Scansione Rete',
|
||||
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
||||
NETWORK_BLANK_SSID: 'lasciare vuoto per disattivare WiFi',
|
||||
NETWORK_BLANK_BSSID: 'lasciare vuoto per usare solo SSID',
|
||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
||||
TX_POWER: 'Potenza Tx',
|
||||
HOSTNAME: 'Nome ospite',
|
||||
NETWORK_DISABLE_SLEEP: 'Disabilita la modalità sospensione Wi-Fi',
|
||||
@@ -304,55 +303,56 @@ const it: Translation = {
|
||||
WRITEABLE: 'Scrivibile',
|
||||
SHOWING: 'Visualizza',
|
||||
SEARCH: 'Ricerca',
|
||||
CERT: 'Certificato radice TLS (lasciare vuoto per l\'insecure)',
|
||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
||||
ENABLE_TLS: 'Abilita TLS',
|
||||
ON: 'On',
|
||||
OFF: 'Off',
|
||||
POLARITY: 'Polarity',
|
||||
ACTIVEHIGH: 'Active High',
|
||||
ACTIVELOW: 'Active Low',
|
||||
UNCHANGED: 'Unchanged',
|
||||
ALWAYS: 'Always',
|
||||
ACTIVITY: 'Activity',
|
||||
CONFIGURE: 'Configure {0}',
|
||||
SYSTEM_MEMORY: 'System Memory',
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
||||
SECURITY_1: 'Add or remove users',
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
||||
MODULES: 'Module',
|
||||
MODULES_1: 'Attiva o disattiva i moduli esterni',
|
||||
MODULES_UPDATED: 'Modules updated',
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules',
|
||||
MODULES_NONE: 'No external modules detected',
|
||||
RENAME: 'Rename',
|
||||
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
|
||||
SYSTEM_MEMORY: 'System Memory', // TODO translate
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
|
||||
SECURITY_1: 'Add or remove users', // TODO translate
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware', // TODO translate
|
||||
MODULES: 'Module', // TODO translate
|
||||
MODULES_1: 'Attiva o disattiva i moduli esterni', // TODO translate
|
||||
MODULES_UPDATED: 'Modules updated', // TODO translate
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules', // TODO translate
|
||||
MODULES_NONE: 'No external modules detected', // TODO translate
|
||||
RENAME: 'Rename', // TODO translate
|
||||
ENABLE_MODBUS: 'Abilita Modbus',
|
||||
VIEW_LOG: 'Visualizza log per diagnosticare problemi',
|
||||
UPLOAD_DRAG: 'trascina e rilascia un file qui o clicca per selezionare uno',
|
||||
SERVICES: 'Servizi',
|
||||
ALLVALUES: 'Tutti i valori',
|
||||
SPECIAL_FUNCTIONS: 'Funzioni speciali',
|
||||
WAIT_FIRMWARE: 'Firmware è in upload e installazione',
|
||||
INSTALL_VERSION: 'Questo installerà la versione {1} {0}. Sei sicuro?',
|
||||
UPDATE_AVAILABLE: 'aggiornamento disponibile',
|
||||
LATEST_VERSION: 'Stai usando la versione più recente del firmware {0}',
|
||||
PLEASE_WAIT: 'Attendere',
|
||||
RESTARTING_PRE: 'Inizializzazione',
|
||||
RESTARTING_POST: 'Preparazione',
|
||||
AUTO_SCROLL: 'Scorrimento automatico',
|
||||
DASHBOARD: 'Pannello di controllo',
|
||||
DEVELOPER_MODE: 'Modalità sviluppatore',
|
||||
BYTES: 'Byte',
|
||||
BITMASK: 'Bitmask',
|
||||
DUPLICATE: 'Duplicato',
|
||||
DASHBOARD_1: 'Tutte le entità EMS che sono attive e marcate come preferite, più tutte le entità personalizzate, piani di programmazione e dati dei sensori esterni sono visualizzati di seguito.',
|
||||
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
|
||||
NO_DATA_2: 'modulo per marcarle.',
|
||||
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
|
||||
THIS_VERSION: 'Questa versione',
|
||||
PLATFORM: 'Piattaforma',
|
||||
RELEASE_TYPE: 'Tipo di rilascio',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Connessione internet richiesta per il controllo automatico delle versioni e l\'aggiornamento',
|
||||
SWITCH_RELEASE_TYPE: 'Cambia in {0} rilascio'
|
||||
VIEW_LOG: 'View log to diagnose issues', // TODO translate
|
||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one', // TODO translate
|
||||
SERVICES: 'Services', // TODO translate
|
||||
ALLVALUES: 'All Values', // TODO translate
|
||||
SPECIAL_FUNCTIONS: 'Special Functions', // TODO translate
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing', // TODO translate
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?', // TODO translate
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!', // TODO translate
|
||||
LATEST_VERSION: 'You are using the latest firmware version', // TODO translate
|
||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||
RESTARTING_POST: 'Preparing', // TODO translate
|
||||
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||
DASHBOARD: 'Dashboard', // TODO translate
|
||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicate', // TODO translate
|
||||
UPGRADE: 'Upgrade', // TODO translate
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading',
|
||||
};
|
||||
|
||||
export default it;
|
||||
|
||||
@@ -162,7 +162,6 @@ const nl: Translation = {
|
||||
UPLOAD: 'Upload',
|
||||
DOWNLOAD: '{{D|d|d}}ownload',
|
||||
INSTALL: 'Installeren',
|
||||
REINSTALL: 'Opnieuw installeren',
|
||||
ABORTED: 'afgebroken',
|
||||
FAILED: 'mislukt',
|
||||
SUCCESSFUL: 'successvol',
|
||||
@@ -253,13 +252,13 @@ const nl: Translation = {
|
||||
AP_CLIENTS: 'AP Clients',
|
||||
AP_MAX_CLIENTS: 'Max Clients',
|
||||
AP_LOCAL_IP: 'Local IP',
|
||||
NETWORK_SCAN: 'Scan WiFi Netwerken',
|
||||
NETWORK_SCAN: 'Scan WiFi Networken',
|
||||
IDLE: 'Idle',
|
||||
LOST: 'Verloren',
|
||||
SCANNING: 'Scannen',
|
||||
SCAN_AGAIN: 'Opnieuw scannen',
|
||||
NETWORK_SCANNER: 'Netwerk Scanner',
|
||||
NETWORK_NO_WIFI: 'Geen WiFi netwerken gevonden',
|
||||
NETWORK_NO_WIFI: 'Geen WiFi networken gevonden',
|
||||
NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen',
|
||||
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
||||
TX_POWER: 'Tx Vermogen',
|
||||
@@ -274,7 +273,7 @@ const nl: Translation = {
|
||||
NETWORK_SUBNET: 'Subnetmasker',
|
||||
NETWORK_DNS: 'DNS Servers',
|
||||
ADDRESS_OF: '{0} Address',
|
||||
ADMINISTRATOR: 'Administrator',
|
||||
ADMINISTRATOR: 'Administrator',
|
||||
GUEST: 'Gast',
|
||||
NEW: 'Nieuwe',
|
||||
NEW_NAME_OF: 'Hernoem {0}',
|
||||
@@ -321,7 +320,7 @@ const nl: Translation = {
|
||||
DOWNLOAD_UPLOAD_1: 'Download en upload instellingen en firmware',
|
||||
MODULES: 'Module',
|
||||
MODULES_1: 'Externe modules activeren of deactiveren',
|
||||
MODULES_UPDATED: 'Modules geüpdatet',
|
||||
MODULES_UPDATED: 'Modules geüpdatet',
|
||||
MODULES_DESCRIPTION: 'Klik op de module om EMS-ESP library modules te activeren of te deactiveren',
|
||||
MODULES_NONE: 'Geen externe modules gedetecteerd',
|
||||
RENAME: 'Hernoemen',
|
||||
@@ -332,18 +331,19 @@ const nl: Translation = {
|
||||
ALLVALUES: 'All waarden',
|
||||
SPECIAL_FUNCTIONS: 'Speciale functies',
|
||||
WAIT_FIRMWARE: 'Firmware wordt geüpload en geïnstalleerd',
|
||||
INSTALL_VERSION: 'Hiermee wordt versie {1} {0}. Weet je het zeker?',
|
||||
UPDATE_AVAILABLE: 'update beschikbaar',
|
||||
LATEST_VERSION: 'U gebruikt de nieuwste {0} firmwareversie',
|
||||
INSTALL_VERSION: 'Hiermee wordt versie {0} geïnstalleerd. Weet je het zeker?',
|
||||
UPGRADE_AVAILABLE: 'Er is een firmware-upgrade beschikbaar!',
|
||||
LATEST_VERSION: 'U gebruikt de nieuwste firmwareversie',
|
||||
PLEASE_WAIT: 'Een ogenblik geduld',
|
||||
RESTARTING_PRE: 'Initialiseren',
|
||||
RESTARTING_POST: 'Voorbereiding',
|
||||
AUTO_SCROLL: 'Automatisch Scrollen',
|
||||
DASHBOARD: 'Dashboard',
|
||||
DEVELOPER_MODE: 'Ontwikkelaarsmodus',
|
||||
BYTES: 'Bytes',
|
||||
BITMASK: 'Bit Mask',
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicaat',
|
||||
UPGRADE: 'Upgraden',
|
||||
DASHBOARD_1: 'Alle EMS-entiteiten die actief zijn en als favoriet zijn gemarkeerd, plus alle aangepaste entiteiten en externe sensorgegevens worden hieronder weergegeven.',
|
||||
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
|
||||
NO_DATA_2: 'module om ze te markeren.',
|
||||
@@ -351,8 +351,8 @@ const nl: Translation = {
|
||||
THIS_VERSION: 'Deze Versie',
|
||||
PLATFORM: 'Platform',
|
||||
RELEASE_TYPE: 'Release Typ',
|
||||
REINSTALL: 'Opnieuw Installeren',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internetverbinding vereist voor automatische versiecontrole en -upgrade',
|
||||
SWITCH_RELEASE_TYPE: 'Switch naar {0} release'
|
||||
};
|
||||
|
||||
export default nl;
|
||||
@@ -43,7 +43,7 @@ const no: Translation = {
|
||||
RESET: 'Nullstill',
|
||||
APPLY_CHANGES: 'Utfør endringer({0})',
|
||||
UPDATE: 'Oppdater',
|
||||
EXECUTE: 'Utfør',
|
||||
EXECUTE: 'Execute', // TODO translate
|
||||
REMOVE: 'Fjern',
|
||||
PROBLEM_UPDATING: 'Problem med oppdatering',
|
||||
PROBLEM_LOADING: 'Problem med opplasting',
|
||||
@@ -72,7 +72,7 @@ const no: Translation = {
|
||||
TX_ISSUES: 'Tx problemer - prøv en annen Tx Modus',
|
||||
DISCONNECTED: 'Frakoblet',
|
||||
EMS_SCAN: 'Er du sikker på du vil starte full søking av EMS bussen?',
|
||||
DATA_TRAFFIC: 'Data trafikk',
|
||||
DATA_TRAFFIC: 'Data Traffic', // TODO translate
|
||||
EMS_DEVICE: 'EMS Enhet',
|
||||
SUCCESS: 'VELLYKKET',
|
||||
FAIL: 'MISLYKKET',
|
||||
@@ -115,9 +115,9 @@ const no: Translation = {
|
||||
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
|
||||
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
|
||||
REMOTE_TIMEOUT: 'Remote timeout',
|
||||
REMOTE_TIMEOUT_EN: 'Deaktiver fjernstyring på manglende romtemperatur',
|
||||
HEATINGOFF: 'Start kjele med tvingt oppvarming av',
|
||||
MIN_DURATION: 'Ventetid',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote control on missing room temperature', // TODO translate
|
||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
||||
MIN_DURATION: 'Wait time',
|
||||
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
|
||||
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
|
||||
TRIGGER_TIME: 'Aktiveringstid',
|
||||
@@ -162,7 +162,6 @@ const no: Translation = {
|
||||
UPLOAD: 'Opplasning',
|
||||
DOWNLOAD: '{{N|n|n}}edlasting',
|
||||
INSTALL: 'Installer',
|
||||
REINSTALL: 'Ominstaller',
|
||||
ABORTED: 'avbrutt',
|
||||
FAILED: 'feilet',
|
||||
SUCCESSFUL: 'vellykket',
|
||||
@@ -175,9 +174,9 @@ const no: Translation = {
|
||||
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
|
||||
SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
|
||||
AVAILABLE_VERSION: 'Tilgjengelige versjoner',
|
||||
STABLE: 'Stabil',
|
||||
DEVELOPMENT: 'Utvikling',
|
||||
AVAILABLE_VERSION: 'Latest Available Versions', // TODO translate
|
||||
STABLE: 'Stable', // TODO translate
|
||||
DEVELOPMENT: 'Development',
|
||||
UPTIME: 'System Oppetid',
|
||||
FREE_MEMORY: 'Ledig Memory',
|
||||
PSRAM: 'PSRAM (Størrelse / Ledig)',
|
||||
@@ -186,9 +185,9 @@ const no: Translation = {
|
||||
FILESYSTEM: 'File System (Brukt / Ledig)',
|
||||
BUFFER_SIZE: 'Max Buffer Størrelse',
|
||||
COMPACT: 'Komprimere',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
|
||||
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', // TODO translate
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', // TODO translate
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Ukjent feil, prøv igjen',
|
||||
TIME_SET: 'Still in tid',
|
||||
MANAGE_USERS: 'Administrer Brukere',
|
||||
@@ -224,7 +223,7 @@ const no: Translation = {
|
||||
MQTT_INT_THERMOSTATS: 'Termostat',
|
||||
MQTT_INT_SOLAR: 'Solpaneler',
|
||||
MQTT_INT_MIXER: 'Blandeventil',
|
||||
MQTT_INT_WATER: 'Vannmoduler',
|
||||
MQTT_INT_WATER: 'Water Modules', // TODO translate
|
||||
MQTT_QUEUE: 'MQTT Queue',
|
||||
DEFAULT: 'Standard',
|
||||
MQTT_ENTITY_FORMAT: 'Enhets ID format',
|
||||
@@ -261,7 +260,7 @@ const no: Translation = {
|
||||
NETWORK_SCANNER: 'Nettverk Scanner',
|
||||
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
||||
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk',
|
||||
NETWORK_BLANK_BSSID: 'la feltet være blankt for å bruke kun SSID',
|
||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
||||
TX_POWER: 'Tx Effekt',
|
||||
HOSTNAME: 'Hostname',
|
||||
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
|
||||
@@ -298,61 +297,62 @@ const no: Translation = {
|
||||
SCHEDULE_TIMER_1: 'ved oppstart',
|
||||
SCHEDULE_TIMER_2: 'hvert minutt',
|
||||
SCHEDULE_TIMER_3: 'hver time',
|
||||
CUSTOM_ENTITIES: 'Personlige entiteter',
|
||||
ENTITIES_HELP_1: 'Hent personlige entiteter fra EMS bussen',
|
||||
ENTITIES_UPDATED: 'Entiteter oppdatert',
|
||||
WRITEABLE: 'Skrivbar',
|
||||
SHOWING: 'Viser',
|
||||
SEARCH: 'Søk',
|
||||
CERT: 'TLS rot sertifikat (la feltet stå blankt for usikkert)',
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
|
||||
WRITEABLE: 'Writeable', // TODO translate
|
||||
SHOWING: 'Showing', // TODO translate
|
||||
SEARCH: 'Search', // TODO translate
|
||||
CERT: 'TLS root certificate (leave blank for insecure)', // TODO translate
|
||||
ENABLE_TLS: 'Aktiviser TLS',
|
||||
ON: 'På',
|
||||
OFF: 'Av',
|
||||
POLARITY: 'Polarity',
|
||||
ACTIVEHIGH: 'Active High',
|
||||
ACTIVELOW: 'Active Low',
|
||||
UNCHANGED: 'Unchanged',
|
||||
ALWAYS: 'Always',
|
||||
ACTIVITY: 'Aktivitet',
|
||||
CONFIGURE: 'Konfigurer {0}',
|
||||
SYSTEM_MEMORY: 'System Memory',
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
||||
SECURITY_1: 'Add or remove users',
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
||||
MODULES: 'Modul',
|
||||
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
|
||||
SYSTEM_MEMORY: 'System Memory', // TODO translate
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
|
||||
SECURITY_1: 'Add or remove users', // TODO translate
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware', // TODO translate
|
||||
MODULES: 'Module', // TODO translate
|
||||
MODULES_1: 'Aktiver eller deaktiver eksterne moduler',
|
||||
MODULES_UPDATED: 'Moduler oppdatert',
|
||||
MODULES_DESCRIPTION: 'Klikk på modulen for å aktivere eller deaktivere EMS-ESP biblioteksmoduler',
|
||||
MODULES_NONE: 'Ingen eksterne moduler funnet',
|
||||
RENAME: 'Gi nytt navn',
|
||||
MODULES_UPDATED: 'Modules updated', // TODO translate
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules', // TODO translate
|
||||
MODULES_NONE: 'No external modules detected', // TODO translate
|
||||
RENAME: 'Rename', // TODO translate
|
||||
ENABLE_MODBUS: 'Aktiver Modbus',
|
||||
VIEW_LOG: 'Se logg for å diagnostisere problemer',
|
||||
UPLOAD_DRAG: 'dra og slippe en fil her eller klikk for å velge en',
|
||||
SERVICES: 'Tjenester',
|
||||
ALLVALUES: 'Alle verdier',
|
||||
SPECIAL_FUNCTIONS: 'Spesielle funksjoner',
|
||||
WAIT_FIRMWARE: 'Firmware er i opplasting og installasjon',
|
||||
INSTALL_VERSION: 'Dette vil {0} versjon {1}. Er du sikker?',
|
||||
UPDATE_AVAILABLE: 'oppdatering tilgjengelig',
|
||||
LATEST_VERSION: 'Du bruker den nyeste {0} firmware versjonen',
|
||||
PLEASE_WAIT: 'Vennligst vent',
|
||||
RESTARTING_PRE: 'Initialiserer',
|
||||
RESTARTING_POST: 'Forbereder',
|
||||
AUTO_SCROLL: 'Automatisk rulling',
|
||||
DASHBOARD: 'Dashboard',
|
||||
DEVELOPER_MODE: 'Utvikler modus',
|
||||
BYTES: 'Bytes',
|
||||
BITMASK: 'Bitmask',
|
||||
DUPLICATE: 'Duplikat',
|
||||
DASHBOARD_1: 'Alle EMS enheter som er aktive og merket som favoritt, pluss alle personlige enheter, planlegg og eksterne sensor data er vist nedenfor.',
|
||||
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
|
||||
NO_DATA_2: 'modul for å markere dem.',
|
||||
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
|
||||
THIS_VERSION: 'Denne versjonen',
|
||||
PLATFORM: 'Plattform',
|
||||
RELEASE_TYPE: 'Utgivelses type',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internettilkobling kreves for automatisk versjonskontroll og oppgradering',
|
||||
SWITCH_RELEASE_TYPE: 'Bytt til {0} utgivelse'
|
||||
VIEW_LOG: 'View log to diagnose issues', // TODO translate
|
||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one', // TODO translate
|
||||
SERVICES: 'Services', // TODO translate
|
||||
ALLVALUES: 'All Values', // TODO translate
|
||||
SPECIAL_FUNCTIONS: 'Special Functions', // TODO translate
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing', // TODO translate
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?', // TODO translate
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!', // TODO translate
|
||||
LATEST_VERSION: 'You are using the latest firmware version', // TODO translate
|
||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||
RESTARTING_POST: 'Preparing', // TODO translate
|
||||
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||
DASHBOARD: 'Dashboard', // TODO translate
|
||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicate', // TODO translate
|
||||
UPGRADE: 'Upgrade', // TODO translate
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading',
|
||||
};
|
||||
|
||||
export default no;
|
||||
|
||||
@@ -72,7 +72,7 @@ const pl: BaseTranslation = {
|
||||
TX_ISSUES: 'problem z zapisem na magistralę EMS, spróbuj wybrać inny "Tryb transmisji (Tx)"',
|
||||
DISCONNECTED: 'brak połączenia',
|
||||
EMS_SCAN: 'Czy na pewno wykonać pełne skanowanie magistrali EMS?',
|
||||
DATA_TRAFFIC: 'Ruch Danych',
|
||||
DATA_TRAFFIC: 'Data Traffic', // TODO translate
|
||||
EMS_DEVICE: 'Urządzenie EMS',
|
||||
SUCCESS: 'Udane',
|
||||
FAIL: 'Nieudane',
|
||||
@@ -115,7 +115,7 @@ const pl: BaseTranslation = {
|
||||
READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)',
|
||||
UNDERCLOCK_CPU: 'Obniż taktowanie CPU',
|
||||
REMOTE_TIMEOUT: 'Remote timeout',
|
||||
REMOTE_TIMEOUT_EN: 'Wyłącz kontrolę zdalną przy braku temperatury pomieszczenia',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote control on missing room temperature', // TODO translate
|
||||
HEATINGOFF: 'Uruchom kocioł z wymuszonym wyłączonym grzaniem',
|
||||
MIN_DURATION: 'Wait time',
|
||||
ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica',
|
||||
@@ -162,7 +162,6 @@ const pl: BaseTranslation = {
|
||||
UPLOAD: 'Wysyłanie',
|
||||
DOWNLOAD: '{{P|p||P}}obier{{anie|z||z}}',
|
||||
INSTALL: 'Zainstalować',
|
||||
REINSTALL: 'Zainstalować ponownie',
|
||||
ABORTED: 'zostało przerwane!',
|
||||
FAILED: 'nie powiodł{{o|a|}} się!',
|
||||
SUCCESSFUL: 'powiodło się.',
|
||||
@@ -175,9 +174,9 @@ const pl: BaseTranslation = {
|
||||
FACTORY_RESET: 'Ustawienia fabryczne',
|
||||
SYSTEM_FACTORY_TEXT: 'Interfejs EMS-ESP został przywrócony do ustawień fabrycznych i zostanie teraz ponownie uruchomiony.',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP?',
|
||||
AVAILABLE_VERSION: 'Najnowsze dostępne wersje',
|
||||
STABLE: 'Stabilna',
|
||||
DEVELOPMENT: 'Testowa',
|
||||
AVAILABLE_VERSION: 'Latest Available Versions', // TODO translate
|
||||
STABLE: 'Stable', // TODO translate
|
||||
DEVELOPMENT: 'Testowe',
|
||||
UPTIME: 'Czas działania systemu',
|
||||
FREE_MEMORY: 'Wolne Memory',
|
||||
PSRAM: 'PSRAM (rozmiar / wolne)',
|
||||
@@ -186,9 +185,9 @@ const pl: BaseTranslation = {
|
||||
FILESYSTEM: 'System plików (wykorzystane / wolne)',
|
||||
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
||||
COMPACT: 'Kompaktowy',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
|
||||
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', // TODO translate
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', // TODO translate
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
||||
TIME_SET: 'Zegar został ustawiony.',
|
||||
MANAGE_USERS: 'Zarządzanie użytkownikami',
|
||||
@@ -319,40 +318,41 @@ const pl: BaseTranslation = {
|
||||
APPLICATION_SETTINGS_1: 'Modyfikacja ustawień aplikacji EMS-ESP',
|
||||
SECURITY_1: 'Dodawanie i usuwanie użytkowników',
|
||||
DOWNLOAD_UPLOAD_1: 'Pobieranie/wysyłanie ustawień i firmware',
|
||||
MODULES: 'Moduł',
|
||||
MODULES: 'Module', // TODO translate
|
||||
MODULES_1: 'Aktywuj lub dezaktywuj moduły zewnętrzne',
|
||||
MODULES_UPDATED: 'Zaktualizowano moduły',
|
||||
MODULES_DESCRIPTION: 'Kliknij na moduł aby aktywować lub dezaktywować bibliotekę modułów EMS-ESP',
|
||||
MODULES_NONE: 'Brak wykrytych modułów zewnętrznych',
|
||||
RENAME: 'Zmień nazwę',
|
||||
MODULES_UPDATED: 'Modules updated', // TODO translate
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules', // TODO translate
|
||||
MODULES_NONE: 'No external modules detected', // TODO translate
|
||||
RENAME: 'Rename', // TODO translate
|
||||
ENABLE_MODBUS: 'Aktywuj Modbus',
|
||||
VIEW_LOG: 'Zdiagnozuj problemy',
|
||||
UPLOAD_DRAG: 'przeciągnij i upuść plik lub kliknij tutaj',
|
||||
SERVICES: 'Usługi',
|
||||
ALLVALUES: 'Wszystkie wartości',
|
||||
SPECIAL_FUNCTIONS: 'Specjalne funkcje',
|
||||
WAIT_FIRMWARE: 'Firma jest wysyłana i instaluje się',
|
||||
INSTALL_VERSION: 'To zainstaluje wersję {1} {0}. Jesteś pewny?',
|
||||
UPDATE_AVAILABLE: 'aktualizacja dostępna',
|
||||
LATEST_VERSION: 'Jesteś używając najnowszej wersji firmware {0}',
|
||||
PLEASE_WAIT: 'Proszę czekać',
|
||||
RESTARTING_PRE: 'Inicjalizacja',
|
||||
RESTARTING_POST: 'Przygotowanie',
|
||||
AUTO_SCROLL: 'Auto Scroll',
|
||||
DASHBOARD: 'Pulpit',
|
||||
DEVELOPER_MODE: 'Tryb programisty',
|
||||
BYTES: 'Bajty',
|
||||
BITMASK: 'Bit Mask',
|
||||
DUPLICATE: 'Duplicate',
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.',
|
||||
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
|
||||
NO_DATA_2: 'moduł do ich oznaczenia.',
|
||||
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
|
||||
THIS_VERSION: 'Ta wersja',
|
||||
PLATFORM: 'Platforma',
|
||||
RELEASE_TYPE: 'Typ wydania',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Połączenie internetowe jest wymagane do automatycznej kontroli wersji i aktualizacji',
|
||||
SWITCH_RELEASE_TYPE: 'Zmień na {0} wydanie'
|
||||
VIEW_LOG: 'View log to diagnose issues', // TODO translate
|
||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one', // TODO translate
|
||||
SERVICES: 'Services', // TODO translate
|
||||
ALLVALUES: 'All Values', // TODO translate
|
||||
SPECIAL_FUNCTIONS: 'Special Functions', // TODO translate
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing', // TODO translate
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?', // TODO translate
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!', // TODO translate
|
||||
LATEST_VERSION: 'You are using the latest firmware version', // TODO translate
|
||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||
RESTARTING_POST: 'Preparing', // TODO translate
|
||||
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||
DASHBOARD: 'Dashboard', // TODO translate
|
||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicate', // TODO translate
|
||||
UPGRADE: 'Upgrade', // TODO translate
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading', // TODO translate
|
||||
};
|
||||
|
||||
export default pl;
|
||||
|
||||
@@ -155,14 +155,13 @@ const sk: Translation = {
|
||||
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_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',
|
||||
UPLOAD: 'Nahrať',
|
||||
DOWNLOAD: '{{S|s|s}}tiahnuť',
|
||||
INSTALL: 'Inštalovať',
|
||||
REINSTALL: 'Inštalovať znova',
|
||||
ABORTED: 'zrušené',
|
||||
FAILED: 'chybné',
|
||||
SUCCESSFUL: 'úspešné',
|
||||
@@ -188,7 +187,7 @@ const sk: Translation = {
|
||||
COMPACT: 'Kompaktné',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
|
||||
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
||||
TIME_SET: 'Nastavený čas',
|
||||
MANAGE_USERS: 'Správa používateľov',
|
||||
@@ -324,7 +323,7 @@ const sk: Translation = {
|
||||
MODULES_UPDATED: 'Aktualizované moduly',
|
||||
MODULES_DESCRIPTION: 'Kliknutím na modul aktivujete alebo deaktivujete moduly knižnice EMS-ESP',
|
||||
MODULES_NONE: 'Neboli zistené žiadne externé moduly',
|
||||
RENAME: 'Premenovať',
|
||||
RENAME: 'Premenovať',
|
||||
ENABLE_MODBUS: 'Povoliť Modbus',
|
||||
VIEW_LOG: 'Zobrazte log na diagnostiku problémov',
|
||||
UPLOAD_DRAG: 'presuňte sem súbor alebo ho kliknutím vyberte',
|
||||
@@ -332,18 +331,19 @@ const sk: Translation = {
|
||||
ALLVALUES: 'Všetky hodnoty',
|
||||
SPECIAL_FUNCTIONS: 'Špeciálne funkcie',
|
||||
WAIT_FIRMWARE: 'Firmvér sa nahráva a inštaluje',
|
||||
INSTALL_VERSION: 'Týmto sa {0} verzia {1}. Si si istý?',
|
||||
UPDATE_AVAILABLE: 'dostupná aktualizácia',
|
||||
LATEST_VERSION: 'Používate poslednú {0} verziu firmvéru',
|
||||
INSTALL_VERSION: 'Týmto sa inštalovať verzia {0}. Si si istý?',
|
||||
UPGRADE_AVAILABLE: 'K dispozícii je aktualizácia firmvéru!',
|
||||
LATEST_VERSION: 'Používate poslednú verziu firmvéru',
|
||||
PLEASE_WAIT: 'Čakajte prosím',
|
||||
RESTARTING_PRE: 'Prebieha inicializácia',
|
||||
RESTARTING_POST: 'Príprava',
|
||||
AUTO_SCROLL: 'Automatické rolovanie',
|
||||
DASHBOARD: 'Panel',
|
||||
DEVELOPER_MODE: 'Režim vývojára',
|
||||
BYTES: 'Bytov',
|
||||
BITMASK: 'Bitová maska',
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicitné',
|
||||
UPGRADE: 'Inovovať',
|
||||
DASHBOARD_1: 'Všetky entity EMS, ktoré sú aktívne a označené ako obľúbené, plus všetky vlastné entity, plány a údaje externých senzorov sú zobrazené nižšie.',
|
||||
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
|
||||
NO_DATA_2: 'modul na ich označenie.',
|
||||
@@ -351,8 +351,8 @@ const sk: Translation = {
|
||||
THIS_VERSION: 'Táto verzia',
|
||||
PLATFORM: 'Platforma',
|
||||
RELEASE_TYPE: 'Typ vydania',
|
||||
REINSTALL: 'Preinštalovať',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internetové pripojenie je potrebné pre automatickú kontrolu a aktualizáciu',
|
||||
SWITCH_RELEASE_TYPE: 'Prepnúť na {0} verziu'
|
||||
};
|
||||
|
||||
export default sk;
|
||||
|
||||
@@ -162,7 +162,6 @@ const sv: Translation = {
|
||||
UPLOAD: 'Uppladdning',
|
||||
DOWNLOAD: '{{N|n|n}}edladdning',
|
||||
INSTALL: 'Installera',
|
||||
REINSTALL: 'Återinstallera',
|
||||
ABORTED: 'Avbruten',
|
||||
FAILED: 'Misslyckades',
|
||||
SUCCESSFUL: 'Lyckades',
|
||||
@@ -188,7 +187,7 @@ const sv: Translation = {
|
||||
COMPACT: 'Komprimerad',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
|
||||
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)',
|
||||
UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Okänt fel, var god försök igen',
|
||||
TIME_SET: 'Ställ in tid',
|
||||
MANAGE_USERS: 'Användare',
|
||||
@@ -332,27 +331,28 @@ const sv: Translation = {
|
||||
ALLVALUES: 'Alla värden',
|
||||
SPECIAL_FUNCTIONS: 'Specialfunktioner',
|
||||
WAIT_FIRMWARE: 'Firmware laddas upp och installeras',
|
||||
INSTALL_VERSION: 'Det här kommer {0} version {1}. Är du säker?',
|
||||
UPDATE_AVAILABLE: 'uppdatering tillgänglig',
|
||||
LATEST_VERSION: 'Du använder den senaste {0} firmwareversionen.',
|
||||
INSTALL_VERSION: 'Det här kommer installera version {0}. Är du säker?',
|
||||
UPGRADE_AVAILABLE: 'Det finns en tillgänglig firmwareupgradering!',
|
||||
LATEST_VERSION: 'Du använder den senaste firmwareversionen.',
|
||||
PLEASE_WAIT: 'Var god vänta',
|
||||
RESTARTING_PRE: 'Initialiserar',
|
||||
RESTARTING_POST: 'Förbereder',
|
||||
AUTO_SCROLL: 'Autoskrolla',
|
||||
DASHBOARD: 'Kontrollpanel',
|
||||
DEVELOPER_MODE: 'Utvecklarläge',
|
||||
BYTES: 'Bytes',
|
||||
BITMASK: 'Bitmask',
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Dublett',
|
||||
DASHBOARD_1: 'Alla EMS-enheter som är aktiva och markerade som favorit, plus alla anpassade entiteter, scheman och externa sensor-data visas nedan.',
|
||||
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
|
||||
NO_DATA_2: 'modul för att markera dem.',
|
||||
NO_DATA_3: 'För att se alla tillgängliga enheter, gå till',
|
||||
THIS_VERSION: 'Denna version',
|
||||
PLATFORM: 'Plattform',
|
||||
RELEASE_TYPE: 'Utgivelsestyp',
|
||||
UPGRADE: 'Uppgradera',
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internetanslutning krävs för automatisk version kontroll och uppdatering',
|
||||
SWITCH_RELEASE_TYPE: 'Byt till {0} utgåva'
|
||||
};
|
||||
|
||||
export default sv;
|
||||
|
||||
@@ -43,7 +43,7 @@ const tr: Translation = {
|
||||
RESET: 'Reset',
|
||||
APPLY_CHANGES: 'Apply Changes ({0})',
|
||||
UPDATE: 'Update',
|
||||
EXECUTE: 'Uygulamak',
|
||||
EXECUTE: 'Execute', // TODO translate
|
||||
REMOVE: 'Kaldır',
|
||||
PROBLEM_UPDATING: 'Güncelleme Sorunu',
|
||||
PROBLEM_LOADING: 'Yükleme Sorunu',
|
||||
@@ -72,7 +72,7 @@ const tr: Translation = {
|
||||
TX_ISSUES: 'Tx sorunu - başka bir Tx Modu deneyin',
|
||||
DISCONNECTED: 'Bağlantı kesildi',
|
||||
EMS_SCAN: 'EMS Hattında tam bir cihaz taraması başlatmak istediğinizden emin misiniz?',
|
||||
DATA_TRAFFIC: 'Veri trafiği',
|
||||
DATA_TRAFFIC: 'Data Traffic', // TODO translate
|
||||
EMS_DEVICE: 'EMS Cihazı',
|
||||
SUCCESS: 'BAŞARILI',
|
||||
FAIL: 'HATA',
|
||||
@@ -115,8 +115,8 @@ const tr: Translation = {
|
||||
READONLY: 'Salt okunur modu devreye al (bütün giden EMS Tx Yazma komutlarını engeller)',
|
||||
UNDERCLOCK_CPU: 'İşlemci hızını düşür',
|
||||
REMOTE_TIMEOUT: 'Remote timeout',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote on missing room temperature',
|
||||
HEATINGOFF: 'Start boiler with forced heating off',
|
||||
REMOTE_TIMEOUT_EN: 'Disable remote on missing room temperature', // TODO translate
|
||||
HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
|
||||
MIN_DURATION: 'Wait time',
|
||||
ENABLE_SHOWER_TIMER: 'Duş Sayacını Devreye Al',
|
||||
ENABLE_SHOWER_ALERT: 'Duş Alarmını Devreye Al',
|
||||
@@ -162,7 +162,6 @@ const tr: Translation = {
|
||||
UPLOAD: 'Yükleme',
|
||||
DOWNLOAD: '{{İ|i|i}}İndirme',
|
||||
INSTALL: 'Düzenlemek',
|
||||
REINSTALL: 'Yeniden düzenlemek',
|
||||
ABORTED: 'iptal edildi',
|
||||
FAILED: 'başarısız',
|
||||
SUCCESSFUL: 'başarılı',
|
||||
@@ -175,8 +174,8 @@ const tr: Translation = {
|
||||
FACTORY_RESET: 'Fabrika ayarına dönme',
|
||||
SYSTEM_FACTORY_TEXT: 'Cihaz fabrika ayarlarına döndü ve şimdi yendiden başlatılacak',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?',
|
||||
AVAILABLE_VERSION: 'En son kullanılabilir sürümler',
|
||||
STABLE: 'Kararlı',
|
||||
AVAILABLE_VERSION: 'Latest Available Versions', // TODO translate
|
||||
STABLE: 'Stable', // TODO translate
|
||||
DEVELOPMENT: 'Geliştirme',
|
||||
UPTIME: 'Sistem Çalışma Süresi',
|
||||
FREE_MEMORY: 'Yığın Memory',
|
||||
@@ -186,9 +185,9 @@ const tr: Translation = {
|
||||
FILESYSTEM: 'Dosya Sistemi (Kullanılmış / Boş)',
|
||||
BUFFER_SIZE: 'En fazla bellek boyutu',
|
||||
COMPACT: 'Sıkışık',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
|
||||
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
|
||||
UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', // TODO translate
|
||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', // TODO translate
|
||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', // TODO translate
|
||||
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
||||
TIME_SET: 'Zaman ayarı',
|
||||
MANAGE_USERS: 'Kullanıcıları yönet',
|
||||
@@ -224,7 +223,7 @@ const tr: Translation = {
|
||||
MQTT_INT_THERMOSTATS: 'Termostatlar',
|
||||
MQTT_INT_SOLAR: 'Güneş Enerjisi Modülleri',
|
||||
MQTT_INT_MIXER: 'Karışım Modülleri',
|
||||
MQTT_INT_WATER: 'Su Modülleri',
|
||||
MQTT_INT_WATER: 'Water Modules', // TODO translate
|
||||
MQTT_QUEUE: 'MQTT Sırası',
|
||||
DEFAULT: 'Varsayılan',
|
||||
MQTT_ENTITY_FORMAT: 'Varlık Kimlik biçimi',
|
||||
@@ -261,7 +260,7 @@ const tr: Translation = {
|
||||
NETWORK_SCANNER: 'Ağ Tarayıcısı',
|
||||
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
|
||||
NETWORK_BLANK_SSID: 'Kablosuz ağı devre dışı bırakmak için boş bırakın',
|
||||
NETWORK_BLANK_BSSID: 'sadece SSID kullanmak için boş bırakın',
|
||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID', // TODO translate
|
||||
TX_POWER: 'Aktarım gücü',
|
||||
HOSTNAME: 'Ana Makine Adı',
|
||||
NETWORK_DISABLE_SLEEP: 'Kablosuz uyku modunu devre dışına al',
|
||||
@@ -281,78 +280,79 @@ const tr: Translation = {
|
||||
ENTITY: 'varlık',
|
||||
MIN: 'min',
|
||||
MAX: 'maks',
|
||||
BLOCK_NAVIGATE_1: 'Değiştirilmişleriniz var',
|
||||
BLOCK_NAVIGATE_2: 'Farklı bir sayfaya geçerseniz değiştirilmişleriniz kaybolacak. Bu sayfadan çıkmak istediğinize emin misiniz?',
|
||||
STAY: 'Kal',
|
||||
LEAVE: 'Çık',
|
||||
SCHEDULER: 'Zamanlayıcı',
|
||||
SCHEDULER_HELP_1: 'Komutları zamanlayarak otomatikleştirin. Benzersiz bir ad belirtin API/MQTT aracılığıyla etkinleştirmek/devre dışı bırakma',
|
||||
SCHEDULER_HELP_2: 'Başlangıçta bir kere tetiklemek için 00:00 kullanın',
|
||||
SCHEDULE: 'Zamanlama',
|
||||
TIME: 'Zaman',
|
||||
TIMER: 'Zamanlayıcı',
|
||||
BLOCK_NAVIGATE_1: 'You have unsaved changes', // TODO translate
|
||||
BLOCK_NAVIGATE_2: 'If you navigate to a different page, your unsaved changes will be lost. Are you sure you want to leave this page?', // TODO translate
|
||||
STAY: 'Stay', // TODO translate
|
||||
LEAVE: 'Leave', // TODO translate
|
||||
SCHEDULER: 'Scheduler', // TODO translate
|
||||
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT', // TODO translate
|
||||
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', // TODO translate
|
||||
SCHEDULE: 'Schedule', // TODO translate
|
||||
TIME: 'Time', // TODO translate
|
||||
TIMER: 'Timer', // TODO translate
|
||||
ONCHANGE: 'Değişimde',
|
||||
CONDITION: 'Durum',
|
||||
IMMEDIATE: 'hemen',
|
||||
SCHEDULE_UPDATED: 'Zamanlama güncellendi',
|
||||
SCHEDULE_TIMER_1: 'Başlangıçta',
|
||||
SCHEDULE_TIMER_2: 'her dakikada',
|
||||
SCHEDULE_TIMER_3: 'her saatte',
|
||||
CUSTOM_ENTITIES: 'Özel Varlıklar',
|
||||
ENTITIES_HELP_1: 'Özel varlıkları EMS hattından alın',
|
||||
ENTITIES_UPDATED: 'Varlıklar güncellendi',
|
||||
WRITEABLE: 'Yazılabilir',
|
||||
SHOWING: 'Görüntüleniyor',
|
||||
SEARCH: 'Ara',
|
||||
CERT: 'TLS kök sertifikası (güvenli olmayan için boş bırakın)',
|
||||
CONDITION: 'Durum', // TODO translate
|
||||
IMMEDIATE: 'hemen', // TODO translate
|
||||
SCHEDULE_UPDATED: 'Schedule updated', // TODO translate
|
||||
SCHEDULE_TIMER_1: 'on startup', // TODO translate
|
||||
SCHEDULE_TIMER_2: 'every minute', // TODO translate
|
||||
SCHEDULE_TIMER_3: 'every hour', // TODO translate
|
||||
CUSTOM_ENTITIES: 'Custom Entities', // TODO translate
|
||||
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', // TODO translate
|
||||
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
|
||||
WRITEABLE: 'Writeable', // TODO translate
|
||||
SHOWING: 'Showing', // TODO translate
|
||||
SEARCH: 'Search', // TODO translate
|
||||
CERT: 'TLS root certificate (leave blank for insecure)',
|
||||
ENABLE_TLS: 'TLS deveye al',
|
||||
ON: 'On',
|
||||
OFF: 'Off',
|
||||
POLARITY: 'Polarity',
|
||||
ACTIVEHIGH: 'Aktif Yüksek',
|
||||
ACTIVELOW: 'Aktif Düşük',
|
||||
UNCHANGED: 'Değiştirilmedi',
|
||||
ALWAYS: 'Her zaman',
|
||||
ACTIVITY: 'Faaliyet',
|
||||
CONFIGURE: 'Yapılandır {0}',
|
||||
SYSTEM_MEMORY: 'Sistem Belleği',
|
||||
APPLICATION_SETTINGS_1: 'EMS-ESP Uygulama Ayarlarını Değiştir',
|
||||
SECURITY_1: 'Kullanıcıları ekle veya kaldır',
|
||||
DOWNLOAD_UPLOAD_1: 'Ayarları ve firmware dosyasını indir ve yükle',
|
||||
MODULES: 'Modül',
|
||||
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
|
||||
SYSTEM_MEMORY: 'System Memory', // TODO translate
|
||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
|
||||
SECURITY_1: 'Add or remove users', // TODO translate
|
||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware', // TODO translate
|
||||
MODULES: 'Module', // TODO translate
|
||||
MODULES_1: 'Harici modülleri etkinleştirin veya devre dışı bırakın',
|
||||
MODULES_UPDATED: 'Modülleri güncellendi',
|
||||
MODULES_DESCRIPTION: 'Modüle tıklayarak etkinleştirin veya devre dışı bırakın EMS-ESP kütüphane modülleri',
|
||||
MODULES_NONE: 'Hiçbir harici modül tespit edilmedi',
|
||||
RENAME: 'Yeniden Adlandır',
|
||||
ENABLE_MODBUS: 'Modbus\'ı etkinleştir',
|
||||
VIEW_LOG: 'Sorunu tanımlamak için günlüğü görüntüleyin',
|
||||
UPLOAD_DRAG: 'Bir dosya buraya sürükleyip bırakın veya seçmek için tıklayın',
|
||||
SERVICES: 'Hizmetler',
|
||||
ALLVALUES: 'Tüm Değerler',
|
||||
SPECIAL_FUNCTIONS: 'Özel Fonksiyonlar',
|
||||
WAIT_FIRMWARE: 'Firmware yükleniyor ve yükleniyor',
|
||||
INSTALL_VERSION: 'Bu {0} sürümü {1} yükleyecek. Emin misiniz?',
|
||||
UPDATE_AVAILABLE: 'güncellendi!',
|
||||
LATEST_VERSION: 'En son {0} firmware sürümünü kullanıyorsunuz.',
|
||||
PLEASE_WAIT: 'Lütfen bekleyin',
|
||||
RESTARTING_PRE: 'Başlatılıyor',
|
||||
RESTARTING_POST: 'Hazırlanıyor',
|
||||
AUTO_SCROLL: 'Otomatik kaydırma',
|
||||
DASHBOARD: 'Kontrol Paneli',
|
||||
DEVELOPER_MODE: 'Geliştirici Modu',
|
||||
BYTES: 'Bayt',
|
||||
BITMASK: 'Bit Maskesi',
|
||||
DUPLICATE: 'Çift',
|
||||
DASHBOARD_1: 'Tüm aktif ve Favori olarak işaretlenmiş EMS varlıkları, artı tüm özel varlıklar, zamanlayıcılar ve harici sensör verileri aşağıda görüntüleniyor.',
|
||||
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın',
|
||||
NO_DATA_2: 'modülünü kullanın.',
|
||||
NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git',
|
||||
THIS_VERSION: 'Bu Sürüm',
|
||||
PLATFORM: 'Platforma',
|
||||
RELEASE_TYPE: 'Sürüm Tipi',
|
||||
INTERNET_CONNECTION_REQUIRED: 'Otomatik sürüm kontrolü ve güncelleme için internet bağlantısı gereklidir',
|
||||
SWITCH_RELEASE_TYPE: '{0} sürümüne geç'
|
||||
MODULES_UPDATED: 'Modules updated', // TODO translate
|
||||
MODULES_DESCRIPTION: 'Click on the Module to activate or de-activate EMS-ESP library modules', // TODO translate
|
||||
MODULES_NONE: 'No external modules detected', // TODO translate
|
||||
RENAME: 'Rename', // TODO translate
|
||||
ENABLE_MODBUS: 'Enable Modbus', // TODO translate
|
||||
VIEW_LOG: 'View log to diagnose issues', // TODO translate
|
||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one', // TODO translate
|
||||
SERVICES: 'Services', // TODO translate
|
||||
ALLVALUES: 'All Values', // TODO translate
|
||||
SPECIAL_FUNCTIONS: 'Special Functions', // TODO translate
|
||||
WAIT_FIRMWARE: 'Firmware is uploading and installing', // TODO translate
|
||||
INSTALL_VERSION: 'This will install version {0}. Are you sure?', // TODO translate
|
||||
UPGRADE_AVAILABLE: 'There is a firmware upgrade available!', // TODO translate
|
||||
LATEST_VERSION: 'You are using the latest firmware version.', // TODO translate
|
||||
PLEASE_WAIT: 'Please wait', // TODO translate
|
||||
RESTARTING_PRE: 'Initializing', // TODO translate
|
||||
RESTARTING_POST: 'Preparing', // TODO translate
|
||||
AUTO_SCROLL: 'Auto Scroll', // TODO translate
|
||||
DASHBOARD: 'Dashboard', // TODO translate
|
||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||
BYTES: 'Bytes', // TODO translate
|
||||
BITMASK: 'Bit Mask',// TODO translate
|
||||
DUPLICATE: 'Duplicate', // TODO translate
|
||||
UPGRADE: 'Upgrade', // TODO translate
|
||||
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.', // TODO translate
|
||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the', // TODO translate
|
||||
NO_DATA_2: 'module to mark them.', // TODO translate
|
||||
NO_DATA_3: 'To see all available entities go to', // TODO translate
|
||||
THIS_VERSION: 'This Version', // TODO translate
|
||||
PLATFORM: 'Platform', // TODO translate
|
||||
RELEASE_TYPE: 'Release Type', // TODO translate
|
||||
REINSTALL: 'Re-install', // TODO translate
|
||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading', // TODO translate
|
||||
};
|
||||
|
||||
export default tr;
|
||||
|
||||
@@ -13,7 +13,7 @@ const router = createBrowserRouter(
|
||||
createRoutesFromElements(<Route path="/*" element={<App />} />)
|
||||
);
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</StrictMode>
|
||||
|
||||
@@ -29,10 +29,10 @@ export const updateValue =
|
||||
|
||||
export const updateValueDirty =
|
||||
(
|
||||
origData: unknown,
|
||||
origData,
|
||||
dirtyFlags: string[],
|
||||
setDirtyFlags: React.Dispatch<React.SetStateAction<string[]>>,
|
||||
updateDataValue: (value: unknown) => void
|
||||
updateDataValue: (unknown) => void
|
||||
) =>
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const updated_value = extractEventValue(event);
|
||||
|
||||
@@ -1,108 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Target modern browsers for better performance
|
||||
"target": "ES2022",
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
|
||||
// Optimized library selection
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"types": ["node", "vite/client"],
|
||||
|
||||
// JavaScript handling
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"types": ["node"],
|
||||
"allowJs": false,
|
||||
"checkJs": false,
|
||||
|
||||
// Module system optimized for Vite
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
// Emit configuration
|
||||
"noEmit": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"sourceMap": false,
|
||||
|
||||
// React/JSX configuration
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react",
|
||||
|
||||
// Strict type checking for better code quality
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Additional checks
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
|
||||
// Performance optimizations
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": ".tsbuildinfo",
|
||||
|
||||
// Path mapping for cleaner imports
|
||||
"baseUrl": ".",
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"jsx": "react-jsx",
|
||||
"noImplicitAny": false,
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@/components/*": ["src/components/*"],
|
||||
"@/utils/*": ["src/utils/*"],
|
||||
"@/types/*": ["src/types/*"],
|
||||
"@/hooks/*": ["src/hooks/*"],
|
||||
"@/services/*": ["src/services/*"],
|
||||
"@/assets/*": ["src/assets/*"],
|
||||
// Support for bare imports from src directory
|
||||
"App": ["src/App"],
|
||||
"AppRouting": ["src/AppRouting"],
|
||||
"CustomTheme": ["src/CustomTheme"],
|
||||
"SignIn": ["src/SignIn"],
|
||||
"AuthenticatedRouting": ["src/AuthenticatedRouting"],
|
||||
"env": ["src/env"],
|
||||
"components": ["src/components"],
|
||||
"contexts": ["src/contexts"],
|
||||
"i18n": ["src/i18n"],
|
||||
"utils": ["src/utils"],
|
||||
"validators": ["src/validators"],
|
||||
"types": ["src/types"],
|
||||
"api": ["src/api"],
|
||||
"app": ["src/app"],
|
||||
// Wildcard patterns for subdirectories
|
||||
"components/*": ["src/components/*"],
|
||||
"contexts/*": ["src/contexts/*"],
|
||||
"i18n/*": ["src/i18n/*"],
|
||||
"utils/*": ["src/utils/*"],
|
||||
"validators/*": ["src/validators/*"],
|
||||
"types/*": ["src/types/*"],
|
||||
"api/*": ["src/api/*"],
|
||||
"app/*": ["src/app/*"]
|
||||
"@": ["src"],
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*", "vite.config.ts", "progmem-generator.js"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
".tsbuildinfo",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx"
|
||||
],
|
||||
"ts-node": {
|
||||
"esm": true
|
||||
}
|
||||
"include": ["src/**/*", "vite.config.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
@@ -1,331 +1,131 @@
|
||||
import preact from '@preact/preset-vite';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { defineConfig } from 'vite';
|
||||
import { Plugin } from 'vite';
|
||||
import viteImagemin from 'vite-plugin-imagemin';
|
||||
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
||||
import zlib from 'zlib';
|
||||
|
||||
// @ts-expect-error - mock server doesn't have type declarations
|
||||
import mockServer from '../mock-api/mockServer.js';
|
||||
|
||||
// Plugin to display bundle size information
|
||||
const bundleSizeReporter = (): Plugin => {
|
||||
return {
|
||||
name: 'bundle-size-reporter',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
writeBundle(options: any, bundle: any) {
|
||||
console.log('\n📦 Bundle Size Report:');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
let totalSize = 0;
|
||||
const files: Array<{ name: string; size: number; gzipSize?: number }> = [];
|
||||
|
||||
for (const [fileName, chunk] of Object.entries(
|
||||
bundle as Record<string, unknown>
|
||||
)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((chunk as any).type === 'chunk' || (chunk as any).type === 'asset') {
|
||||
const filePath = path.join((options.dir as string) || 'dist', fileName);
|
||||
let size = 0;
|
||||
let gzipSize = 0;
|
||||
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
size = stats.size;
|
||||
totalSize += size;
|
||||
|
||||
// Calculate gzip size
|
||||
const fileContent = fs.readFileSync(filePath);
|
||||
gzipSize = zlib.gzipSync(fileContent).length;
|
||||
|
||||
files.push({
|
||||
name: fileName,
|
||||
size,
|
||||
gzipSize
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(`Could not read file ${fileName}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort files by size (largest first)
|
||||
files.sort((a, b) => b.size - a.size);
|
||||
|
||||
// Display individual file sizes
|
||||
files.forEach((file) => {
|
||||
const sizeKB = (file.size / 1024).toFixed(2);
|
||||
const gzipKB = file.gzipSize ? (file.gzipSize / 1024).toFixed(2) : 'N/A';
|
||||
console.log(
|
||||
`📄 ${file.name.padEnd(30)} ${sizeKB.padStart(8)} KB (${gzipKB} KB gzipped)`
|
||||
);
|
||||
});
|
||||
|
||||
console.log('='.repeat(50));
|
||||
console.log(`📊 Total Bundle Size: ${(totalSize / 1024).toFixed(2)} KB`);
|
||||
|
||||
// Calculate and display gzip total
|
||||
const totalGzipSize = files.reduce(
|
||||
(sum, file) => sum + (file.gzipSize || 0),
|
||||
0
|
||||
);
|
||||
console.log(`🗜️ Total Gzipped Size: ${(totalGzipSize / 1024).toFixed(2)} KB`);
|
||||
|
||||
// Show compression ratio
|
||||
const compressionRatio = (
|
||||
((totalSize - totalGzipSize) / totalSize) *
|
||||
100
|
||||
).toFixed(1);
|
||||
console.log(`📈 Compression Ratio: ${compressionRatio}%`);
|
||||
|
||||
console.log('='.repeat(50));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default defineConfig(
|
||||
({ command, mode }: { command: string; mode: string }) => {
|
||||
if (command === 'serve') {
|
||||
console.log('Preparing for standalone build with server, mode=' + mode);
|
||||
return {
|
||||
plugins: [
|
||||
preact({
|
||||
// Keep dev tools enabled for development
|
||||
devToolsEnabled: true,
|
||||
prefreshEnabled: true
|
||||
}),
|
||||
viteTsconfigPaths(),
|
||||
bundleSizeReporter(), // Add bundle size reporting
|
||||
mockServer()
|
||||
],
|
||||
server: {
|
||||
open: true,
|
||||
port: mode == 'production' ? 4173 : 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3080',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
},
|
||||
'/rest': 'http://localhost:3080',
|
||||
'/gh': 'http://localhost:3080' // mock for GitHub API
|
||||
}
|
||||
},
|
||||
// Optimize development builds
|
||||
build: {
|
||||
target: 'es2020',
|
||||
minify: false, // Disable minification for faster dev builds
|
||||
sourcemap: true // Enable source maps for debugging
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (mode === 'hosted') {
|
||||
console.log('Preparing for hosted build');
|
||||
return {
|
||||
plugins: [
|
||||
preact({
|
||||
// Enable Preact optimizations for hosted build
|
||||
devToolsEnabled: false,
|
||||
prefreshEnabled: false
|
||||
}),
|
||||
viteTsconfigPaths(),
|
||||
bundleSizeReporter() // Add bundle size reporting
|
||||
],
|
||||
build: {
|
||||
target: 'es2020',
|
||||
chunkSizeWarningLimit: 512,
|
||||
minify: 'terser',
|
||||
cssMinify: true,
|
||||
assetsInlineLimit: 4096,
|
||||
terserOptions: {
|
||||
compress: {
|
||||
passes: 3,
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
dead_code: true,
|
||||
unused: true
|
||||
},
|
||||
mangle: {
|
||||
toplevel: true
|
||||
},
|
||||
ecma: 2020
|
||||
},
|
||||
rollupOptions: {
|
||||
treeshake: {
|
||||
moduleSideEffects: false
|
||||
},
|
||||
output: {
|
||||
manualChunks(id: string) {
|
||||
if (id.includes('node_modules')) {
|
||||
if (id.includes('preact')) {
|
||||
return '@preact';
|
||||
}
|
||||
return 'vendor';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
console.log('Preparing for production, optimized build');
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
if (command === 'serve') {
|
||||
console.log('Preparing for standalone build with server, mode=' + mode);
|
||||
return {
|
||||
plugins: [
|
||||
preact({
|
||||
// Enable Preact optimizations
|
||||
devToolsEnabled: false,
|
||||
prefreshEnabled: false
|
||||
}),
|
||||
viteTsconfigPaths(),
|
||||
// Enable image optimization for size reduction
|
||||
{
|
||||
...viteImagemin({
|
||||
verbose: false,
|
||||
gifsicle: {
|
||||
optimizationLevel: 7,
|
||||
interlaced: false
|
||||
},
|
||||
optipng: {
|
||||
optimizationLevel: 7
|
||||
},
|
||||
mozjpeg: {
|
||||
quality: 20
|
||||
},
|
||||
pngquant: {
|
||||
quality: [0.8, 0.9],
|
||||
speed: 4
|
||||
},
|
||||
svgo: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'removeViewBox'
|
||||
},
|
||||
{
|
||||
name: 'removeEmptyAttrs',
|
||||
active: false
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
enforce: 'pre'
|
||||
},
|
||||
visualizer({
|
||||
template: 'treemap', // or sunburst
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
filename: '../analyse.html' // will be saved in project's root
|
||||
}),
|
||||
bundleSizeReporter() // Add bundle size reporting
|
||||
],
|
||||
|
||||
build: {
|
||||
// Target modern browsers for smaller bundles
|
||||
target: 'es2020',
|
||||
chunkSizeWarningLimit: 512,
|
||||
minify: 'terser',
|
||||
// Enable CSS minification
|
||||
cssMinify: true,
|
||||
// Optimize asset handling
|
||||
assetsInlineLimit: 4096, // Inline small assets
|
||||
terserOptions: {
|
||||
compress: {
|
||||
passes: 6,
|
||||
arrows: true,
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
sequences: true
|
||||
// Additional aggressive compression options
|
||||
// dead_code: true,
|
||||
// hoist_funs: true,
|
||||
// hoist_vars: true,
|
||||
// if_return: true,
|
||||
// join_vars: true,
|
||||
// loops: true,
|
||||
// pure_getters: true,
|
||||
// reduce_vars: true,
|
||||
// side_effects: false,
|
||||
// switches: true,
|
||||
// unsafe: true,
|
||||
// unsafe_arrows: true,
|
||||
// unsafe_comps: true,
|
||||
// unsafe_Function: true,
|
||||
// unsafe_math: true,
|
||||
// unsafe_proto: true,
|
||||
// unsafe_regexp: true,
|
||||
// unsafe_undefined: true,
|
||||
// unused: true
|
||||
plugins: [preact(), viteTsconfigPaths(), mockServer()],
|
||||
server: {
|
||||
open: true,
|
||||
port: mode == 'production' ? 4173 : 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3080',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
},
|
||||
mangle: {
|
||||
toplevel: true, // Enable top-level mangling
|
||||
module: true // Enable module mangling
|
||||
// properties: {
|
||||
// regex: /^_/ // Mangle properties starting with _
|
||||
// }
|
||||
},
|
||||
ecma: 2020, // Updated to modern ECMAScript
|
||||
enclose: false,
|
||||
keep_classnames: false,
|
||||
keep_fnames: false,
|
||||
ie8: false,
|
||||
module: false,
|
||||
safari10: false,
|
||||
toplevel: true // Enable top-level optimization
|
||||
},
|
||||
|
||||
rollupOptions: {
|
||||
// Enable tree shaking
|
||||
treeshake: {
|
||||
moduleSideEffects: false,
|
||||
propertyReadSideEffects: false,
|
||||
tryCatchDeoptimization: false
|
||||
},
|
||||
output: {
|
||||
// Optimize chunk naming for better caching
|
||||
chunkFileNames: 'assets/[name]-[hash].js',
|
||||
entryFileNames: 'assets/[name]-[hash].js',
|
||||
assetFileNames: 'assets/[name]-[hash].[ext]',
|
||||
manualChunks(id: string) {
|
||||
if (id.includes('node_modules')) {
|
||||
// More granular chunk splitting for better caching
|
||||
if (id.includes('react-router')) {
|
||||
return '@react-router';
|
||||
}
|
||||
if (id.includes('preact')) {
|
||||
return '@preact';
|
||||
}
|
||||
if (id.includes('uuid')) {
|
||||
return '@uuid';
|
||||
}
|
||||
if (id.includes('axios') || id.includes('fetch')) {
|
||||
return '@http';
|
||||
}
|
||||
if (id.includes('lodash') || id.includes('ramda')) {
|
||||
return '@utils';
|
||||
}
|
||||
return 'vendor';
|
||||
}
|
||||
// Split large application modules
|
||||
if (id.includes('components/')) {
|
||||
return 'components';
|
||||
}
|
||||
if (id.includes('pages/') || id.includes('routes/')) {
|
||||
return 'pages';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
// Enable source maps for debugging (optional)
|
||||
sourcemap: false // Disable for production to save space
|
||||
}
|
||||
'/rest': 'http://localhost:3080',
|
||||
'/gh': 'http://localhost:3080' // mock for GitHub API
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
if (mode === 'hosted') {
|
||||
console.log('Preparing for hosted build');
|
||||
return {
|
||||
plugins: [preact(), viteTsconfigPaths()],
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1024
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
console.log('Preparing for production, optimized build');
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
preact(),
|
||||
viteTsconfigPaths(),
|
||||
{
|
||||
...viteImagemin({
|
||||
verbose: false,
|
||||
gifsicle: {
|
||||
optimizationLevel: 7,
|
||||
interlaced: false
|
||||
},
|
||||
optipng: {
|
||||
optimizationLevel: 7
|
||||
},
|
||||
mozjpeg: {
|
||||
quality: 20
|
||||
},
|
||||
pngquant: {
|
||||
quality: [0.8, 0.9],
|
||||
speed: 4
|
||||
},
|
||||
svgo: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'removeViewBox'
|
||||
},
|
||||
{
|
||||
name: 'removeEmptyAttrs',
|
||||
active: false
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
enforce: 'pre'
|
||||
},
|
||||
visualizer({
|
||||
template: 'treemap', // or sunburst
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
filename: '../analyse.html' // will be saved in project's root
|
||||
})
|
||||
],
|
||||
|
||||
build: {
|
||||
// target: 'es2022',
|
||||
chunkSizeWarningLimit: 1024,
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
passes: 4,
|
||||
arrows: true,
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
sequences: true
|
||||
},
|
||||
mangle: {
|
||||
// toplevel: true
|
||||
// module: true
|
||||
// properties: {
|
||||
// regex: /^_/
|
||||
// }
|
||||
},
|
||||
ecma: 5,
|
||||
enclose: false,
|
||||
keep_classnames: false,
|
||||
keep_fnames: false,
|
||||
ie8: false,
|
||||
module: false,
|
||||
nameCache: null,
|
||||
safari10: false,
|
||||
toplevel: false
|
||||
},
|
||||
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id: string) {
|
||||
if (id.includes('node_modules')) {
|
||||
// creating a chunk to react routes deps. Reducing the vendor chunk size
|
||||
if (id.includes('react-router')) {
|
||||
return '@react-router';
|
||||
}
|
||||
return 'vendor';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user