6969#define ALARM_COUNT 3
7070#define WEATHER_HOURS 12
7171#define WEATHER_DAYS 7
72+ #define SCREENSAVER_IDLE_MS (2 * 60 * 1000)
7273#define TIME_ZONE "CST6CDT,M3.2.0,M11.1.0"
7374
7475typedef struct __attribute__((packed )) {
@@ -377,10 +378,16 @@ static volatile bool weather_done;
377378static bool weather_ok ;
378379static bool weather_keyboard_visible ;
379380static bool weather_has_data ;
380- static char weather_location [64 ] = "Chicago " ;
381+ static char weather_location [64 ] = "Milwaukee, Wisconsin " ;
381382static char weather_pending_location [64 ];
382383static char weather_error [96 ];
383384static weather_data_t weather_data ;
385+ static time_t weather_fetched_at ;
386+ static lv_obj_t * screensaver ;
387+ static lv_obj_t * screensaver_panel ;
388+ static lv_obj_t * screensaver_time ;
389+ static lv_obj_t * screensaver_date ;
390+ static lv_obj_t * screensaver_weather ;
384391
385392static void show_launcher (void );
386393static void show_files (const char * path );
@@ -392,6 +399,7 @@ static void show_clock(void);
392399static void show_gpio (void );
393400static void show_scope (void );
394401static void show_weather (void );
402+ static void weather_start (const char * location );
395403static void clear_content (void );
396404static void browser_link_clicked (lv_event_t * event );
397405static lv_obj_t * button (lv_obj_t * parent , const char * text , lv_event_cb_t callback );
@@ -1387,9 +1395,17 @@ static bool weather_parse_forecast(const char *json, weather_data_t *data)
13871395static void load_weather_location (void )
13881396{
13891397 nvs_handle_t handle ;
1390- if (nvs_open ("tab5" , NVS_READONLY , & handle ) != ESP_OK ) return ;
1398+ if (nvs_open ("tab5" , NVS_READWRITE , & handle ) != ESP_OK ) return ;
13911399 size_t size = sizeof (weather_location );
13921400 nvs_get_str (handle , "weather_loc" , weather_location , & size );
1401+ uint8_t migrated = 0 ;
1402+ if (nvs_get_u8 (handle , "weather_mke" , & migrated ) != ESP_OK ) {
1403+ if (strcmp (weather_location , "Chicago" ) == 0 )
1404+ snprintf (weather_location , sizeof (weather_location ), "Milwaukee, Wisconsin" );
1405+ nvs_set_str (handle , "weather_loc" , weather_location );
1406+ nvs_set_u8 (handle , "weather_mke" , 1 );
1407+ nvs_commit (handle );
1408+ }
13931409 nvs_close (handle );
13941410}
13951411
@@ -1451,6 +1467,7 @@ static void weather_task(void *argument)
14511467 }
14521468 weather_data = next ;
14531469 weather_has_data = true;
1470+ weather_fetched_at = time (NULL );
14541471 weather_ok = true;
14551472 snprintf (weather_location , sizeof (weather_location ), "%s" , weather_pending_location );
14561473 save_weather_location ();
@@ -3596,6 +3613,92 @@ static void weather_clicked(lv_event_t *event)
35963613 show_weather ();
35973614}
35983615
3616+ static void screensaver_close (void )
3617+ {
3618+ if (!screensaver ) return ;
3619+ lv_obj_delete_async (screensaver );
3620+ screensaver = screensaver_panel = screensaver_time = screensaver_date = screensaver_weather = NULL ;
3621+ }
3622+
3623+ static void screensaver_touched (lv_event_t * event )
3624+ {
3625+ (void )event ;
3626+ screensaver_close ();
3627+ }
3628+
3629+ static void screensaver_update (void )
3630+ {
3631+ if (!screensaver ) return ;
3632+ time_t now = time (NULL );
3633+ struct tm local ;
3634+ localtime_r (& now , & local );
3635+ char text [48 ];
3636+ strftime (text , sizeof (text ), "%I:%M %p" , & local );
3637+ if (text [0 ] == '0' ) memmove (text , text + 1 , strlen (text ));
3638+ lv_label_set_text (screensaver_time , text );
3639+ strftime (text , sizeof (text ), "%A, %B %d, %Y" , & local );
3640+ lv_label_set_text (screensaver_date , text );
3641+ if (weather_has_data ) {
3642+ lv_label_set_text_fmt (screensaver_weather , "%d F\n%s\n%s\n\nBAT %d%%" ,
3643+ weather_round (weather_data .temperature ), weather_condition (weather_data .code ),
3644+ weather_data .place , battery_percent );
3645+ } else {
3646+ lv_label_set_text (screensaver_weather , wifi_connected ? "Loading Milwaukee weather..." :
3647+ "Milwaukee weather needs Wi-Fi" );
3648+ }
3649+ static const int16_t offsets [][2 ] = {{-45 , -70 }, {45 , -35 }, {-30 , 15 }, {35 , 60 }};
3650+ size_t position = local .tm_min % (sizeof (offsets ) / sizeof (offsets [0 ]));
3651+ lv_obj_align (screensaver_panel , LV_ALIGN_CENTER , offsets [position ][0 ], offsets [position ][1 ]);
3652+ }
3653+
3654+ static void screensaver_show (void )
3655+ {
3656+ if (screensaver ) return ;
3657+ screensaver = lv_obj_create (lv_layer_top ());
3658+ lv_obj_set_size (screensaver , SCREEN_WIDTH , SCREEN_HEIGHT );
3659+ lv_obj_align (screensaver , LV_ALIGN_CENTER , 0 , 0 );
3660+ lv_obj_clear_flag (screensaver , LV_OBJ_FLAG_SCROLLABLE );
3661+ lv_obj_add_flag (screensaver , LV_OBJ_FLAG_CLICKABLE );
3662+ lv_obj_set_style_bg_color (screensaver , lv_color_hex (0x080b12 ), 0 );
3663+ lv_obj_set_style_bg_opa (screensaver , LV_OPA_COVER , 0 );
3664+ lv_obj_set_style_border_width (screensaver , 0 , 0 );
3665+ lv_obj_set_style_radius (screensaver , 0 , 0 );
3666+ lv_obj_set_style_text_color (screensaver , lv_color_white (), 0 );
3667+ lv_obj_add_event_cb (screensaver , screensaver_touched , LV_EVENT_PRESSED , NULL );
3668+
3669+ screensaver_panel = lv_obj_create (screensaver );
3670+ lv_obj_remove_style_all (screensaver_panel );
3671+ lv_obj_set_size (screensaver_panel , 620 , 650 );
3672+ lv_obj_set_flex_flow (screensaver_panel , LV_FLEX_FLOW_COLUMN );
3673+ lv_obj_set_flex_align (screensaver_panel , LV_FLEX_ALIGN_CENTER , LV_FLEX_ALIGN_CENTER , LV_FLEX_ALIGN_CENTER );
3674+ lv_obj_set_style_pad_row (screensaver_panel , 28 , 0 );
3675+ screensaver_time = lv_label_create (screensaver_panel );
3676+ lv_obj_set_style_text_font (screensaver_time , & lv_font_montserrat_48 , 0 );
3677+ screensaver_date = lv_label_create (screensaver_panel );
3678+ lv_obj_set_style_text_font (screensaver_date , & lv_font_montserrat_28 , 0 );
3679+ screensaver_weather = lv_label_create (screensaver_panel );
3680+ lv_obj_set_width (screensaver_weather , 600 );
3681+ lv_obj_set_style_text_font (screensaver_weather , & lv_font_montserrat_28 , 0 );
3682+ lv_obj_set_style_text_align (screensaver_weather , LV_TEXT_ALIGN_CENTER , 0 );
3683+ lv_obj_t * source = lv_label_create (screensaver_panel );
3684+ lv_label_set_text (source , "Weather data: Open-Meteo | Touch to wake" );
3685+ screensaver_update ();
3686+ if (wifi_connected && (!weather_has_data || time (NULL ) - weather_fetched_at > 15 * 60 ))
3687+ weather_start (weather_location );
3688+ }
3689+
3690+ static void screensaver_tick (lv_timer_t * timer )
3691+ {
3692+ (void )timer ;
3693+ uint32_t inactive = lv_display_get_inactive_time (NULL );
3694+ if (screensaver ) {
3695+ if (inactive < 1000 || alarm_active ) screensaver_close ();
3696+ else screensaver_update ();
3697+ } else if (!alarm_active && inactive >= SCREENSAVER_IDLE_MS ) {
3698+ screensaver_show ();
3699+ }
3700+ }
3701+
35993702static void show_launcher (void )
36003703{
36013704 static int32_t columns [] = {
@@ -3684,6 +3787,7 @@ void app_main(void)
36843787 lv_timer_create (battery_tick , 5000 , NULL );
36853788 clock_tick (NULL );
36863789 lv_timer_create (clock_tick , 1000 , NULL );
3790+ lv_timer_create (screensaver_tick , 1000 , NULL );
36873791
36883792 content = lv_obj_create (screen );
36893793 lv_obj_set_size (content , 720 , 1180 );
0 commit comments