#pragma once #include #include #include // Maximum number of widgets static constexpr size_t MAX_WIDGETS = 64; static constexpr size_t MAX_SCREENS = 8; static constexpr size_t MAX_TEXT_LEN = 32; static constexpr size_t MAX_SCREEN_NAME_LEN = 24; static constexpr size_t MAX_BG_IMAGE_PATH_LEN = 48; static constexpr size_t CHART_MAX_SERIES = 3; static constexpr size_t MAX_CONDITIONS = 3; static constexpr size_t MAX_FORMAT_LEN = 16; // Shorter format strings for left/right values static constexpr size_t MAX_SUBBUTTONS = 6; // Sub-buttons for RoomCard enum class WidgetType : uint8_t { LABEL = 0, BUTTON = 1, LED = 2, ICON = 3, TABVIEW = 4, TABPAGE = 5, POWERFLOW = 6, POWERNODE = 7, POWERLINK = 8, CHART = 9, CLOCK = 10, ROOMCARD = 11, }; enum class IconPosition : uint8_t { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3, }; enum class ScreenMode : uint8_t { FULLSCREEN = 0, MODAL = 1, }; enum class BgImageMode : uint8_t { NONE = 0, STRETCH = 1, CENTER = 2, TILE = 3, }; enum class ButtonAction : uint8_t { KNX = 0, JUMP = 1, BACK = 2, }; enum class ChartPeriod : uint8_t { HOUR_1 = 0, HOUR_3 = 1, HOUR_5 = 2, HOUR_12 = 3, HOUR_24 = 4, MONTH_1 = 5, }; // Text source: static text or KNX group address enum class TextSource : uint8_t { STATIC = 0, // Static text KNX_DPT_TEMP = 1, // KNX Temperature (DPT 9.001) KNX_DPT_SWITCH = 2, // KNX Switch (DPT 1.001) KNX_DPT_PERCENT = 3, // KNX Percent (DPT 5.001) KNX_DPT_TEXT = 4, // KNX Text (DPT 16.000) KNX_DPT_POWER = 5, // KNX Power (DPT 14.056) KNX_DPT_ENERGY = 6, // KNX Energy (DPT 13.013) KNX_DPT_DECIMALFACTOR = 7, // KNX Decimal Factor (DPT 5.005) KNX_DPT_TIME = 8, // KNX Time of day (DPT 10.001) KNX_DPT_DATE = 9, // KNX Date (DPT 11.001) KNX_DPT_DATETIME = 10, // KNX DateTime (DPT 19.001) SYSTEM_TIME = 11, // System Time (RTC) SYSTEM_DATE = 12, // System Date (RTC) SYSTEM_DATETIME = 13, // System DateTime (RTC) }; enum class TextAlign : uint8_t { LEFT = 0, CENTER = 1, RIGHT = 2, }; // Color as RGB888 struct Color { uint8_t r, g, b; uint32_t toLvColor() const { return (r << 16) | (g << 8) | b; } static Color fromHex(uint32_t hex) { return { .r = static_cast((hex >> 16) & 0xFF), .g = static_cast((hex >> 8) & 0xFF), .b = static_cast(hex & 0xFF) }; } }; // Condition operator for conditional styling enum class ConditionOp : uint8_t { LESS = 0, // value < threshold LESS_EQUAL = 1, // value <= threshold EQUAL = 2, // value == threshold GREATER_EQUAL = 3, // value >= threshold GREATER = 4, // value > threshold NOT_EQUAL = 5, // value != threshold }; // Which KNX address to use for condition evaluation enum class ConditionSource : uint8_t { PRIMARY = 0, // knxAddress (bottom value) SECONDARY = 1, // knxAddress2 (left value) TERTIARY = 2, // knxAddress3 (right value) }; // Style to apply when condition is met struct ConditionStyle { uint32_t iconCodepoint; // 0 = don't change icon Color textColor; Color bgColor; uint8_t bgOpacity; uint8_t flags; // Bits: 0=useTextColor, 1=useBgColor, 2=useBgOpacity, 3=hide static constexpr uint8_t FLAG_USE_TEXT_COLOR = 0x01; static constexpr uint8_t FLAG_USE_BG_COLOR = 0x02; static constexpr uint8_t FLAG_USE_BG_OPACITY = 0x04; static constexpr uint8_t FLAG_HIDE = 0x08; }; // A single style condition struct StyleCondition { float threshold; // 4 bytes ConditionOp op; // 1 byte ConditionSource source; // 1 byte - which value to check uint8_t priority; // 1 byte (lower = higher priority) ConditionStyle style; // 12 bytes bool enabled; // 1 byte // Total: 20 bytes per condition }; // Sub-button position around RoomCard bubble (8 positions, 45° apart) enum class SubButtonPosition : uint8_t { TOP = 0, TOP_RIGHT = 1, RIGHT = 2, BOTTOM_RIGHT = 3, BOTTOM = 4, BOTTOM_LEFT = 5, LEFT = 6, TOP_LEFT = 7, }; // Sub-button action type enum class SubButtonAction : uint8_t { TOGGLE_KNX = 0, // Toggle KNX switch NAVIGATE = 1, // Navigate to screen }; // Sub-button configuration for RoomCard (20 bytes) struct SubButtonConfig { uint32_t iconCodepoint; // 4 bytes - Icon codepoint 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 Color colorOff; // 3 bytes - Color when OFF SubButtonPosition position; // 1 byte - Position around bubble SubButtonAction action; // 1 byte - Action type 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 }; // Shadow configuration struct ShadowConfig { int8_t offsetX; int8_t offsetY; uint8_t blur; uint8_t spread; Color color; bool enabled; }; // Widget configuration struct WidgetConfig { // Basic properties uint8_t id; // Unique ID (0-255) WidgetType type; int16_t x, y; int16_t width, height; bool visible; // Text properties TextSource textSource; char text[MAX_TEXT_LEN]; // Static text or format string uint16_t knxAddress; // KNX group address (GA) for read binding uint8_t fontSize; // Font size index (0=14, 1=18, 2=22, 3=28, 4=36, 5=48) uint8_t textAlign; // TextAlign: 0=left, 1=center, 2=right bool isContainer; // For buttons: use as container (no internal label/icon) // Colors Color textColor; Color bgColor; uint8_t bgOpacity; // 0-255 uint8_t borderRadius; // Shadow ShadowConfig shadow; // Button specific bool isToggle; // For buttons: toggle mode uint16_t knxAddressWrite; // KNX group address (GA) to write on click ButtonAction action; // Button action (KNX, Jump, Back) uint8_t targetScreen; // Target screen ID for jump // Icon properties (for Label, Button, Icon widgets) uint32_t iconCodepoint; // Unicode codepoint (0 = no icon) uint8_t iconPosition; // IconPosition: 0=left, 1=right, 2=top, 3=bottom uint8_t iconSize; // Font size index (0-5), same as fontSize int8_t iconGap; // Gap between icon and text (px) // Hierarchy int8_t parentId; // ID of parent widget (-1 = root/screen) // Chart properties uint8_t chartPeriod; uint8_t chartSeriesCount; uint16_t chartKnxAddress[CHART_MAX_SERIES]; TextSource chartTextSource[CHART_MAX_SERIES]; Color chartSeriesColor[CHART_MAX_SERIES]; // Secondary KNX address (for PowerNode LEFT value) uint16_t knxAddress2; TextSource textSource2; char text2[MAX_FORMAT_LEN]; // Format string for left value (short) // Tertiary KNX address (for PowerNode RIGHT value) uint16_t knxAddress3; TextSource textSource3; char text3[MAX_FORMAT_LEN]; // Format string for right value (short) // Conditional styling uint8_t conditionCount; StyleCondition conditions[MAX_CONDITIONS]; // RoomCard sub-buttons uint8_t subButtonCount; uint8_t subButtonSize; // Sub-button size in pixels (default 40) uint8_t subButtonDistance; // Distance from center in pixels (default 80) SubButtonConfig subButtons[MAX_SUBBUTTONS]; // Serialization size (fixed for NVS storage) // 197 + 1 (subButtonCount) + 1 (subButtonSize) + 1 (subButtonDistance) + 120 (6 subButtons * 20) = 320 static constexpr size_t SERIALIZED_SIZE = 320; void serialize(uint8_t* buf) const; void deserialize(const uint8_t* buf); // Create default label static WidgetConfig createLabel(uint8_t id, int16_t x, int16_t y, const char* text); // Create KNX-bound label static WidgetConfig createKnxLabel(uint8_t id, int16_t x, int16_t y, TextSource source, uint16_t knxAddr, const char* format); // Create button static WidgetConfig createButton(uint8_t id, int16_t x, int16_t y, const char* text, uint16_t knxAddrWrite, bool toggle); }; // Screen configuration (holds all widgets) struct ScreenConfig { uint8_t id; char name[MAX_SCREEN_NAME_LEN]; ScreenMode mode; Color backgroundColor; char bgImagePath[MAX_BG_IMAGE_PATH_LEN]; // Background image path (e.g., "/images/bg.png") BgImageMode bgImageMode; // 0=none, 1=stretch, 2=center, 3=tile uint8_t widgetCount; WidgetConfig widgets[MAX_WIDGETS]; // Modal-specific properties (only used when mode == MODAL) int16_t modalX; // Modal position X (0 = centered) int16_t modalY; // Modal position Y (0 = centered) int16_t modalWidth; // Modal width (0 = auto from content) int16_t modalHeight; // Modal height (0 = auto from content) uint8_t modalBorderRadius; bool modalDimBackground; // Dim the background behind modal void clear(uint8_t newId = 0, const char* newName = nullptr); int addWidget(const WidgetConfig& widget); // Returns widget ID or -1 bool removeWidget(uint8_t id); WidgetConfig* findWidget(uint8_t id); const WidgetConfig* findWidget(uint8_t id) const; }; struct GuiConfig { uint8_t screenCount; ScreenConfig screens[MAX_SCREENS]; uint8_t startScreenId; bool standbyEnabled; uint8_t standbyScreenId; uint16_t standbyMinutes; uint16_t knxTimeAddress; uint16_t knxDateAddress; uint16_t knxDateTimeAddress; uint16_t knxNightModeAddress; void clear(); ScreenConfig* findScreen(uint8_t id); const ScreenConfig* findScreen(uint8_t id) const; };