Backup
This commit is contained in:
parent
8e90872c75
commit
d24507263f
@ -132,6 +132,7 @@ WidgetManager& WidgetManager::instance() {
|
|||||||
|
|
||||||
WidgetManager::WidgetManager() {
|
WidgetManager::WidgetManager() {
|
||||||
// widgets_ is default-initialized to nullptr
|
// widgets_ is default-initialized to nullptr
|
||||||
|
portMUX_INITIALIZE(&knxCacheMux_);
|
||||||
uiQueue_ = xQueueCreate(UI_EVENT_QUEUE_LEN, sizeof(UiEvent));
|
uiQueue_ = xQueueCreate(UI_EVENT_QUEUE_LEN, sizeof(UiEvent));
|
||||||
if (!uiQueue_) {
|
if (!uiQueue_) {
|
||||||
ESP_LOGE(TAG, "Failed to create UI event queue");
|
ESP_LOGE(TAG, "Failed to create UI event queue");
|
||||||
@ -324,6 +325,13 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
|||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "LVGL lock acquired");
|
ESP_LOGI(TAG, "LVGL lock acquired");
|
||||||
|
|
||||||
|
lv_display_t* disp = lv_display_get_default();
|
||||||
|
bool invEnabled = true;
|
||||||
|
if (disp) {
|
||||||
|
invEnabled = lv_display_is_invalidation_enabled(disp);
|
||||||
|
lv_display_enable_invalidation(disp, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Reset all input devices BEFORE destroying widgets to clear any
|
// Reset all input devices BEFORE destroying widgets to clear any
|
||||||
// pending input state and prevent use-after-free on widget objects
|
// pending input state and prevent use-after-free on widget objects
|
||||||
ESP_LOGI(TAG, "Resetting input devices...");
|
ESP_LOGI(TAG, "Resetting input devices...");
|
||||||
@ -337,6 +345,7 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
|||||||
// Now destroy C++ widgets (which deletes LVGL objects) under LVGL lock
|
// Now destroy C++ widgets (which deletes LVGL objects) under LVGL lock
|
||||||
ESP_LOGI(TAG, "Destroying widgets...");
|
ESP_LOGI(TAG, "Destroying widgets...");
|
||||||
destroyAllWidgets();
|
destroyAllWidgets();
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
ESP_LOGI(TAG, "Widgets destroyed");
|
ESP_LOGI(TAG, "Widgets destroyed");
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Creating new widgets for screen '%s' (%d widgets)...",
|
ESP_LOGI(TAG, "Creating new widgets for screen '%s' (%d widgets)...",
|
||||||
@ -344,6 +353,12 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
|||||||
lv_obj_t* root = lv_scr_act();
|
lv_obj_t* root = lv_scr_act();
|
||||||
createAllWidgets(*screen, root);
|
createAllWidgets(*screen, root);
|
||||||
ESP_LOGI(TAG, "Widgets created");
|
ESP_LOGI(TAG, "Widgets created");
|
||||||
|
applyCachedValuesToWidgets();
|
||||||
|
|
||||||
|
if (disp) {
|
||||||
|
lv_display_enable_invalidation(disp, invEnabled);
|
||||||
|
}
|
||||||
|
lv_obj_invalidate(lv_scr_act());
|
||||||
|
|
||||||
esp_lv_adapter_unlock();
|
esp_lv_adapter_unlock();
|
||||||
ESP_LOGI(TAG, "applyScreen(%d) - complete", screenId);
|
ESP_LOGI(TAG, "applyScreen(%d) - complete", screenId);
|
||||||
@ -448,6 +463,8 @@ void WidgetManager::showModalScreen(const ScreenConfig& screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyCachedValuesToWidgets();
|
||||||
|
|
||||||
esp_lv_adapter_unlock();
|
esp_lv_adapter_unlock();
|
||||||
ESP_LOGI(TAG, "Modal screen %d opened (%ldx%ld)", screen.id, modalWidth, modalHeight);
|
ESP_LOGI(TAG, "Modal screen %d opened (%ldx%ld)", screen.id, modalWidth, modalHeight);
|
||||||
}
|
}
|
||||||
@ -751,6 +768,7 @@ void WidgetManager::onKnxValue(uint16_t groupAddr, float value, TextSource sourc
|
|||||||
event.groupAddr = groupAddr;
|
event.groupAddr = groupAddr;
|
||||||
event.textSource = source;
|
event.textSource = source;
|
||||||
event.value = value;
|
event.value = value;
|
||||||
|
cacheKnxValue(groupAddr, source, value);
|
||||||
enqueueUiEvent(event);
|
enqueueUiEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,6 +781,7 @@ void WidgetManager::onKnxSwitch(uint16_t groupAddr, bool value) {
|
|||||||
event.type = UiEventType::KNX_SWITCH;
|
event.type = UiEventType::KNX_SWITCH;
|
||||||
event.groupAddr = groupAddr;
|
event.groupAddr = groupAddr;
|
||||||
event.state = value;
|
event.state = value;
|
||||||
|
cacheKnxSwitch(groupAddr, value);
|
||||||
enqueueUiEvent(event);
|
enqueueUiEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,6 +801,7 @@ void WidgetManager::onKnxText(uint16_t groupAddr, const char* text) {
|
|||||||
} else {
|
} else {
|
||||||
event.text[0] = '\0';
|
event.text[0] = '\0';
|
||||||
}
|
}
|
||||||
|
cacheKnxText(groupAddr, event.text);
|
||||||
enqueueUiEvent(event);
|
enqueueUiEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,6 +838,41 @@ void WidgetManager::processUiQueue() {
|
|||||||
esp_lv_adapter_unlock();
|
esp_lv_adapter_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WidgetManager::applyCachedValuesToWidgets() {
|
||||||
|
for (auto& widget : widgets_) {
|
||||||
|
if (!widget) continue;
|
||||||
|
|
||||||
|
uint16_t addr = widget->getKnxAddress();
|
||||||
|
if (addr == 0) continue;
|
||||||
|
|
||||||
|
TextSource source = widget->getTextSource();
|
||||||
|
if (source == TextSource::STATIC) continue;
|
||||||
|
|
||||||
|
if (source == TextSource::KNX_DPT_SWITCH) {
|
||||||
|
bool state = false;
|
||||||
|
if (getCachedKnxSwitch(addr, &state)) {
|
||||||
|
widget->onKnxSwitch(state);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == TextSource::KNX_DPT_TEXT) {
|
||||||
|
char text[MAX_TEXT_LEN] = {};
|
||||||
|
if (getCachedKnxText(addr, text, sizeof(text))) {
|
||||||
|
widget->onKnxText(text);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNumericTextSource(source)) {
|
||||||
|
float value = 0.0f;
|
||||||
|
if (getCachedKnxValue(addr, source, &value)) {
|
||||||
|
widget->onKnxValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WidgetManager::applyKnxValue(uint16_t groupAddr, float value, TextSource source) {
|
void WidgetManager::applyKnxValue(uint16_t groupAddr, float value, TextSource source) {
|
||||||
for (auto& widget : widgets_) {
|
for (auto& widget : widgets_) {
|
||||||
if (widget && widget->getKnxAddress() == groupAddr &&
|
if (widget && widget->getKnxAddress() == groupAddr &&
|
||||||
@ -843,6 +898,161 @@ void WidgetManager::applyKnxText(uint16_t groupAddr, const char* text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WidgetManager::cacheKnxValue(uint16_t groupAddr, TextSource source, float value) {
|
||||||
|
if (groupAddr == 0) return;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
|
||||||
|
size_t freeIndex = KNX_CACHE_SIZE;
|
||||||
|
for (size_t i = 0; i < KNX_CACHE_SIZE; ++i) {
|
||||||
|
auto& entry = knxNumericCache_[i];
|
||||||
|
if (entry.valid) {
|
||||||
|
if (entry.groupAddr == groupAddr && entry.source == source) {
|
||||||
|
entry.value = value;
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (freeIndex == KNX_CACHE_SIZE) {
|
||||||
|
freeIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = freeIndex;
|
||||||
|
if (index == KNX_CACHE_SIZE) {
|
||||||
|
index = knxNumericCacheNext_;
|
||||||
|
knxNumericCacheNext_ = (knxNumericCacheNext_ + 1) % KNX_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
auto& entry = knxNumericCache_[index];
|
||||||
|
entry.groupAddr = groupAddr;
|
||||||
|
entry.source = source;
|
||||||
|
entry.value = value;
|
||||||
|
entry.valid = true;
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetManager::cacheKnxSwitch(uint16_t groupAddr, bool value) {
|
||||||
|
if (groupAddr == 0) return;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
|
||||||
|
size_t freeIndex = KNX_CACHE_SIZE;
|
||||||
|
for (size_t i = 0; i < KNX_CACHE_SIZE; ++i) {
|
||||||
|
auto& entry = knxSwitchCache_[i];
|
||||||
|
if (entry.valid) {
|
||||||
|
if (entry.groupAddr == groupAddr) {
|
||||||
|
entry.value = value;
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (freeIndex == KNX_CACHE_SIZE) {
|
||||||
|
freeIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = freeIndex;
|
||||||
|
if (index == KNX_CACHE_SIZE) {
|
||||||
|
index = knxSwitchCacheNext_;
|
||||||
|
knxSwitchCacheNext_ = (knxSwitchCacheNext_ + 1) % KNX_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
auto& entry = knxSwitchCache_[index];
|
||||||
|
entry.groupAddr = groupAddr;
|
||||||
|
entry.value = value;
|
||||||
|
entry.valid = true;
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetManager::cacheKnxText(uint16_t groupAddr, const char* text) {
|
||||||
|
if (groupAddr == 0) return;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
|
||||||
|
size_t freeIndex = KNX_CACHE_SIZE;
|
||||||
|
for (size_t i = 0; i < KNX_CACHE_SIZE; ++i) {
|
||||||
|
auto& entry = knxTextCache_[i];
|
||||||
|
if (entry.valid) {
|
||||||
|
if (entry.groupAddr == groupAddr) {
|
||||||
|
if (text) {
|
||||||
|
strncpy(entry.text, text, MAX_TEXT_LEN - 1);
|
||||||
|
entry.text[MAX_TEXT_LEN - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
entry.text[0] = '\0';
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (freeIndex == KNX_CACHE_SIZE) {
|
||||||
|
freeIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = freeIndex;
|
||||||
|
if (index == KNX_CACHE_SIZE) {
|
||||||
|
index = knxTextCacheNext_;
|
||||||
|
knxTextCacheNext_ = (knxTextCacheNext_ + 1) % KNX_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
auto& entry = knxTextCache_[index];
|
||||||
|
entry.groupAddr = groupAddr;
|
||||||
|
if (text) {
|
||||||
|
strncpy(entry.text, text, MAX_TEXT_LEN - 1);
|
||||||
|
entry.text[MAX_TEXT_LEN - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
entry.text[0] = '\0';
|
||||||
|
}
|
||||||
|
entry.valid = true;
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetManager::getCachedKnxValue(uint16_t groupAddr, TextSource source, float* out) const {
|
||||||
|
if (groupAddr == 0 || out == nullptr) return false;
|
||||||
|
bool found = false;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
for (const auto& entry : knxNumericCache_) {
|
||||||
|
if (entry.valid && entry.groupAddr == groupAddr && entry.source == source) {
|
||||||
|
*out = entry.value;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetManager::getCachedKnxSwitch(uint16_t groupAddr, bool* out) const {
|
||||||
|
if (groupAddr == 0 || out == nullptr) return false;
|
||||||
|
bool found = false;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
for (const auto& entry : knxSwitchCache_) {
|
||||||
|
if (entry.valid && entry.groupAddr == groupAddr) {
|
||||||
|
*out = entry.value;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetManager::getCachedKnxText(uint16_t groupAddr, char* out, size_t outSize) const {
|
||||||
|
if (groupAddr == 0 || out == nullptr || outSize == 0) return false;
|
||||||
|
bool found = false;
|
||||||
|
portENTER_CRITICAL(&knxCacheMux_);
|
||||||
|
for (const auto& entry : knxTextCache_) {
|
||||||
|
if (entry.valid && entry.groupAddr == groupAddr) {
|
||||||
|
strncpy(out, entry.text, outSize - 1);
|
||||||
|
out[outSize - 1] = '\0';
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&knxCacheMux_);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidgetManager::isNumericTextSource(TextSource source) {
|
||||||
|
return source == TextSource::KNX_DPT_TEMP ||
|
||||||
|
source == TextSource::KNX_DPT_PERCENT ||
|
||||||
|
source == TextSource::KNX_DPT_POWER ||
|
||||||
|
source == TextSource::KNX_DPT_ENERGY ||
|
||||||
|
source == TextSource::KNX_DPT_DECIMALFACTOR;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to parse hex color string
|
// Helper function to parse hex color string
|
||||||
static uint32_t parseHexColor(const char* colorStr) {
|
static uint32_t parseHexColor(const char* colorStr) {
|
||||||
if (!colorStr || colorStr[0] != '#') return 0;
|
if (!colorStr || colorStr[0] != '#') return 0;
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/portmacro.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -76,15 +77,44 @@ private:
|
|||||||
char text[UI_EVENT_TEXT_LEN];
|
char text[UI_EVENT_TEXT_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr size_t KNX_CACHE_SIZE = MAX_WIDGETS * MAX_SCREENS;
|
||||||
|
|
||||||
|
struct KnxNumericCacheEntry {
|
||||||
|
uint16_t groupAddr = 0;
|
||||||
|
TextSource source = TextSource::STATIC;
|
||||||
|
float value = 0.0f;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KnxSwitchCacheEntry {
|
||||||
|
uint16_t groupAddr = 0;
|
||||||
|
bool value = false;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KnxTextCacheEntry {
|
||||||
|
uint16_t groupAddr = 0;
|
||||||
|
char text[MAX_TEXT_LEN] = {};
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
void loadFromSdCard();
|
void loadFromSdCard();
|
||||||
void saveToSdCard();
|
void saveToSdCard();
|
||||||
void destroyAllWidgets();
|
void destroyAllWidgets();
|
||||||
void createAllWidgets(const ScreenConfig& screen, lv_obj_t* parent);
|
void createAllWidgets(const ScreenConfig& screen, lv_obj_t* parent);
|
||||||
bool enqueueUiEvent(const UiEvent& event);
|
bool enqueueUiEvent(const UiEvent& event);
|
||||||
void processUiQueue();
|
void processUiQueue();
|
||||||
|
void applyCachedValuesToWidgets();
|
||||||
void applyKnxValue(uint16_t groupAddr, float value, TextSource source);
|
void applyKnxValue(uint16_t groupAddr, float value, TextSource source);
|
||||||
void applyKnxSwitch(uint16_t groupAddr, bool value);
|
void applyKnxSwitch(uint16_t groupAddr, bool value);
|
||||||
void applyKnxText(uint16_t groupAddr, const char* text);
|
void applyKnxText(uint16_t groupAddr, const char* text);
|
||||||
|
void cacheKnxValue(uint16_t groupAddr, TextSource source, float value);
|
||||||
|
void cacheKnxSwitch(uint16_t groupAddr, bool value);
|
||||||
|
void cacheKnxText(uint16_t groupAddr, const char* text);
|
||||||
|
bool getCachedKnxValue(uint16_t groupAddr, TextSource source, float* out) const;
|
||||||
|
bool getCachedKnxSwitch(uint16_t groupAddr, bool* out) const;
|
||||||
|
bool getCachedKnxText(uint16_t groupAddr, char* out, size_t outSize) const;
|
||||||
|
static bool isNumericTextSource(TextSource source);
|
||||||
|
|
||||||
void createDefaultConfig();
|
void createDefaultConfig();
|
||||||
void applyScreen(uint8_t screenId);
|
void applyScreen(uint8_t screenId);
|
||||||
@ -118,4 +148,12 @@ private:
|
|||||||
lv_obj_t* modalContainer_ = nullptr;
|
lv_obj_t* modalContainer_ = nullptr;
|
||||||
lv_obj_t* modalDimmer_ = nullptr;
|
lv_obj_t* modalDimmer_ = nullptr;
|
||||||
QueueHandle_t uiQueue_ = nullptr;
|
QueueHandle_t uiQueue_ = nullptr;
|
||||||
|
|
||||||
|
std::array<KnxNumericCacheEntry, KNX_CACHE_SIZE> knxNumericCache_ = {};
|
||||||
|
std::array<KnxSwitchCacheEntry, KNX_CACHE_SIZE> knxSwitchCache_ = {};
|
||||||
|
std::array<KnxTextCacheEntry, KNX_CACHE_SIZE> knxTextCache_ = {};
|
||||||
|
size_t knxNumericCacheNext_ = 0;
|
||||||
|
size_t knxSwitchCacheNext_ = 0;
|
||||||
|
size_t knxTextCacheNext_ = 0;
|
||||||
|
mutable portMUX_TYPE knxCacheMux_ = {};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "LabelWidget.hpp"
|
#include "LabelWidget.hpp"
|
||||||
#include "../Fonts.hpp"
|
#include "../Fonts.hpp"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
LabelWidget::LabelWidget(const WidgetConfig& config)
|
LabelWidget::LabelWidget(const WidgetConfig& config)
|
||||||
: Widget(config)
|
: Widget(config)
|
||||||
@ -22,6 +23,13 @@ static lv_flex_align_t toFlexAlign(uint8_t align) {
|
|||||||
return LV_FLEX_ALIGN_CENTER;
|
return LV_FLEX_ALIGN_CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_label_text_if_changed(lv_obj_t* label, const char* text) {
|
||||||
|
if (!label || !text) return;
|
||||||
|
const char* current = lv_label_get_text(label);
|
||||||
|
if (current && strcmp(current, text) == 0) return;
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -203,7 +211,7 @@ void LabelWidget::onKnxValue(float value) {
|
|||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), config_.text, value);
|
snprintf(buf, sizeof(buf), config_.text, value);
|
||||||
}
|
}
|
||||||
lv_label_set_text(label, buf);
|
set_label_text_if_changed(label, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LabelWidget::onKnxSwitch(bool value) {
|
void LabelWidget::onKnxSwitch(bool value) {
|
||||||
@ -211,7 +219,7 @@ void LabelWidget::onKnxSwitch(bool value) {
|
|||||||
if (label == nullptr) return;
|
if (label == nullptr) return;
|
||||||
if (config_.textSource != TextSource::KNX_DPT_SWITCH) return;
|
if (config_.textSource != TextSource::KNX_DPT_SWITCH) return;
|
||||||
|
|
||||||
lv_label_set_text(label, value ? "EIN" : "AUS");
|
set_label_text_if_changed(label, value ? "EIN" : "AUS");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LabelWidget::onKnxText(const char* text) {
|
void LabelWidget::onKnxText(const char* text) {
|
||||||
@ -219,5 +227,5 @@ void LabelWidget::onKnxText(const char* text) {
|
|||||||
if (label == nullptr) return;
|
if (label == nullptr) return;
|
||||||
if (config_.textSource != TextSource::KNX_DPT_TEXT) return;
|
if (config_.textSource != TextSource::KNX_DPT_TEXT) return;
|
||||||
|
|
||||||
lv_label_set_text(label, text);
|
set_label_text_if_changed(label, text);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -216,12 +216,18 @@ void PowerLinkWidget::dotAnimExec(void* var, int32_t value) {
|
|||||||
|
|
||||||
void PowerLinkWidget::updateAnimation(float speed, bool reverse) {
|
void PowerLinkWidget::updateAnimation(float speed, bool reverse) {
|
||||||
if (dot_ == nullptr || obj_ == nullptr) return;
|
if (dot_ == nullptr || obj_ == nullptr) return;
|
||||||
|
bool shouldHide = (speed <= 0.01f || pathLen_ <= 0.5f);
|
||||||
|
bool isHidden = lv_obj_has_flag(dot_, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
if (std::fabs(speed - speed_) < 0.01f && reverse == reverse_ && isHidden == shouldHide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
speed_ = speed;
|
speed_ = speed;
|
||||||
reverse_ = reverse;
|
reverse_ = reverse;
|
||||||
|
|
||||||
lv_anim_del(this, nullptr);
|
lv_anim_del(this, nullptr);
|
||||||
|
|
||||||
if (speed_ <= 0.01f || pathLen_ <= 0.5f) {
|
if (shouldHide) {
|
||||||
lv_obj_add_flag(dot_, LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(dot_, LV_OBJ_FLAG_HIDDEN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,13 @@ 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) {
|
||||||
|
if (!label || !text) return;
|
||||||
|
const char* current = lv_label_get_text(label);
|
||||||
|
if (current && strcmp(current, text) == 0) return;
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
}
|
||||||
|
|
||||||
int PowerNodeWidget::encodeUtf8(uint32_t codepoint, char* buf) {
|
int PowerNodeWidget::encodeUtf8(uint32_t codepoint, char* buf) {
|
||||||
if (codepoint < 0x80) {
|
if (codepoint < 0x80) {
|
||||||
buf[0] = static_cast<char>(codepoint);
|
buf[0] = static_cast<char>(codepoint);
|
||||||
@ -152,7 +159,7 @@ void PowerNodeWidget::applyStyle() {
|
|||||||
|
|
||||||
void PowerNodeWidget::updateValueText(const char* text) {
|
void PowerNodeWidget::updateValueText(const char* text) {
|
||||||
if (valueLabel_ == nullptr || text == nullptr) return;
|
if (valueLabel_ == nullptr || text == nullptr) return;
|
||||||
lv_label_set_text(valueLabel_, text);
|
set_label_text_if_changed(valueLabel_, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerNodeWidget::onKnxValue(float value) {
|
void PowerNodeWidget::onKnxValue(float value) {
|
||||||
|
|||||||
@ -10,6 +10,9 @@ CONFIG_LV_FONT_MONTSERRAT_48=y
|
|||||||
# Keep LVGL draw thread stack reasonable to avoid xTaskCreate failures
|
# Keep LVGL draw thread stack reasonable to avoid xTaskCreate failures
|
||||||
CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768
|
CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768
|
||||||
|
|
||||||
|
# Increase LVGL heap to avoid draw task OOM with dynamic UI
|
||||||
|
CONFIG_LV_MEM_SIZE_KILOBYTES=128
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user