diff --git a/firmware/components/connectivity-manager/src/wifi_manager.c b/firmware/components/connectivity-manager/src/wifi_manager.c index 037fc9b..b8a48ca 100644 --- a/firmware/components/connectivity-manager/src/wifi_manager.c +++ b/firmware/components/connectivity-manager/src/wifi_manager.c @@ -29,6 +29,18 @@ static EventGroupHandle_t s_wifi_event_group; static const char *TAG = "wifi_manager"; +static void led_status_reconnect() +{ + led_behavior_t led_behavior = { + .on_time_ms = 250, + .off_time_ms = 100, + .color = {.red = 50, .green = 50, .blue = 0}, + .index = 0, + .mode = LED_MODE_BLINK, + }; + led_status_set_behavior(led_behavior); +} + static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) @@ -40,6 +52,8 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e { ESP_LOGW(TAG, "WIFI_EVENT_STA_DISCONNECTED: Verbindung verloren, versuche erneut..."); xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + led_status_reconnect(); + esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { @@ -49,8 +63,10 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) { - ESP_LOGW(TAG, "IP_EVENT_STA_LOST_IP: IP-Adresse verloren!"); + ESP_LOGW(TAG, "IP_EVENT_STA_LOST_IP: IP-Adresse verloren! Versuche Reconnect..."); xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + led_status_reconnect(); + esp_wifi_connect(); } } @@ -105,14 +121,7 @@ void wifi_manager_init() if (have_ssid && have_password) { - led_behavior_t led_behavior = { - .on_time_ms = 250, - .off_time_ms = 100, - .color = {.red = 50, .green = 50, .blue = 0}, - .index = 0, - .mode = LED_MODE_BLINK, - }; - led_status_set_behavior(led_behavior); + led_status_reconnect(); ESP_LOGI(TAG, "Found WiFi configuration: SSID='%s'", ssid); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); diff --git a/firmware/components/insa/include/common/Menu.h b/firmware/components/insa/include/common/Menu.h index a9b992a..a51f57d 100644 --- a/firmware/components/insa/include/common/Menu.h +++ b/firmware/components/insa/include/common/Menu.h @@ -175,6 +175,21 @@ class Menu : public Widget */ void toggle(const MenuItem &menuItem); + /** + * @brief Setzt den Zustand eines Toggle-Menüeintrags explizit + * @param menuItem Der zu ändernde Toggle-Menüeintrag + * @param state Neuer Zustand (true = aktiviert, false = deaktiviert) + * + * @pre menuItem muss vom Typ TOGGLE sein + * @post Der Wert des Menüeintrags wird auf den angegebenen Zustand gesetzt + * + * @details Diese Methode setzt den Wert eines Toggle-Menüeintrags gezielt auf den gewünschten Zustand. + * Der geänderte Eintrag ersetzt das Original in der internen Menüstruktur. + * + * @note Diese Methode verändert direkt den internen Zustand des Menüs. + */ + void setToggle(const MenuItem &menuItem, const bool state); + /** * @brief Changes the selected value of a selection menu item based on button input * @param menuItem The selection menu item to modify diff --git a/firmware/components/insa/include/common/Widget.h b/firmware/components/insa/include/common/Widget.h index 603c6b4..a6d03ca 100644 --- a/firmware/components/insa/include/common/Widget.h +++ b/firmware/components/insa/include/common/Widget.h @@ -13,6 +13,7 @@ #include "u8g2.h" #include "common/Common.h" +#include "message_manager.h" /** * @class Widget @@ -49,7 +50,9 @@ class Widget * @details Ensures that derived class destructors are called correctly when * a widget is destroyed through a base class pointer. */ - virtual ~Widget() = default; + virtual ~Widget(); + + virtual void onMessageReceived(const message_t *msg); /** * @brief Called when the widget becomes active or enters the foreground @@ -178,4 +181,8 @@ class Widget * the u8g2 context and assumes it is managed externally. */ u8g2_t *u8g2; + + private: + static std::vector s_instances; + static void globalMessageCallback(const message_t *msg); }; \ No newline at end of file diff --git a/firmware/components/insa/include/ui/LightMenu.h b/firmware/components/insa/include/ui/LightMenu.h index 10a4d43..d393466 100644 --- a/firmware/components/insa/include/ui/LightMenu.h +++ b/firmware/components/insa/include/ui/LightMenu.h @@ -120,6 +120,8 @@ class LightMenu final : public Menu */ void onButtonPressed(const MenuItem &menuItem, ButtonType button) override; + void onMessageReceived(const message_t *msg); + /** * @brief Pointer to menu options configuration structure * @details Stores a reference to the menu configuration passed during construction. diff --git a/firmware/components/insa/src/common/Menu.cpp b/firmware/components/insa/src/common/Menu.cpp index 0b88cb4..62b8282 100644 --- a/firmware/components/insa/src/common/Menu.cpp +++ b/firmware/components/insa/src/common/Menu.cpp @@ -28,7 +28,7 @@ constexpr int BOTTOM_OFFSET = 10; Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options) { - // Set up button callback using lambda to forward to member function + // Set up button callback using a lambda to forward to the member function m_options->onButtonClicked = [this](const ButtonType button) { OnButtonClicked(button); }; } @@ -82,6 +82,12 @@ void Menu::toggle(const MenuItem &menuItem) replaceItem(menuItem.getId(), item); } +void Menu::setToggle(const MenuItem &menuItem, const bool state) +{ + const auto item = menuItem.copyWith(state ? std::to_string(true) : std::to_string(false)); + replaceItem(menuItem.getId(), item); +} + MenuItem Menu::switchValue(const MenuItem &menuItem, ButtonType button) { MenuItem result = menuItem; @@ -134,13 +140,13 @@ void Menu::Render() m_selected_item = 0; } - // Early return if no items to render + // Early return if there are no items to render if (m_items.empty()) { return; } - // Clear screen with black background + // Clear the screen with a black background u8g2_SetDrawColor(u8g2, 0); u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height); @@ -151,7 +157,7 @@ void Menu::Render() drawScrollBar(); drawSelectionBox(); - // Calculate center position for main item + // Calculate center position for the main item const int centerY = u8g2->height / 2 + 3; // Render the currently selected item (main/center item) @@ -176,7 +182,7 @@ void Menu::Render() void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const { - // Set font and draw main text + // Set font and draw the main text u8g2_SetFont(u8g2, font); u8g2_DrawStr(u8g2, x, y, item->getText().c_str()); @@ -206,7 +212,7 @@ void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, } case MenuItemTypes::TOGGLE: { - // Draw checkbox frame + // Draw the checkbox frame const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN; const int frameY = y - UIConstants::FRAME_OFFSET; u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE); @@ -272,7 +278,7 @@ void Menu::onPressedDown() if (m_items.empty()) return; - // Wrap around to first item when at the end + // Wrap around to the first item when at the end m_selected_item = (m_selected_item + 1) % m_items.size(); } @@ -281,7 +287,7 @@ void Menu::onPressedUp() if (m_items.empty()) return; - // Wrap around to last item when at the beginning + // Wrap around to the last item when at the beginning m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1; } @@ -314,7 +320,7 @@ void Menu::onPressedSelect() const void Menu::onPressedBack() const { - // Navigate back to previous screen if callback is available + // Navigate back to the previous screen if callback is available if (m_options && m_options->popScreen) { m_options->popScreen(); diff --git a/firmware/components/insa/src/common/Widget.cpp b/firmware/components/insa/src/common/Widget.cpp index 16054b0..274efd9 100644 --- a/firmware/components/insa/src/common/Widget.cpp +++ b/firmware/components/insa/src/common/Widget.cpp @@ -1,7 +1,20 @@ #include "common/Widget.h" +#include + +std::vector Widget::s_instances; Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2) { + s_instances.push_back(this); + if (s_instances.size() == 1) + { + message_manager_register_listener(globalMessageCallback); + } +} + +Widget::~Widget() +{ + s_instances.erase(std::remove(s_instances.begin(), s_instances.end(), this), s_instances.end()); } void Widget::onEnter() @@ -36,3 +49,15 @@ const char *Widget::getName() const { return "Widget"; } + +void Widget::onMessageReceived(const message_t *msg) +{ +} + +void Widget::globalMessageCallback(const message_t *msg) +{ + for (auto *w : s_instances) + { + w->onMessageReceived(msg); + } +} diff --git a/firmware/components/insa/src/ui/LightMenu.cpp b/firmware/components/insa/src/ui/LightMenu.cpp index 1ae2885..7dce010 100644 --- a/firmware/components/insa/src/ui/LightMenu.cpp +++ b/firmware/components/insa/src/ui/LightMenu.cpp @@ -1,8 +1,8 @@ #include "ui/LightMenu.h" - #include "led_strip_ws2812.h" #include "message_manager.h" #include "simulator.h" +#include /** * @namespace LightMenuItem @@ -72,7 +72,7 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto { toggle(menuItem); const auto value = getItem(menuItem.getId()).getValue() == "1"; - // Änderung über message_manager posten + // Post change via message_manager message_t msg = {}; msg.type = MESSAGE_TYPE_SETTINGS; msg.data.settings.type = SETTINGS_TYPE_BOOL; @@ -129,4 +129,15 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto } } +void LightMenu::onMessageReceived(const message_t *msg) +{ + // Here you can react to messages, e.g. set toggle status + // Example: If light_active was changed, synchronize toggle + if (msg && msg->type == MESSAGE_TYPE_SETTINGS && msg->data.settings.type == SETTINGS_TYPE_BOOL && + std::strcmp(msg->data.settings.key, "light_active") == 0) + { + setToggle(getItem(LightMenuItem::ACTIVATE), msg->data.settings.value.bool_value); + } +} + IMPLEMENT_GET_NAME(LightMenu)