This commit is contained in:
Thomas Peterson 2026-01-30 08:51:59 +01:00
parent 631d1eb250
commit 87e8deab0e
23 changed files with 3711 additions and 396 deletions

1
.gitignore vendored
View File

@ -30,7 +30,6 @@ build/
*.map *.map
flasher_args.json # Generated in build directory flasher_args.json # Generated in build directory
sdkconfig.old sdkconfig.old
sdkconfig
# ESP-IDF dependencies # ESP-IDF dependencies
# For older versions or manual component management # For older versions or manual component management

View File

@ -1,190 +1,77 @@
dependencies: dependencies:
espressif/button: espressif/button:
component_hash: dependencies: []
fccb18c37f1cfe0797b74a53a44d3f400f5fd01f4993b40052dfb7f401915089
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: '*'
- name: idf
require: private
version: '>=4.0'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__button
type: local
version: 4.1.5 version: 4.1.5
espressif/cmake_utilities: espressif/cmake_utilities:
component_hash: component_hash:
351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f 05165f30922b422b4b90c08845e6d449329b97370fbd06309803d8cb539d79e3
dependencies: dependencies:
- name: idf - name: idf
require: private require: private
version: '>=4.1' version: '>=4.1'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 0.5.3 version: 1.1.1
espressif/eppp_link: espressif/eppp_link:
component_hash: dependencies: []
9472e6825f4bb71eca2b39cf1bc92659c1ac60bfd7416560ad033a7dd8641b17
dependencies:
- name: espressif/esp_serial_slave_link
registry_url: https://components.espressif.com
require: private
version: ^1.1.0
- name: idf
require: private
version: '>=5.2'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__eppp_link
type: local
version: 1.1.4 version: 1.1.4
espressif/esp_hosted: espressif/esp_hosted:
component_hash: dependencies: []
49424510d8cf3659aa4bcf787e7b4abbf11848a25e7e5f133cf7f3324860d066
dependencies:
- name: idf
require: private
version: '>=5.3'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_hosted
version: 2.11.3 type: local
version: 2.11.5
espressif/esp_lcd_touch: espressif/esp_lcd_touch:
component_hash: dependencies: []
3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862
dependencies:
- name: idf
require: private
version: '>=4.4.2'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lcd_touch
type: local
version: 1.2.1 version: 1.2.1
espressif/esp_lcd_touch_gt911: espressif/esp_lcd_touch_gt911:
component_hash: dependencies: []
be02e243d18b9a661bc13b0d22c0a5cfa3f708cf04d6eb059772276c8c8a4d76
dependencies:
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.2.0
- name: idf
require: private
version: '>=4.4.2'
source: source:
registry_url: https://components.espressif.com/ path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lcd_touch_gt911
type: local
version: 1.2.0~1 version: 1.2.0~1
espressif/esp_lv_decoder: espressif/esp_lv_decoder:
component_hash: dependencies: []
0eb7b2bceaf73484ef80f5004337ee31617b2450a3d40621812998a47e7dd349
dependencies:
- name: espressif/esp_new_jpeg
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/libpng
registry_url: https://components.espressif.com
require: private
version: 1.*
- name: idf
require: private
version: '>=5.3'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: private
version: '>=8,<10'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lv_decoder
targets: type: local
- esp32
- esp32s2
- esp32s3
- esp32p4
- esp32c2
- esp32c3
- esp32c5
- esp32c6
version: 0.3.2 version: 0.3.2
espressif/esp_lv_fs: espressif/esp_lv_fs:
component_hash: dependencies: []
66896007884b817df34c964f9a114fff538ee2674e99fee7159162498b93f94b
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/esp_mmap_assets
registry_url: https://components.espressif.com
require: private
version: '>=1.2'
- name: idf
require: private
version: '>=4.4'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: private
version: '>=8,<10'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lv_fs
type: local
version: 1.0.1 version: 1.0.1
espressif/esp_lvgl_adapter: espressif/esp_lvgl_adapter:
component_hash: dependencies: []
4ba6ad754b2533cb582bff81ba672ea7a682f4724a02327e57f96e9c73d330c2
dependencies:
- name: espressif/button
registry_url: https://components.espressif.com
require: public
version: 4.*
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: 1.*
- name: espressif/esp_lv_decoder
registry_url: https://components.espressif.com
require: public
version: 0.*
- name: espressif/esp_lv_fs
registry_url: https://components.espressif.com
require: public
version: 1.*
- name: espressif/freetype
registry_url: https://components.espressif.com
require: public
version: 2.*
- name: espressif/knob
registry_url: https://components.espressif.com
require: public
version: 1.*
- name: idf
require: private
version: '>=5.5'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: private
version: '>=8,<10'
source: source:
registry_url: https://components.espressif.com/ path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lvgl_adapter
type: local
version: 0.3.0 version: 0.3.0
espressif/esp_lvgl_port: espressif/esp_lvgl_port:
component_hash: dependencies: []
f872401524cb645ee6ff1c9242d44fb4ddcfd4d37d7be8b9ed3f4e85a404efcd
dependencies:
- name: idf
require: private
version: '>=5.1'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: public
version: '>=8,<10'
source: source:
registry_url: https://components.espressif.com/ path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lvgl_port
type: local
version: 2.7.0 version: 2.7.0
espressif/esp_mmap_assets: espressif/esp_mmap_assets:
component_hash: component_hash:
@ -198,7 +85,7 @@ dependencies:
require: private require: private
version: '>=5.0' version: '>=5.0'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 1.4.0 version: 1.4.0
espressif/esp_new_jpeg: espressif/esp_new_jpeg:
@ -206,7 +93,7 @@ dependencies:
e6af208a875abd0ecfc0213d3751a11b504b463ebde6930f24096047925fa5c1 e6af208a875abd0ecfc0213d3751a11b504b463ebde6930f24096047925fa5c1
dependencies: [] dependencies: []
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
targets: targets:
- esp32 - esp32
@ -226,49 +113,15 @@ dependencies:
require: private require: private
version: '>=5.0' version: '>=5.0'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 1.1.2 version: 1.1.2
espressif/esp_tinyusb:
component_hash:
6f1f0c140990bf27a86611e3c1f47f9fa8468bffc3bf1eb6d551cb09f31f8908
dependencies:
- name: idf
require: private
version: '>=5.0'
- name: espressif/tinyusb
registry_url: https://components.espressif.com
require: public
version: '>=0.17.0~2'
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32s2
- esp32s3
- esp32p4
- esp32h4
version: 2.0.1~1
espressif/esp_wifi_remote: espressif/esp_wifi_remote:
component_hash: dependencies: []
e5cb66acbf9b3e115dab6b8e1262a5473e7de6e4106d32a2a8ca7384bd21fc00
dependencies:
- name: espressif/esp_hosted
registry_url: https://components.espressif.com
require: private
rules:
- if: target in [esp32h2, esp32p4]
version: '>=2.11'
- name: espressif/wifi_remote_over_eppp
registry_url: https://components.espressif.com
require: private
version: '>=0.1'
- name: idf
require: private
version: '>=5.3'
source: source:
registry_url: https://components.espressif.com/ path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_wifi_remote
type: local
version: 1.3.1 version: 1.3.1
espressif/freetype: espressif/freetype:
component_hash: component_hash:
@ -278,23 +131,15 @@ dependencies:
require: private require: private
version: '>=4.4' version: '>=4.4'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 2.13.3~1 version: 2.13.3~1
espressif/i2c_bus: espressif/i2c_bus:
component_hash: dependencies: []
4e990dc11734316186b489b362c61d41f23f79d58bc169795cec215e528cba14
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: '*'
- name: idf
require: private
version: '>=4.0'
source: source:
registry_url: https://components.espressif.com path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/espressif__i2c_bus
type: local
version: 1.5.0 version: 1.5.0
espressif/knob: espressif/knob:
component_hash: component_hash:
@ -308,7 +153,7 @@ dependencies:
require: private require: private
version: '>=4.4.1' version: '>=4.4.1'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 1.0.2 version: 1.0.2
espressif/libpng: espressif/libpng:
@ -323,25 +168,9 @@ dependencies:
require: private require: private
version: ^1.2.13 version: ^1.2.13
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 1.6.54 version: 1.6.54
espressif/tinyusb:
component_hash:
5ea9d3b6d6b0734a0a0b3491967aa0e1bece2974132294dbda5dd2839b247bfa
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com
type: service
targets:
- esp32s2
- esp32s3
- esp32p4
- esp32h4
version: 0.19.0~2
espressif/wifi_remote_over_eppp: espressif/wifi_remote_over_eppp:
component_hash: component_hash:
e1b4c485ed5afe36615b9b555dfdcbe4be33898dc3732b5bedf235bba45bd286 e1b4c485ed5afe36615b9b555dfdcbe4be33898dc3732b5bedf235bba45bd286
@ -354,7 +183,7 @@ dependencies:
require: private require: private
version: '>=5.3' version: '>=5.3'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 0.3.0 version: 0.3.0
espressif/zlib: espressif/zlib:
@ -365,7 +194,7 @@ dependencies:
require: private require: private
version: '>=4.4' version: '>=4.4'
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 1.3.1 version: 1.3.1
idf: idf:
@ -377,38 +206,40 @@ dependencies:
17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f
dependencies: [] dependencies: []
source: source:
registry_url: https://components.espressif.com registry_url: https://components.espressif.com/
type: service type: service
version: 9.4.0 version: 9.4.0
waveshare/esp_lcd_jd9365_10_1: waveshare/esp_lcd_jd9365_10_1:
component_hash: dependencies: []
6c1336b93a37df2b5be42c49c4c364d0bacdbf96f053a934f881349457fac679
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/i2c_bus
registry_url: https://components.espressif.com
require: private
version: ^1.3.0
- name: idf
require: private
version: '>=5.3'
source: source:
registry_url: https://components.espressif.com/ path:
type: service /home/thomas/projekte/test1/knxdisplay/managed_components/waveshare__esp_lcd_jd9365_10_1
targets: type: local
- esp32p4
version: 1.0.4 version: 1.0.4
direct_dependencies: direct_dependencies:
- espressif/button
- espressif/cmake_utilities
- espressif/eppp_link
- espressif/esp_hosted
- espressif/esp_lcd_touch
- espressif/esp_lcd_touch_gt911 - espressif/esp_lcd_touch_gt911
- espressif/esp_lv_decoder
- espressif/esp_lv_fs
- espressif/esp_lvgl_adapter - espressif/esp_lvgl_adapter
- espressif/esp_lvgl_port - espressif/esp_lvgl_port
- espressif/esp_tinyusb - espressif/esp_mmap_assets
- espressif/esp_new_jpeg
- espressif/esp_serial_slave_link
- espressif/esp_wifi_remote - espressif/esp_wifi_remote
- espressif/freetype
- espressif/i2c_bus
- espressif/knob
- espressif/libpng
- espressif/wifi_remote_over_eppp
- espressif/zlib
- idf - idf
- lvgl/lvgl
- waveshare/esp_lcd_jd9365_10_1 - waveshare/esp_lcd_jd9365_10_1
manifest_hash: 4224a9627ef40f4e86cf66ea3d5037d4b4dbfca46c368078b42e6988300c7f2f manifest_hash: ee0446e4a514a791315863af0d77bdcf353d357a7b8081e3e2b252138a66497b
target: esp32p4 target: esp32p4
version: 2.0.0 version: 2.0.0

View File

@ -17,7 +17,7 @@ idf_component_register(SRCS "HistoryStore.cpp" "KnxWorker.cpp" "Nvs.cpp" "main.c
"webserver/KnxHandlers.cpp" "webserver/KnxHandlers.cpp"
"webserver/StatusHandlers.cpp" "webserver/StatusHandlers.cpp"
"webserver/FileManagerHandlers.cpp" "webserver/FileManagerHandlers.cpp"
PRIV_REQUIRES spi_flash esp_driver_ppa esp_lcd usb PRIV_REQUIRES spi_flash esp_driver_ppa esp_lcd
REQUIRES esp_mm esp_eth esp_driver_ppa esp_timer lvgl knx ethernet_init esp_wifi_remote esp_netif esp_event nvs_flash esp_http_server fatfs sdmmc json tinyusb REQUIRES esp_mm esp_eth esp_driver_ppa esp_timer lvgl knx ethernet_init esp_wifi_remote esp_netif esp_event nvs_flash esp_http_server fatfs sdmmc json
INCLUDE_DIRS "webserver" "widgets" INCLUDE_DIRS "webserver" "widgets"
EMBED_TXTFILES "embedded/filemanager.html") EMBED_TXTFILES "embedded/filemanager.html")

View File

@ -1,9 +1,14 @@
#include "Gui.hpp" #include "Gui.hpp"
#include "esp_lv_adapter.h" #include "esp_lv_adapter.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_system.h"
#include "Gui/WifiSetting.hpp" #include "Gui/WifiSetting.hpp"
#include "Gui/EthSetting.hpp" #include "Gui/EthSetting.hpp"
#include "WidgetManager.hpp" #include "WidgetManager.hpp"
#include "lvgl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
KnxWorker Gui::knxWorker; // Define the static member KnxWorker Gui::knxWorker; // Define the static member

View File

@ -482,7 +482,6 @@ void HistoryStore::saveToSdCard() {
} }
void HistoryStore::loadFromSdCard() { void HistoryStore::loadFromSdCard() {
return;
if (!SdCard::instance().isMounted()) return; if (!SdCard::instance().isMounted()) return;
FILE* f = fopen(HISTORY_FILE, "rb"); FILE* f = fopen(HISTORY_FILE, "rb");

View File

@ -4,8 +4,6 @@
#include "sdmmc_cmd.h" #include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h" #include "driver/sdmmc_host.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h" #include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "tinyusb_default_config.h"
#include "tinyusb_msc.h"
#include <sys/stat.h> #include <sys/stat.h>
static const char* TAG = "SdCard"; static const char* TAG = "SdCard";
@ -111,67 +109,3 @@ bool SdCard::init() {
return true; return true;
} }
bool SdCard::enableUsbMsc() {
if (usbMscActive_) {
ESP_LOGW(TAG, "USB MSC already active");
return true;
}
if (!s_card) {
ESP_LOGE(TAG, "SD card not initialized");
return false;
}
ESP_LOGI(TAG, "Enabling USB Mass Storage mode...");
// Unmount filesystem first (so PC has exclusive access)
if (mounted_) {
ESP_LOGI(TAG, "Unmounting SD card from filesystem...");
esp_err_t ret = esp_vfs_fat_sdcard_unmount(MOUNT_POINT, s_card);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to unmount SD card: %s", esp_err_to_name(ret));
return false;
}
mounted_ = false;
}
// Initialize TinyUSB
ESP_LOGI(TAG, "Initializing TinyUSB...");
const tinyusb_config_t cfg = TINYUSB_DEFAULT_CONFIG();
esp_err_t ret = tinyusb_driver_install(&cfg);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to install TinyUSB driver: %s", esp_err_to_name(ret));
return false;
}
// Initialize MSC storage with SD card
ESP_LOGI(TAG, "Initializing USB MSC with SD card...");
tinyusb_msc_storage_config_t storage_cfg = {};
storage_cfg.medium.card = s_card;
storage_cfg.mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB;
storage_cfg.fat_fs.base_path = nullptr;
storage_cfg.fat_fs.config = VFS_FAT_MOUNT_DEFAULT_CONFIG();
storage_cfg.fat_fs.config.max_files = 5;
storage_cfg.fat_fs.config.allocation_unit_size = 16 * 1024;
storage_cfg.fat_fs.do_not_format = true;
storage_cfg.fat_fs.format_flags = 0;
tinyusb_msc_storage_handle_t storage_hdl = nullptr;
ret = tinyusb_msc_new_storage_sdmmc(&storage_cfg, &storage_hdl);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize MSC storage: %s", esp_err_to_name(ret));
return false;
}
if (storage_hdl == nullptr) {
ESP_LOGE(TAG, "MSC storage handle not initialized");
return false;
}
usbMscActive_ = true;
ESP_LOGI(TAG, "USB Mass Storage mode enabled!");
ESP_LOGI(TAG, "Connect USB cable to access SD card from PC.");
ESP_LOGI(TAG, "Reboot device to return to normal mode.");
return true;
}

View File

@ -5,11 +5,6 @@ public:
static SdCard& instance(); static SdCard& instance();
bool init(); bool init();
bool isMounted() const { return mounted_; } bool isMounted() const { return mounted_; }
bool isUsbMscActive() const { return usbMscActive_; }
// Enable USB Mass Storage mode (unmounts SD from ESP, exposes via USB)
// Returns true if successful. Requires reboot to return to normal mode.
bool enableUsbMsc();
static constexpr const char* MOUNT_POINT = "/sdcard"; static constexpr const char* MOUNT_POINT = "/sdcard";
@ -20,5 +15,4 @@ private:
SdCard& operator=(const SdCard&) = delete; SdCard& operator=(const SdCard&) = delete;
bool mounted_ = false; bool mounted_ = false;
bool usbMscActive_ = false;
}; };

View File

@ -16,6 +16,21 @@
static const char* TAG = "WidgetMgr"; static const char* TAG = "WidgetMgr";
static constexpr uint8_t SCREEN_ID_NONE = 0xFF; static constexpr uint8_t SCREEN_ID_NONE = 0xFF;
#if LV_USE_OBJ_NAME
static void dump_flex_objects(lv_obj_t* obj, uint8_t depth) {
if (!obj) return;
if (lv_obj_get_style_layout(obj, LV_PART_MAIN) == LV_LAYOUT_FLEX) {
char name[64];
lv_obj_get_name_resolved(obj, name, sizeof(name));
ESP_LOGI("FlexMap", "flex obj=%p name=%s depth=%u", obj, name, depth);
}
uint32_t count = lv_obj_get_child_count(obj);
for (uint32_t i = 0; i < count; ++i) {
dump_flex_objects(lv_obj_get_child(obj, static_cast<int32_t>(i)), depth + 1);
}
}
#endif
static bool is_valid_utf8(const char* text, size_t len) { static bool is_valid_utf8(const char* text, size_t len) {
size_t i = 0; size_t i = 0;
while (i < len) { while (i < len) {
@ -358,6 +373,10 @@ void WidgetManager::applyScreen(uint8_t screenId) {
createAllWidgets(*screen, root); createAllWidgets(*screen, root);
ESP_LOGI(TAG, "Widgets created"); ESP_LOGI(TAG, "Widgets created");
applyCachedValuesToWidgets(); applyCachedValuesToWidgets();
#if LV_USE_OBJ_NAME
ESP_LOGI(TAG, "Flex containers for screen '%s':", screen->name);
dump_flex_objects(root, 0);
#endif
if (disp) { if (disp) {
lv_display_enable_invalidation(disp, invEnabled); lv_display_enable_invalidation(disp, invEnabled);

View File

@ -61,7 +61,6 @@ h1{color:#00d4ff;margin-bottom:1rem;font-size:1.5rem}
<h1>SD Card File Manager</h1> <h1>SD Card File Manager</h1>
<div class="status"> <div class="status">
<div class="status-item"><span class="status-dot" id="sdStatus"></span><span id="sdText">SD Card</span></div> <div class="status-item"><span class="status-dot" id="sdStatus"></span><span id="sdText">SD Card</span></div>
<div class="status-item"><span class="status-dot" id="usbStatus"></span><span id="usbText">USB MSC</span></div>
</div> </div>
<div class="drop-zone" id="dropZone"> <div class="drop-zone" id="dropZone">
<p>Dateien hier ablegen oder <label style="color:#00d4ff;cursor:pointer">ausw&auml;hlen<input type="file" id="fileInput" multiple></label></p> <p>Dateien hier ablegen oder <label style="color:#00d4ff;cursor:pointer">ausw&auml;hlen<input type="file" id="fileInput" multiple></label></p>
@ -173,8 +172,6 @@ async function updateStatus(){
try{const s=await API.status(); try{const s=await API.status();
$('sdStatus').className='status-dot '+(s.sdMounted?'ok':'err'); $('sdStatus').className='status-dot '+(s.sdMounted?'ok':'err');
$('sdText').textContent='SD: '+(s.sdMounted?'OK':'Nicht eingebunden'); $('sdText').textContent='SD: '+(s.sdMounted?'OK':'Nicht eingebunden');
$('usbStatus').className='status-dot '+(s.usbMscActive?'ok':'err');
$('usbText').textContent='USB: '+(s.usbMscActive?'Aktiv':'Inaktiv');
}catch(e){}} }catch(e){}}
async function uploadFiles(files){ async function uploadFiles(files){
const bar=$('uploadBar'),prog=$('uploadProgress'); const bar=$('uploadBar'),prog=$('uploadProgress');

View File

@ -19,4 +19,3 @@ dependencies:
espressif/esp_lcd_touch_gt911: '*' espressif/esp_lcd_touch_gt911: '*'
espressif/esp_lvgl_adapter: '*' espressif/esp_lvgl_adapter: '*'
espressif/esp_wifi_remote: '*' espressif/esp_wifi_remote: '*'
espressif/esp_tinyusb: ^2.0.1

View File

@ -40,6 +40,8 @@ public:
// Initialize LVGL adapter // Initialize LVGL adapter
esp_lv_adapter_config_t cfg = ESP_LV_ADAPTER_DEFAULT_CONFIG(); esp_lv_adapter_config_t cfg = ESP_LV_ADAPTER_DEFAULT_CONFIG();
cfg.stack_in_psram = true;
cfg.task_stack_size = 16 * 1024;
ESP_ERROR_CHECK(esp_lv_adapter_init(&cfg)); ESP_ERROR_CHECK(esp_lv_adapter_init(&cfg));
// Register display // Register display
@ -50,6 +52,7 @@ public:
1280, // Vertical resolution 1280, // Vertical resolution
ESP_LV_ADAPTER_ROTATE_90 // Rotation ESP_LV_ADAPTER_ROTATE_90 // Rotation
); );
disp_cfg.profile.buffer_height = 4; // Keep internal draw buffer small.
lv_disp_t* lv_display = esp_lv_adapter_register_display(&disp_cfg); lv_disp_t* lv_display = esp_lv_adapter_register_display(&disp_cfg);
assert(lv_display != NULL); assert(lv_display != NULL);

View File

@ -1,29 +1,7 @@
#include "WebServer.hpp" #include "WebServer.hpp"
#include "../SdCard.hpp" #include "../SdCard.hpp"
#include "esp_log.h"
#include <cstring>
static const char* TAG = "WebServer";
esp_err_t WebServer::postUsbModeHandler(httpd_req_t* req) {
ESP_LOGI(TAG, "Enabling USB Mass Storage mode");
bool success = SdCard::instance().enableUsbMsc();
cJSON* json = cJSON_CreateObject();
if (success) {
cJSON_AddStringToObject(json, "status", "ok");
cJSON_AddStringToObject(json, "message", "USB MSC enabled. Connect USB cable to access SD card. Reboot to return to normal mode.");
} else {
cJSON_AddStringToObject(json, "status", "error");
cJSON_AddStringToObject(json, "message", "Failed to enable USB MSC");
}
return sendJsonObject(req, json);
}
esp_err_t WebServer::getStatusHandler(httpd_req_t* req) { esp_err_t WebServer::getStatusHandler(httpd_req_t* req) {
cJSON* json = cJSON_CreateObject(); cJSON* json = cJSON_CreateObject();
cJSON_AddBoolToObject(json, "sdMounted", SdCard::instance().isMounted()); cJSON_AddBoolToObject(json, "sdMounted", SdCard::instance().isMounted());
cJSON_AddBoolToObject(json, "usbMscActive", SdCard::instance().isUsbMscActive());
return sendJsonObject(req, json); return sendJsonObject(req, json);
} }

View File

@ -71,9 +71,6 @@ void WebServer::start() {
httpd_register_uri_handler(server_, &postKnxReset); httpd_register_uri_handler(server_, &postKnxReset);
// Status routes // Status routes
httpd_uri_t postUsbMode = { .uri = "/api/usb-mode", .method = HTTP_POST, .handler = postUsbModeHandler, .user_ctx = nullptr };
httpd_register_uri_handler(server_, &postUsbMode);
httpd_uri_t getStatus = { .uri = "/api/status", .method = HTTP_GET, .handler = getStatusHandler, .user_ctx = nullptr }; httpd_uri_t getStatus = { .uri = "/api/status", .method = HTTP_GET, .handler = getStatusHandler, .user_ctx = nullptr };
httpd_register_uri_handler(server_, &getStatus); httpd_register_uri_handler(server_, &getStatus);

View File

@ -37,7 +37,6 @@ private:
static esp_err_t postKnxResetHandler(httpd_req_t* req); static esp_err_t postKnxResetHandler(httpd_req_t* req);
// Status handlers (StatusHandlers.cpp) // Status handlers (StatusHandlers.cpp)
static esp_err_t postUsbModeHandler(httpd_req_t* req);
static esp_err_t getStatusHandler(httpd_req_t* req); static esp_err_t getStatusHandler(httpd_req_t* req);
// File manager handlers (FileManagerHandlers.cpp) // File manager handlers (FileManagerHandlers.cpp)

View File

@ -2,6 +2,7 @@
#include "../WidgetManager.hpp" #include "../WidgetManager.hpp"
#include "../Fonts.hpp" #include "../Fonts.hpp"
#include "esp_log.h" #include "esp_log.h"
#include <cstdio>
static const char* TAG = "ButtonWidget"; static const char* TAG = "ButtonWidget";
@ -17,6 +18,24 @@ static lv_flex_align_t toFlexAlign(uint8_t align) {
return LV_FLEX_ALIGN_CENTER; return LV_FLEX_ALIGN_CENTER;
} }
static void set_obj_name(lv_obj_t* obj, const char* base, uint8_t id, const char* suffix) {
#if LV_USE_OBJ_NAME
if (!obj || !base) return;
char name[48];
if (suffix && suffix[0] != '\0') {
snprintf(name, sizeof(name), "%s#%u_%s", base, id, suffix);
} else {
snprintf(name, sizeof(name), "%s#%u", base, id);
}
lv_obj_set_name(obj, name);
#else
(void)obj;
(void)base;
(void)id;
(void)suffix;
#endif
}
ButtonWidget::ButtonWidget(const WidgetConfig& config) ButtonWidget::ButtonWidget(const WidgetConfig& config)
: Widget(config) : Widget(config)
, contentContainer_(nullptr) , contentContainer_(nullptr)
@ -114,6 +133,7 @@ lv_obj_t* ButtonWidget::create(lv_obj_t* parent) {
lv_obj_set_pos(obj_, config_.x, config_.y); lv_obj_set_pos(obj_, config_.x, config_.y);
lv_obj_set_size(obj_, config_.width > 0 ? config_.width : 100, lv_obj_set_size(obj_, config_.width > 0 ? config_.width : 100,
config_.height > 0 ? config_.height : 50); config_.height > 0 ? config_.height : 50);
set_obj_name(obj_, "Button", config_.id, nullptr);
lv_obj_add_event_cb(obj_, clickCallback, LV_EVENT_CLICKED, this); lv_obj_add_event_cb(obj_, clickCallback, LV_EVENT_CLICKED, this);
@ -127,6 +147,7 @@ lv_obj_t* ButtonWidget::create(lv_obj_t* parent) {
if (contentContainer_ == nullptr) { if (contentContainer_ == nullptr) {
return obj_; // Continue without icon container return obj_; // Continue without icon container
} }
set_obj_name(contentContainer_, "Button", config_.id, "content");
lv_obj_remove_style_all(contentContainer_); lv_obj_remove_style_all(contentContainer_);
lv_obj_set_size(contentContainer_, LV_PCT(100), LV_PCT(100)); lv_obj_set_size(contentContainer_, LV_PCT(100), LV_PCT(100));
lv_obj_center(contentContainer_); lv_obj_center(contentContainer_);
@ -138,6 +159,7 @@ lv_obj_t* ButtonWidget::create(lv_obj_t* parent) {
if (iconFirst) { if (iconFirst) {
iconLabel_ = lv_label_create(contentContainer_); iconLabel_ = lv_label_create(contentContainer_);
set_obj_name(iconLabel_, "Button", config_.id, "icon");
char iconText[5]; char iconText[5];
encodeUtf8(config_.iconCodepoint, iconText); encodeUtf8(config_.iconCodepoint, iconText);
lv_label_set_text(iconLabel_, iconText); lv_label_set_text(iconLabel_, iconText);
@ -146,11 +168,13 @@ lv_obj_t* ButtonWidget::create(lv_obj_t* parent) {
// Create text label // Create text label
label_ = lv_label_create(contentContainer_); label_ = lv_label_create(contentContainer_);
set_obj_name(label_, "Button", config_.id, "text");
lv_label_set_text(label_, config_.text); lv_label_set_text(label_, config_.text);
lv_obj_clear_flag(label_, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(label_, LV_OBJ_FLAG_CLICKABLE);
if (!iconFirst) { if (!iconFirst) {
iconLabel_ = lv_label_create(contentContainer_); iconLabel_ = lv_label_create(contentContainer_);
set_obj_name(iconLabel_, "Button", config_.id, "icon");
char iconText[5]; char iconText[5];
encodeUtf8(config_.iconCodepoint, iconText); encodeUtf8(config_.iconCodepoint, iconText);
lv_label_set_text(iconLabel_, iconText); lv_label_set_text(iconLabel_, iconText);
@ -161,6 +185,7 @@ lv_obj_t* ButtonWidget::create(lv_obj_t* parent) {
} else { } else {
// Simple button without icon // Simple button without icon
label_ = lv_label_create(obj_); label_ = lv_label_create(obj_);
set_obj_name(label_, "Button", config_.id, "text");
lv_label_set_text(label_, config_.text); lv_label_set_text(label_, config_.text);
applyTextAlignment(); applyTextAlignment();
lv_obj_clear_flag(label_, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(label_, LV_OBJ_FLAG_CLICKABLE);

View File

@ -8,7 +8,6 @@ ChartWidget::ChartWidget(const WidgetConfig& config)
} }
lv_obj_t* ChartWidget::create(lv_obj_t* parent) { lv_obj_t* ChartWidget::create(lv_obj_t* parent) {
return nullptr;
obj_ = lv_obj_create(parent); obj_ = lv_obj_create(parent);
if (!obj_) return nullptr; if (!obj_) return nullptr;
lv_obj_remove_style_all(obj_); lv_obj_remove_style_all(obj_);

View File

@ -30,6 +30,24 @@ static void set_label_text_if_changed(lv_obj_t* label, const char* text) {
lv_label_set_text(label, text); lv_label_set_text(label, text);
} }
static void set_obj_name(lv_obj_t* obj, const char* base, uint8_t id, const char* suffix) {
#if LV_USE_OBJ_NAME
if (!obj || !base) return;
char name[48];
if (suffix && suffix[0] != '\0') {
snprintf(name, sizeof(name), "%s#%u_%s", base, id, suffix);
} else {
snprintf(name, sizeof(name), "%s#%u", base, id);
}
lv_obj_set_name(obj, name);
#else
(void)obj;
(void)base;
(void)id;
(void)suffix;
#endif
}
int LabelWidget::encodeUtf8(uint32_t codepoint, char* buf) { int LabelWidget::encodeUtf8(uint32_t codepoint, char* buf) {
if (codepoint < 0x80) { if (codepoint < 0x80) {
buf[0] = static_cast<char>(codepoint); buf[0] = static_cast<char>(codepoint);
@ -103,6 +121,7 @@ lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
} }
container_ = obj_; container_ = obj_;
set_obj_name(container_, "Label", config_.id, "container");
if (hasIcon) { if (hasIcon) {
// Create elements in correct order based on icon position // Create elements in correct order based on icon position
@ -111,6 +130,7 @@ lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
if (iconFirst) { if (iconFirst) {
iconLabel_ = lv_label_create(container_); iconLabel_ = lv_label_create(container_);
set_obj_name(iconLabel_, "Label", config_.id, "icon");
char iconText[5]; char iconText[5];
encodeUtf8(config_.iconCodepoint, iconText); encodeUtf8(config_.iconCodepoint, iconText);
lv_label_set_text(iconLabel_, iconText); lv_label_set_text(iconLabel_, iconText);
@ -118,11 +138,13 @@ lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
} }
textLabel_ = lv_label_create(container_); textLabel_ = lv_label_create(container_);
set_obj_name(textLabel_, "Label", config_.id, "text");
lv_label_set_text(textLabel_, config_.text); lv_label_set_text(textLabel_, config_.text);
lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE);
if (!iconFirst) { if (!iconFirst) {
iconLabel_ = lv_label_create(container_); iconLabel_ = lv_label_create(container_);
set_obj_name(iconLabel_, "Label", config_.id, "icon");
char iconText[5]; char iconText[5];
encodeUtf8(config_.iconCodepoint, iconText); encodeUtf8(config_.iconCodepoint, iconText);
lv_label_set_text(iconLabel_, iconText); lv_label_set_text(iconLabel_, iconText);
@ -133,6 +155,7 @@ lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
} else { } else {
// Container with just text label (for bg/radius) // Container with just text label (for bg/radius)
textLabel_ = lv_label_create(container_); textLabel_ = lv_label_create(container_);
set_obj_name(textLabel_, "Label", config_.id, "text");
lv_label_set_text(textLabel_, config_.text); lv_label_set_text(textLabel_, config_.text);
lv_obj_center(textLabel_); lv_obj_center(textLabel_);
lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE);
@ -141,6 +164,7 @@ lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
// Simple label without container // Simple label without container
obj_ = lv_label_create(parent); obj_ = lv_label_create(parent);
textLabel_ = obj_; textLabel_ = obj_;
set_obj_name(obj_, "Label", config_.id, nullptr);
lv_label_set_text(obj_, config_.text); lv_label_set_text(obj_, config_.text);
lv_obj_set_pos(obj_, config_.x, config_.y); lv_obj_set_pos(obj_, config_.x, config_.y);
if (config_.width > 0 && config_.height > 0) { if (config_.width > 0 && config_.height > 0) {

View File

@ -10,11 +10,30 @@ PowerNodeWidget::PowerNodeWidget(const WidgetConfig& config)
valueFormat_[0] = '\0'; valueFormat_[0] = '\0';
} }
static void set_label_text_if_changed(lv_obj_t* label, const char* text) { static bool set_label_text_if_changed(lv_obj_t* label, const char* text) {
if (!label || !text) return; if (!label || !text) return false;
const char* current = lv_label_get_text(label); const char* current = lv_label_get_text(label);
if (current && strcmp(current, text) == 0) return; if (current && strcmp(current, text) == 0) return false;
lv_label_set_text(label, text); lv_label_set_text(label, text);
return true;
}
static void set_obj_name(lv_obj_t* obj, const char* base, uint8_t id, const char* suffix) {
#if LV_USE_OBJ_NAME
if (!obj || !base) return;
char name[48];
if (suffix && suffix[0] != '\0') {
snprintf(name, sizeof(name), "%s#%u_%s", base, id, suffix);
} else {
snprintf(name, sizeof(name), "%s#%u", base, id);
}
lv_obj_set_name(obj, name);
#else
(void)obj;
(void)base;
(void)id;
(void)suffix;
#endif
} }
int PowerNodeWidget::encodeUtf8(uint32_t codepoint, char* buf) { int PowerNodeWidget::encodeUtf8(uint32_t codepoint, char* buf) {
@ -73,6 +92,7 @@ lv_obj_t* PowerNodeWidget::create(lv_obj_t* parent) {
if (obj_ == nullptr) { if (obj_ == nullptr) {
return nullptr; return nullptr;
} }
set_obj_name(obj_, "PowerNode", config_.id, nullptr);
lv_obj_remove_style_all(obj_); lv_obj_remove_style_all(obj_);
lv_obj_set_pos(obj_, config_.x, config_.y); lv_obj_set_pos(obj_, config_.x, config_.y);
@ -80,19 +100,18 @@ lv_obj_t* PowerNodeWidget::create(lv_obj_t* parent) {
config_.width > 0 ? config_.width : 120, config_.width > 0 ? config_.width : 120,
config_.height > 0 ? config_.height : 120); config_.height > 0 ? config_.height : 120);
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_layout(obj_, LV_LAYOUT_NONE);
lv_obj_set_flex_flow(obj_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(obj_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(obj_, 6, 0); lv_obj_set_style_pad_all(obj_, 6, 0);
lv_obj_set_style_pad_gap(obj_, 2, 0);
if (labelText_[0] != '\0') { if (labelText_[0] != '\0') {
labelLabel_ = lv_label_create(obj_); labelLabel_ = lv_label_create(obj_);
set_obj_name(labelLabel_, "PowerNode", config_.id, "label");
lv_label_set_text(labelLabel_, labelText_); lv_label_set_text(labelLabel_, labelText_);
lv_obj_clear_flag(labelLabel_, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(labelLabel_, LV_OBJ_FLAG_CLICKABLE);
} }
if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) { if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) {
iconLabel_ = lv_label_create(obj_); iconLabel_ = lv_label_create(obj_);
set_obj_name(iconLabel_, "PowerNode", config_.id, "icon");
char iconText[5]; char iconText[5];
encodeUtf8(config_.iconCodepoint, iconText); encodeUtf8(config_.iconCodepoint, iconText);
lv_label_set_text(iconLabel_, iconText); lv_label_set_text(iconLabel_, iconText);
@ -100,6 +119,7 @@ lv_obj_t* PowerNodeWidget::create(lv_obj_t* parent) {
} }
valueLabel_ = lv_label_create(obj_); valueLabel_ = lv_label_create(obj_);
set_obj_name(valueLabel_, "PowerNode", config_.id, "value");
if (valueFormat_[0] != '\0') { if (valueFormat_[0] != '\0') {
lv_label_set_text(valueLabel_, valueFormat_); lv_label_set_text(valueLabel_, valueFormat_);
} else { } else {
@ -154,11 +174,64 @@ void PowerNodeWidget::applyStyle() {
lv_obj_set_style_text_font(valueLabel_, Fonts::bySizeIndex(valueSizeIdx), 0); lv_obj_set_style_text_font(valueLabel_, Fonts::bySizeIndex(valueSizeIdx), 0);
lv_obj_set_style_text_align(valueLabel_, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(valueLabel_, LV_TEXT_ALIGN_CENTER, 0);
} }
layoutChildren();
} }
void PowerNodeWidget::updateValueText(const char* text) { void PowerNodeWidget::updateValueText(const char* text) {
if (valueLabel_ == nullptr || text == nullptr) return; if (valueLabel_ == nullptr || text == nullptr) return;
set_label_text_if_changed(valueLabel_, text); if (set_label_text_if_changed(valueLabel_, text)) {
layoutChildren();
}
}
void PowerNodeWidget::layoutChildren() {
if (obj_ == nullptr) return;
lv_obj_update_layout(obj_);
lv_coord_t width = lv_obj_get_width(obj_);
lv_coord_t height = lv_obj_get_height(obj_);
if (width <= 0 || height <= 0) return;
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj_, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj_, LV_PART_MAIN);
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj_, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj_, LV_PART_MAIN);
lv_coord_t avail_w = width - pad_left - pad_right;
lv_coord_t avail_h = height - pad_top - pad_bottom;
if (avail_w <= 0 || avail_h <= 0) return;
lv_obj_t* items[3];
uint8_t count = 0;
if (labelLabel_) items[count++] = labelLabel_;
if (iconLabel_) items[count++] = iconLabel_;
if (valueLabel_) items[count++] = valueLabel_;
if (count == 0) return;
const lv_coord_t gap = 2;
lv_coord_t total = 0;
for (uint8_t i = 0; i < count; ++i) {
total += lv_obj_get_height(items[i]);
}
total += gap * (count - 1);
lv_coord_t start_y = pad_top;
if (avail_h > total) {
start_y += (avail_h - total) / 2;
}
lv_coord_t y = start_y;
for (uint8_t i = 0; i < count; ++i) {
lv_coord_t item_w = lv_obj_get_width(items[i]);
lv_coord_t x = pad_left;
if (avail_w > item_w) {
x += (avail_w - item_w) / 2;
}
lv_obj_set_pos(items[i], x, y);
y += lv_obj_get_height(items[i]) + gap;
}
} }
void PowerNodeWidget::onKnxValue(float value) { void PowerNodeWidget::onKnxValue(float value) {

View File

@ -21,5 +21,6 @@ private:
void parseText(); void parseText();
void updateValueText(const char* text); void updateValueText(const char* text);
void layoutChildren();
static int encodeUtf8(uint32_t codepoint, char* buf); static int encodeUtf8(uint32_t codepoint, char* buf);
}; };

View File

@ -655,7 +655,6 @@
</div> </div>
</div> </div>
<div class="top-actions"> <div class="top-actions">
<button class="btn ghost" onclick="enableUsbMode()">USB-Modus</button>
<button class="btn ghost prog" id="knxProgBtn" onclick="toggleKnxProg()" aria-pressed="false" title="KNX Programmiermodus">KNX Prog AUS</button> <button class="btn ghost prog" id="knxProgBtn" onclick="toggleKnxProg()" aria-pressed="false" title="KNX Programmiermodus">KNX Prog AUS</button>
<button class="btn ghost danger" onclick="resetKnxSettings()">KNX Reset</button> <button class="btn ghost danger" onclick="resetKnxSettings()">KNX Reset</button>
<button class="btn ghost danger" onclick="resetConfig()">Zuruecksetzen</button> <button class="btn ghost danger" onclick="resetConfig()">Zuruecksetzen</button>
@ -2200,22 +2199,6 @@
} }
} }
async function enableUsbMode() {
if (!confirm('USB-Modus aktivieren?\n\nDie SD-Karte wird als USB-Laufwerk verfuegbar.\nZum Beenden: Geraet neu starten.')) return;
try {
const resp = await fetch('/api/usb-mode', { method: 'POST' });
const data = await resp.json();
if (data.status === 'ok') {
showStatus('USB-Modus aktiv!');
alert('USB Mass Storage aktiviert!\n\nVerbinde das USB-Kabel mit dem PC.\nDie SD-Karte erscheint als Wechseldatentraeger.\n\nZum Beenden: Geraet neu starten.');
} else {
showStatus('USB-Modus fehlgeschlagen', true);
}
} catch (e) {
showStatus('Fehler beim Aktivieren', true);
}
}
function showStatus(msg, isError = false) { function showStatus(msg, isError = false) {
const el = document.getElementById('status'); const el = document.getElementById('status');
el.textContent = msg; el.textContent = msg;

3469
sdkconfig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,9 @@ CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768
# Increase LVGL heap to avoid draw task OOM with dynamic UI # Increase LVGL heap to avoid draw task OOM with dynamic UI
CONFIG_LV_MEM_SIZE_KILOBYTES=128 CONFIG_LV_MEM_SIZE_KILOBYTES=128
# Enable object names for LVGL debug mapping
CONFIG_LV_USE_OBJ_NAME=y
# Enable FreeType fonts for extended glyph coverage (e.g. umlauts) # Enable FreeType fonts for extended glyph coverage (e.g. umlauts)
CONFIG_LV_USE_FREETYPE=y CONFIG_LV_USE_FREETYPE=y
CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE=y CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE=y

View File

@ -9,7 +9,6 @@
</div> </div>
<div class="flex items-center gap-2.5 flex-wrap justify-end"> <div class="flex items-center gap-2.5 flex-wrap justify-end">
<button class="border border-border bg-panel-2 text-text px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 hover:bg-[#e4ebf2] active:translate-y-0.5" @click="emit('open-settings')">Einstellungen</button> <button class="border border-border bg-panel-2 text-text px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 hover:bg-[#e4ebf2] active:translate-y-0.5" @click="emit('open-settings')">Einstellungen</button>
<button class="border border-border bg-panel-2 text-text px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 hover:bg-[#e4ebf2] active:translate-y-0.5" @click="enableUsbMode">USB-Modus</button>
<button <button
:class="[ :class="[
'border px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 active:translate-y-0.5', 'border px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 active:translate-y-0.5',
@ -73,21 +72,6 @@ async function handleReset() {
} }
} }
async function enableUsbMode() {
if (!confirm('USB-Modus aktivieren?\n\nDie SD-Karte wird als USB-Laufwerk verfuegbar.\nZum Beenden: Geraet neu starten.')) return;
try {
const resp = await fetch('/api/usb-mode', { method: 'POST' });
const data = await resp.json();
if (data.status === 'ok') {
alert('USB Mass Storage aktiviert!\n\nVerbinde das USB-Kabel mit dem PC.\nDie SD-Karte erscheint als Wechseldatentraeger.\n\nZum Beenden: Geraet neu starten.');
} else {
alert('USB-Modus fehlgeschlagen');
}
} catch (e) {
alert('Fehler beim Aktivieren');
}
}
async function loadKnxProgMode() { async function loadKnxProgMode() {
try { try {
const resp = await fetch('/api/knx/prog'); const resp = await fetch('/api/knx/prog');