v3 M1c (firmware): Provider screen — active source + tap-to-switch
New SCREEN_PROVIDER (Menu → "Provider"): shows the active provider's brand name
in the accent colour (recolors live on a theme switch via the shared accent
style) and its "i / c" position among the enabled providers. A Switch button
notifies {"cmd":"provnext"}; the daemon owns the enabled set/order and cycles to
the next one, so the watch stays dumb. When only one provider is enabled the
button is replaced by a hint.
main.cpp parses the optional pnm/pi/pc payload fields into ui_set_provider_badge.
Builds green on all four boards (206/216/18/216_c6); verified on 2.06 hardware
(OpenAI green theme + "2 / 3" + Switch).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,11 @@ static lv_obj_t* sess_cost_lbl;
|
||||
static lv_obj_t* sess_tokens_lbl;
|
||||
static lv_obj_t* sess_gen_lbl;
|
||||
static lv_obj_t* sess_msgs_lbl;
|
||||
static lv_obj_t* provider_container; // which usage source the watch shows (v3)
|
||||
static lv_obj_t* prov_name_lbl; // active provider's brand name (accent-coloured)
|
||||
static lv_obj_t* prov_pos_lbl; // "1 / 3" position among enabled providers
|
||||
static lv_obj_t* prov_switch_btn; // tap → {"cmd":"provnext"} (daemon cycles)
|
||||
static lv_obj_t* prov_hint_lbl; // shown instead of the button when only one is enabled
|
||||
static lv_obj_t* nowplaying_container; // media now-playing (Phase 5)
|
||||
static lv_obj_t* np_icon_lbl; // play / pause / music glyph
|
||||
static lv_obj_t* np_title_lbl; // track title (scrolls if long)
|
||||
@@ -192,6 +197,7 @@ static const AppEntry APPS[] = {
|
||||
{ SCREEN_NOWPLAYING, "Now Playing", LV_SYMBOL_PLAY },
|
||||
{ SCREEN_HOMEASSIST, "Home", LV_SYMBOL_HOME },
|
||||
{ SCREEN_DIMMER, "Dimmer", LV_SYMBOL_SETTINGS },
|
||||
{ SCREEN_PROVIDER, "Provider", LV_SYMBOL_SHUFFLE },
|
||||
{ SCREEN_BLUETOOTH, "Bluetooth", LV_SYMBOL_BLUETOOTH },
|
||||
};
|
||||
#define APP_COUNT (sizeof(APPS) / sizeof(APPS[0]))
|
||||
@@ -736,6 +742,78 @@ static void init_session_screen(lv_obj_t* scr) {
|
||||
lv_obj_add_flag(session_container, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
// Provider (v3): which usage source the watch is displaying. Shows the active
|
||||
// provider's brand name in the accent colour and its position among the enabled
|
||||
// set; the Switch button asks the daemon to advance to the next one (the daemon
|
||||
// owns the enabled list/order, so the watch just says "next"). Name/position are
|
||||
// refreshed from ui_set_provider_badge() as payloads arrive.
|
||||
static void prov_switch_cb(lv_event_t* e) {
|
||||
(void)e;
|
||||
ble_send_command("{\"cmd\":\"provnext\"}");
|
||||
if (prov_pos_lbl) lv_label_set_text(prov_pos_lbl, "Switching\xE2\x80\xA6"); // …
|
||||
}
|
||||
|
||||
static void init_provider_screen(lv_obj_t* scr) {
|
||||
provider_container = lv_obj_create(scr);
|
||||
lv_obj_set_size(provider_container, L.scr_w, L.scr_h);
|
||||
lv_obj_set_pos(provider_container, 0, 0);
|
||||
lv_obj_set_style_bg_opa(provider_container, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(provider_container, 0, 0);
|
||||
lv_obj_set_style_pad_all(provider_container, 0, 0);
|
||||
lv_obj_clear_flag(provider_container, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
make_screen_title(provider_container, "Provider");
|
||||
|
||||
lv_obj_t* cap = lv_label_create(provider_container);
|
||||
lv_label_set_text(cap, "Showing on watch");
|
||||
lv_obj_set_style_text_font(cap, &font_styrene_16, 0);
|
||||
lv_obj_set_style_text_color(cap, COL_DIM, 0);
|
||||
lv_obj_align(cap, LV_ALIGN_TOP_MID, 0, L.content_y + 34);
|
||||
|
||||
prov_name_lbl = lv_label_create(provider_container);
|
||||
lv_label_set_text(prov_name_lbl, "Claude Code");
|
||||
lv_obj_set_style_text_font(prov_name_lbl, &font_tiempos_34, 0);
|
||||
lv_obj_add_style(prov_name_lbl, &s_accent_style, 0); // brand colour, recolors on switch
|
||||
lv_obj_align(prov_name_lbl, LV_ALIGN_TOP_MID, 0, L.content_y + 70);
|
||||
|
||||
prov_pos_lbl = lv_label_create(provider_container);
|
||||
lv_label_set_text(prov_pos_lbl, "");
|
||||
lv_obj_set_style_text_font(prov_pos_lbl, &font_styrene_20, 0);
|
||||
lv_obj_set_style_text_color(prov_pos_lbl, COL_DIM, 0);
|
||||
lv_obj_align(prov_pos_lbl, LV_ALIGN_TOP_MID, 0, L.content_y + 122);
|
||||
|
||||
// Switch button — advance to the next enabled provider.
|
||||
prov_switch_btn = lv_obj_create(provider_container);
|
||||
lv_obj_set_size(prov_switch_btn, 190, 74);
|
||||
lv_obj_align(prov_switch_btn, LV_ALIGN_TOP_MID, 0, L.content_y + 178);
|
||||
lv_obj_set_style_bg_color(prov_switch_btn, COL_PANEL, 0);
|
||||
lv_obj_set_style_bg_opa(prov_switch_btn, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_radius(prov_switch_btn, 16, 0);
|
||||
lv_obj_set_style_border_width(prov_switch_btn, 0, 0);
|
||||
lv_obj_set_style_border_width(prov_switch_btn, 3, LV_STATE_PRESSED); // accent ring on press
|
||||
lv_obj_set_style_border_color(prov_switch_btn, COL_ACCENT, LV_STATE_PRESSED);
|
||||
lv_obj_clear_flag(prov_switch_btn, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(prov_switch_btn, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(prov_switch_btn, prov_switch_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
// Brand text is Styrene (ASCII-only); LVGL symbol glyphs live in Montserrat,
|
||||
// so a symbol here would render as tofu. Keep the label plain "Switch".
|
||||
lv_obj_t* sw_lbl = lv_label_create(prov_switch_btn);
|
||||
lv_label_set_text(sw_lbl, "Switch");
|
||||
lv_obj_set_style_text_font(sw_lbl, &font_styrene_20, 0);
|
||||
lv_obj_set_style_text_color(sw_lbl, COL_TEXT, 0);
|
||||
lv_obj_center(sw_lbl);
|
||||
|
||||
prov_hint_lbl = lv_label_create(provider_container);
|
||||
lv_label_set_text(prov_hint_lbl, "Only provider enabled");
|
||||
lv_obj_set_style_text_font(prov_hint_lbl, &font_styrene_16, 0);
|
||||
lv_obj_set_style_text_color(prov_hint_lbl, COL_DIM, 0);
|
||||
lv_obj_align(prov_hint_lbl, LV_ALIGN_TOP_MID, 0, L.content_y + 200);
|
||||
lv_obj_add_flag(prov_hint_lbl, LV_OBJ_FLAG_HIDDEN); // shown only when count == 1
|
||||
|
||||
lv_obj_add_flag(provider_container, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
// Now Playing: the Windows media session relayed by the daemon. A play/pause glyph
|
||||
// and status line reflect playback; the title scrolls if it overflows. Independent
|
||||
// of rate-limit data, so it stays live even when the OAuth token is unavailable.
|
||||
@@ -1313,6 +1391,7 @@ void ui_init(void) {
|
||||
init_soundpad_screen(scr);
|
||||
init_session_screen(scr);
|
||||
init_nowplaying_screen(scr);
|
||||
init_provider_screen(scr);
|
||||
init_homeassist_screen(scr);
|
||||
init_dimmer_screen(scr);
|
||||
init_battery_screen(scr);
|
||||
@@ -1546,6 +1625,7 @@ void ui_show_screen(screen_t screen) {
|
||||
if (soundpad_container) lv_obj_add_flag(soundpad_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (session_container) lv_obj_add_flag(session_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (nowplaying_container) lv_obj_add_flag(nowplaying_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (provider_container) lv_obj_add_flag(provider_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (homeassist_container) lv_obj_add_flag(homeassist_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (dimmer_container) lv_obj_add_flag(dimmer_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (battery_container) lv_obj_add_flag(battery_container, LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -1562,6 +1642,7 @@ void ui_show_screen(screen_t screen) {
|
||||
case SCREEN_SOUNDPAD: lv_obj_clear_flag(soundpad_container, LV_OBJ_FLAG_HIDDEN); break;
|
||||
case SCREEN_SESSION: lv_obj_clear_flag(session_container, LV_OBJ_FLAG_HIDDEN); break;
|
||||
case SCREEN_NOWPLAYING: lv_obj_clear_flag(nowplaying_container, LV_OBJ_FLAG_HIDDEN); break;
|
||||
case SCREEN_PROVIDER: lv_obj_clear_flag(provider_container, LV_OBJ_FLAG_HIDDEN); break;
|
||||
case SCREEN_BLUETOOTH: bt_refresh(); lv_obj_clear_flag(bt_container, LV_OBJ_FLAG_HIDDEN); break;
|
||||
case SCREEN_HOMEASSIST:
|
||||
lv_obj_clear_flag(homeassist_container, LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -1633,6 +1714,27 @@ void ui_set_theme(const char* pv, uint32_t accent_rgb) {
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the Provider screen's name + "i / c" position, and show the Switch
|
||||
// button unless exactly one provider is enabled (then a hint replaces it). Safe
|
||||
// to call on every payload; name==nullptr leaves the current name in place.
|
||||
void ui_set_provider_badge(const char* name, int index, int count) {
|
||||
if (!provider_container) return;
|
||||
if (name && *name && prov_name_lbl) lv_label_set_text(prov_name_lbl, name);
|
||||
if (prov_pos_lbl) {
|
||||
if (count > 0 && index > 0) lv_label_set_text_fmt(prov_pos_lbl, "%d / %d", index, count);
|
||||
else lv_label_set_text(prov_pos_lbl, "");
|
||||
}
|
||||
bool single = (count == 1); // count 0 (unknown) keeps the button visible
|
||||
if (prov_switch_btn) {
|
||||
if (single) lv_obj_add_flag(prov_switch_btn, LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_clear_flag(prov_switch_btn, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
if (prov_hint_lbl) {
|
||||
if (single) lv_obj_clear_flag(prov_hint_lbl, LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(prov_hint_lbl, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_update_ble_status(ble_state_t state, const char* name, const char* mac) {
|
||||
(void)name; (void)mac;
|
||||
bool was_connected = s_ble_connected;
|
||||
|
||||
Reference in New Issue
Block a user