timed-remote

Flipper Zero app for sending delayed IR commands
git clone git://src.adamsgaard.dk/timed-remote # fast
git clone https://src.adamsgaard.dk/timed-remote.git # slow
Log | Files | Refs | README | LICENSE Back to index

scene_timer_running.c (4698B)


      1 #include "../helpers/ir_helper.h"
      2 #include "../helpers/time_helper.h"
      3 #include "../timed_remote.h"
      4 #include "timed_remote_scene.h"
      5 
      6 static void timer_callback(void *context) {
      7   TimedRemoteApp *app = context;
      8   view_dispatcher_send_custom_event(app->view_dispatcher,
      9                                     TimedRemoteEventTimerTick);
     10 }
     11 
     12 static void update_display(TimedRemoteApp *app) {
     13   uint8_t h, m, s;
     14   time_helper_seconds_to_hms(app->seconds_remaining, &h, &m, &s);
     15 
     16   char time_str[16];
     17   snprintf(time_str, sizeof(time_str), "%02d:%02d:%02d", h, m, s);
     18 
     19   widget_reset(app->widget);
     20   widget_add_string_element(app->widget, 64, 5, AlignCenter, AlignTop,
     21                             FontSecondary, app->signal_name);
     22   widget_add_string_element(app->widget, 64, 25, AlignCenter, AlignTop,
     23                             FontBigNumbers, time_str);
     24 
     25   if (app->repeat_count > 0 && app->repeat_count < 255) {
     26     /* Fixed repeat count (1-99) */
     27     char repeat_str[24];
     28     snprintf(repeat_str, sizeof(repeat_str), "Repeat: %d/%d",
     29              app->repeat_count - app->repeats_remaining + 1, app->repeat_count);
     30     widget_add_string_element(app->widget, 64, 52, AlignCenter, AlignTop,
     31                               FontSecondary, repeat_str);
     32   } else if (app->repeat_count == 255) {
     33     /* Unlimited repeat */
     34     widget_add_string_element(app->widget, 64, 52, AlignCenter, AlignTop,
     35                               FontSecondary, "Repeat: Unlimited");
     36   }
     37 }
     38 
     39 void timed_remote_scene_timer_running_on_enter(void *context) {
     40   TimedRemoteApp *app = context;
     41 
     42   /* Calculate initial remaining time */
     43   if (app->timer_mode == TimerModeCountdown) {
     44     app->seconds_remaining =
     45         time_helper_hms_to_seconds(app->hours, app->minutes, app->seconds);
     46   } else {
     47     /* Scheduled mode - calculate time until target */
     48     app->seconds_remaining =
     49         time_helper_seconds_until(app->hours, app->minutes, app->seconds);
     50     if (app->seconds_remaining == 0) {
     51       /* Target time already passed - fire immediately */
     52       view_dispatcher_send_custom_event(app->view_dispatcher,
     53                                         TimedRemoteEventTimerFired);
     54       return;
     55     }
     56   }
     57 
     58   /* Initialize repeat tracking for non-repeat or scheduled */
     59   if (app->repeat_count == 0) {
     60     app->repeats_remaining = 1;
     61   }
     62 
     63   update_display(app);
     64   view_dispatcher_switch_to_view(app->view_dispatcher, TimedRemoteViewWidget);
     65 
     66   /* Start 1-second timer */
     67   app->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app);
     68   furi_timer_start(app->timer, furi_kernel_get_tick_frequency()); /* 1 second */
     69 }
     70 
     71 bool timed_remote_scene_timer_running_on_event(void *context,
     72                                                SceneManagerEvent event) {
     73   TimedRemoteApp *app = context;
     74   bool consumed = false;
     75 
     76   if (event.type == SceneManagerEventTypeCustom) {
     77     if (event.event == TimedRemoteEventTimerTick) {
     78       if (app->seconds_remaining > 0) {
     79         app->seconds_remaining--;
     80         update_display(app);
     81       }
     82 
     83       if (app->seconds_remaining == 0) {
     84         /* Timer fired! */
     85         view_dispatcher_send_custom_event(app->view_dispatcher,
     86                                           TimedRemoteEventTimerFired);
     87       }
     88       consumed = true;
     89     } else if (event.event == TimedRemoteEventTimerFired) {
     90       /* Transmit IR signal */
     91       if (app->ir_signal) {
     92         ir_helper_transmit(app->ir_signal);
     93       }
     94 
     95       if (app->repeat_count == 255) {
     96         /* Unlimited repeat */
     97         app->seconds_remaining =
     98             time_helper_hms_to_seconds(app->hours, app->minutes, app->seconds);
     99         update_display(app);
    100       } else {
    101         app->repeats_remaining--;
    102 
    103         if (app->repeat_count != 0 && app->repeats_remaining > 0) {
    104           /* Reset countdown for next repeat */
    105           app->seconds_remaining = time_helper_hms_to_seconds(
    106               app->hours, app->minutes, app->seconds);
    107           update_display(app);
    108         } else {
    109           /* Done - show confirmation */
    110           scene_manager_next_scene(app->scene_manager, TimedRemoteSceneConfirm);
    111         }
    112       }
    113       consumed = true;
    114     }
    115   } else if (event.type == SceneManagerEventTypeBack) {
    116     /* User pressed Back - cancel timer */
    117     scene_manager_search_and_switch_to_previous_scene(app->scene_manager,
    118                                                       TimedRemoteSceneIrBrowse);
    119     consumed = true;
    120   }
    121 
    122   return consumed;
    123 }
    124 
    125 void timed_remote_scene_timer_running_on_exit(void *context) {
    126   TimedRemoteApp *app = context;
    127 
    128   /* Stop and free timer */
    129   if (app->timer) {
    130     furi_timer_stop(app->timer);
    131     furi_timer_free(app->timer);
    132     app->timer = NULL;
    133   }
    134 
    135   widget_reset(app->widget);
    136 }