#include "ClockWidget.hpp" #include #ifndef PI #define PI 3.14159265358979323846f #endif ClockWidget::ClockWidget(const WidgetConfig& config) : Widget(config) { } lv_obj_t* ClockWidget::create(lv_obj_t* parent) { obj_ = lv_obj_create(parent); if (!obj_) return nullptr; lv_obj_remove_style_all(obj_); lv_obj_set_pos(obj_, config_.x, config_.y); lv_obj_set_size(obj_, config_.width > 0 ? config_.width : 200, config_.height > 0 ? config_.height : 200); lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE); // Create hands hourHand_ = lv_line_create(obj_); minuteHand_ = lv_line_create(obj_); secondHand_ = lv_line_create(obj_); centerDot_ = lv_obj_create(obj_); // Style center dot lv_obj_remove_style_all(centerDot_); lv_obj_set_size(centerDot_, 8, 8); lv_obj_set_style_radius(centerDot_, LV_RADIUS_CIRCLE, 0); lv_obj_center(centerDot_); // Initial update to set positions (will be updated by loop) time_t now; time(&now); struct tm t; localtime_r(&now, &t); updateHands(t); return obj_; } void ClockWidget::applyStyle() { if (!obj_) return; // Face style (background) if (config_.bgOpacity > 0) { lv_obj_set_style_bg_color(obj_, lv_color_make( config_.bgColor.r, config_.bgColor.g, config_.bgColor.b), 0); lv_obj_set_style_bg_opa(obj_, config_.bgOpacity, 0); } if (config_.borderRadius > 0) { lv_obj_set_style_radius(obj_, config_.borderRadius, 0); // Usually 50% for circle } else { lv_obj_set_style_radius(obj_, LV_RADIUS_CIRCLE, 0); } lv_obj_set_style_border_width(obj_, 2, 0); lv_obj_set_style_border_color(obj_, lv_color_make( config_.textColor.r, config_.textColor.g, config_.textColor.b), 0); // Hand colors lv_color_t handColor = lv_color_make( config_.textColor.r, config_.textColor.g, config_.textColor.b); lv_color_t secColor = lv_color_make(200, 50, 50); // Red for second hand if (hourHand_) { lv_obj_set_style_line_width(hourHand_, 6, 0); lv_obj_set_style_line_color(hourHand_, handColor, 0); lv_obj_set_style_line_rounded(hourHand_, true, 0); } if (minuteHand_) { lv_obj_set_style_line_width(minuteHand_, 4, 0); lv_obj_set_style_line_color(minuteHand_, handColor, 0); lv_obj_set_style_line_rounded(minuteHand_, true, 0); } if (secondHand_) { lv_obj_set_style_line_width(secondHand_, 2, 0); lv_obj_set_style_line_color(secondHand_, secColor, 0); lv_obj_set_style_line_rounded(secondHand_, true, 0); } if (centerDot_) { lv_obj_set_style_bg_color(centerDot_, handColor, 0); } // Force redraw of hands with new style/dimensions lastSecond_ = -1; time_t now; time(&now); struct tm t; localtime_r(&now, &t); updateHands(t); } void ClockWidget::updateHands(const struct tm& t) { if (t.tm_sec == lastSecond_ && t.tm_min == lastMinute_ && t.tm_hour == lastHour_) return; lastSecond_ = t.tm_sec; lastMinute_ = t.tm_min; lastHour_ = t.tm_hour; int32_t w = lv_obj_get_width(obj_); int32_t h = lv_obj_get_height(obj_); int32_t cx = w / 2; int32_t cy = h / 2; int32_t radius = (w < h ? w : h) / 2; // Lengths int32_t lenSec = radius - 10; int32_t lenMin = radius - 20; int32_t lenHour = radius * 0.6f; // Angles (0 is top, clockwise) // Second: 6 deg per sec float angleSec = t.tm_sec * 6.0f; // Minute: 6 deg per min + 0.1 deg per sec float angleMin = t.tm_min * 6.0f + t.tm_sec * 0.1f; // Hour: 30 deg per hour + 0.5 deg per min float angleHour = (t.tm_hour % 12) * 30.0f + t.tm_min * 0.5f; auto calcPoints = [&](float angle, int32_t len, lv_point_precise_t* p) { float rad = (angle - 90.0f) * PI / 180.0f; p[0].x = cx; p[0].y = cy; p[1].x = cx + static_cast(cos(rad) * len); p[1].y = cy + static_cast(sin(rad) * len); }; if (secondHand_) { calcPoints(angleSec, lenSec, secondPoints_); lv_line_set_points(secondHand_, secondPoints_, 2); } if (minuteHand_) { calcPoints(angleMin, lenMin, minutePoints_); lv_line_set_points(minuteHand_, minutePoints_, 2); } if (hourHand_) { calcPoints(angleHour, lenHour, hourPoints_); lv_line_set_points(hourHand_, hourPoints_, 2); } } void ClockWidget::onKnxTime(const struct tm& value, TextSource source) { // Only accept system time updates or explicitly configured KNX time sources if we supported them // For now, ClockWidget listens to SYSTEM_TIME via WidgetManager broadcast updateHands(value); }