Files
pi-dashboard/components/dashboard_ui/dashboard_ui.c

483 lines
17 KiB
C
Raw Normal View History

2026-02-15 04:15:30 +09:00
#include "dashboard_ui.h"
2026-02-15 18:13:53 +09:00
#include "user_fonts.h"
2026-02-15 04:15:30 +09:00
#include <stdio.h>
#include <string.h>
/* ---------- Layout constants ---------- */
2026-02-15 18:13:53 +09:00
#define SCREEN_W 400
#define SCREEN_H 300
#define TOP_BAR_H 24
#define TIME_BAR_H 50
#define BOT_H 24
#define MAIN_Y (TOP_BAR_H + TIME_BAR_H) /* 74 */
#define MAIN_H (SCREEN_H - MAIN_Y - BOT_H) /* 202 */
#define LEFT_COL_W 200
#define RIGHT_COL_X 200
2026-02-15 04:15:30 +09:00
/* ---------- Static widget handles ---------- */
/* Top bar */
static lv_obj_t *lbl_ip;
static lv_obj_t *lbl_batt;
2026-02-16 14:39:21 +09:00
static lv_obj_t *lbl_batt_v;
2026-02-15 18:13:53 +09:00
static lv_obj_t *bar_batt_top;
2026-02-15 04:15:30 +09:00
static lv_obj_t *lbl_ws;
2026-02-15 18:13:53 +09:00
/* Time bar */
static lv_obj_t *lbl_clock;
static lv_obj_t *lbl_date;
2026-02-15 04:15:30 +09:00
/* Pi stats bars + labels */
static lv_obj_t *bar_cpu;
static lv_obj_t *lbl_cpu_val;
static lv_obj_t *bar_ram;
static lv_obj_t *lbl_ram_val;
static lv_obj_t *bar_disk;
static lv_obj_t *lbl_disk_val;
static lv_obj_t *lbl_cpu_temp;
/* Services table */
static lv_obj_t *tbl_services;
2026-02-15 18:13:53 +09:00
static int s_service_count;
2026-02-16 20:55:12 +09:00
static int s_dup_rows; /* duplicate rows appended for looping */
static lv_coord_t s_row_h; /* measured row height in px */
2026-02-15 04:15:30 +09:00
2026-02-15 21:46:18 +09:00
/* Local sensors (bottom bar) */
static lv_obj_t *lbl_local;
2026-02-15 04:15:30 +09:00
static lv_obj_t *lbl_uptime;
2026-02-15 21:46:18 +09:00
/* Status image placeholder */
static lv_obj_t *img_status;
2026-02-15 04:15:30 +09:00
/* Network */
static lv_obj_t *lbl_net;
2026-02-15 18:13:53 +09:00
/* Auto-scroll timer */
static lv_timer_t *s_scroll_timer;
/* ---------- Styles ---------- */
2026-02-15 04:15:30 +09:00
static lv_style_t style_bar_bg;
static lv_style_t style_bar_ind;
2026-02-15 18:13:53 +09:00
static lv_style_t style_bar_batt_bg;
static lv_style_t style_bar_batt_ind;
2026-02-15 04:15:30 +09:00
static void init_styles(void)
{
/* Bar background: white with 1px black border */
lv_style_init(&style_bar_bg);
lv_style_set_bg_color(&style_bar_bg, lv_color_white());
lv_style_set_bg_opa(&style_bar_bg, LV_OPA_COVER);
lv_style_set_border_color(&style_bar_bg, lv_color_black());
lv_style_set_border_width(&style_bar_bg, 1);
lv_style_set_radius(&style_bar_bg, 0);
/* Bar indicator: solid black fill */
lv_style_init(&style_bar_ind);
lv_style_set_bg_color(&style_bar_ind, lv_color_black());
lv_style_set_bg_opa(&style_bar_ind, LV_OPA_COVER);
lv_style_set_radius(&style_bar_ind, 0);
2026-02-15 18:13:53 +09:00
/* Battery bar on black top bar: inverted colors */
lv_style_init(&style_bar_batt_bg);
lv_style_set_bg_color(&style_bar_batt_bg, lv_color_black());
lv_style_set_bg_opa(&style_bar_batt_bg, LV_OPA_COVER);
lv_style_set_border_color(&style_bar_batt_bg, lv_color_white());
lv_style_set_border_width(&style_bar_batt_bg, 1);
lv_style_set_radius(&style_bar_batt_bg, 0);
lv_style_init(&style_bar_batt_ind);
lv_style_set_bg_color(&style_bar_batt_ind, lv_color_white());
lv_style_set_bg_opa(&style_bar_batt_ind, LV_OPA_COVER);
lv_style_set_radius(&style_bar_batt_ind, 0);
2026-02-15 04:15:30 +09:00
}
static lv_obj_t *create_bar(lv_obj_t *parent, int x, int y, int w, int h)
{
lv_obj_t *bar = lv_bar_create(parent);
lv_obj_set_pos(bar, x, y);
lv_obj_set_size(bar, w, h);
lv_bar_set_range(bar, 0, 100);
lv_bar_set_value(bar, 0, LV_ANIM_OFF);
lv_obj_add_style(bar, &style_bar_bg, LV_PART_MAIN);
lv_obj_add_style(bar, &style_bar_ind, LV_PART_INDICATOR);
return bar;
}
static lv_obj_t *create_label(lv_obj_t *parent, int x, int y, const lv_font_t *font, const char *text)
{
lv_obj_t *lbl = lv_label_create(parent);
lv_obj_set_pos(lbl, x, y);
lv_obj_set_style_text_font(lbl, font, 0);
lv_obj_set_style_text_color(lbl, lv_color_black(), 0);
lv_label_set_text(lbl, text);
return lbl;
}
2026-02-15 18:13:53 +09:00
/* ---------- Auto-scroll callback ---------- */
static void scroll_timer_cb(lv_timer_t *timer)
{
(void)timer;
2026-02-16 20:55:12 +09:00
if (s_dup_rows <= 0 || s_row_h <= 0) {
/* Too few services or not yet measured — no scrolling */
2026-02-15 18:13:53 +09:00
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
return;
}
lv_coord_t cur_y = lv_obj_get_scroll_y(tbl_services);
2026-02-16 20:55:12 +09:00
lv_coord_t wrap_y = s_service_count * s_row_h;
if (cur_y >= wrap_y) {
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
cur_y = 0;
}
2026-02-16 22:28:26 +09:00
// lv_obj_scroll_to_y(tbl_services, cur_y + s_row_h, LV_ANIM_OFF);
const int delta_y = s_row_h / 8;
lv_obj_scroll_to_y(tbl_services, cur_y + delta_y, LV_ANIM_ON);
2026-02-16 20:55:12 +09:00
}
/* Measure row height, compute visible rows, append duplicates for seamless loop */
static void fill_duplicate_rows(int count)
{
s_dup_rows = 0;
s_row_h = 0;
if (count <= 0) return;
/* Trim table to exactly count rows so measurement is clean */
lv_table_set_row_cnt(tbl_services, count);
lv_obj_update_layout(tbl_services);
lv_coord_t content_h = lv_obj_get_self_height(tbl_services);
lv_coord_t row_h = content_h / count;
if (row_h <= 0) return;
lv_coord_t visible_h = lv_obj_get_content_height(tbl_services);
int visible_rows = (visible_h + row_h - 1) / row_h; /* ceil */
if (count <= visible_rows) return; /* everything fits — no scrolling */
for (int i = 0; i < visible_rows; i++) {
lv_table_set_cell_value(tbl_services, count + i, 0,
lv_table_get_cell_value(tbl_services, i, 0));
lv_table_set_cell_value(tbl_services, count + i, 1,
lv_table_get_cell_value(tbl_services, i, 1));
2026-02-15 18:13:53 +09:00
}
2026-02-16 20:55:12 +09:00
s_dup_rows = visible_rows;
s_row_h = row_h;
2026-02-15 18:13:53 +09:00
}
/* ---------- Create UI sections ---------- */
2026-02-15 04:15:30 +09:00
static void create_top_bar(lv_obj_t *parent)
{
lv_obj_t *bar_cont = lv_obj_create(parent);
lv_obj_set_pos(bar_cont, 0, 0);
lv_obj_set_size(bar_cont, SCREEN_W, TOP_BAR_H);
lv_obj_set_style_bg_color(bar_cont, lv_color_black(), 0);
lv_obj_set_style_bg_opa(bar_cont, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(bar_cont, 0, 0);
lv_obj_set_style_radius(bar_cont, 0, 0);
lv_obj_set_style_pad_all(bar_cont, 2, 0);
lv_obj_clear_flag(bar_cont, LV_OBJ_FLAG_SCROLLABLE);
2026-02-15 18:13:53 +09:00
/* IP address — left */
2026-02-15 04:15:30 +09:00
lbl_ip = lv_label_create(bar_cont);
lv_obj_set_style_text_color(lbl_ip, lv_color_white(), 0);
2026-02-15 18:13:53 +09:00
lv_obj_set_style_text_font(lbl_ip, &InziuIosevka_Slab_CC_12px, 0);
2026-02-15 04:15:30 +09:00
lv_obj_align(lbl_ip, LV_ALIGN_LEFT_MID, 2, 0);
lv_label_set_text(lbl_ip, "N/A");
2026-02-15 18:13:53 +09:00
/* WS status — center-left */
2026-02-15 04:15:30 +09:00
lbl_ws = lv_label_create(bar_cont);
lv_obj_set_style_text_color(lbl_ws, lv_color_white(), 0);
2026-02-15 18:13:53 +09:00
lv_obj_set_style_text_font(lbl_ws, &InziuIosevka_Slab_CC_12px, 0);
lv_obj_align(lbl_ws, LV_ALIGN_LEFT_MID, 140, 0);
2026-02-15 04:15:30 +09:00
lv_label_set_text(lbl_ws, "WS:---");
2026-02-15 18:13:53 +09:00
2026-02-16 14:39:21 +09:00
/* Battery voltage — left of bar */
lbl_batt_v = lv_label_create(bar_cont);
lv_obj_set_style_text_color(lbl_batt_v, lv_color_white(), 0);
lv_obj_set_style_text_font(lbl_batt_v, &InziuIosevka_Slab_CC_12px, 0);
lv_obj_align(lbl_batt_v, LV_ALIGN_RIGHT_MID, -72, 0);
lv_label_set_text(lbl_batt_v, "-.--V");
2026-02-15 18:13:53 +09:00
/* Battery bar (24x10) — right */
bar_batt_top = lv_bar_create(bar_cont);
lv_obj_set_size(bar_batt_top, 24, 10);
lv_obj_align(bar_batt_top, LV_ALIGN_RIGHT_MID, -40, 0);
lv_bar_set_range(bar_batt_top, 0, 100);
lv_bar_set_value(bar_batt_top, 0, LV_ANIM_OFF);
lv_obj_add_style(bar_batt_top, &style_bar_batt_bg, LV_PART_MAIN);
lv_obj_add_style(bar_batt_top, &style_bar_batt_ind, LV_PART_INDICATOR);
2026-02-16 14:39:21 +09:00
/* Battery positive terminal nub */
lv_obj_t *batt_nub = lv_obj_create(bar_cont);
lv_obj_set_size(batt_nub, 2, 4);
2026-02-16 23:33:42 +09:00
lv_obj_align(batt_nub, LV_ALIGN_RIGHT_MID, -38, 0);
2026-02-16 14:39:21 +09:00
lv_obj_set_style_bg_color(batt_nub, lv_color_white(), 0);
lv_obj_set_style_bg_opa(batt_nub, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(batt_nub, 0, 0);
lv_obj_set_style_radius(batt_nub, 0, 0);
lv_obj_set_style_pad_all(batt_nub, 0, 0);
lv_obj_clear_flag(batt_nub, LV_OBJ_FLAG_SCROLLABLE);
2026-02-15 18:13:53 +09:00
/* Battery text — right of bar */
lbl_batt = lv_label_create(bar_cont);
lv_obj_set_style_text_color(lbl_batt, lv_color_white(), 0);
lv_obj_set_style_text_font(lbl_batt, &InziuIosevka_Slab_CC_12px, 0);
lv_obj_align(lbl_batt, LV_ALIGN_RIGHT_MID, -2, 0);
lv_label_set_text(lbl_batt, "--%");
2026-02-15 04:15:30 +09:00
}
2026-02-15 18:13:53 +09:00
static void create_time_bar(lv_obj_t *parent)
2026-02-15 04:15:30 +09:00
{
2026-02-15 18:13:53 +09:00
lv_obj_t *bar_cont = lv_obj_create(parent);
lv_obj_set_pos(bar_cont, 0, TOP_BAR_H);
lv_obj_set_size(bar_cont, SCREEN_W, TIME_BAR_H);
lv_obj_set_style_bg_color(bar_cont, lv_color_white(), 0);
lv_obj_set_style_bg_opa(bar_cont, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(bar_cont, lv_color_black(), 0);
lv_obj_set_style_border_width(bar_cont, 1, 0);
lv_obj_set_style_border_side(bar_cont, LV_BORDER_SIDE_BOTTOM, 0);
lv_obj_set_style_radius(bar_cont, 0, 0);
lv_obj_set_style_pad_all(bar_cont, 0, 0);
lv_obj_clear_flag(bar_cont, LV_OBJ_FLAG_SCROLLABLE);
/* Clock HH:MM:SS — left */
lbl_clock = lv_label_create(bar_cont);
lv_obj_set_style_text_font(lbl_clock, &DSEG14C_BI_32px, 0);
lv_obj_set_style_text_color(lbl_clock, lv_color_black(), 0);
lv_obj_align(lbl_clock, LV_ALIGN_LEFT_MID, 10, 0);
lv_label_set_text(lbl_clock, "--:--:--");
/* Date YYYY/MM/DD DAY — right */
lbl_date = lv_label_create(bar_cont);
lv_obj_set_style_text_font(lbl_date, &InziuIosevka_Slab_CC_20px, 0);
lv_obj_set_style_text_color(lbl_date, lv_color_black(), 0);
2026-02-16 20:55:12 +09:00
lv_obj_align(lbl_date, LV_ALIGN_RIGHT_MID, -20, 0);
2026-02-15 18:13:53 +09:00
lv_label_set_text(lbl_date, "----/--/-- ---");
2026-02-15 04:15:30 +09:00
}
2026-02-15 18:13:53 +09:00
static void create_main_section(lv_obj_t *parent)
2026-02-15 04:15:30 +09:00
{
2026-02-16 14:52:20 +09:00
/* === Left column: Services + Pi Vitals === */
2026-02-16 21:31:43 +09:00
create_label(parent, 4, MAIN_Y + 1, &InziuIosevka_Slab_CC_12px, "SERVICES");
2026-02-15 04:15:30 +09:00
tbl_services = lv_table_create(parent);
2026-02-15 18:13:53 +09:00
lv_obj_set_pos(tbl_services, 4, MAIN_Y + 16);
2026-02-16 20:55:12 +09:00
lv_obj_set_size(tbl_services, 190, 82);
2026-02-15 04:15:30 +09:00
lv_table_set_col_cnt(tbl_services, 2);
lv_table_set_col_width(tbl_services, 0, 110);
2026-02-15 18:13:53 +09:00
lv_table_set_col_width(tbl_services, 1, 65);
lv_obj_set_style_text_font(tbl_services, &InziuIosevka_Slab_CC_12px, 0);
2026-02-15 04:15:30 +09:00
lv_obj_set_style_border_color(tbl_services, lv_color_black(), 0);
lv_obj_set_style_border_width(tbl_services, 1, 0);
lv_obj_set_style_pad_ver(tbl_services, 2, LV_PART_ITEMS);
lv_obj_set_style_pad_hor(tbl_services, 4, LV_PART_ITEMS);
2026-02-15 18:13:53 +09:00
lv_obj_add_flag(tbl_services, LV_OBJ_FLAG_SCROLLABLE);
2026-02-15 04:15:30 +09:00
for (int i = 0; i < WS_MAX_SERVICES; i++) {
lv_table_set_cell_value(tbl_services, i, 0, "---");
lv_table_set_cell_value(tbl_services, i, 1, "---");
}
2026-02-16 22:28:26 +09:00
/* Timer for each shift of 1/8 line's height */
2026-02-16 23:33:42 +09:00
s_scroll_timer = lv_timer_create(scroll_timer_cb, 120, NULL);
2026-02-15 18:13:53 +09:00
2026-02-16 14:52:20 +09:00
/* === Left column: Pi Vitals (below services) === */
int rx = 0;
2026-02-15 18:13:53 +09:00
int row_h = 18; /* vertical spacing per stat row */
int lbl_w = 36; /* width reserved for "CPU " etc */
int bar_w = 82;
int bar_h = 12;
int val_x = rx + 4 + lbl_w + bar_w + 4; /* value label after bar */
2026-02-16 20:55:12 +09:00
int temp_x = rx + 155; /* TEMP column, right of value labels */
2026-02-15 18:13:53 +09:00
2026-02-16 20:55:12 +09:00
/* Pi Vitals header */
2026-02-16 21:31:43 +09:00
create_label(parent, rx + 4, 175, &InziuIosevka_Slab_CC_12px, "PI VITALS");
2026-02-15 18:13:53 +09:00
2026-02-16 20:55:12 +09:00
int ry = 192;
2026-02-15 18:13:53 +09:00
/* CPU [========] 12% TEMP */
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "CPU");
bar_cpu = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
lbl_cpu_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
create_label(parent, temp_x, ry, &InziuIosevka_Slab_CC_12px, "TEMP");
/* RAM [========] 58% 45 C */
ry += row_h;
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "RAM");
bar_ram = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
lbl_ram_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
lbl_cpu_temp = create_label(parent, temp_x, ry, &InziuIosevka_Slab_CC_20px, "-- C");
/* DISK [========] 44% */
ry += row_h;
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "DISK");
bar_disk = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
lbl_disk_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
/* uptime */
ry += row_h;
lbl_uptime = create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "Uptime: --h");
2026-02-16 14:52:20 +09:00
/* === Right column: Status image (200x200) === */
2026-02-15 21:46:18 +09:00
img_status = lv_img_create(parent);
2026-02-16 20:55:12 +09:00
lv_obj_set_pos(img_status, 200, MAIN_Y + 1);
2026-02-16 14:52:20 +09:00
lv_obj_set_size(img_status, 200, 200);
2026-02-15 22:14:06 +09:00
lv_obj_set_style_bg_color(img_status, lv_color_white(), 0);
lv_obj_set_style_bg_opa(img_status, LV_OPA_COVER, 0);
2026-02-15 04:15:30 +09:00
}
static void create_bottom_bar(lv_obj_t *parent)
{
int y0 = SCREEN_H - BOT_H;
lv_obj_t *bot_cont = lv_obj_create(parent);
lv_obj_set_pos(bot_cont, 0, y0);
lv_obj_set_size(bot_cont, SCREEN_W, BOT_H);
2026-02-16 23:33:42 +09:00
lv_obj_set_style_bg_color(bot_cont, lv_color_black(), 0);
2026-02-15 04:15:30 +09:00
lv_obj_set_style_bg_opa(bot_cont, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(bot_cont, lv_color_black(), 0);
lv_obj_set_style_border_width(bot_cont, 1, 0);
lv_obj_set_style_border_side(bot_cont, LV_BORDER_SIDE_TOP, 0);
lv_obj_set_style_radius(bot_cont, 0, 0);
lv_obj_set_style_pad_all(bot_cont, 4, 0);
lv_obj_clear_flag(bot_cont, LV_OBJ_FLAG_SCROLLABLE);
lbl_net = lv_label_create(bot_cont);
2026-02-15 18:13:53 +09:00
lv_obj_set_style_text_font(lbl_net, &InziuIosevka_Slab_CC_12px, 0);
2026-02-16 23:33:42 +09:00
lv_obj_set_style_text_color(lbl_net, lv_color_white(), 0);
2026-02-15 04:15:30 +09:00
lv_obj_align(lbl_net, LV_ALIGN_LEFT_MID, 0, 0);
2026-02-16 20:55:12 +09:00
lv_label_set_text(lbl_net, "NETWORK DOWN: ---- kBps / UP: ---- kBps");
2026-02-15 21:46:18 +09:00
/* Local sensor readings — right-aligned */
lbl_local = lv_label_create(bot_cont);
lv_obj_set_style_text_font(lbl_local, &InziuIosevka_Slab_CC_12px, 0);
2026-02-16 23:33:42 +09:00
lv_obj_set_style_text_color(lbl_local, lv_color_white(), 0);
2026-02-15 21:46:18 +09:00
lv_obj_align(lbl_local, LV_ALIGN_RIGHT_MID, 0, 0);
lv_label_set_text(lbl_local, "T: --.- H: --%");
2026-02-15 04:15:30 +09:00
}
void dashboard_ui_create(void)
{
init_styles();
lv_obj_t *scr = lv_scr_act();
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
create_top_bar(scr);
2026-02-15 18:13:53 +09:00
create_time_bar(scr);
create_main_section(scr);
2026-02-15 04:15:30 +09:00
create_bottom_bar(scr);
}
/* ---------- Update functions ---------- */
void dashboard_ui_update_stats(const pi_stats_t *stats)
{
if (!stats || !stats->valid) return;
char buf[32];
/* Bars */
lv_bar_set_value(bar_cpu, (int)stats->cpu_pct, LV_ANIM_OFF);
snprintf(buf, sizeof(buf), "%d%%", (int)stats->cpu_pct);
lv_label_set_text(lbl_cpu_val, buf);
lv_bar_set_value(bar_ram, (int)stats->mem_pct, LV_ANIM_OFF);
snprintf(buf, sizeof(buf), "%d%%", (int)stats->mem_pct);
lv_label_set_text(lbl_ram_val, buf);
lv_bar_set_value(bar_disk, (int)stats->disk_pct, LV_ANIM_OFF);
snprintf(buf, sizeof(buf), "%d%%", (int)stats->disk_pct);
lv_label_set_text(lbl_disk_val, buf);
/* CPU temp */
2026-02-15 18:13:53 +09:00
snprintf(buf, sizeof(buf), "%.0f C", stats->cpu_temp);
2026-02-15 04:15:30 +09:00
lv_label_set_text(lbl_cpu_temp, buf);
/* Services table */
2026-02-15 18:13:53 +09:00
s_service_count = stats->service_count;
2026-02-15 04:15:30 +09:00
for (int i = 0; i < stats->service_count && i < WS_MAX_SERVICES; i++) {
2026-02-16 20:47:44 +09:00
const char *tag;
switch (stats->services[i].status) {
case SVC_RUNNING: tag = "[RUN]"; break;
case SVC_WARNING: tag = "[WARN]"; break;
default: tag = "[STOP]"; break;
}
2026-02-15 04:15:30 +09:00
lv_table_set_cell_value(tbl_services, i, 0, stats->services[i].name);
2026-02-16 20:47:44 +09:00
lv_table_set_cell_value(tbl_services, i, 1, tag);
2026-02-15 04:15:30 +09:00
}
2026-02-16 20:55:12 +09:00
/* Measure row height, compute visible rows, append duplicates */
fill_duplicate_rows(stats->service_count);
2026-02-15 04:15:30 +09:00
/* Uptime */
snprintf(buf, sizeof(buf), "Uptime: %.0fh", stats->uptime_hrs);
lv_label_set_text(lbl_uptime, buf);
/* Network */
char net_buf[64];
2026-02-16 20:55:12 +09:00
snprintf(net_buf, sizeof(net_buf), "NETWORK DOWN: %.0f kBps / UP: %.0f kBps",
2026-02-15 04:15:30 +09:00
stats->net_rx_kbps, stats->net_tx_kbps);
lv_label_set_text(lbl_net, net_buf);
}
2026-02-16 14:39:21 +09:00
void dashboard_ui_update_local(float temp, float humidity, uint8_t battery, bool charging, float voltage)
2026-02-15 04:15:30 +09:00
{
char buf[32];
2026-02-15 21:46:18 +09:00
snprintf(buf, sizeof(buf), "T: %.1f H: %.0f%%", temp, humidity);
lv_label_set_text(lbl_local, buf);
2026-02-15 04:15:30 +09:00
2026-02-16 14:39:21 +09:00
/* Update top bar battery voltage */
snprintf(buf, sizeof(buf), "%.2fV", voltage);
lv_label_set_text(lbl_batt_v, buf);
2026-02-15 18:13:53 +09:00
/* Update top bar battery text + bar */
2026-02-16 14:39:21 +09:00
snprintf(buf, sizeof(buf), charging ? "%d%%C" : "%d%%", battery);
2026-02-15 04:15:30 +09:00
lv_label_set_text(lbl_batt, buf);
2026-02-15 18:13:53 +09:00
lv_bar_set_value(bar_batt_top, battery, LV_ANIM_OFF);
2026-02-15 04:15:30 +09:00
}
2026-02-15 18:13:53 +09:00
void dashboard_ui_update_time(int h, int m, int s, int year, int month, int day, int weekday)
2026-02-15 04:15:30 +09:00
{
2026-02-15 18:13:53 +09:00
static const char *day_names[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char buf[32];
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", h, m, s);
lv_label_set_text(lbl_clock, buf);
const char *wd = (weekday >= 0 && weekday <= 6) ? day_names[weekday] : "---";
snprintf(buf, sizeof(buf), "%04d/%02d/%02d %s", year, month, day, wd);
lv_label_set_text(lbl_date, buf);
2026-02-15 04:15:30 +09:00
}
void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, const char *ip_str)
{
/* IP / WiFi status */
lv_label_set_text(lbl_ip, ip_str ? ip_str : "N/A");
/* WS status */
const char *ws_str;
switch (ws_state) {
case WS_STATE_CONNECTED: ws_str = "WS:LIVE"; break;
case WS_STATE_CONNECTING: ws_str = "WS:..."; break;
case WS_STATE_DISCONNECTED: ws_str = "WS:OFF"; break;
case WS_STATE_ERROR: ws_str = "WS:ERR"; break;
default: ws_str = "WS:---"; break;
}
lv_label_set_text(lbl_ws, ws_str);
}
2026-02-15 21:46:18 +09:00
void dashboard_ui_update_status_image(const lv_img_dsc_t *dsc)
{
if (dsc) {
lv_img_set_src(img_status, dsc);
}
}