commit 3fc23431d5701c11f9714b6154894808739cf1fc
parent 1c9a4403c1711ab87c34efd9ddcfb65155f7c621
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date: Fri, 30 Jan 2026 22:56:08 +0100
refactor: simplify app by removing IR recording and consolidating timer config
- Remove IR recording scenes and functionality (7 scenes deleted)
- Create unified timer config scene combining mode, time input, and repeat options
- Start app directly at IR file browser
- Fix scheduled mode button index calculation
- Simplify scene flow from 10 scenes to 5
Diffstat:
16 files changed, 209 insertions(+), 689 deletions(-)
diff --git a/helpers/ir_helper.c b/helpers/ir_helper.c
@@ -50,106 +50,8 @@ static void ir_signal_list_add(IrSignalList *list, InfraredSignal *signal,
list->count++;
}
-/* ========== Recording ========== */
-
-typedef struct {
- InfraredSignal *signal;
- bool received;
- FuriSemaphore *semaphore;
-} RecordContext;
-
-static void ir_record_callback(void *context,
- InfraredWorkerSignal *received_signal) {
- RecordContext *ctx = context;
-
- const InfraredMessage *message =
- infrared_worker_get_decoded_signal(received_signal);
- if (message) {
- ctx->signal = infrared_signal_alloc();
- infrared_signal_set_message(ctx->signal, message);
- ctx->received = true;
- } else {
- /* Raw signal */
- const uint32_t *timings;
- size_t timings_cnt;
- infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
- if (timings && timings_cnt > 0) {
- ctx->signal = infrared_signal_alloc();
- infrared_signal_set_raw_signal(ctx->signal, timings, timings_cnt,
- INFRARED_COMMON_CARRIER_FREQUENCY,
- INFRARED_COMMON_DUTY_CYCLE);
- ctx->received = true;
- }
- }
-
- furi_semaphore_release(ctx->semaphore);
-}
-
-bool ir_helper_record(InfraredSignal **signal, uint32_t timeout_ms) {
- InfraredWorker *worker = infrared_worker_alloc();
-
- RecordContext ctx = {
- .signal = NULL,
- .received = false,
- .semaphore = furi_semaphore_alloc(0, 1),
- };
-
- infrared_worker_rx_set_received_signal_callback(worker, ir_record_callback,
- &ctx);
- infrared_worker_rx_start(worker);
-
- FuriStatus status = furi_semaphore_acquire(ctx.semaphore, timeout_ms);
-
- infrared_worker_rx_stop(worker);
- infrared_worker_free(worker);
- furi_semaphore_free(ctx.semaphore);
-
- if (status == FuriStatusOk && ctx.received) {
- *signal = ctx.signal;
- return true;
- }
-
- if (ctx.signal) {
- infrared_signal_free(ctx.signal);
- }
- *signal = NULL;
- return false;
-}
-
/* ========== File I/O ========== */
-bool ir_helper_save(InfraredSignal *signal, const char *name,
- const char *path) {
- Storage *storage = furi_record_open(RECORD_STORAGE);
- FlipperFormat *ff = flipper_format_file_alloc(storage);
- bool success = false;
-
- do {
- /* Check if file exists */
- if (storage_file_exists(storage, path)) {
- /* Append mode */
- if (!flipper_format_file_open_append(ff, path))
- break;
- } else {
- /* Create new file */
- if (!flipper_format_file_open_always(ff, path))
- break;
- if (!flipper_format_write_header_cstr(ff, IR_FILE_HEADER,
- IR_FILE_VERSION))
- break;
- }
-
- if (!infrared_signal_save(signal, ff, name))
- break;
-
- success = true;
- } while (false);
-
- flipper_format_free(ff);
- furi_record_close(RECORD_STORAGE);
- return success;
-}
-
bool ir_helper_load_file(const char *path, IrSignalList *list) {
Storage *storage = furi_record_open(RECORD_STORAGE);
FlipperFormat *ff = flipper_format_file_alloc(storage);
diff --git a/helpers/ir_helper.h b/helpers/ir_helper.h
@@ -32,26 +32,6 @@ IrSignalList *ir_signal_list_alloc(void);
void ir_signal_list_free(IrSignalList *list);
/**
- * Record an IR signal from the receiver.
- * Blocks until signal is received or timeout.
- *
- * @param signal Output: allocated InfraredSignal (caller must free)
- * @param timeout_ms Timeout in milliseconds
- * @return true if signal recorded, false on timeout/error
- */
-bool ir_helper_record(InfraredSignal **signal, uint32_t timeout_ms);
-
-/**
- * Save a signal to an .ir file (appends if file exists).
- *
- * @param signal The signal to save
- * @param name Signal name (e.g., "Power")
- * @param path File path (e.g., "/ext/infrared/timed_remote.ir")
- * @return true on success
- */
-bool ir_helper_save(InfraredSignal *signal, const char *name, const char *path);
-
-/**
* Load all signals from an .ir file.
*
* @param path File path to load
diff --git a/scenes/scene_confirm.c b/scenes/scene_confirm.c
@@ -30,9 +30,9 @@ bool timed_remote_scene_confirm_on_event(void *context,
if (event.type == SceneManagerEventTypeCustom) {
if (event.event == TimedRemoteEventConfirmDone) {
- /* Return to main menu */
+ /* Return to file browser */
scene_manager_search_and_switch_to_previous_scene(
- app->scene_manager, TimedRemoteSceneMainMenu);
+ app->scene_manager, TimedRemoteSceneIrBrowse);
consumed = true;
}
}
diff --git a/scenes/scene_ir_record.c b/scenes/scene_ir_record.c
@@ -1,57 +0,0 @@
-#include "../helpers/ir_helper.h"
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-#define RECORD_TIMEOUT_MS 30000
-
-void timed_remote_scene_ir_record_on_enter(void *context) {
- TimedRemoteApp *app = context;
-
- widget_reset(app->widget);
- widget_add_string_element(app->widget, 64, 10, AlignCenter, AlignTop,
- FontPrimary, "Recording IR...");
- widget_add_string_element(app->widget, 64, 26, AlignCenter, AlignTop,
- FontSecondary, "Point remote at Flipper");
- widget_add_string_element(app->widget, 64, 40, AlignCenter, AlignTop,
- FontSecondary, "and press a button");
-
- view_dispatcher_switch_to_view(app->view_dispatcher, TimedRemoteViewWidget);
-
- /* Start recording in a thread or use async approach */
- /* For now, blocking call - consider making async for better UX */
- InfraredSignal *signal = NULL;
- bool success = ir_helper_record(&signal, RECORD_TIMEOUT_MS);
-
- if (success && signal) {
- /* Free any previous signal */
- if (app->ir_signal) {
- infrared_signal_free(app->ir_signal);
- }
- app->ir_signal = signal;
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventRecordComplete);
- } else {
- /* Timeout or error - go back */
- scene_manager_previous_scene(app->scene_manager);
- }
-}
-
-bool timed_remote_scene_ir_record_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventRecordComplete) {
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneSignalName);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_ir_record_on_exit(void *context) {
- TimedRemoteApp *app = context;
- widget_reset(app->widget);
-}
diff --git a/scenes/scene_ir_select.c b/scenes/scene_ir_select.c
@@ -59,7 +59,8 @@ bool timed_remote_scene_ir_select_on_event(void *context,
if (event.type == SceneManagerEventTypeCustom) {
if (event.event == TimedRemoteEventSignalSelected) {
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneTimerMode);
+ scene_manager_next_scene(app->scene_manager,
+ TimedRemoteSceneTimerConfig);
consumed = true;
}
}
diff --git a/scenes/scene_ir_source.c b/scenes/scene_ir_source.c
@@ -1,54 +0,0 @@
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-enum {
- IrSourceIndexRecord,
- IrSourceIndexBrowse,
-};
-
-static void ir_source_callback(void *context, uint32_t index) {
- TimedRemoteApp *app = context;
- if (index == IrSourceIndexRecord) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventRecordSignal);
- } else if (index == IrSourceIndexBrowse) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventBrowseFiles);
- }
-}
-
-void timed_remote_scene_ir_source_on_enter(void *context) {
- TimedRemoteApp *app = context;
-
- submenu_reset(app->submenu);
- submenu_set_header(app->submenu, "Select IR Source");
- submenu_add_item(app->submenu, "Record New Signal", IrSourceIndexRecord,
- ir_source_callback, app);
- submenu_add_item(app->submenu, "Browse IR Files", IrSourceIndexBrowse,
- ir_source_callback, app);
-
- view_dispatcher_switch_to_view(app->view_dispatcher, TimedRemoteViewSubmenu);
-}
-
-bool timed_remote_scene_ir_source_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventRecordSignal) {
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneIrRecord);
- consumed = true;
- } else if (event.event == TimedRemoteEventBrowseFiles) {
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneIrBrowse);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_ir_source_on_exit(void *context) {
- TimedRemoteApp *app = context;
- submenu_reset(app->submenu);
-}
diff --git a/scenes/scene_main_menu.c b/scenes/scene_main_menu.c
@@ -1,45 +0,0 @@
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-enum {
- MainMenuIndexStartTimer,
-};
-
-static void main_menu_callback(void *context, uint32_t index) {
- TimedRemoteApp *app = context;
- if (index == MainMenuIndexStartTimer) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventStartTimer);
- }
-}
-
-void timed_remote_scene_main_menu_on_enter(void *context) {
- TimedRemoteApp *app = context;
-
- submenu_reset(app->submenu);
- submenu_set_header(app->submenu, "Timed Remote");
- submenu_add_item(app->submenu, "Start New Timer", MainMenuIndexStartTimer,
- main_menu_callback, app);
-
- view_dispatcher_switch_to_view(app->view_dispatcher, TimedRemoteViewSubmenu);
-}
-
-bool timed_remote_scene_main_menu_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventStartTimer) {
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneIrSource);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_main_menu_on_exit(void *context) {
- TimedRemoteApp *app = context;
- submenu_reset(app->submenu);
-}
diff --git a/scenes/scene_repeat_options.c b/scenes/scene_repeat_options.c
@@ -1,100 +0,0 @@
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-enum {
- RepeatOptionsIndexToggle,
- RepeatOptionsIndexCount,
- RepeatOptionsIndexConfirm,
-};
-
-static void repeat_toggle_change(VariableItem *item) {
- TimedRemoteApp *app = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
- app->repeat_enabled = (index == 1);
- variable_item_set_current_value_text(item,
- app->repeat_enabled ? "On" : "Off");
-}
-
-static void repeat_count_change(VariableItem *item) {
- TimedRemoteApp *app = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
- app->repeat_count = index; /* 0 = unlimited, 1-99 = fixed */
-
- char buf[16];
- if (app->repeat_count == 0) {
- snprintf(buf, sizeof(buf), "Unlimited");
- } else {
- snprintf(buf, sizeof(buf), "%d", app->repeat_count);
- }
- variable_item_set_current_value_text(item, buf);
-}
-
-static void repeat_options_enter_callback(void *context, uint32_t index) {
- TimedRemoteApp *app = context;
- if (index == RepeatOptionsIndexConfirm) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventRepeatConfigured);
- }
-}
-
-void timed_remote_scene_repeat_options_on_enter(void *context) {
- TimedRemoteApp *app = context;
- VariableItem *item;
-
- variable_item_list_reset(app->variable_item_list);
-
- /* Repeat toggle: Off/On */
- item = variable_item_list_add(app->variable_item_list, "Repeat", 2,
- repeat_toggle_change, app);
- variable_item_set_current_value_index(item, app->repeat_enabled ? 1 : 0);
- variable_item_set_current_value_text(item,
- app->repeat_enabled ? "On" : "Off");
-
- /* Repeat count: 0 = unlimited, 1-99 = fixed */
- item = variable_item_list_add(app->variable_item_list, "Count", 100,
- repeat_count_change, app);
- variable_item_set_current_value_index(item, app->repeat_count);
- if (app->repeat_count == 0) {
- variable_item_set_current_value_text(item, "Unlimited");
- } else {
- char buf[8];
- snprintf(buf, sizeof(buf), "%d", app->repeat_count);
- variable_item_set_current_value_text(item, buf);
- }
-
- /* Confirm button */
- variable_item_list_add(app->variable_item_list, ">> Start Timer <<", 0, NULL,
- NULL);
-
- variable_item_list_set_enter_callback(app->variable_item_list,
- repeat_options_enter_callback, app);
-
- view_dispatcher_switch_to_view(app->view_dispatcher,
- TimedRemoteViewVariableItemList);
-}
-
-bool timed_remote_scene_repeat_options_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventRepeatConfigured) {
- /* Initialize repeats remaining */
- app->repeats_remaining =
- app->repeat_enabled
- ? (app->repeat_count == 0 ? 255 : app->repeat_count)
- : 1;
- scene_manager_next_scene(app->scene_manager,
- TimedRemoteSceneTimerRunning);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_repeat_options_on_exit(void *context) {
- TimedRemoteApp *app = context;
- variable_item_list_reset(app->variable_item_list);
-}
diff --git a/scenes/scene_signal_name.c b/scenes/scene_signal_name.c
@@ -1,61 +0,0 @@
-#include "../helpers/ir_helper.h"
-#include "../helpers/time_helper.h"
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-#define TIMED_REMOTE_IR_PATH "/ext/infrared/timed_remote.ir"
-
-static void signal_name_text_input_callback(void *context) {
- TimedRemoteApp *app = context;
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventRecordComplete);
-}
-
-void timed_remote_scene_signal_name_on_enter(void *context) {
- TimedRemoteApp *app = context;
-
- /* Generate default name based on timestamp */
- time_helper_generate_signal_name(app->text_input_buffer,
- sizeof(app->text_input_buffer));
-
- text_input_reset(app->text_input);
- text_input_set_header_text(app->text_input, "Name this signal:");
- text_input_set_result_callback(
- app->text_input, signal_name_text_input_callback, app,
- app->text_input_buffer, sizeof(app->text_input_buffer),
- true /* clear default text on focus */);
-
- view_dispatcher_switch_to_view(app->view_dispatcher,
- TimedRemoteViewTextInput);
-}
-
-bool timed_remote_scene_signal_name_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventRecordComplete) {
- /* Save the signal name */
- strncpy(app->signal_name, app->text_input_buffer,
- sizeof(app->signal_name) - 1);
- app->signal_name[sizeof(app->signal_name) - 1] = '\0';
-
- /* Save signal to file */
- if (app->ir_signal) {
- ir_helper_save(app->ir_signal, app->signal_name, TIMED_REMOTE_IR_PATH);
- }
-
- /* Proceed to timer mode selection */
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneTimerMode);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_signal_name_on_exit(void *context) {
- TimedRemoteApp *app = context;
- text_input_reset(app->text_input);
-}
diff --git a/scenes/scene_time_input.c b/scenes/scene_time_input.c
@@ -1,116 +0,0 @@
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-/* State indices for VariableItemList */
-enum {
- TimeInputIndexHours,
- TimeInputIndexMinutes,
- TimeInputIndexSeconds,
- TimeInputIndexConfirm,
-};
-
-static void time_input_hours_change(VariableItem *item) {
- TimedRemoteApp *app = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
- app->hours = index;
- char buf[4];
- snprintf(buf, sizeof(buf), "%02d", app->hours);
- variable_item_set_current_value_text(item, buf);
-}
-
-static void time_input_minutes_change(VariableItem *item) {
- TimedRemoteApp *app = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
- app->minutes = index;
- char buf[4];
- snprintf(buf, sizeof(buf), "%02d", app->minutes);
- variable_item_set_current_value_text(item, buf);
-}
-
-static void time_input_seconds_change(VariableItem *item) {
- TimedRemoteApp *app = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
- app->seconds = index;
- char buf[4];
- snprintf(buf, sizeof(buf), "%02d", app->seconds);
- variable_item_set_current_value_text(item, buf);
-}
-
-static void time_input_enter_callback(void *context, uint32_t index) {
- TimedRemoteApp *app = context;
- if (index == TimeInputIndexConfirm) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventTimeSet);
- }
-}
-
-void timed_remote_scene_time_input_on_enter(void *context) {
- TimedRemoteApp *app = context;
- VariableItem *item;
- char buf[4];
-
- variable_item_list_reset(app->variable_item_list);
-
- /* Note: VariableItemList doesn't support headers, the mode context is
- * implicit */
-
- /* Hours: 0-23 */
- item = variable_item_list_add(app->variable_item_list, "Hours", 24,
- time_input_hours_change, app);
- variable_item_set_current_value_index(item, app->hours);
- snprintf(buf, sizeof(buf), "%02d", app->hours);
- variable_item_set_current_value_text(item, buf);
-
- /* Minutes: 0-59 */
- item = variable_item_list_add(app->variable_item_list, "Minutes", 60,
- time_input_minutes_change, app);
- variable_item_set_current_value_index(item, app->minutes);
- snprintf(buf, sizeof(buf), "%02d", app->minutes);
- variable_item_set_current_value_text(item, buf);
-
- /* Seconds: 0-59 */
- item = variable_item_list_add(app->variable_item_list, "Seconds", 60,
- time_input_seconds_change, app);
- variable_item_set_current_value_index(item, app->seconds);
- snprintf(buf, sizeof(buf), "%02d", app->seconds);
- variable_item_set_current_value_text(item, buf);
-
- /* Confirm button */
- variable_item_list_add(app->variable_item_list, ">> Start Timer <<", 0, NULL,
- NULL);
-
- variable_item_list_set_enter_callback(app->variable_item_list,
- time_input_enter_callback, app);
-
- view_dispatcher_switch_to_view(app->view_dispatcher,
- TimedRemoteViewVariableItemList);
-}
-
-bool timed_remote_scene_time_input_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventTimeSet) {
- if (app->timer_mode == TimerModeCountdown) {
- /* Countdown mode - go to repeat options */
- scene_manager_next_scene(app->scene_manager,
- TimedRemoteSceneRepeatOptions);
- } else {
- /* Scheduled mode - go directly to timer */
- app->repeat_enabled = false;
- scene_manager_next_scene(app->scene_manager,
- TimedRemoteSceneTimerRunning);
- }
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_time_input_on_exit(void *context) {
- TimedRemoteApp *app = context;
- variable_item_list_reset(app->variable_item_list);
-}
diff --git a/scenes/scene_timer_config.c b/scenes/scene_timer_config.c
@@ -0,0 +1,191 @@
+#include "../timed_remote.h"
+#include "timed_remote_scene.h"
+
+enum {
+ TimerConfigIndexMode,
+ TimerConfigIndexHours,
+ TimerConfigIndexMinutes,
+ TimerConfigIndexSeconds,
+ TimerConfigIndexRepeat,
+ TimerConfigIndexCount,
+ TimerConfigIndexConfirm,
+};
+
+static void timer_config_mode_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->timer_mode = (index == 0) ? TimerModeCountdown : TimerModeScheduled;
+ variable_item_set_current_value_text(
+ item, app->timer_mode == TimerModeCountdown ? "Countdown" : "Scheduled");
+
+ /* Disable repeat in scheduled mode */
+ if (app->timer_mode == TimerModeScheduled) {
+ app->repeat_enabled = false;
+ }
+
+ /* Trigger rebuild to show/hide repeat options */
+ view_dispatcher_send_custom_event(app->view_dispatcher,
+ TimedRemoteEventModeChanged);
+}
+
+static void timer_config_hours_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->hours = index;
+ char buf[4];
+ snprintf(buf, sizeof(buf), "%02d", app->hours);
+ variable_item_set_current_value_text(item, buf);
+}
+
+static void timer_config_minutes_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->minutes = index;
+ char buf[4];
+ snprintf(buf, sizeof(buf), "%02d", app->minutes);
+ variable_item_set_current_value_text(item, buf);
+}
+
+static void timer_config_seconds_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->seconds = index;
+ char buf[4];
+ snprintf(buf, sizeof(buf), "%02d", app->seconds);
+ variable_item_set_current_value_text(item, buf);
+}
+
+static void timer_config_repeat_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->repeat_enabled = (index == 1);
+ variable_item_set_current_value_text(item,
+ app->repeat_enabled ? "On" : "Off");
+}
+
+static void timer_config_count_change(VariableItem *item) {
+ TimedRemoteApp *app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ app->repeat_count = index; /* 0 = unlimited, 1-99 = fixed */
+
+ char buf[16];
+ if (app->repeat_count == 0) {
+ snprintf(buf, sizeof(buf), "Unlimited");
+ } else {
+ snprintf(buf, sizeof(buf), "%d", app->repeat_count);
+ }
+ variable_item_set_current_value_text(item, buf);
+}
+
+static void timer_config_enter_callback(void *context, uint32_t index) {
+ TimedRemoteApp *app = context;
+ /* In countdown mode, confirm is at index 6, in scheduled mode it's at index 4 */
+ uint32_t confirm_index = app->timer_mode == TimerModeCountdown
+ ? TimerConfigIndexConfirm
+ : (TimerConfigIndexSeconds + 1);
+ if (index == confirm_index) {
+ view_dispatcher_send_custom_event(app->view_dispatcher,
+ TimedRemoteEventTimerConfigured);
+ }
+}
+
+static void build_timer_config_list(TimedRemoteApp *app) {
+ VariableItem *item;
+ char buf[16];
+
+ variable_item_list_reset(app->variable_item_list);
+
+ /* Mode: Countdown / Scheduled */
+ item = variable_item_list_add(app->variable_item_list, "Mode", 2,
+ timer_config_mode_change, app);
+ variable_item_set_current_value_index(
+ item, app->timer_mode == TimerModeCountdown ? 0 : 1);
+ variable_item_set_current_value_text(
+ item, app->timer_mode == TimerModeCountdown ? "Countdown" : "Scheduled");
+
+ /* Hours: 0-23 */
+ item = variable_item_list_add(app->variable_item_list, "Hours", 24,
+ timer_config_hours_change, app);
+ variable_item_set_current_value_index(item, app->hours);
+ snprintf(buf, sizeof(buf), "%02d", app->hours);
+ variable_item_set_current_value_text(item, buf);
+
+ /* Minutes: 0-59 */
+ item = variable_item_list_add(app->variable_item_list, "Minutes", 60,
+ timer_config_minutes_change, app);
+ variable_item_set_current_value_index(item, app->minutes);
+ snprintf(buf, sizeof(buf), "%02d", app->minutes);
+ variable_item_set_current_value_text(item, buf);
+
+ /* Seconds: 0-59 */
+ item = variable_item_list_add(app->variable_item_list, "Seconds", 60,
+ timer_config_seconds_change, app);
+ variable_item_set_current_value_index(item, app->seconds);
+ snprintf(buf, sizeof(buf), "%02d", app->seconds);
+ variable_item_set_current_value_text(item, buf);
+
+ /* Repeat options - only for countdown mode */
+ if (app->timer_mode == TimerModeCountdown) {
+ /* Repeat toggle: Off/On */
+ item = variable_item_list_add(app->variable_item_list, "Repeat", 2,
+ timer_config_repeat_change, app);
+ variable_item_set_current_value_index(item, app->repeat_enabled ? 1 : 0);
+ variable_item_set_current_value_text(item,
+ app->repeat_enabled ? "On" : "Off");
+
+ /* Repeat count: 0 = unlimited, 1-99 = fixed */
+ item = variable_item_list_add(app->variable_item_list, "Count", 100,
+ timer_config_count_change, app);
+ variable_item_set_current_value_index(item, app->repeat_count);
+ if (app->repeat_count == 0) {
+ variable_item_set_current_value_text(item, "Unlimited");
+ } else {
+ snprintf(buf, sizeof(buf), "%d", app->repeat_count);
+ variable_item_set_current_value_text(item, buf);
+ }
+ }
+
+ /* Confirm button */
+ variable_item_list_add(app->variable_item_list, ">> Start Timer <<", 0, NULL,
+ NULL);
+
+ variable_item_list_set_enter_callback(app->variable_item_list,
+ timer_config_enter_callback, app);
+}
+
+void timed_remote_scene_timer_config_on_enter(void *context) {
+ TimedRemoteApp *app = context;
+ build_timer_config_list(app);
+ view_dispatcher_switch_to_view(app->view_dispatcher,
+ TimedRemoteViewVariableItemList);
+}
+
+bool timed_remote_scene_timer_config_on_event(void *context,
+ SceneManagerEvent event) {
+ TimedRemoteApp *app = context;
+ bool consumed = false;
+
+ if (event.type == SceneManagerEventTypeCustom) {
+ if (event.event == TimedRemoteEventModeChanged) {
+ /* Rebuild the list to show/hide repeat options */
+ build_timer_config_list(app);
+ consumed = true;
+ } else if (event.event == TimedRemoteEventTimerConfigured) {
+ /* Initialize repeats remaining */
+ app->repeats_remaining =
+ app->repeat_enabled
+ ? (app->repeat_count == 0 ? 255 : app->repeat_count)
+ : 1;
+ scene_manager_next_scene(app->scene_manager,
+ TimedRemoteSceneTimerRunning);
+ consumed = true;
+ }
+ }
+
+ return consumed;
+}
+
+void timed_remote_scene_timer_config_on_exit(void *context) {
+ TimedRemoteApp *app = context;
+ variable_item_list_reset(app->variable_item_list);
+}
diff --git a/scenes/scene_timer_mode.c b/scenes/scene_timer_mode.c
@@ -1,56 +0,0 @@
-#include "../timed_remote.h"
-#include "timed_remote_scene.h"
-
-enum {
- TimerModeIndexCountdown,
- TimerModeIndexScheduled,
-};
-
-static void timer_mode_callback(void *context, uint32_t index) {
- TimedRemoteApp *app = context;
- if (index == TimerModeIndexCountdown) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventModeCountdown);
- } else if (index == TimerModeIndexScheduled) {
- view_dispatcher_send_custom_event(app->view_dispatcher,
- TimedRemoteEventModeScheduled);
- }
-}
-
-void timed_remote_scene_timer_mode_on_enter(void *context) {
- TimedRemoteApp *app = context;
-
- submenu_reset(app->submenu);
- submenu_set_header(app->submenu, "Timer Mode");
- submenu_add_item(app->submenu, "Countdown (in X time)",
- TimerModeIndexCountdown, timer_mode_callback, app);
- submenu_add_item(app->submenu, "Scheduled (at X time)",
- TimerModeIndexScheduled, timer_mode_callback, app);
-
- view_dispatcher_switch_to_view(app->view_dispatcher, TimedRemoteViewSubmenu);
-}
-
-bool timed_remote_scene_timer_mode_on_event(void *context,
- SceneManagerEvent event) {
- TimedRemoteApp *app = context;
- bool consumed = false;
-
- if (event.type == SceneManagerEventTypeCustom) {
- if (event.event == TimedRemoteEventModeCountdown) {
- app->timer_mode = TimerModeCountdown;
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneTimeInput);
- consumed = true;
- } else if (event.event == TimedRemoteEventModeScheduled) {
- app->timer_mode = TimerModeScheduled;
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneTimeInput);
- consumed = true;
- }
- }
-
- return consumed;
-}
-
-void timed_remote_scene_timer_mode_on_exit(void *context) {
- TimedRemoteApp *app = context;
- submenu_reset(app->submenu);
-}
diff --git a/scenes/scene_timer_running.c b/scenes/scene_timer_running.c
@@ -106,7 +106,7 @@ bool timed_remote_scene_timer_running_on_event(void *context,
} else if (event.type == SceneManagerEventTypeBack) {
/* User pressed Back - cancel timer */
scene_manager_search_and_switch_to_previous_scene(app->scene_manager,
- TimedRemoteSceneMainMenu);
+ TimedRemoteSceneIrBrowse);
consumed = true;
}
diff --git a/scenes/timed_remote_scene.c b/scenes/timed_remote_scene.c
@@ -3,44 +3,26 @@
/* Scene handler tables */
void (*const timed_remote_scene_on_enter_handlers[])(void *) = {
- timed_remote_scene_main_menu_on_enter,
- timed_remote_scene_ir_source_on_enter,
- timed_remote_scene_ir_record_on_enter,
- timed_remote_scene_signal_name_on_enter,
timed_remote_scene_ir_browse_on_enter,
timed_remote_scene_ir_select_on_enter,
- timed_remote_scene_timer_mode_on_enter,
- timed_remote_scene_time_input_on_enter,
- timed_remote_scene_repeat_options_on_enter,
+ timed_remote_scene_timer_config_on_enter,
timed_remote_scene_timer_running_on_enter,
timed_remote_scene_confirm_on_enter,
};
bool (*const timed_remote_scene_on_event_handlers[])(void *,
SceneManagerEvent) = {
- timed_remote_scene_main_menu_on_event,
- timed_remote_scene_ir_source_on_event,
- timed_remote_scene_ir_record_on_event,
- timed_remote_scene_signal_name_on_event,
timed_remote_scene_ir_browse_on_event,
timed_remote_scene_ir_select_on_event,
- timed_remote_scene_timer_mode_on_event,
- timed_remote_scene_time_input_on_event,
- timed_remote_scene_repeat_options_on_event,
+ timed_remote_scene_timer_config_on_event,
timed_remote_scene_timer_running_on_event,
timed_remote_scene_confirm_on_event,
};
void (*const timed_remote_scene_on_exit_handlers[])(void *) = {
- timed_remote_scene_main_menu_on_exit,
- timed_remote_scene_ir_source_on_exit,
- timed_remote_scene_ir_record_on_exit,
- timed_remote_scene_signal_name_on_exit,
timed_remote_scene_ir_browse_on_exit,
timed_remote_scene_ir_select_on_exit,
- timed_remote_scene_timer_mode_on_exit,
- timed_remote_scene_time_input_on_exit,
- timed_remote_scene_repeat_options_on_exit,
+ timed_remote_scene_timer_config_on_exit,
timed_remote_scene_timer_running_on_exit,
timed_remote_scene_confirm_on_exit,
};
diff --git a/scenes/timed_remote_scene.h b/scenes/timed_remote_scene.h
@@ -4,15 +4,9 @@
/* Scene IDs */
typedef enum {
- TimedRemoteSceneMainMenu,
- TimedRemoteSceneIrSource,
- TimedRemoteSceneIrRecord,
- TimedRemoteSceneSignalName,
TimedRemoteSceneIrBrowse,
TimedRemoteSceneIrSelect,
- TimedRemoteSceneTimerMode,
- TimedRemoteSceneTimeInput,
- TimedRemoteSceneRepeatOptions,
+ TimedRemoteSceneTimerConfig,
TimedRemoteSceneTimerRunning,
TimedRemoteSceneConfirm,
TimedRemoteSceneCount,
@@ -26,23 +20,12 @@ typedef enum {
/* Custom events */
typedef enum {
- /* Main menu */
- TimedRemoteEventStartTimer,
- /* IR source */
- TimedRemoteEventRecordSignal,
- TimedRemoteEventBrowseFiles,
- /* Recording complete */
- TimedRemoteEventRecordComplete,
/* File/signal selected */
TimedRemoteEventFileSelected,
TimedRemoteEventSignalSelected,
- /* Timer mode */
- TimedRemoteEventModeCountdown,
- TimedRemoteEventModeScheduled,
- /* Time input complete */
- TimedRemoteEventTimeSet,
- /* Repeat options */
- TimedRemoteEventRepeatConfigured,
+ /* Timer configuration */
+ TimedRemoteEventModeChanged,
+ TimedRemoteEventTimerConfigured,
/* Timer events */
TimedRemoteEventTimerTick,
TimedRemoteEventTimerFired,
@@ -51,26 +34,6 @@ typedef enum {
} TimedRemoteCustomEvent;
/* Scene handlers - declared extern, defined in individual scene files */
-extern void timed_remote_scene_main_menu_on_enter(void *context);
-extern bool timed_remote_scene_main_menu_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_main_menu_on_exit(void *context);
-
-extern void timed_remote_scene_ir_source_on_enter(void *context);
-extern bool timed_remote_scene_ir_source_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_ir_source_on_exit(void *context);
-
-extern void timed_remote_scene_ir_record_on_enter(void *context);
-extern bool timed_remote_scene_ir_record_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_ir_record_on_exit(void *context);
-
-extern void timed_remote_scene_signal_name_on_enter(void *context);
-extern bool timed_remote_scene_signal_name_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_signal_name_on_exit(void *context);
-
extern void timed_remote_scene_ir_browse_on_enter(void *context);
extern bool timed_remote_scene_ir_browse_on_event(void *context,
SceneManagerEvent event);
@@ -81,20 +44,10 @@ extern bool timed_remote_scene_ir_select_on_event(void *context,
SceneManagerEvent event);
extern void timed_remote_scene_ir_select_on_exit(void *context);
-extern void timed_remote_scene_timer_mode_on_enter(void *context);
-extern bool timed_remote_scene_timer_mode_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_timer_mode_on_exit(void *context);
-
-extern void timed_remote_scene_time_input_on_enter(void *context);
-extern bool timed_remote_scene_time_input_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_time_input_on_exit(void *context);
-
-extern void timed_remote_scene_repeat_options_on_enter(void *context);
-extern bool timed_remote_scene_repeat_options_on_event(void *context,
- SceneManagerEvent event);
-extern void timed_remote_scene_repeat_options_on_exit(void *context);
+extern void timed_remote_scene_timer_config_on_enter(void *context);
+extern bool timed_remote_scene_timer_config_on_event(void *context,
+ SceneManagerEvent event);
+extern void timed_remote_scene_timer_config_on_exit(void *context);
extern void timed_remote_scene_timer_running_on_enter(void *context);
extern bool timed_remote_scene_timer_running_on_event(void *context,
diff --git a/timed_remote.c b/timed_remote.c
@@ -107,8 +107,8 @@ int32_t timed_remote_app(void *p) {
TimedRemoteApp *app = timed_remote_app_alloc();
- /* Start with main menu scene */
- scene_manager_next_scene(app->scene_manager, TimedRemoteSceneMainMenu);
+ /* Start with file browser scene */
+ scene_manager_next_scene(app->scene_manager, TimedRemoteSceneIrBrowse);
/* Run event loop */
view_dispatcher_run(app->view_dispatcher);