[FL-2627] Flipper applications: SDK, build and debug system (#1387)
* Added support for running applications from SD card (FAPs - Flipper Application Packages) * Added plugin_dist target for fbt to build FAPs * All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default * Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them * Added debugging support for FAPs with fbt debug & VSCode * Added public firmware API with automated versioning Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: SG <who.just.the.doctor@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
23
applications/main/infrared/application.fam
Normal file
23
applications/main/infrared/application.fam
Normal file
@@ -0,0 +1,23 @@
|
||||
App(
|
||||
appid="infrared",
|
||||
name="Infrared",
|
||||
apptype=FlipperAppType.APP,
|
||||
entry_point="infrared_app",
|
||||
cdefines=["APP_INFRARED"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
provides=["infrared_start"],
|
||||
icon="A_Infrared_14",
|
||||
stack_size=3 * 1024,
|
||||
order=40,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="infrared_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="infrared_on_system_start",
|
||||
requires=["infrared"],
|
||||
order=20,
|
||||
)
|
||||
466
applications/main/infrared/infrared.c
Normal file
466
applications/main/infrared/infrared.c
Normal file
@@ -0,0 +1,466 @@
|
||||
#include "infrared_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static const NotificationSequence* infrared_notification_sequences[] = {
|
||||
&sequence_success,
|
||||
&sequence_set_only_green_255,
|
||||
&sequence_reset_green,
|
||||
&sequence_solid_yellow,
|
||||
&sequence_reset_rgb,
|
||||
&sequence_blink_start_cyan,
|
||||
&sequence_blink_start_magenta,
|
||||
&sequence_blink_stop,
|
||||
};
|
||||
|
||||
static void infrared_make_app_folder(Infrared* infrared) {
|
||||
if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) {
|
||||
dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder");
|
||||
}
|
||||
}
|
||||
|
||||
static bool infrared_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
return scene_manager_handle_custom_event(infrared->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool infrared_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
return scene_manager_handle_back_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
scene_manager_handle_tick_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
furi_assert(infrared->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
infrared->rpc_ctx = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
|
||||
} else {
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void infrared_find_vacant_remote_name(string_t name, const char* path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
string_t base_path;
|
||||
string_init_set_str(base_path, path);
|
||||
|
||||
if(string_end_with_str_p(base_path, INFRARED_APP_EXTENSION)) {
|
||||
size_t filename_start = string_search_rchar(base_path, '/');
|
||||
string_left(base_path, filename_start);
|
||||
}
|
||||
|
||||
string_printf(base_path, "%s/%s%s", path, string_get_cstr(name), INFRARED_APP_EXTENSION);
|
||||
|
||||
FS_Error status = storage_common_stat(storage, string_get_cstr(base_path), NULL);
|
||||
|
||||
if(status == FSE_OK) {
|
||||
/* If the suggested name is occupied, try another one (name2, name3, etc) */
|
||||
size_t dot = string_search_rchar(base_path, '.');
|
||||
string_left(base_path, dot);
|
||||
|
||||
string_t path_temp;
|
||||
string_init(path_temp);
|
||||
|
||||
uint32_t i = 1;
|
||||
do {
|
||||
string_printf(
|
||||
path_temp, "%s%u%s", string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION);
|
||||
status = storage_common_stat(storage, string_get_cstr(path_temp), NULL);
|
||||
} while(status == FSE_OK);
|
||||
|
||||
string_clear(path_temp);
|
||||
|
||||
if(status == FSE_NOT_EXIST) {
|
||||
string_cat_printf(name, "%u", i);
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(base_path);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static Infrared* infrared_alloc() {
|
||||
Infrared* infrared = malloc(sizeof(Infrared));
|
||||
|
||||
string_init(infrared->file_path);
|
||||
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
app_state->is_learning_new_remote = false;
|
||||
app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);
|
||||
app_state->edit_target = InfraredEditTargetNone;
|
||||
app_state->edit_mode = InfraredEditModeNone;
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
|
||||
infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared);
|
||||
infrared->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
infrared->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
view_dispatcher_enable_queue(view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(view_dispatcher, infrared);
|
||||
view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100);
|
||||
|
||||
infrared->storage = furi_record_open(RECORD_STORAGE);
|
||||
infrared->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
infrared->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
infrared->worker = infrared_worker_alloc();
|
||||
infrared->remote = infrared_remote_alloc();
|
||||
infrared->received_signal = infrared_signal_alloc();
|
||||
infrared->brute_force = infrared_brute_force_alloc();
|
||||
|
||||
infrared->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu));
|
||||
|
||||
infrared->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
|
||||
|
||||
infrared->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
|
||||
|
||||
infrared->button_menu = button_menu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu));
|
||||
|
||||
infrared->popup = popup_alloc();
|
||||
view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup));
|
||||
|
||||
infrared->view_stack = view_stack_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack));
|
||||
|
||||
if(app_state->is_debug_enabled) {
|
||||
infrared->debug_view = infrared_debug_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher,
|
||||
InfraredViewDebugView,
|
||||
infrared_debug_view_get_view(infrared->debug_view));
|
||||
}
|
||||
|
||||
infrared->button_panel = button_panel_alloc();
|
||||
infrared->loading = loading_alloc();
|
||||
infrared->progress = infrared_progress_view_alloc();
|
||||
|
||||
return infrared;
|
||||
}
|
||||
|
||||
static void infrared_free(Infrared* infrared) {
|
||||
furi_assert(infrared);
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
|
||||
if(infrared->rpc_ctx) {
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(infrared->rpc_ctx);
|
||||
infrared->rpc_ctx = NULL;
|
||||
}
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu);
|
||||
submenu_free(infrared->submenu);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
|
||||
text_input_free(infrared->text_input);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
|
||||
dialog_ex_free(infrared->dialog_ex);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu);
|
||||
button_menu_free(infrared->button_menu);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup);
|
||||
popup_free(infrared->popup);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewStack);
|
||||
view_stack_free(infrared->view_stack);
|
||||
|
||||
if(app_state->is_debug_enabled) {
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView);
|
||||
infrared_debug_view_free(infrared->debug_view);
|
||||
}
|
||||
|
||||
button_panel_free(infrared->button_panel);
|
||||
loading_free(infrared->loading);
|
||||
infrared_progress_view_free(infrared->progress);
|
||||
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
scene_manager_free(infrared->scene_manager);
|
||||
|
||||
infrared_brute_force_free(infrared->brute_force);
|
||||
infrared_signal_free(infrared->received_signal);
|
||||
infrared_remote_free(infrared->remote);
|
||||
infrared_worker_free(infrared->worker);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
infrared->notifications = NULL;
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
infrared->dialogs = NULL;
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
infrared->gui = NULL;
|
||||
|
||||
string_clear(infrared->file_path);
|
||||
|
||||
free(infrared);
|
||||
}
|
||||
|
||||
bool infrared_add_remote_with_button(
|
||||
Infrared* infrared,
|
||||
const char* button_name,
|
||||
InfraredSignal* signal) {
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
string_t new_name, new_path;
|
||||
string_init_set_str(new_name, INFRARED_DEFAULT_REMOTE_NAME);
|
||||
string_init_set_str(new_path, INFRARED_APP_FOLDER);
|
||||
|
||||
infrared_find_vacant_remote_name(new_name, string_get_cstr(new_path));
|
||||
string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION);
|
||||
|
||||
infrared_remote_reset(remote);
|
||||
infrared_remote_set_name(remote, string_get_cstr(new_name));
|
||||
infrared_remote_set_path(remote, string_get_cstr(new_path));
|
||||
|
||||
string_clear(new_name);
|
||||
string_clear(new_path);
|
||||
return infrared_remote_add_button(remote, button_name, signal);
|
||||
}
|
||||
|
||||
bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
const char* remote_path = infrared_remote_get_path(remote);
|
||||
|
||||
if(!strcmp(infrared_remote_get_name(remote), name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
string_t new_name;
|
||||
string_init_set_str(new_name, name);
|
||||
|
||||
infrared_find_vacant_remote_name(new_name, remote_path);
|
||||
|
||||
string_t new_path;
|
||||
string_init_set(new_path, infrared_remote_get_path(remote));
|
||||
if(string_end_with_str_p(new_path, INFRARED_APP_EXTENSION)) {
|
||||
size_t filename_start = string_search_rchar(new_path, '/');
|
||||
string_left(new_path, filename_start);
|
||||
}
|
||||
string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
FS_Error status = storage_common_rename(
|
||||
storage, infrared_remote_get_path(remote), string_get_cstr(new_path));
|
||||
infrared_remote_set_name(remote, string_get_cstr(new_name));
|
||||
infrared_remote_set_path(remote, string_get_cstr(new_path));
|
||||
|
||||
string_clear(new_name);
|
||||
string_clear(new_path);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return (status == FSE_OK || status == FSE_EXIST);
|
||||
}
|
||||
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
if(infrared->app_state.is_transmitting) {
|
||||
FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active");
|
||||
return;
|
||||
} else {
|
||||
infrared->app_state.is_transmitting = true;
|
||||
}
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size);
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_worker_set_decoded_signal(infrared->worker, message);
|
||||
}
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedIrSend);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
|
||||
infrared_worker_tx_set_get_signal_callback(
|
||||
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
|
||||
infrared_worker_tx_start(infrared->worker);
|
||||
}
|
||||
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
|
||||
furi_assert(button_index < infrared_remote_get_button_count(infrared->remote));
|
||||
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index);
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(button);
|
||||
|
||||
infrared_tx_start_signal(infrared, signal);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
void infrared_tx_start_received(Infrared* infrared) {
|
||||
infrared_tx_start_signal(infrared, infrared->received_signal);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
void infrared_tx_stop(Infrared* infrared) {
|
||||
if(!infrared->app_state.is_transmitting) {
|
||||
FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped");
|
||||
return;
|
||||
} else {
|
||||
infrared->app_state.is_transmitting = false;
|
||||
}
|
||||
|
||||
infrared_worker_tx_stop(infrared->worker);
|
||||
infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL);
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
}
|
||||
|
||||
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void infrared_text_store_clear(Infrared* infrared, uint32_t bank) {
|
||||
memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE);
|
||||
}
|
||||
|
||||
void infrared_play_notification_message(Infrared* infrared, uint32_t message) {
|
||||
furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*));
|
||||
notification_message(infrared->notifications, infrared_notification_sequences[message]);
|
||||
}
|
||||
|
||||
void infrared_show_loading_popup(Infrared* infrared, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
Loading* loading = infrared->loading;
|
||||
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
|
||||
view_stack_add_view(view_stack, loading_get_view(loading));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// Restore default timer priority
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
infrared_signal_set_message(
|
||||
infrared->received_signal, infrared_worker_get_decoded_signal(received_signal));
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_size;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_size);
|
||||
infrared_signal_set_raw_signal(
|
||||
infrared->received_signal,
|
||||
timings,
|
||||
timings_size,
|
||||
INFRARED_COMMON_CARRIER_FREQUENCY,
|
||||
INFRARED_COMMON_DUTY_CYCLE);
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived);
|
||||
}
|
||||
|
||||
void infrared_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone);
|
||||
}
|
||||
|
||||
void infrared_popup_closed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);
|
||||
}
|
||||
|
||||
int32_t infrared_app(void* p) {
|
||||
Infrared* infrared = infrared_alloc();
|
||||
|
||||
infrared_make_app_folder(infrared);
|
||||
|
||||
bool is_remote_loaded = false;
|
||||
bool is_rpc_mode = false;
|
||||
|
||||
if(p && strlen(p)) {
|
||||
uint32_t rpc_ctx = 0;
|
||||
if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
|
||||
infrared->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_system_app_set_callback(
|
||||
infrared->rpc_ctx, infrared_rpc_command_callback, infrared);
|
||||
rpc_system_app_send_started(infrared->rpc_ctx);
|
||||
is_rpc_mode = true;
|
||||
} else {
|
||||
string_set_str(infrared->file_path, (const char*)p);
|
||||
is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
if(!is_remote_loaded) {
|
||||
dialog_message_show_storage_error(
|
||||
infrared->dialogs, "Failed to load\nselected remote");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_rpc_mode) {
|
||||
view_dispatcher_attach_to_gui(
|
||||
infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc);
|
||||
} else {
|
||||
view_dispatcher_attach_to_gui(
|
||||
infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen);
|
||||
if(is_remote_loaded) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart);
|
||||
}
|
||||
}
|
||||
|
||||
view_dispatcher_run(infrared->view_dispatcher);
|
||||
|
||||
infrared_free(infrared);
|
||||
return 0;
|
||||
}
|
||||
3
applications/main/infrared/infrared.h
Normal file
3
applications/main/infrared/infrared.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Infrared Infrared;
|
||||
155
applications/main/infrared/infrared_brute_force.c
Normal file
155
applications/main/infrared/infrared_brute_force.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "infrared_brute_force.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <m-dict.h>
|
||||
#include <m-string.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#include "infrared_signal.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t index;
|
||||
uint32_t count;
|
||||
} InfraredBruteForceRecord;
|
||||
|
||||
DICT_DEF2(
|
||||
InfraredBruteForceRecordDict,
|
||||
string_t,
|
||||
STRING_OPLIST,
|
||||
InfraredBruteForceRecord,
|
||||
M_POD_OPLIST);
|
||||
|
||||
struct InfraredBruteForce {
|
||||
FlipperFormat* ff;
|
||||
const char* db_filename;
|
||||
string_t current_record_name;
|
||||
InfraredBruteForceRecordDict_t records;
|
||||
};
|
||||
|
||||
InfraredBruteForce* infrared_brute_force_alloc() {
|
||||
InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce));
|
||||
brute_force->ff = NULL;
|
||||
brute_force->db_filename = NULL;
|
||||
string_init(brute_force->current_record_name);
|
||||
InfraredBruteForceRecordDict_init(brute_force->records);
|
||||
return brute_force;
|
||||
}
|
||||
|
||||
void infrared_brute_force_free(InfraredBruteForce* brute_force) {
|
||||
furi_assert(!brute_force->ff);
|
||||
InfraredBruteForceRecordDict_clear(brute_force->records);
|
||||
string_clear(brute_force->current_record_name);
|
||||
free(brute_force);
|
||||
}
|
||||
|
||||
void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) {
|
||||
brute_force->db_filename = db_filename;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) {
|
||||
furi_assert(brute_force->db_filename);
|
||||
bool success = false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
|
||||
|
||||
success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename);
|
||||
if(success) {
|
||||
string_t signal_name;
|
||||
string_init(signal_name);
|
||||
while(flipper_format_read_string(ff, "name", signal_name)) {
|
||||
InfraredBruteForceRecord* record =
|
||||
InfraredBruteForceRecordDict_get(brute_force->records, signal_name);
|
||||
if(record) {
|
||||
++(record->count);
|
||||
}
|
||||
}
|
||||
string_clear(signal_name);
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_start(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
uint32_t* record_count) {
|
||||
bool success = false;
|
||||
*record_count = 0;
|
||||
|
||||
InfraredBruteForceRecordDict_it_t it;
|
||||
for(InfraredBruteForceRecordDict_it(it, brute_force->records);
|
||||
!InfraredBruteForceRecordDict_end_p(it);
|
||||
InfraredBruteForceRecordDict_next(it)) {
|
||||
const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it);
|
||||
if(record->value.index == index) {
|
||||
*record_count = record->value.count;
|
||||
if(*record_count) {
|
||||
string_set(brute_force->current_record_name, record->key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(*record_count) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
brute_force->ff = flipper_format_buffered_file_alloc(storage);
|
||||
success =
|
||||
flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename);
|
||||
if(!success) {
|
||||
flipper_format_free(brute_force->ff);
|
||||
brute_force->ff = NULL;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) {
|
||||
return brute_force->ff;
|
||||
}
|
||||
|
||||
void infrared_brute_force_stop(InfraredBruteForce* brute_force) {
|
||||
furi_assert(string_size(brute_force->current_record_name));
|
||||
furi_assert(brute_force->ff);
|
||||
|
||||
string_reset(brute_force->current_record_name);
|
||||
flipper_format_free(brute_force->ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
brute_force->ff = NULL;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) {
|
||||
furi_assert(string_size(brute_force->current_record_name));
|
||||
furi_assert(brute_force->ff);
|
||||
bool success = false;
|
||||
|
||||
string_t signal_name;
|
||||
string_init(signal_name);
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
|
||||
do {
|
||||
success = infrared_signal_read(signal, brute_force->ff, signal_name);
|
||||
} while(success && !string_equal_p(brute_force->current_record_name, signal_name));
|
||||
|
||||
if(success) {
|
||||
infrared_signal_transmit(signal);
|
||||
}
|
||||
|
||||
infrared_signal_free(signal);
|
||||
string_clear(signal_name);
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_brute_force_add_record(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
const char* name) {
|
||||
InfraredBruteForceRecord value = {.index = index, .count = 0};
|
||||
string_t key;
|
||||
string_init_set_str(key, name);
|
||||
InfraredBruteForceRecordDict_set_at(brute_force->records, key, value);
|
||||
string_clear(key);
|
||||
}
|
||||
22
applications/main/infrared/infrared_brute_force.h
Normal file
22
applications/main/infrared/infrared_brute_force.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct InfraredBruteForce InfraredBruteForce;
|
||||
|
||||
InfraredBruteForce* infrared_brute_force_alloc();
|
||||
void infrared_brute_force_free(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename);
|
||||
bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force);
|
||||
bool infrared_brute_force_start(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
uint32_t* record_count);
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_stop(InfraredBruteForce* brute_force);
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_add_record(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
const char* name);
|
||||
359
applications/main/infrared/infrared_cli.c
Normal file
359
applications/main/infrared/infrared_cli.c
Normal file
@@ -0,0 +1,359 @@
|
||||
#include <m-string.h>
|
||||
#include <cli/cli.h>
|
||||
#include <infrared.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <furi_hal_infrared.h>
|
||||
#include <flipper_format.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#include "infrared_signal.h"
|
||||
|
||||
#define INFRARED_CLI_BUF_SIZE 10
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
|
||||
static void infrared_cli_process_decode(Cli* cli, string_t args);
|
||||
|
||||
static const struct {
|
||||
const char* cmd;
|
||||
void (*process_function)(Cli* cli, string_t args);
|
||||
} infrared_cli_commands[] = {
|
||||
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
||||
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
||||
{.cmd = "decode", .process_function = infrared_cli_process_decode},
|
||||
};
|
||||
|
||||
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
furi_assert(received_signal);
|
||||
char buf[100];
|
||||
size_t buf_cnt;
|
||||
Cli* cli = (Cli*)context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
|
||||
buf_cnt = snprintf(
|
||||
buf,
|
||||
sizeof(buf),
|
||||
"%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_cnt;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
|
||||
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
for(size_t i = 0; i < timings_cnt; ++i) {
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
}
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "\r\n");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
InfraredWorker* worker = infrared_worker_alloc();
|
||||
infrared_worker_rx_start(worker);
|
||||
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
|
||||
|
||||
printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
infrared_worker_rx_stop(worker);
|
||||
infrared_worker_free(worker);
|
||||
}
|
||||
|
||||
static void infrared_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("\tir rx\r\n");
|
||||
printf("\tir tx <protocol> <address> <command>\r\n");
|
||||
printf("\t<command> and <address> are hex-formatted\r\n");
|
||||
printf("\tAvailable protocols:");
|
||||
for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) {
|
||||
printf(" %s", infrared_get_protocol_name((InfraredProtocol)i));
|
||||
}
|
||||
printf("\r\n");
|
||||
printf("\tRaw format:\r\n");
|
||||
printf("\tir tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n");
|
||||
printf(
|
||||
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
|
||||
INFRARED_MIN_FREQUENCY,
|
||||
INFRARED_MAX_FREQUENCY);
|
||||
printf("\tir decode <input_file> [<output_file>]\r\n");
|
||||
}
|
||||
|
||||
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
|
||||
char protocol_name[32];
|
||||
InfraredMessage message;
|
||||
int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command);
|
||||
|
||||
if(parsed != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
message.protocol = infrared_get_protocol_by_name(protocol_name);
|
||||
message.repeat = false;
|
||||
infrared_signal_set_message(signal, &message);
|
||||
return infrared_signal_is_valid(signal);
|
||||
}
|
||||
|
||||
static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) {
|
||||
char frequency_str[INFRARED_CLI_BUF_SIZE];
|
||||
char duty_cycle_str[INFRARED_CLI_BUF_SIZE];
|
||||
int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
|
||||
|
||||
if(parsed != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT);
|
||||
uint32_t frequency = atoi(frequency_str);
|
||||
float duty_cycle = (float)atoi(duty_cycle_str) / 100;
|
||||
|
||||
str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE;
|
||||
|
||||
size_t timings_size = 0;
|
||||
while(1) {
|
||||
while(*str == ' ') {
|
||||
++str;
|
||||
}
|
||||
|
||||
char timing_str[INFRARED_CLI_BUF_SIZE];
|
||||
if(sscanf(str, "%9s", timing_str) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
str += strlen(timing_str);
|
||||
uint32_t timing = atoi(timing_str);
|
||||
|
||||
if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) {
|
||||
break;
|
||||
}
|
||||
|
||||
timings[timings_size] = timing;
|
||||
++timings_size;
|
||||
}
|
||||
|
||||
infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
|
||||
free(timings);
|
||||
|
||||
return infrared_signal_is_valid(signal);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
const char* str = string_get_cstr(args);
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
|
||||
bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal);
|
||||
if(success) {
|
||||
infrared_signal_transmit(signal);
|
||||
} else {
|
||||
printf("Wrong arguments.\r\n");
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
|
||||
infrared_signal_free(signal);
|
||||
}
|
||||
|
||||
static bool
|
||||
infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
|
||||
bool ret = infrared_signal_save(signal, file, name);
|
||||
if(!ret) {
|
||||
printf("Failed to save signal: \"%s\"\r\n", name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_raw_signal(
|
||||
InfraredRawSignal* raw_signal,
|
||||
InfraredDecoderHandler* decoder,
|
||||
FlipperFormat* output_file,
|
||||
const char* signal_name) {
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
bool ret = false, level = true, is_decoded = false;
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < raw_signal->timings_size; ++i) {
|
||||
// TODO: Any infrared_check_decoder_ready() magic?
|
||||
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
|
||||
|
||||
if(message) {
|
||||
is_decoded = true;
|
||||
printf(
|
||||
"Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
message->address,
|
||||
message->command,
|
||||
(message->repeat ? "R" : ""));
|
||||
if(output_file && !message->repeat) {
|
||||
infrared_signal_set_message(signal, message);
|
||||
if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
|
||||
}
|
||||
}
|
||||
|
||||
level = !level;
|
||||
}
|
||||
|
||||
if(i == raw_signal->timings_size) {
|
||||
if(!is_decoded && output_file) {
|
||||
infrared_signal_set_raw_signal(
|
||||
signal,
|
||||
raw_signal->timings,
|
||||
raw_signal->timings_size,
|
||||
raw_signal->frequency,
|
||||
raw_signal->duty_cycle);
|
||||
ret = infrared_cli_save_signal(signal, output_file, signal_name);
|
||||
} else {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
infrared_reset_decoder(decoder);
|
||||
infrared_signal_free(signal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
|
||||
bool ret = false;
|
||||
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
InfraredDecoderHandler* decoder = infrared_alloc_decoder();
|
||||
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
|
||||
while(infrared_signal_read(signal, input_file, tmp)) {
|
||||
ret = false;
|
||||
if(!infrared_signal_is_valid(signal)) {
|
||||
printf("Invalid signal\r\n");
|
||||
break;
|
||||
}
|
||||
if(!infrared_signal_is_raw(signal)) {
|
||||
if(output_file &&
|
||||
!infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) {
|
||||
break;
|
||||
} else {
|
||||
printf("Skipping decoded signal\r\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size);
|
||||
if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp)))
|
||||
break;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
infrared_free_decoder(decoder);
|
||||
infrared_signal_free(signal);
|
||||
string_clear(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void infrared_cli_process_decode(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
|
||||
FlipperFormat* output_file = NULL;
|
||||
|
||||
uint32_t version;
|
||||
string_t tmp, header, input_path, output_path;
|
||||
string_init(tmp);
|
||||
string_init(header);
|
||||
string_init(input_path);
|
||||
string_init(output_path);
|
||||
|
||||
do {
|
||||
if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
|
||||
printf("Wrong arguments.\r\n");
|
||||
infrared_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
args_read_probably_quoted_string_and_trim(args, output_path);
|
||||
if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) {
|
||||
printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path));
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_header(input_file, header, &version) ||
|
||||
(!string_start_with_str_p(header, "IR")) || version != 1) {
|
||||
printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path));
|
||||
break;
|
||||
}
|
||||
if(!string_empty_p(output_path)) {
|
||||
printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
output_file = flipper_format_file_alloc(storage);
|
||||
}
|
||||
if(output_file &&
|
||||
!flipper_format_file_open_always(output_file, string_get_cstr(output_path))) {
|
||||
printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
break;
|
||||
}
|
||||
if(output_file && !flipper_format_write_header(output_file, header, version)) {
|
||||
printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
break;
|
||||
}
|
||||
if(!infrared_cli_decode_file(input_file, output_file)) {
|
||||
break;
|
||||
}
|
||||
printf("File successfully decoded.\r\n");
|
||||
} while(false);
|
||||
|
||||
string_clear(tmp);
|
||||
string_clear(header);
|
||||
string_clear(input_path);
|
||||
string_clear(output_path);
|
||||
|
||||
flipper_format_free(input_file);
|
||||
if(output_file) flipper_format_free(output_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(context);
|
||||
if(furi_hal_infrared_is_busy()) {
|
||||
printf("INFRARED is busy. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
string_t command;
|
||||
string_init(command);
|
||||
args_read_string_and_trim(args, command);
|
||||
|
||||
size_t i = 0;
|
||||
for(; i < COUNT_OF(infrared_cli_commands); ++i) {
|
||||
size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
|
||||
if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i < COUNT_OF(infrared_cli_commands)) {
|
||||
infrared_cli_commands[i].process_function(cli, args);
|
||||
} else {
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
|
||||
string_clear(command);
|
||||
}
|
||||
void infrared_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = (Cli*)furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(infrared_cli_start_ir);
|
||||
#endif
|
||||
}
|
||||
57
applications/main/infrared/infrared_custom_event.h
Normal file
57
applications/main/infrared/infrared_custom_event.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum InfraredCustomEventType {
|
||||
// Reserve first 100 events for button types and indexes, starting from 0
|
||||
InfraredCustomEventTypeReserved = 100,
|
||||
InfraredCustomEventTypeMenuSelected,
|
||||
InfraredCustomEventTypeTransmitStarted,
|
||||
InfraredCustomEventTypeTransmitStopped,
|
||||
InfraredCustomEventTypeSignalReceived,
|
||||
InfraredCustomEventTypeTextEditDone,
|
||||
InfraredCustomEventTypePopupClosed,
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
|
||||
InfraredCustomEventTypeRpcLoad,
|
||||
InfraredCustomEventTypeRpcExit,
|
||||
InfraredCustomEventTypeRpcButtonPress,
|
||||
InfraredCustomEventTypeRpcButtonRelease,
|
||||
InfraredCustomEventTypeRpcSessionClose,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef union {
|
||||
uint32_t packed_value;
|
||||
struct {
|
||||
uint16_t type;
|
||||
int16_t value;
|
||||
} content;
|
||||
} InfraredCustomEvent;
|
||||
#pragma pack(pop)
|
||||
|
||||
static inline uint32_t infrared_custom_event_pack(uint16_t type, int16_t value) {
|
||||
InfraredCustomEvent event = {.content = {.type = type, .value = value}};
|
||||
return event.packed_value;
|
||||
}
|
||||
|
||||
static inline void
|
||||
infrared_custom_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) {
|
||||
InfraredCustomEvent event = {.packed_value = packed_value};
|
||||
if(type) *type = event.content.type;
|
||||
if(value) *value = event.content.value;
|
||||
}
|
||||
|
||||
static inline uint16_t infrared_custom_event_get_type(uint32_t packed_value) {
|
||||
uint16_t type;
|
||||
infrared_custom_event_unpack(packed_value, &type, NULL);
|
||||
return type;
|
||||
}
|
||||
|
||||
static inline int16_t infrared_custom_event_get_value(uint32_t packed_value) {
|
||||
int16_t value;
|
||||
infrared_custom_event_unpack(packed_value, NULL, &value);
|
||||
return value;
|
||||
}
|
||||
140
applications/main/infrared/infrared_i.h
Normal file
140
applications/main/infrared/infrared_i.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <infrared_worker.h>
|
||||
|
||||
#include "infrared.h"
|
||||
#include "infrared_remote.h"
|
||||
#include "infrared_brute_force.h"
|
||||
#include "infrared_custom_event.h"
|
||||
|
||||
#include "scenes/infrared_scene.h"
|
||||
#include "views/infrared_progress_view.h"
|
||||
#include "views/infrared_debug_view.h"
|
||||
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#define INFRARED_FILE_NAME_SIZE 100
|
||||
#define INFRARED_TEXT_STORE_NUM 2
|
||||
#define INFRARED_TEXT_STORE_SIZE 128
|
||||
|
||||
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
|
||||
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
|
||||
|
||||
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
|
||||
#define INFRARED_APP_EXTENSION ".ir"
|
||||
|
||||
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
|
||||
#define INFRARED_LOG_TAG "InfraredApp"
|
||||
|
||||
typedef enum {
|
||||
InfraredButtonIndexNone = -1,
|
||||
} InfraredButtonIndex;
|
||||
|
||||
typedef enum {
|
||||
InfraredEditTargetNone,
|
||||
InfraredEditTargetRemote,
|
||||
InfraredEditTargetButton,
|
||||
} InfraredEditTarget;
|
||||
|
||||
typedef enum {
|
||||
InfraredEditModeNone,
|
||||
InfraredEditModeRename,
|
||||
InfraredEditModeDelete,
|
||||
} InfraredEditMode;
|
||||
|
||||
typedef struct {
|
||||
bool is_learning_new_remote;
|
||||
bool is_debug_enabled;
|
||||
bool is_transmitting;
|
||||
InfraredEditTarget edit_target : 8;
|
||||
InfraredEditMode edit_mode : 8;
|
||||
int32_t current_button_index;
|
||||
} InfraredAppState;
|
||||
|
||||
struct Infrared {
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
Gui* gui;
|
||||
Storage* storage;
|
||||
DialogsApp* dialogs;
|
||||
NotificationApp* notifications;
|
||||
InfraredWorker* worker;
|
||||
InfraredRemote* remote;
|
||||
InfraredSignal* received_signal;
|
||||
InfraredBruteForce* brute_force;
|
||||
|
||||
Submenu* submenu;
|
||||
TextInput* text_input;
|
||||
DialogEx* dialog_ex;
|
||||
ButtonMenu* button_menu;
|
||||
Popup* popup;
|
||||
|
||||
ViewStack* view_stack;
|
||||
InfraredDebugView* debug_view;
|
||||
|
||||
ButtonPanel* button_panel;
|
||||
Loading* loading;
|
||||
InfraredProgressView* progress;
|
||||
|
||||
string_t file_path;
|
||||
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
|
||||
InfraredAppState app_state;
|
||||
|
||||
void* rpc_ctx;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
InfraredViewSubmenu,
|
||||
InfraredViewTextInput,
|
||||
InfraredViewDialogEx,
|
||||
InfraredViewButtonMenu,
|
||||
InfraredViewPopup,
|
||||
InfraredViewStack,
|
||||
InfraredViewDebugView,
|
||||
} InfraredView;
|
||||
|
||||
typedef enum {
|
||||
InfraredNotificationMessageSuccess,
|
||||
InfraredNotificationMessageGreenOn,
|
||||
InfraredNotificationMessageGreenOff,
|
||||
InfraredNotificationMessageYellowOn,
|
||||
InfraredNotificationMessageYellowOff,
|
||||
InfraredNotificationMessageBlinkStartRead,
|
||||
InfraredNotificationMessageBlinkStartSend,
|
||||
InfraredNotificationMessageBlinkStop,
|
||||
} InfraredNotificationMessage;
|
||||
|
||||
bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal);
|
||||
bool infrared_rename_current_remote(Infrared* infrared, const char* name);
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal);
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index);
|
||||
void infrared_tx_start_received(Infrared* infrared);
|
||||
void infrared_tx_stop(Infrared* infrared);
|
||||
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...);
|
||||
void infrared_text_store_clear(Infrared* infrared, uint32_t bank);
|
||||
void infrared_play_notification_message(Infrared* infrared, uint32_t message);
|
||||
void infrared_show_loading_popup(Infrared* infrared, bool show);
|
||||
|
||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);
|
||||
void infrared_text_input_callback(void* context);
|
||||
void infrared_popup_closed_callback(void* context);
|
||||
189
applications/main/infrared/infrared_remote.c
Normal file
189
applications/main/infrared/infrared_remote.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "infrared_remote.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <m-string.h>
|
||||
#include <m-array.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <storage/storage.h>
|
||||
#include <core/common_defines.h>
|
||||
|
||||
#define TAG "InfraredRemote"
|
||||
|
||||
ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST);
|
||||
|
||||
struct InfraredRemote {
|
||||
InfraredButtonArray_t buttons;
|
||||
string_t name;
|
||||
string_t path;
|
||||
};
|
||||
|
||||
static void infrared_remote_clear_buttons(InfraredRemote* remote) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
infrared_remote_button_free(*InfraredButtonArray_cref(it));
|
||||
}
|
||||
InfraredButtonArray_reset(remote->buttons);
|
||||
}
|
||||
|
||||
InfraredRemote* infrared_remote_alloc() {
|
||||
InfraredRemote* remote = malloc(sizeof(InfraredRemote));
|
||||
InfraredButtonArray_init(remote->buttons);
|
||||
string_init(remote->name);
|
||||
string_init(remote->path);
|
||||
return remote;
|
||||
}
|
||||
|
||||
void infrared_remote_free(InfraredRemote* remote) {
|
||||
infrared_remote_clear_buttons(remote);
|
||||
InfraredButtonArray_clear(remote->buttons);
|
||||
string_clear(remote->path);
|
||||
string_clear(remote->name);
|
||||
free(remote);
|
||||
}
|
||||
|
||||
void infrared_remote_reset(InfraredRemote* remote) {
|
||||
infrared_remote_clear_buttons(remote);
|
||||
string_reset(remote->name);
|
||||
string_reset(remote->path);
|
||||
}
|
||||
|
||||
void infrared_remote_set_name(InfraredRemote* remote, const char* name) {
|
||||
string_set_str(remote->name, name);
|
||||
}
|
||||
|
||||
const char* infrared_remote_get_name(InfraredRemote* remote) {
|
||||
return string_get_cstr(remote->name);
|
||||
}
|
||||
|
||||
void infrared_remote_set_path(InfraredRemote* remote, const char* path) {
|
||||
string_set_str(remote->path, path);
|
||||
}
|
||||
|
||||
const char* infrared_remote_get_path(InfraredRemote* remote) {
|
||||
return string_get_cstr(remote->path);
|
||||
}
|
||||
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote) {
|
||||
return InfraredButtonArray_size(remote->buttons);
|
||||
}
|
||||
|
||||
InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
return *InfraredButtonArray_get(remote->buttons, index);
|
||||
}
|
||||
|
||||
bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
|
||||
for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
|
||||
if(!strcmp(infrared_remote_button_get_name(button), name)) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
infrared_remote_button_set_name(button, name);
|
||||
infrared_remote_button_set_signal(button, signal);
|
||||
InfraredButtonArray_push_back(remote->buttons, button);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index);
|
||||
infrared_remote_button_set_name(button, new_name);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
InfraredRemoteButton* button;
|
||||
InfraredButtonArray_pop_at(&button, remote->buttons, index);
|
||||
infrared_remote_button_free(button);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
bool infrared_remote_store(InfraredRemote* remote) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
const char* path = string_get_cstr(remote->path);
|
||||
|
||||
FURI_LOG_I(TAG, "store file: \'%s\'", path);
|
||||
|
||||
bool success = flipper_format_file_open_always(ff, path) &&
|
||||
flipper_format_write_header_cstr(ff, "IR signals file", 1);
|
||||
if(success) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_cref(it);
|
||||
success = infrared_signal_save(
|
||||
infrared_remote_button_get_signal(button),
|
||||
ff,
|
||||
infrared_remote_button_get_name(button));
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_load(InfraredRemote* remote, string_t path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
|
||||
FURI_LOG_I(TAG, "load file: \'%s\'", string_get_cstr(path));
|
||||
bool success = flipper_format_buffered_file_open_existing(ff, string_get_cstr(path));
|
||||
|
||||
if(success) {
|
||||
uint32_t version;
|
||||
success = flipper_format_read_header(ff, buf, &version) &&
|
||||
!string_cmp_str(buf, "IR signals file") && (version == 1);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
path_extract_filename(path, buf, true);
|
||||
infrared_remote_clear_buttons(remote);
|
||||
infrared_remote_set_name(remote, string_get_cstr(buf));
|
||||
infrared_remote_set_path(remote, string_get_cstr(path));
|
||||
|
||||
for(bool can_read = true; can_read;) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf);
|
||||
if(can_read) {
|
||||
infrared_remote_button_set_name(button, string_get_cstr(buf));
|
||||
InfraredButtonArray_push_back(remote->buttons, button);
|
||||
} else {
|
||||
infrared_remote_button_free(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buf);
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_remove(InfraredRemote* remote) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
FS_Error status = storage_common_remove(storage, string_get_cstr(remote->path));
|
||||
infrared_remote_reset(remote);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return (status == FSE_OK || status == FSE_NOT_EXIST);
|
||||
}
|
||||
29
applications/main/infrared/infrared_remote.h
Normal file
29
applications/main/infrared/infrared_remote.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "infrared_remote_button.h"
|
||||
|
||||
typedef struct InfraredRemote InfraredRemote;
|
||||
|
||||
InfraredRemote* infrared_remote_alloc();
|
||||
void infrared_remote_free(InfraredRemote* remote);
|
||||
void infrared_remote_reset(InfraredRemote* remote);
|
||||
|
||||
void infrared_remote_set_name(InfraredRemote* remote, const char* name);
|
||||
const char* infrared_remote_get_name(InfraredRemote* remote);
|
||||
|
||||
void infrared_remote_set_path(InfraredRemote* remote, const char* path);
|
||||
const char* infrared_remote_get_path(InfraredRemote* remote);
|
||||
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote);
|
||||
InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index);
|
||||
bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index);
|
||||
|
||||
bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal);
|
||||
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index);
|
||||
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index);
|
||||
|
||||
bool infrared_remote_store(InfraredRemote* remote);
|
||||
bool infrared_remote_load(InfraredRemote* remote, string_t path);
|
||||
bool infrared_remote_remove(InfraredRemote* remote);
|
||||
38
applications/main/infrared/infrared_remote_button.c
Normal file
38
applications/main/infrared/infrared_remote_button.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "infrared_remote_button.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <m-string.h>
|
||||
|
||||
struct InfraredRemoteButton {
|
||||
string_t name;
|
||||
InfraredSignal* signal;
|
||||
};
|
||||
|
||||
InfraredRemoteButton* infrared_remote_button_alloc() {
|
||||
InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton));
|
||||
string_init(button->name);
|
||||
button->signal = infrared_signal_alloc();
|
||||
return button;
|
||||
}
|
||||
|
||||
void infrared_remote_button_free(InfraredRemoteButton* button) {
|
||||
string_clear(button->name);
|
||||
infrared_signal_free(button->signal);
|
||||
free(button);
|
||||
}
|
||||
|
||||
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) {
|
||||
string_set_str(button->name, name);
|
||||
}
|
||||
|
||||
const char* infrared_remote_button_get_name(InfraredRemoteButton* button) {
|
||||
return string_get_cstr(button->name);
|
||||
}
|
||||
|
||||
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
|
||||
infrared_signal_set_signal(button->signal, signal);
|
||||
}
|
||||
|
||||
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) {
|
||||
return button->signal;
|
||||
}
|
||||
14
applications/main/infrared/infrared_remote_button.h
Normal file
14
applications/main/infrared/infrared_remote_button.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "infrared_signal.h"
|
||||
|
||||
typedef struct InfraredRemoteButton InfraredRemoteButton;
|
||||
|
||||
InfraredRemoteButton* infrared_remote_button_alloc();
|
||||
void infrared_remote_button_free(InfraredRemoteButton* button);
|
||||
|
||||
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
|
||||
const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
|
||||
|
||||
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
|
||||
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);
|
||||
264
applications/main/infrared/infrared_signal.c
Normal file
264
applications/main/infrared/infrared_signal.c
Normal file
@@ -0,0 +1,264 @@
|
||||
#include "infrared_signal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <core/check.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <infrared_transmit.h>
|
||||
|
||||
#define TAG "InfraredSignal"
|
||||
|
||||
struct InfraredSignal {
|
||||
bool is_raw;
|
||||
union {
|
||||
InfraredMessage message;
|
||||
InfraredRawSignal raw;
|
||||
} payload;
|
||||
};
|
||||
|
||||
static void infrared_signal_clear_timings(InfraredSignal* signal) {
|
||||
if(signal->is_raw) {
|
||||
free(signal->payload.raw.timings);
|
||||
signal->payload.raw.timings_size = 0;
|
||||
signal->payload.raw.timings = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool infrared_signal_is_message_valid(InfraredMessage* message) {
|
||||
if(!infrared_is_protocol_valid(message->protocol)) {
|
||||
FURI_LOG_E(TAG, "Unknown protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t address_length = infrared_get_protocol_address_length(message->protocol);
|
||||
uint32_t address_mask = (1UL << address_length) - 1;
|
||||
|
||||
if(message->address != (message->address & address_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Address is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
address_mask,
|
||||
message->address);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t command_length = infrared_get_protocol_command_length(message->protocol);
|
||||
uint32_t command_mask = (1UL << command_length) - 1;
|
||||
|
||||
if(message->command != (message->command & command_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Command is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
command_mask,
|
||||
message->command);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
|
||||
if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Frequency is out of range (%lX - %lX): %lX",
|
||||
INFRARED_MIN_FREQUENCY,
|
||||
INFRARED_MAX_FREQUENCY,
|
||||
raw->frequency);
|
||||
return false;
|
||||
|
||||
} else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) {
|
||||
FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle);
|
||||
return false;
|
||||
|
||||
} else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Timings amount is out of range (0 - %lX): %lX",
|
||||
MAX_TIMINGS_AMOUNT,
|
||||
raw->timings_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
|
||||
const char* protocol_name = infrared_get_protocol_name(message->protocol);
|
||||
return flipper_format_write_string_cstr(ff, "type", "parsed") &&
|
||||
flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
|
||||
flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) &&
|
||||
flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
|
||||
furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
|
||||
return flipper_format_write_string_cstr(ff, "type", "raw") &&
|
||||
flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
|
||||
flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) &&
|
||||
flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size);
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "protocol", buf)) break;
|
||||
|
||||
InfraredMessage message;
|
||||
message.protocol = infrared_get_protocol_by_name(string_get_cstr(buf));
|
||||
|
||||
success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) &&
|
||||
flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) &&
|
||||
infrared_signal_is_message_valid(&message);
|
||||
|
||||
if(!success) break;
|
||||
|
||||
infrared_signal_set_message(signal, &message);
|
||||
} while(0);
|
||||
|
||||
string_clear(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {
|
||||
uint32_t timings_size, frequency;
|
||||
float duty_cycle;
|
||||
|
||||
bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) &&
|
||||
flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) &&
|
||||
flipper_format_get_value_count(ff, "data", &timings_size);
|
||||
|
||||
if(!success || timings_size > MAX_TIMINGS_AMOUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);
|
||||
success = flipper_format_read_uint32(ff, "data", timings, timings_size);
|
||||
|
||||
if(success) {
|
||||
infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
|
||||
}
|
||||
|
||||
free(timings);
|
||||
return success;
|
||||
}
|
||||
|
||||
InfraredSignal* infrared_signal_alloc() {
|
||||
InfraredSignal* signal = malloc(sizeof(InfraredSignal));
|
||||
|
||||
signal->is_raw = false;
|
||||
signal->payload.message.protocol = InfraredProtocolUnknown;
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
void infrared_signal_free(InfraredSignal* signal) {
|
||||
infrared_signal_clear_timings(signal);
|
||||
free(signal);
|
||||
}
|
||||
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal) {
|
||||
return signal->is_raw;
|
||||
}
|
||||
|
||||
bool infrared_signal_is_valid(InfraredSignal* signal) {
|
||||
return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
|
||||
infrared_signal_is_message_valid(&signal->payload.message);
|
||||
}
|
||||
|
||||
void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
|
||||
if(other->is_raw) {
|
||||
const InfraredRawSignal* raw = &other->payload.raw;
|
||||
infrared_signal_set_raw_signal(
|
||||
signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
|
||||
} else {
|
||||
const InfraredMessage* message = &other->payload.message;
|
||||
infrared_signal_set_message(signal, message);
|
||||
}
|
||||
}
|
||||
|
||||
void infrared_signal_set_raw_signal(
|
||||
InfraredSignal* signal,
|
||||
const uint32_t* timings,
|
||||
size_t timings_size,
|
||||
uint32_t frequency,
|
||||
float duty_cycle) {
|
||||
infrared_signal_clear_timings(signal);
|
||||
|
||||
signal->is_raw = true;
|
||||
|
||||
signal->payload.raw.timings_size = timings_size;
|
||||
signal->payload.raw.frequency = frequency;
|
||||
signal->payload.raw.duty_cycle = duty_cycle;
|
||||
|
||||
signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
|
||||
memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
|
||||
furi_assert(signal->is_raw);
|
||||
return &signal->payload.raw;
|
||||
}
|
||||
|
||||
void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
|
||||
infrared_signal_clear_timings(signal);
|
||||
|
||||
signal->is_raw = false;
|
||||
signal->payload.message = *message;
|
||||
}
|
||||
|
||||
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
|
||||
furi_assert(!signal->is_raw);
|
||||
return &signal->payload.message;
|
||||
}
|
||||
|
||||
bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
|
||||
if(!flipper_format_write_comment_cstr(ff, "") ||
|
||||
!flipper_format_write_string_cstr(ff, "name", name)) {
|
||||
return false;
|
||||
} else if(signal->is_raw) {
|
||||
return infrared_signal_save_raw(&signal->payload.raw, ff);
|
||||
} else {
|
||||
return infrared_signal_save_message(&signal->payload.message, ff);
|
||||
}
|
||||
}
|
||||
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "name", buf)) break;
|
||||
string_set(name, buf);
|
||||
if(!flipper_format_read_string(ff, "type", buf)) break;
|
||||
if(!string_cmp_str(buf, "raw")) {
|
||||
success = infrared_signal_read_raw(signal, ff);
|
||||
} else if(!string_cmp_str(buf, "parsed")) {
|
||||
success = infrared_signal_read_message(signal, ff);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) ");
|
||||
}
|
||||
} while(0);
|
||||
|
||||
string_clear(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_signal_transmit(InfraredSignal* signal) {
|
||||
if(signal->is_raw) {
|
||||
InfraredRawSignal* raw_signal = &signal->payload.raw;
|
||||
infrared_send_raw_ext(
|
||||
raw_signal->timings,
|
||||
raw_signal->timings_size,
|
||||
true,
|
||||
raw_signal->frequency,
|
||||
raw_signal->duty_cycle);
|
||||
} else {
|
||||
InfraredMessage* message = &signal->payload.message;
|
||||
infrared_send(message, 1);
|
||||
}
|
||||
}
|
||||
41
applications/main/infrared/infrared_signal.h
Normal file
41
applications/main/infrared/infrared_signal.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <infrared.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
typedef struct InfraredSignal InfraredSignal;
|
||||
|
||||
typedef struct {
|
||||
size_t timings_size;
|
||||
uint32_t* timings;
|
||||
uint32_t frequency;
|
||||
float duty_cycle;
|
||||
} InfraredRawSignal;
|
||||
|
||||
InfraredSignal* infrared_signal_alloc();
|
||||
void infrared_signal_free(InfraredSignal* signal);
|
||||
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal);
|
||||
bool infrared_signal_is_valid(InfraredSignal* signal);
|
||||
|
||||
void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other);
|
||||
|
||||
void infrared_signal_set_raw_signal(
|
||||
InfraredSignal* signal,
|
||||
const uint32_t* timings,
|
||||
size_t timings_size,
|
||||
uint32_t frequency,
|
||||
float duty_cycle);
|
||||
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal);
|
||||
|
||||
void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message);
|
||||
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
|
||||
|
||||
bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name);
|
||||
|
||||
void infrared_signal_transmit(InfraredSignal* signal);
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "../../infrared_i.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_progress_back_callback(void* context) {
|
||||
Infrared* infrared = context;
|
||||
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
InfraredProgressView* progress = infrared->progress;
|
||||
infrared_progress_view_set_progress_total(progress, record_count);
|
||||
infrared_progress_view_set_back_callback(
|
||||
progress, infrared_scene_universal_common_progress_back_callback, infrared);
|
||||
view_stack_add_view(view_stack, infrared_progress_view_get_view(progress));
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
InfraredProgressView* progress = infrared->progress;
|
||||
view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress));
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
bool consumed = false;
|
||||
|
||||
if(infrared_brute_force_is_started(brute_force)) {
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
bool success = infrared_brute_force_send_next(brute_force);
|
||||
if(success) {
|
||||
success = infrared_progress_view_increase_progress(infrared->progress);
|
||||
}
|
||||
if(!success) {
|
||||
infrared_brute_force_stop(brute_force);
|
||||
infrared_scene_universal_common_hide_popup(infrared);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) {
|
||||
infrared_brute_force_stop(brute_force);
|
||||
infrared_scene_universal_common_hide_popup(infrared);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(infrared_custom_event_get_type(event.event) ==
|
||||
InfraredCustomEventTypeButtonSelected) {
|
||||
uint32_t record_count;
|
||||
if(infrared_brute_force_start(
|
||||
brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
|
||||
DOLPHIN_DEED(DolphinDeedIrBruteForce);
|
||||
infrared_scene_universal_common_show_popup(infrared, record_count);
|
||||
} else {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_universal_common_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
|
||||
button_panel_reset(button_panel);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context);
|
||||
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event);
|
||||
void infrared_scene_universal_common_on_exit(void* context);
|
||||
void infrared_scene_universal_common_item_callback(void* context, uint32_t index);
|
||||
30
applications/main/infrared/scenes/infrared_scene.c
Normal file
30
applications/main/infrared/scenes/infrared_scene.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "infrared_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const infrared_on_enter_handlers[])(void*) = {
|
||||
#include "infrared_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const infrared_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "infrared_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const infrared_on_exit_handlers[])(void* context) = {
|
||||
#include "infrared_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers infrared_scene_handlers = {
|
||||
.on_enter_handlers = infrared_on_enter_handlers,
|
||||
.on_event_handlers = infrared_on_event_handlers,
|
||||
.on_exit_handlers = infrared_on_exit_handlers,
|
||||
.scene_num = InfraredSceneNum,
|
||||
};
|
||||
29
applications/main/infrared/scenes/infrared_scene.h
Normal file
29
applications/main/infrared/scenes/infrared_scene.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) InfraredScene##id,
|
||||
typedef enum {
|
||||
#include "infrared_scene_config.h"
|
||||
InfraredSceneNum,
|
||||
} InfraredScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers infrared_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "infrared_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "infrared_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "infrared_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
59
applications/main/infrared/scenes/infrared_scene_ask_back.c
Normal file
59
applications/main/infrared/scenes/infrared_scene_ask_back.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_back_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop);
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Stay");
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
}
|
||||
|
||||
bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultLeft) {
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneStart);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemote);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultRight) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_ask_back_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
48
applications/main/infrared/scenes/infrared_scene_ask_retry.c
Normal file
48
applications/main/infrared/scenes/infrared_scene_ask_retry.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_retry_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Stay");
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
}
|
||||
|
||||
bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultLeft) {
|
||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultRight) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_ask_retry_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
20
applications/main/infrared/scenes/infrared_scene_config.h
Normal file
20
applications/main/infrared/scenes/infrared_scene_config.h
Normal file
@@ -0,0 +1,20 @@
|
||||
ADD_SCENE(infrared, start, Start)
|
||||
ADD_SCENE(infrared, ask_back, AskBack)
|
||||
ADD_SCENE(infrared, ask_retry, AskRetry)
|
||||
ADD_SCENE(infrared, edit, Edit)
|
||||
ADD_SCENE(infrared, edit_delete, EditDelete)
|
||||
ADD_SCENE(infrared, edit_delete_done, EditDeleteDone)
|
||||
ADD_SCENE(infrared, edit_button_select, EditButtonSelect)
|
||||
ADD_SCENE(infrared, edit_rename, EditRename)
|
||||
ADD_SCENE(infrared, edit_rename_done, EditRenameDone)
|
||||
ADD_SCENE(infrared, learn, Learn)
|
||||
ADD_SCENE(infrared, learn_done, LearnDone)
|
||||
ADD_SCENE(infrared, learn_enter_name, LearnEnterName)
|
||||
ADD_SCENE(infrared, learn_success, LearnSuccess)
|
||||
ADD_SCENE(infrared, remote, Remote)
|
||||
ADD_SCENE(infrared, remote_list, RemoteList)
|
||||
ADD_SCENE(infrared, universal, Universal)
|
||||
ADD_SCENE(infrared, universal_tv, UniversalTV)
|
||||
ADD_SCENE(infrared, debug, Debug)
|
||||
ADD_SCENE(infrared, error_databases, ErrorDatabases)
|
||||
ADD_SCENE(infrared, rpc, Rpc)
|
||||
69
applications/main/infrared/scenes/infrared_scene_debug.c
Normal file
69
applications/main/infrared/scenes/infrared_scene_debug.c
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_debug_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
|
||||
infrared_worker_rx_set_received_signal_callback(
|
||||
worker, infrared_signal_received_callback, context);
|
||||
infrared_worker_rx_enable_blink_on_receiving(worker, true);
|
||||
infrared_worker_rx_start(worker);
|
||||
|
||||
infrared_debug_view_set_text(infrared->debug_view, "Received signals\nwill appear here");
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDebugView);
|
||||
}
|
||||
|
||||
bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeSignalReceived) {
|
||||
InfraredDebugView* debug_view = infrared->debug_view;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size);
|
||||
|
||||
printf("RAW, %d samples:\r\n", raw->timings_size);
|
||||
for(size_t i = 0; i < raw->timings_size; ++i) {
|
||||
printf("%lu ", raw->timings[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_debug_view_set_text(
|
||||
debug_view,
|
||||
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
|
||||
printf(
|
||||
"== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_debug_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
infrared_worker_rx_stop(worker);
|
||||
infrared_worker_rx_enable_blink_on_receiving(worker, false);
|
||||
infrared_worker_rx_set_received_signal_callback(worker, NULL, NULL);
|
||||
}
|
||||
101
applications/main/infrared/scenes/infrared_scene_edit.c
Normal file
101
applications/main/infrared/scenes/infrared_scene_edit.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexAddButton,
|
||||
SubmenuIndexRenameButton,
|
||||
SubmenuIndexDeleteButton,
|
||||
SubmenuIndexRenameRemote,
|
||||
SubmenuIndexDeleteRemote,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Add Button",
|
||||
SubmenuIndexAddButton,
|
||||
infrared_scene_edit_submenu_callback,
|
||||
context);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Rename Button",
|
||||
SubmenuIndexRenameButton,
|
||||
infrared_scene_edit_submenu_callback,
|
||||
context);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Delete Button",
|
||||
SubmenuIndexDeleteButton,
|
||||
infrared_scene_edit_submenu_callback,
|
||||
context);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Rename Remote",
|
||||
SubmenuIndexRenameRemote,
|
||||
infrared_scene_edit_submenu_callback,
|
||||
context);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Delete Remote",
|
||||
SubmenuIndexDeleteRemote,
|
||||
infrared_scene_edit_submenu_callback,
|
||||
context);
|
||||
|
||||
const uint32_t submenu_index = scene_manager_get_scene_state(scene_manager, InfraredSceneEdit);
|
||||
submenu_set_selected_item(submenu, submenu_index);
|
||||
scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, SubmenuIndexAddButton);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
const uint32_t submenu_index = event.event;
|
||||
scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, submenu_index);
|
||||
|
||||
if(submenu_index == SubmenuIndexAddButton) {
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexRenameButton) {
|
||||
infrared->app_state.edit_target = InfraredEditTargetButton;
|
||||
infrared->app_state.edit_mode = InfraredEditModeRename;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexDeleteButton) {
|
||||
infrared->app_state.edit_target = InfraredEditTargetButton;
|
||||
infrared->app_state.edit_mode = InfraredEditModeDelete;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexRenameRemote) {
|
||||
infrared->app_state.edit_target = InfraredEditTargetRemote;
|
||||
infrared->app_state.edit_mode = InfraredEditModeRename;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRename);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexDeleteRemote) {
|
||||
infrared->app_state.edit_target = InfraredEditTargetRemote;
|
||||
infrared->app_state.edit_mode = InfraredEditModeDelete;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
|
||||
const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ?
|
||||
"Rename Button:" :
|
||||
"Delete Button:";
|
||||
submenu_set_header(submenu, header);
|
||||
|
||||
const size_t button_count = infrared_remote_get_button_count(remote);
|
||||
for(size_t i = 0; i < button_count; ++i) {
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
infrared_remote_button_get_name(button),
|
||||
i,
|
||||
infrared_scene_edit_button_select_submenu_callback,
|
||||
context);
|
||||
}
|
||||
|
||||
if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {
|
||||
submenu_set_selected_item(submenu, app_state->current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
app_state->current_button_index = event.event;
|
||||
const InfraredEditMode edit_mode = app_state->edit_mode;
|
||||
if(edit_mode == InfraredEditModeRename) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRename);
|
||||
} else if(edit_mode == InfraredEditModeDelete) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_button_select_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
112
applications/main/infrared/scenes/infrared_scene_edit_delete.c
Normal file
112
applications/main/infrared/scenes/infrared_scene_edit_delete.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void
|
||||
infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
int32_t current_button_index = infrared->app_state.current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop);
|
||||
InfraredRemoteButton* current_button =
|
||||
infrared_remote_get_button(remote, current_button_index);
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(current_button);
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\nRAW\n%ld samples",
|
||||
infrared_remote_button_get_name(current_button),
|
||||
raw->timings_size);
|
||||
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
|
||||
infrared_remote_button_get_name(current_button),
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
}
|
||||
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\n with %lu buttons",
|
||||
infrared_remote_get_name(remote),
|
||||
infrared_remote_get_button_count(remote));
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Delete");
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultRight) {
|
||||
bool success = false;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_delete_button(remote, app_state->current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_remote_remove(remote);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
|
||||
} else {
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_edit_delete_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
popup_set_context(popup, context);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemote);
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList};
|
||||
if(!scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes))) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
}
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
107
applications/main/infrared/scenes/infrared_scene_edit_rename.c
Normal file
107
applications/main/infrared/scenes/infrared_scene_edit_rename.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
size_t enter_name_length = 0;
|
||||
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
text_input_set_header_text(text_input, "Name the button");
|
||||
|
||||
const int32_t current_button_index = infrared->app_state.current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
InfraredRemoteButton* current_button =
|
||||
infrared_remote_get_button(remote, current_button_index);
|
||||
enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH;
|
||||
strncpy(
|
||||
infrared->text_store[0],
|
||||
infrared_remote_button_get_name(current_button),
|
||||
enter_name_length);
|
||||
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
text_input_set_header_text(text_input, "Name the remote");
|
||||
enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH;
|
||||
strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length);
|
||||
|
||||
string_t folder_path;
|
||||
string_init(folder_path);
|
||||
|
||||
if(string_end_with_str_p(infrared->file_path, INFRARED_APP_EXTENSION)) {
|
||||
path_extract_dirname(string_get_cstr(infrared->file_path), folder_path);
|
||||
}
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
string_get_cstr(folder_path),
|
||||
INFRARED_APP_EXTENSION,
|
||||
infrared_remote_get_name(remote));
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
string_clear(folder_path);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
infrared_text_input_callback,
|
||||
context,
|
||||
infrared->text_store[0],
|
||||
enter_name_length,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTextEditDone) {
|
||||
bool success = false;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
const int32_t current_button_index = app_state->current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_rename_button(
|
||||
remote, infrared->text_store[0], current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_rename_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
|
||||
void* validator_context = text_input_get_validator_callback_context(text_input);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
|
||||
if(validator_context) {
|
||||
validator_is_file_free((ValidatorIsFile*)validator_context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_edit_rename_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
popup_set_context(popup, context);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
infrared->scene_manager, InfraredSceneRemote)) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_rename_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_error_databases_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43);
|
||||
popup_set_text(
|
||||
popup, "Function requires\nSD card with fresh\ndatabases.", 47, 17, AlignLeft, AlignTop);
|
||||
|
||||
popup_set_context(popup, context);
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
}
|
||||
|
||||
bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
infrared->scene_manager, InfraredSceneUniversal);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_error_databases_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
popup_reset(infrared->popup);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);
|
||||
}
|
||||
45
applications/main/infrared/scenes/infrared_scene_learn.c
Normal file
45
applications/main/infrared/scenes/infrared_scene_learn.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_learn_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
|
||||
infrared_worker_rx_set_received_signal_callback(
|
||||
worker, infrared_signal_received_callback, context);
|
||||
infrared_worker_rx_start(worker);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartRead);
|
||||
|
||||
popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31);
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
popup_set_text(
|
||||
popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
|
||||
popup_set_callback(popup, NULL);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeSignalReceived) {
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_learn_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
|
||||
infrared_worker_rx_stop(infrared->worker);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_learn_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
DOLPHIN_DEED(DolphinDeedIrSave);
|
||||
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
|
||||
} else {
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
popup_set_context(popup, context);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
infrared->scene_manager, InfraredSceneRemote)) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_learn_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_learn_enter_name_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size);
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%.4s_%0*lX",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
}
|
||||
|
||||
text_input_set_header_text(text_input, "Name the button");
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
infrared_text_input_callback,
|
||||
context,
|
||||
infrared->text_store[0],
|
||||
INFRARED_MAX_BUTTON_NAME_LENGTH,
|
||||
true);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTextEditDone) {
|
||||
bool success = false;
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
success =
|
||||
infrared_add_remote_with_button(infrared, infrared->text_store[0], signal);
|
||||
} else {
|
||||
success =
|
||||
infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_learn_enter_name_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
105
applications/main/infrared/scenes/infrared_scene_learn_success.c
Normal file
105
applications/main/infrared/scenes/infrared_scene_learn_success.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static void
|
||||
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_learn_success_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedIrLearnSuccess);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
|
||||
infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size);
|
||||
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop);
|
||||
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
uint8_t addr_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
|
||||
uint8_t cmd_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4);
|
||||
uint8_t max_digits = MAX(addr_digits, cmd_digits);
|
||||
max_digits = MIN(max_digits, 7);
|
||||
size_t label_x_offset = 63 + (7 - max_digits) * 3;
|
||||
|
||||
infrared_text_store_set(infrared, 0, "%s", infrared_get_protocol_name(message->protocol));
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
1,
|
||||
"A: 0x%0*lX\nC: 0x%0*lX\n",
|
||||
addr_digits,
|
||||
message->address,
|
||||
cmd_digits,
|
||||
message->command);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, infrared->text_store[0], 95, 7, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, infrared->text_store[1], label_x_offset, 34, AlignLeft, AlignCenter);
|
||||
}
|
||||
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Save");
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Send");
|
||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_learn_success_dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
dialog_ex_enable_extended_events(dialog_ex);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
if(is_transmitter_idle) {
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneAskBack);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultLeft) {
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneAskRetry);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultRight) {
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearnEnterName);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExPressCenter) {
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
infrared_tx_start_received(infrared);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExReleaseCenter) {
|
||||
infrared_tx_stop(infrared);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_learn_success_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
}
|
||||
121
applications/main/infrared/scenes/infrared_scene_remote.c
Normal file
121
applications/main/infrared/scenes/infrared_scene_remote.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
ButtonIndexPlus = -2,
|
||||
ButtonIndexEdit = -1,
|
||||
ButtonIndexNA = 0,
|
||||
} ButtonIndex;
|
||||
|
||||
static void
|
||||
infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {
|
||||
Infrared* infrared = context;
|
||||
|
||||
uint16_t custom_type;
|
||||
if(type == InputTypePress) {
|
||||
custom_type = InfraredCustomEventTypeTransmitStarted;
|
||||
} else if(type == InputTypeRelease) {
|
||||
custom_type = InfraredCustomEventTypeTransmitStopped;
|
||||
} else if(type == InputTypeShort) {
|
||||
custom_type = InfraredCustomEventTypeMenuSelected;
|
||||
} else {
|
||||
furi_crash("Unexpected input type");
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, infrared_custom_event_pack(custom_type, index));
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
ButtonMenu* button_menu = infrared->button_menu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
size_t button_count = infrared_remote_get_button_count(remote);
|
||||
for(size_t i = 0; i < button_count; ++i) {
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
infrared_remote_button_get_name(button),
|
||||
i,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeCommon,
|
||||
context);
|
||||
}
|
||||
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
"+",
|
||||
ButtonIndexPlus,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeControl,
|
||||
context);
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
"Edit",
|
||||
ButtonIndexEdit,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeControl,
|
||||
context);
|
||||
|
||||
button_menu_set_header(button_menu, infrared_remote_get_name(remote));
|
||||
const int16_t button_index =
|
||||
(signed)scene_manager_get_scene_state(scene_manager, InfraredSceneRemote);
|
||||
button_menu_set_selected_item(button_menu, button_index);
|
||||
scene_manager_set_scene_state(scene_manager, InfraredSceneRemote, ButtonIndexNA);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewButtonMenu);
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
if(is_transmitter_idle) {
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
} else {
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
const uint16_t custom_type = infrared_custom_event_get_type(event.event);
|
||||
const int16_t button_index = infrared_custom_event_get_value(event.event);
|
||||
|
||||
if(custom_type == InfraredCustomEventTypeTransmitStarted) {
|
||||
furi_assert(button_index >= 0);
|
||||
infrared_tx_start_button_index(infrared, button_index);
|
||||
consumed = true;
|
||||
} else if(custom_type == InfraredCustomEventTypeTransmitStopped) {
|
||||
infrared_tx_stop(infrared);
|
||||
consumed = true;
|
||||
} else if(custom_type == InfraredCustomEventTypeMenuSelected) {
|
||||
furi_assert(button_index < 0);
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_set_scene_state(
|
||||
scene_manager, InfraredSceneRemote, (unsigned)button_index);
|
||||
if(button_index == ButtonIndexPlus) {
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
} else if(button_index == ButtonIndexEdit) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEdit);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
button_menu_reset(infrared->button_menu);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_remote_list_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
|
||||
|
||||
bool success = dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
|
||||
|
||||
if(success) {
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_remote_list_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
100
applications/main/infrared/scenes/infrared_scene_rpc.c
Normal file
100
applications/main/infrared/scenes/infrared_scene_rpc.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "gui/canvas.h"
|
||||
|
||||
typedef enum {
|
||||
InfraredRpcStateIdle,
|
||||
InfraredRpcStateLoaded,
|
||||
InfraredRpcStateSending,
|
||||
} InfraredRpcState;
|
||||
|
||||
void infrared_scene_rpc_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);
|
||||
|
||||
popup_set_context(popup, context);
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
|
||||
scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle);
|
||||
|
||||
notification_message(infrared->notifications, &sequence_display_backlight_on);
|
||||
}
|
||||
|
||||
bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
InfraredRpcState state =
|
||||
scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc);
|
||||
if(event.event == InfraredCustomEventTypeBackPressed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoad) {
|
||||
bool result = false;
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateIdle)) {
|
||||
string_set_str(infrared->file_path, arg);
|
||||
result = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
if(result) {
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
}
|
||||
}
|
||||
const char* remote_name = infrared_remote_get_name(infrared->remote);
|
||||
|
||||
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
|
||||
popup_set_text(
|
||||
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
|
||||
bool result = false;
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateLoaded)) {
|
||||
size_t button_index = 0;
|
||||
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
|
||||
infrared_tx_start_button_index(infrared, button_index);
|
||||
result = true;
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
result = true;
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
}
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcExit) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_rpc_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==
|
||||
InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
}
|
||||
popup_reset(infrared->popup);
|
||||
}
|
||||
84
applications/main/infrared/scenes/infrared_scene_start.c
Normal file
84
applications/main/infrared/scenes/infrared_scene_start.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexUniversalRemotes,
|
||||
SubmenuIndexLearnNewRemote,
|
||||
SubmenuIndexSavedRemotes,
|
||||
SubmenuIndexDebug
|
||||
};
|
||||
|
||||
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_start_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Universal Remotes",
|
||||
SubmenuIndexUniversalRemotes,
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Learn New Remote",
|
||||
SubmenuIndexLearnNewRemote,
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Saved Remotes",
|
||||
SubmenuIndexSavedRemotes,
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
|
||||
if(infrared->app_state.is_debug_enabled) {
|
||||
submenu_add_item(
|
||||
submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);
|
||||
}
|
||||
|
||||
const uint32_t submenu_index =
|
||||
scene_manager_get_scene_state(scene_manager, InfraredSceneStart);
|
||||
submenu_set_selected_item(submenu, submenu_index);
|
||||
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, SubmenuIndexUniversalRemotes);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
|
||||
}
|
||||
|
||||
bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
const uint32_t submenu_index = event.event;
|
||||
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);
|
||||
if(submenu_index == SubmenuIndexUniversalRemotes) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversal);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexLearnNewRemote) {
|
||||
infrared->app_state.is_learning_new_remote = true;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexSavedRemotes) {
|
||||
string_set_str(infrared->file_path, INFRARED_APP_FOLDER);
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexDebug) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_start_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
53
applications/main/infrared/scenes/infrared_scene_universal.c
Normal file
53
applications/main/infrared/scenes/infrared_scene_universal.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexUniversalTV,
|
||||
SubmenuIndexUniversalAudio,
|
||||
SubmenuIndexUniversalAirConditioner,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"TVs",
|
||||
SubmenuIndexUniversalTV,
|
||||
infrared_scene_universal_submenu_callback,
|
||||
context);
|
||||
submenu_set_selected_item(submenu, 0);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUniversalTV) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexUniversalAudio) {
|
||||
//TODO Implement Audio universal remote
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexUniversalAirConditioner) {
|
||||
//TODO Implement A/C universal remote
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_universal_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
111
applications/main/infrared/scenes/infrared_scene_universal_tv.c
Normal file
111
applications/main/infrared/scenes/infrared_scene_universal_tv.c
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_tv_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/tv.ir"));
|
||||
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
uint32_t i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
19,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "POWER");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
19,
|
||||
&I_Mute_25x27,
|
||||
&I_Mute_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "MUTE");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "VOL+");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
66,
|
||||
&I_Up_25x27,
|
||||
&I_Up_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "CH+");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "VOL-");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
98,
|
||||
&I_Down_25x27,
|
||||
&I_Down_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "CH-");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
||||
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
||||
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) {
|
||||
return infrared_scene_universal_common_on_event(context, event);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_tv_on_exit(void* context) {
|
||||
infrared_scene_universal_common_on_exit(context);
|
||||
}
|
||||
59
applications/main/infrared/views/infrared_debug_view.c
Normal file
59
applications/main/infrared/views/infrared_debug_view.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "infrared_debug_view.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define INFRARED_DEBUG_TEXT_LENGTH 64
|
||||
|
||||
struct InfraredDebugView {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char text[INFRARED_DEBUG_TEXT_LENGTH];
|
||||
} InfraredDebugViewModel;
|
||||
|
||||
static void infrared_debug_view_draw_callback(Canvas* canvas, void* model) {
|
||||
InfraredDebugViewModel* debug_view_model = model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "INFRARED monitor\n");
|
||||
canvas_set_font(canvas, FontKeyboard);
|
||||
|
||||
if(strlen(debug_view_model->text)) {
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 64, 43, AlignCenter, AlignCenter, debug_view_model->text);
|
||||
}
|
||||
}
|
||||
|
||||
InfraredDebugView* infrared_debug_view_alloc() {
|
||||
InfraredDebugView* debug_view = malloc(sizeof(InfraredDebugView));
|
||||
debug_view->view = view_alloc();
|
||||
view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(InfraredDebugViewModel));
|
||||
view_set_draw_callback(debug_view->view, infrared_debug_view_draw_callback);
|
||||
view_set_context(debug_view->view, debug_view);
|
||||
return debug_view;
|
||||
}
|
||||
void infrared_debug_view_free(InfraredDebugView* debug_view) {
|
||||
view_free(debug_view->view);
|
||||
free(debug_view);
|
||||
}
|
||||
|
||||
View* infrared_debug_view_get_view(InfraredDebugView* debug_view) {
|
||||
return debug_view->view;
|
||||
}
|
||||
|
||||
void infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
InfraredDebugViewModel* model = view_get_model(debug_view->view);
|
||||
vsnprintf(model->text, INFRARED_DEBUG_TEXT_LENGTH, fmt, args);
|
||||
view_commit_model(debug_view->view, true);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
11
applications/main/infrared/views/infrared_debug_view.h
Normal file
11
applications/main/infrared/views/infrared_debug_view.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct InfraredDebugView InfraredDebugView;
|
||||
|
||||
InfraredDebugView* infrared_debug_view_alloc();
|
||||
void infrared_debug_view_free(InfraredDebugView* debug_view);
|
||||
|
||||
View* infrared_debug_view_get_view(InfraredDebugView* debug_view);
|
||||
void infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...);
|
||||
121
applications/main/infrared/views/infrared_progress_view.c
Normal file
121
applications/main/infrared/views/infrared_progress_view.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include <core/check.h>
|
||||
#include "furi_hal_resources.h"
|
||||
#include "assets_icons.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/view.h"
|
||||
#include "input/input.h"
|
||||
#include "m-string.h"
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include "infrared_progress_view.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct InfraredProgressView {
|
||||
View* view;
|
||||
InfraredProgressViewBackCallback back_callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t progress;
|
||||
size_t progress_total;
|
||||
} InfraredProgressViewModel;
|
||||
|
||||
bool infrared_progress_view_increase_progress(InfraredProgressView* progress) {
|
||||
furi_assert(progress);
|
||||
bool result = false;
|
||||
|
||||
InfraredProgressViewModel* model = view_get_model(progress->view);
|
||||
if(model->progress < model->progress_total) {
|
||||
++model->progress;
|
||||
result = model->progress < model->progress_total;
|
||||
}
|
||||
view_commit_model(progress->view, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
InfraredProgressViewModel* model = (InfraredProgressViewModel*)_model;
|
||||
|
||||
uint8_t x = 0;
|
||||
uint8_t y = 36;
|
||||
uint8_t width = 63;
|
||||
uint8_t height = 59;
|
||||
|
||||
elements_bold_rounded_frame(canvas, x, y, width, height);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, x + 34, y + 9, AlignCenter, AlignCenter, "Sending ...");
|
||||
|
||||
float progress_value = (float)model->progress / model->progress_total;
|
||||
elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value);
|
||||
|
||||
uint8_t percent_value = 100 * model->progress / model->progress_total;
|
||||
char percents_string[10] = {0};
|
||||
snprintf(percents_string, sizeof(percents_string), "%d%%", percent_value);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, x + 33, y + 37, AlignCenter, AlignCenter, percents_string);
|
||||
|
||||
canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_str(canvas, x + 30, y + height - 6, "= stop");
|
||||
}
|
||||
|
||||
void infrared_progress_view_set_progress_total(
|
||||
InfraredProgressView* progress,
|
||||
uint16_t progress_total) {
|
||||
furi_assert(progress);
|
||||
InfraredProgressViewModel* model = view_get_model(progress->view);
|
||||
model->progress = 0;
|
||||
model->progress_total = progress_total;
|
||||
view_commit_model(progress->view, false);
|
||||
}
|
||||
|
||||
bool infrared_progress_view_input_callback(InputEvent* event, void* context) {
|
||||
InfraredProgressView* instance = context;
|
||||
|
||||
if((event->type == InputTypeShort) && (event->key == InputKeyBack)) {
|
||||
if(instance->back_callback) {
|
||||
instance->back_callback(instance->context);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InfraredProgressView* infrared_progress_view_alloc(void) {
|
||||
InfraredProgressView* instance = malloc(sizeof(InfraredProgressView));
|
||||
instance->view = view_alloc();
|
||||
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(InfraredProgressViewModel));
|
||||
InfraredProgressViewModel* model = view_get_model(instance->view);
|
||||
model->progress = 0;
|
||||
model->progress_total = 0;
|
||||
view_commit_model(instance->view, false);
|
||||
view_set_draw_callback(instance->view, infrared_progress_view_draw_callback);
|
||||
view_set_input_callback(instance->view, infrared_progress_view_input_callback);
|
||||
view_set_context(instance->view, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void infrared_progress_view_free(InfraredProgressView* progress) {
|
||||
view_free(progress->view);
|
||||
free(progress);
|
||||
}
|
||||
|
||||
void infrared_progress_view_set_back_callback(
|
||||
InfraredProgressView* instance,
|
||||
InfraredProgressViewBackCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
instance->back_callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
View* infrared_progress_view_get_view(InfraredProgressView* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->view);
|
||||
return instance->view;
|
||||
}
|
||||
68
applications/main/infrared/views/infrared_progress_view.h
Normal file
68
applications/main/infrared/views/infrared_progress_view.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file infrared_progress_view.h
|
||||
* Infrared: Custom Infrared view module.
|
||||
* It shows popup progress bar during brute force.
|
||||
*/
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Anonumous instance */
|
||||
typedef struct InfraredProgressView InfraredProgressView;
|
||||
|
||||
/** Callback for back button handling */
|
||||
typedef void (*InfraredProgressViewBackCallback)(void*);
|
||||
|
||||
/** Allocate and initialize Infrared view
|
||||
*
|
||||
* @retval new allocated instance
|
||||
*/
|
||||
InfraredProgressView* infrared_progress_view_alloc();
|
||||
|
||||
/** Free previously allocated Progress view module instance
|
||||
*
|
||||
* @param instance to free
|
||||
*/
|
||||
void infrared_progress_view_free(InfraredProgressView* instance);
|
||||
|
||||
/** Get progress view module view
|
||||
*
|
||||
* @param instance view module
|
||||
* @retval view
|
||||
*/
|
||||
View* infrared_progress_view_get_view(InfraredProgressView* instance);
|
||||
|
||||
/** Increase progress on progress view module
|
||||
*
|
||||
* @param instance view module
|
||||
* @retval true - value is incremented and maximum is reached,
|
||||
* false - value is incremented and maximum is not reached
|
||||
*/
|
||||
bool infrared_progress_view_increase_progress(InfraredProgressView* instance);
|
||||
|
||||
/** Set maximum progress value
|
||||
*
|
||||
* @param instance - view module
|
||||
* @param progress_max - maximum value of progress
|
||||
*/
|
||||
void infrared_progress_view_set_progress_total(
|
||||
InfraredProgressView* instance,
|
||||
uint16_t progress_max);
|
||||
|
||||
/** Set back button callback
|
||||
*
|
||||
* @param instance - view module
|
||||
* @param callback - callback to call for back button
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
void infrared_progress_view_set_back_callback(
|
||||
InfraredProgressView* instance,
|
||||
InfraredProgressViewBackCallback callback,
|
||||
void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user