diff --git a/.cache/clangd/index/ArcWidget.cpp.C855CC6EB2D676E3.idx b/.cache/clangd/index/ArcWidget.cpp.C855CC6EB2D676E3.idx index 042a7b2..ab37e96 100644 Binary files a/.cache/clangd/index/ArcWidget.cpp.C855CC6EB2D676E3.idx and b/.cache/clangd/index/ArcWidget.cpp.C855CC6EB2D676E3.idx differ diff --git a/.cache/clangd/index/ButtonWidget.cpp.6932614AE5FC71F9.idx b/.cache/clangd/index/ButtonWidget.cpp.6932614AE5FC71F9.idx index 842133a..5d3e3f1 100644 Binary files a/.cache/clangd/index/ButtonWidget.cpp.6932614AE5FC71F9.idx and b/.cache/clangd/index/ButtonWidget.cpp.6932614AE5FC71F9.idx differ diff --git a/.cache/clangd/index/ButtonWidget.hpp.551D0D3595AEDB81.idx b/.cache/clangd/index/ButtonWidget.hpp.551D0D3595AEDB81.idx index 4cbad88..ea30c97 100644 Binary files a/.cache/clangd/index/ButtonWidget.hpp.551D0D3595AEDB81.idx and b/.cache/clangd/index/ButtonWidget.hpp.551D0D3595AEDB81.idx differ diff --git a/.cache/clangd/index/KnxHandlers.cpp.CB0E869699204D5E.idx b/.cache/clangd/index/KnxHandlers.cpp.CB0E869699204D5E.idx index 076949d..a7a3fcb 100644 Binary files a/.cache/clangd/index/KnxHandlers.cpp.CB0E869699204D5E.idx and b/.cache/clangd/index/KnxHandlers.cpp.CB0E869699204D5E.idx differ diff --git a/.cache/clangd/index/KnxWorker.cpp.DE6DF2C0B88ED24E.idx b/.cache/clangd/index/KnxWorker.cpp.DE6DF2C0B88ED24E.idx index 5ae74db..a533842 100644 Binary files a/.cache/clangd/index/KnxWorker.cpp.DE6DF2C0B88ED24E.idx and b/.cache/clangd/index/KnxWorker.cpp.DE6DF2C0B88ED24E.idx differ diff --git a/.cache/clangd/index/KnxWorker.hpp.F9067C35E6DD8F11.idx b/.cache/clangd/index/KnxWorker.hpp.F9067C35E6DD8F11.idx index ca95f19..711dd3e 100644 Binary files a/.cache/clangd/index/KnxWorker.hpp.F9067C35E6DD8F11.idx and b/.cache/clangd/index/KnxWorker.hpp.F9067C35E6DD8F11.idx differ diff --git a/.cache/clangd/index/WidgetConfig.cpp.FD56F9F36C29A5DA.idx b/.cache/clangd/index/WidgetConfig.cpp.FD56F9F36C29A5DA.idx index 2bed32f..f888f8a 100644 Binary files a/.cache/clangd/index/WidgetConfig.cpp.FD56F9F36C29A5DA.idx and b/.cache/clangd/index/WidgetConfig.cpp.FD56F9F36C29A5DA.idx differ diff --git a/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx b/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx index bf327c0..6514f20 100644 Binary files a/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx and b/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx differ diff --git a/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx b/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx index d343169..4ddfceb 100644 Binary files a/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx and b/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx differ diff --git a/main/KnxWorker.cpp b/main/KnxWorker.cpp index 89fbffd..497e898 100644 --- a/main/KnxWorker.cpp +++ b/main/KnxWorker.cpp @@ -321,6 +321,7 @@ bool KnxWorker::getGroupObjectInfo(size_t index, KnxGroupObjectInfo& info) { info.commFlag = go.commFlag(); info.readFlag = go.readEnable(); info.writeFlag = go.writeEnable(); + info.transmitFlag = go.transmitEnable(); // Resolve the primary group address via association/address tables info.groupAddress = resolveGroupAddress(static_cast(index)); @@ -333,6 +334,47 @@ bool KnxWorker::getGroupObjectInfo(size_t index, KnxGroupObjectInfo& info) { #endif } +bool KnxWorker::sendSwitch(uint16_t groupAddr, bool value) { +#if !UART_DEBUG_MODE + if (!knxBau.configured()) { + ESP_LOGW(TAG, "sendSwitch: KNX not configured"); + return false; + } + + // Find the GroupObject index for this group address + size_t goCount = getGroupObjectCount(); + for (size_t i = 1; i <= goCount; i++) { + uint16_t addr = resolveGroupAddress(static_cast(i)); + if (addr == groupAddr) { + GroupObject& go = knx.getGroupObject(i); + + // Check if this GO can transmit + if (!go.transmitEnable()) { + ESP_LOGW(TAG, "sendSwitch: GO %d cannot transmit", (int)i); + return false; + } + + // Set value and trigger send + KNXValue knxVal = value; + if (go.value(knxVal, DPT_Switch)) { + ESP_LOGI(TAG, "sendSwitch: GA=%d GO=%d value=%d", groupAddr, (int)i, value); + return true; + } else { + ESP_LOGW(TAG, "sendSwitch: Failed to set value for GO %d", (int)i); + return false; + } + } + } + + ESP_LOGW(TAG, "sendSwitch: No GO found for GA=%d", groupAddr); + return false; +#else + (void)groupAddr; + (void)value; + return false; +#endif +} + void KnxWorker::formatGroupAddress(uint16_t addr, char* buf, size_t bufSize) { // Format: main/middle/sub (5/3/8 bit) uint8_t main = (addr >> 11) & 0x1F; diff --git a/main/KnxWorker.hpp b/main/KnxWorker.hpp index cc62419..4c90a74 100644 --- a/main/KnxWorker.hpp +++ b/main/KnxWorker.hpp @@ -11,7 +11,8 @@ struct KnxGroupObjectInfo { uint8_t dptSub; // DPT Subtyp bool commFlag; // Kommunikations-Flag bool readFlag; // Lese-Flag - bool writeFlag; // Schreib-Flag + bool writeFlag; // Schreib-Flag (empfangen vom Bus) + bool transmitFlag; // Sende-Flag (senden auf den Bus) }; class KnxWorker { @@ -28,6 +29,9 @@ public: size_t getGroupObjectCount(); bool getGroupObjectInfo(size_t index, KnxGroupObjectInfo& info); + // KNX Telegramm senden + bool sendSwitch(uint16_t groupAddr, bool value); + // Gruppenadresse als String formatieren (z.B. "1/2/3") static void formatGroupAddress(uint16_t addr, char* buf, size_t bufSize); diff --git a/main/WidgetConfig.cpp b/main/WidgetConfig.cpp index cf8b214..aba8c5d 100644 --- a/main/WidgetConfig.cpp +++ b/main/WidgetConfig.cpp @@ -119,10 +119,14 @@ void WidgetConfig::serialize(uint8_t* buf) const { buf[pos++] = cardStyle; for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { const SubButtonConfig& sb = subButtons[i]; - buf[pos++] = sb.iconCodepoint & 0xFF; - buf[pos++] = (sb.iconCodepoint >> 8) & 0xFF; - buf[pos++] = (sb.iconCodepoint >> 16) & 0xFF; - buf[pos++] = (sb.iconCodepoint >> 24) & 0xFF; + buf[pos++] = sb.iconCodepointOff & 0xFF; + buf[pos++] = (sb.iconCodepointOff >> 8) & 0xFF; + buf[pos++] = (sb.iconCodepointOff >> 16) & 0xFF; + buf[pos++] = (sb.iconCodepointOff >> 24) & 0xFF; + buf[pos++] = sb.iconCodepointOn & 0xFF; + buf[pos++] = (sb.iconCodepointOn >> 8) & 0xFF; + buf[pos++] = (sb.iconCodepointOn >> 16) & 0xFF; + buf[pos++] = (sb.iconCodepointOn >> 24) & 0xFF; buf[pos++] = sb.knxAddrRead & 0xFF; buf[pos++] = (sb.knxAddrRead >> 8) & 0xFF; buf[pos++] = sb.knxAddrWrite & 0xFF; @@ -303,9 +307,12 @@ void WidgetConfig::deserialize(const uint8_t* buf) { for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { SubButtonConfig& sb = subButtons[i]; - if (pos + 20 <= SERIALIZED_SIZE) { - sb.iconCodepoint = buf[pos] | (buf[pos + 1] << 8) | - (buf[pos + 2] << 16) | (buf[pos + 3] << 24); + if (pos + 24 <= SERIALIZED_SIZE) { + sb.iconCodepointOff = buf[pos] | (buf[pos + 1] << 8) | + (buf[pos + 2] << 16) | (buf[pos + 3] << 24); + pos += 4; + sb.iconCodepointOn = buf[pos] | (buf[pos + 1] << 8) | + (buf[pos + 2] << 16) | (buf[pos + 3] << 24); pos += 4; sb.knxAddrRead = buf[pos] | (buf[pos + 1] << 8); pos += 2; sb.knxAddrWrite = buf[pos] | (buf[pos + 1] << 8); pos += 2; diff --git a/main/WidgetConfig.hpp b/main/WidgetConfig.hpp index 3e5af61..de52883 100644 --- a/main/WidgetConfig.hpp +++ b/main/WidgetConfig.hpp @@ -169,9 +169,10 @@ enum class SubButtonAction : uint8_t { NAVIGATE = 1, // Navigate to screen }; -// Sub-button configuration for RoomCard (20 bytes) +// Sub-button configuration for RoomCard (24 bytes) struct SubButtonConfig { - uint32_t iconCodepoint; // 4 bytes - Icon codepoint + uint32_t iconCodepointOff; // 4 bytes - Icon codepoint when OFF + uint32_t iconCodepointOn; // 4 bytes - Icon codepoint when ON (0 = use iconCodepointOff) uint16_t knxAddrRead; // 2 bytes - KNX address to read status uint16_t knxAddrWrite; // 2 bytes - KNX address to write on click Color colorOn; // 3 bytes - Color when ON @@ -181,7 +182,7 @@ struct SubButtonConfig { uint8_t targetScreen; // 1 byte - Target screen for navigate bool enabled; // 1 byte - Is this sub-button active? uint8_t _padding[2]; // 2 bytes - Alignment padding - // Total: 20 bytes per SubButton + // Total: 24 bytes per SubButton }; // Text line configuration for RoomCard (24 bytes) @@ -296,8 +297,8 @@ struct WidgetConfig { uint8_t arcValueFontSize; // Center value font size index // Serialization size (fixed for NVS storage) - // 331 + 14 (arcMin + arcMax + arcUnit + arcShowValue + arcScaleOffset + arcScaleColor + arcValueColor + arcValueFontSize) = 345 - static constexpr size_t SERIALIZED_SIZE = 345; + // 345 + 24 (6 subbuttons * 4 bytes for iconCodepointOn) = 369 + static constexpr size_t SERIALIZED_SIZE = 369; void serialize(uint8_t* buf) const; void deserialize(const uint8_t* buf); diff --git a/main/WidgetManager.cpp b/main/WidgetManager.cpp index 210fc23..e9890c0 100644 --- a/main/WidgetManager.cpp +++ b/main/WidgetManager.cpp @@ -4,6 +4,7 @@ #include "widgets/RoomCardTileWidget.hpp" #include "HistoryStore.hpp" #include "SdCard.hpp" +#include "Gui.hpp" #include "esp_lv_adapter.h" #include "esp_log.h" #include "esp_timer.h" @@ -604,13 +605,31 @@ void WidgetManager::handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target case ButtonAction::KNX: default: { if (cfg.knxAddressWrite > 0) { - bool state = false; + bool currentState = false; if (target) { - state = (lv_obj_get_state(target) & LV_STATE_CHECKED) != 0; + currentState = (lv_obj_get_state(target) & LV_STATE_CHECKED) != 0; + } + + // For toggle buttons: send the opposite of current state + // For non-toggle buttons: send ON (true) + bool sendValue = cfg.isToggle ? !currentState : true; + + ESP_LOGI(TAG, "Button %d clicked, KNX write to %d, value=%d (toggle=%d, currentState=%d)", + cfg.id, cfg.knxAddressWrite, sendValue, cfg.isToggle, currentState); + + // Send KNX telegram + if (Gui::knxWorker.sendSwitch(cfg.knxAddressWrite, sendValue)) { + // Update local UI state immediately for responsive feedback + if (cfg.isToggle && target) { + if (sendValue) { + lv_obj_add_state(target, LV_STATE_CHECKED); + } else { + lv_obj_clear_state(target, LV_STATE_CHECKED); + } + } + // Also update the cache + cacheKnxSwitch(cfg.knxAddressWrite, sendValue); } - ESP_LOGI(TAG, "Button %d clicked, KNX write to %d, state=%d", - cfg.id, cfg.knxAddressWrite, state); - // TODO: Send KNX telegram } break; } @@ -680,9 +699,12 @@ void WidgetManager::navigateBack() { void WidgetManager::sendKnxSwitch(uint16_t groupAddr, bool value) { ESP_LOGI(TAG, "sendKnxSwitch: GA=%d, value=%d", groupAddr, value); - // TODO: Send actual KNX telegram via KnxWorker - // For now, just log and update cache so UI reflects the change - cacheKnxSwitch(groupAddr, value); + + // Send actual KNX telegram via KnxWorker + if (Gui::knxWorker.sendSwitch(groupAddr, value)) { + // Update cache so UI reflects the change + cacheKnxSwitch(groupAddr, value); + } } void WidgetManager::enterStandby() { @@ -1693,7 +1715,8 @@ void WidgetManager::getConfigJson(char* buf, size_t bufSize) const { cJSON* sbJson = cJSON_CreateObject(); cJSON_AddNumberToObject(sbJson, "pos", static_cast(sb.position)); - cJSON_AddNumberToObject(sbJson, "icon", sb.iconCodepoint); + cJSON_AddNumberToObject(sbJson, "iconOff", sb.iconCodepointOff); + cJSON_AddNumberToObject(sbJson, "iconOn", sb.iconCodepointOn); cJSON_AddNumberToObject(sbJson, "knxRead", sb.knxAddrRead); cJSON_AddNumberToObject(sbJson, "knxWrite", sb.knxAddrWrite); cJSON_AddNumberToObject(sbJson, "action", static_cast(sb.action)); @@ -2035,9 +2058,10 @@ bool WidgetManager::updateConfigFromJson(const char* json) { cond.style.flags |= ConditionStyle::FLAG_USE_TEXT_COLOR; } - // Background color + // Background color (only if not empty and starts with #) cJSON* bgColor = cJSON_GetObjectItem(condItem, "bgColor"); - if (cJSON_IsString(bgColor)) { + if (cJSON_IsString(bgColor) && bgColor->valuestring && + bgColor->valuestring[0] == '#' && strlen(bgColor->valuestring) >= 4) { cond.style.bgColor = Color::fromHex(parseHexColor(bgColor->valuestring)); cond.style.flags |= ConditionStyle::FLAG_USE_BG_COLOR; } @@ -2139,9 +2163,20 @@ bool WidgetManager::updateConfigFromJson(const char* json) { sb.position = static_cast(pos->valueint); } - cJSON* icon = cJSON_GetObjectItem(sbItem, "icon"); - if (cJSON_IsNumber(icon)) { - sb.iconCodepoint = static_cast(icon->valuedouble); + cJSON* iconOff = cJSON_GetObjectItem(sbItem, "iconOff"); + if (cJSON_IsNumber(iconOff)) { + sb.iconCodepointOff = static_cast(iconOff->valuedouble); + } else { + // Backward compatibility: try old "icon" field + cJSON* icon = cJSON_GetObjectItem(sbItem, "icon"); + if (cJSON_IsNumber(icon)) { + sb.iconCodepointOff = static_cast(icon->valuedouble); + } + } + + cJSON* iconOn = cJSON_GetObjectItem(sbItem, "iconOn"); + if (cJSON_IsNumber(iconOn)) { + sb.iconCodepointOn = static_cast(iconOn->valuedouble); } cJSON* knxRead = cJSON_GetObjectItem(sbItem, "knxRead"); diff --git a/main/webserver/KnxHandlers.cpp b/main/webserver/KnxHandlers.cpp index 7a661d3..21cd121 100644 --- a/main/webserver/KnxHandlers.cpp +++ b/main/webserver/KnxHandlers.cpp @@ -24,7 +24,7 @@ esp_err_t WebServer::getKnxAddressesHandler(httpd_req_t* req) { cJSON_AddStringToObject(obj, "addrStr", addrStr); cJSON_AddBoolToObject(obj, "comm", info.commFlag); cJSON_AddBoolToObject(obj, "read", info.readFlag); - cJSON_AddBoolToObject(obj, "write", info.writeFlag); + cJSON_AddBoolToObject(obj, "write", info.transmitFlag); // transmit = kann auf Bus schreiben cJSON_AddItemToArray(arr, obj); } } diff --git a/main/widgets/ButtonWidget.cpp b/main/widgets/ButtonWidget.cpp index 2224529..3c940e6 100644 --- a/main/widgets/ButtonWidget.cpp +++ b/main/widgets/ButtonWidget.cpp @@ -235,7 +235,9 @@ void ButtonWidget::applyStyle() { lv_obj_set_style_text_color(iconLabel_, lv_color_make( config_.textColor.r, config_.textColor.g, config_.textColor.b), 0); - uint8_t sizeIdx = config_.iconSize < 6 ? config_.iconSize : config_.fontSize; + // Icon fonts support sizes 0-13 (14px to 260px) + uint8_t sizeIdx = config_.iconSize; + if (sizeIdx > 13) sizeIdx = 13; lv_obj_set_style_text_font(iconLabel_, Fonts::iconFont(sizeIdx), 0); } } @@ -336,10 +338,16 @@ bool ButtonWidget::evaluateConditions(float primaryValue, float secondaryValue, } if (bestMatch->style.flags & ConditionStyle::FLAG_USE_BG_COLOR) { - lv_obj_set_style_bg_color(obj_, lv_color_make( + lv_color_t bgColor = lv_color_make( bestMatch->style.bgColor.r, bestMatch->style.bgColor.g, - bestMatch->style.bgColor.b), 0); + bestMatch->style.bgColor.b); + // Set for default state + lv_obj_set_style_bg_color(obj_, bgColor, LV_PART_MAIN | LV_STATE_DEFAULT); + // Also set for checked state (toggle buttons) + lv_obj_set_style_bg_color(obj_, bgColor, LV_PART_MAIN | LV_STATE_CHECKED); + // And for pressed state + lv_obj_set_style_bg_color(obj_, bgColor, LV_PART_MAIN | LV_STATE_PRESSED); } if (bestMatch->style.flags & ConditionStyle::FLAG_USE_BG_OPACITY) { @@ -411,8 +419,45 @@ void ButtonWidget::applyFakeShadowStyle() { } void ButtonWidget::updateIcon(uint32_t codepoint) { - if (!iconLabel_ || codepoint == 0) return; + if (codepoint == 0) return; + + // If no icon label exists yet, we need to create it dynamically + if (!iconLabel_ && obj_ && Fonts::hasIconFont()) { + // For buttons without a content container, we need to create the icon label + if (!contentContainer_) { + // Create a simple icon label directly in the button + iconLabel_ = lv_label_create(obj_); + if (iconLabel_) { + lv_obj_clear_flag(iconLabel_, LV_OBJ_FLAG_CLICKABLE); + // Position it based on icon position setting + if (config_.iconPosition == static_cast(IconPosition::LEFT)) { + lv_obj_align(iconLabel_, LV_ALIGN_LEFT_MID, config_.iconPositionX, 0); + } else if (config_.iconPosition == static_cast(IconPosition::RIGHT)) { + lv_obj_align(iconLabel_, LV_ALIGN_RIGHT_MID, -config_.iconPositionX, 0); + } else { + lv_obj_align(iconLabel_, LV_ALIGN_CENTER, 0, 0); + } + } + } + } + + if (!iconLabel_) return; + + // Set the icon text char iconText[5]; encodeUtf8(codepoint, iconText); lv_label_set_text(iconLabel_, iconText); + + // Apply the correct font size + // If iconSize is 0 (no icon was originally configured), use fontSize instead + // Default to size 2 (22px) if both are 0 + // Icon fonts support sizes 0-13 (14px to 260px) + uint8_t sizeIdx = config_.iconSize > 0 ? config_.iconSize : config_.fontSize; + if (sizeIdx == 0) sizeIdx = 2; // Default to medium size (22px) + if (sizeIdx > 13) sizeIdx = 13; // Cap at max icon font size + lv_obj_set_style_text_font(iconLabel_, Fonts::iconFont(sizeIdx), 0); + + // Apply text color from config + lv_obj_set_style_text_color(iconLabel_, lv_color_make( + config_.textColor.r, config_.textColor.g, config_.textColor.b), 0); } diff --git a/main/widgets/RoomCardWidgetBase.cpp b/main/widgets/RoomCardWidgetBase.cpp index d8274ab..3491047 100644 --- a/main/widgets/RoomCardWidgetBase.cpp +++ b/main/widgets/RoomCardWidgetBase.cpp @@ -115,12 +115,13 @@ void RoomCardWidgetBase::createSubButtonsCommon() { int16_t absY = config_.y + relY; lv_obj_set_pos(btn, absX, absY); - // Create icon - if (cfg.iconCodepoint > 0 && Fonts::hasIconFont()) { + // Create icon (use iconCodepointOff initially, will be updated based on state) + uint32_t initialIcon = cfg.iconCodepointOff; + if (initialIcon > 0 && Fonts::hasIconFont()) { lv_obj_t* icon = lv_label_create(btn); lv_obj_clear_flag(icon, LV_OBJ_FLAG_CLICKABLE); char iconText[5]; - encodeUtf8(cfg.iconCodepoint, iconText); + encodeUtf8(initialIcon, iconText); lv_label_set_text(icon, iconText); lv_obj_center(icon); subButtonIcons_[i] = icon; @@ -158,9 +159,20 @@ void RoomCardWidgetBase::updateSubButtonColor(uint8_t index) { if (index >= MAX_SUBBUTTONS || !subButtonObjs_[index]) return; const SubButtonConfig& cfg = config_.subButtons[index]; - const Color& color = subButtonStates_[index] ? cfg.colorOn : cfg.colorOff; + bool isOn = subButtonStates_[index]; + const Color& color = isOn ? cfg.colorOn : cfg.colorOff; lv_obj_set_style_bg_color(subButtonObjs_[index], lv_color_hex(color.toLvColor()), 0); + + // Update icon based on state + if (subButtonIcons_[index]) { + uint32_t iconCodepoint = isOn && cfg.iconCodepointOn > 0 ? cfg.iconCodepointOn : cfg.iconCodepointOff; + if (iconCodepoint > 0) { + char iconText[5]; + encodeUtf8(iconCodepoint, iconText); + lv_label_set_text(subButtonIcons_[index], iconText); + } + } } void RoomCardWidgetBase::updateTemperature(float value) { diff --git a/sdkconfig b/sdkconfig index b664f67..5ccc069 100644 --- a/sdkconfig +++ b/sdkconfig @@ -2845,8 +2845,8 @@ CONFIG_LV_COLOR_DEPTH=16 # # Memory Settings # -CONFIG_LV_USE_BUILTIN_MALLOC=y -# CONFIG_LV_USE_CLIB_MALLOC is not set +# CONFIG_LV_USE_BUILTIN_MALLOC is not set +CONFIG_LV_USE_CLIB_MALLOC=y # CONFIG_LV_USE_MICROPYTHON_MALLOC is not set # CONFIG_LV_USE_RTTHREAD_MALLOC is not set # CONFIG_LV_USE_CUSTOM_MALLOC is not set @@ -2856,9 +2856,6 @@ CONFIG_LV_USE_BUILTIN_STRING=y CONFIG_LV_USE_BUILTIN_SPRINTF=y # CONFIG_LV_USE_CLIB_SPRINTF is not set # CONFIG_LV_USE_CUSTOM_SPRINTF is not set -CONFIG_LV_MEM_SIZE_KILOBYTES=128 -CONFIG_LV_MEM_POOL_EXPAND_SIZE_KILOBYTES=0 -CONFIG_LV_MEM_ADR=0x0 # end of Memory Settings # diff --git a/web-interface/src/components/SidebarRight.vue b/web-interface/src/components/SidebarRight.vue index 90b391a..628eccd 100644 --- a/web-interface/src/components/SidebarRight.vue +++ b/web-interface/src/components/SidebarRight.vue @@ -71,6 +71,7 @@ const key = computed(() => w.value ? typeKeyFor(w.value.type) : 'label'); const showIconPicker = ref(false); const conditionIconPickerIdx = ref(-1); const subButtonIconPickerIdx = ref(-1); +const subButtonIconType = ref('off'); // 'off' or 'on' const textLineIconPickerIdx = ref(-1); // Map widget types to settings components @@ -110,9 +111,10 @@ function openConditionIconPicker(idx) { showIconPicker.value = true; } -function openSubButtonIconPicker(idx) { +function openSubButtonIconPicker(idx, type = 'off') { conditionIconPickerIdx.value = -1; subButtonIconPickerIdx.value = idx; + subButtonIconType.value = type; textLineIconPickerIdx.value = -1; showIconPicker.value = true; } @@ -131,7 +133,8 @@ const activeIconCodepoint = computed({ return w.value.conditions[conditionIconPickerIdx.value].icon || 0; } if (subButtonIconPickerIdx.value >= 0 && w.value?.subButtons?.[subButtonIconPickerIdx.value]) { - return w.value.subButtons[subButtonIconPickerIdx.value].icon || 0; + const sb = w.value.subButtons[subButtonIconPickerIdx.value]; + return subButtonIconType.value === 'on' ? (sb.iconOn || 0) : (sb.iconOff || 0); } if (textLineIconPickerIdx.value >= 0 && w.value?.textLines?.[textLineIconPickerIdx.value]) { return w.value.textLines[textLineIconPickerIdx.value].icon || 0; @@ -142,7 +145,12 @@ const activeIconCodepoint = computed({ if (conditionIconPickerIdx.value >= 0 && w.value?.conditions?.[conditionIconPickerIdx.value]) { w.value.conditions[conditionIconPickerIdx.value].icon = value; } else if (subButtonIconPickerIdx.value >= 0 && w.value?.subButtons?.[subButtonIconPickerIdx.value]) { - w.value.subButtons[subButtonIconPickerIdx.value].icon = value; + const sb = w.value.subButtons[subButtonIconPickerIdx.value]; + if (subButtonIconType.value === 'on') { + sb.iconOn = value; + } else { + sb.iconOff = value; + } } else if (textLineIconPickerIdx.value >= 0 && w.value?.textLines?.[textLineIconPickerIdx.value]) { w.value.textLines[textLineIconPickerIdx.value].icon = value; } else if (w.value) { @@ -155,6 +163,7 @@ function handleIconPickerClose() { showIconPicker.value = false; conditionIconPickerIdx.value = -1; subButtonIconPickerIdx.value = -1; + subButtonIconType.value = 'off'; textLineIconPickerIdx.value = -1; } diff --git a/web-interface/src/components/widgets/settings/ButtonSettings.vue b/web-interface/src/components/widgets/settings/ButtonSettings.vue index 7948278..c8cfb18 100644 --- a/web-interface/src/components/widgets/settings/ButtonSettings.vue +++ b/web-interface/src/components/widgets/settings/ButtonSettings.vue @@ -147,10 +147,14 @@ -
- +
+
+
+ + +
@@ -190,7 +194,8 @@ const conditionCount = computed({ op: 'eq', priority: props.widget.conditions.length, icon: 0, - textColor: '#FFFFFF' + textColor: '#FFFFFF', + bgColor: '' }); } if (props.widget.conditions.length > target) { diff --git a/web-interface/src/components/widgets/settings/RoomCardSettings.vue b/web-interface/src/components/widgets/settings/RoomCardSettings.vue index 972565b..129049e 100644 --- a/web-interface/src/components/widgets/settings/RoomCardSettings.vue +++ b/web-interface/src/components/widgets/settings/RoomCardSettings.vue @@ -166,11 +166,20 @@
- - + +
+
+ + +
@@ -184,7 +193,7 @@
@@ -193,7 +202,7 @@ @@ -316,7 +325,8 @@ const subButtonCount = computed({ while (props.widget.subButtons.length < target) { props.widget.subButtons.push({ pos: props.widget.subButtons.length, - icon: 0, + iconOff: 0, + iconOn: 0, knxRead: 0, knxWrite: 0, action: 0,