From e0654fe409d5d6afcfe6ea94faba766192252e76 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:52:00 +0300 Subject: [PATCH] [FL-3890] Infrared button operation fails now shows more informative messages (#3859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Error codes enum added * Adjusted signal api to return error codes instead of bool * Remote api adjusted to work with error codes * Brute force logic adjusted to work with error codes * Other application functions adjust to work with error codes * All task callbacks now return ErrorCode through int32t, which belongs to thread * All scenes now work with error codes * More api functions now return error code * Now signal names are buffered and restored in case of error. * New error code enumeration added. Now least significant byte is left for the button index * Some macro to simplify error setup and error check * Error code checks replaced by macro * Different message is now shown when move failed * Comments updated * Fixed error check * Fixed navigation issue while openning broken files from Favorites * Now search by index also returns index in addition to error code * Remote loading logic adjusted * New error codes added and numbers adjusted * New error message when loading library file instead of signal one * Some more remote loading logic adjusted * New error message on rename fail * Grammar mistake fix * Function signature changed * Function usage adjusted according to new signature Co-authored-by: あく --- applications/main/infrared/infrared_app.c | 46 ++-- applications/main/infrared/infrared_app_i.h | 17 +- .../main/infrared/infrared_brute_force.c | 27 ++- .../main/infrared/infrared_brute_force.h | 5 +- applications/main/infrared/infrared_cli.c | 16 +- .../main/infrared/infrared_error_code.h | 45 ++++ applications/main/infrared/infrared_remote.c | 190 ++++++++++----- applications/main/infrared/infrared_remote.h | 48 ++-- applications/main/infrared/infrared_signal.c | 225 +++++++++++++----- applications/main/infrared/infrared_signal.h | 26 +- .../common/infrared_scene_universal_common.c | 8 +- .../scenes/infrared_scene_edit_delete.c | 44 ++-- .../scenes/infrared_scene_edit_move.c | 30 ++- .../scenes/infrared_scene_edit_rename.c | 35 ++- .../scenes/infrared_scene_learn_enter_name.c | 4 +- .../infrared/scenes/infrared_scene_remote.c | 3 +- .../scenes/infrared_scene_remote_list.c | 16 +- .../main/infrared/scenes/infrared_scene_rpc.c | 17 +- 18 files changed, 541 insertions(+), 261 deletions(-) create mode 100644 applications/main/infrared/infrared_error_code.h diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 4ddf0776b..a93fd766d 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -297,7 +297,7 @@ static void infrared_free(InfraredApp* infrared) { free(infrared); } -bool infrared_add_remote_with_button( +InfraredErrorCode infrared_add_remote_with_button( const InfraredApp* infrared, const char* button_name, const InfraredSignal* signal) { @@ -310,21 +310,23 @@ bool infrared_add_remote_with_button( furi_string_cat_printf( new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!infrared_remote_create(remote, furi_string_get_cstr(new_path))) break; - if(!infrared_remote_append_signal(remote, signal, button_name)) break; - success = true; + error = infrared_remote_create(remote, furi_string_get_cstr(new_path)); + if(INFRARED_ERROR_PRESENT(error)) break; + + error = infrared_remote_append_signal(remote, signal, button_name); } while(false); furi_string_free(new_name); furi_string_free(new_path); - return success; + return error; } -bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { +InfraredErrorCode + infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { InfraredRemote* remote = infrared->remote; const char* old_path = infrared_remote_get_path(remote); @@ -344,12 +346,13 @@ bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new path_append(new_path_fstr, furi_string_get_cstr(new_name_fstr)); furi_string_cat(new_path_fstr, INFRARED_APP_EXTENSION); - const bool success = infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr)); + const InfraredErrorCode error = + infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr)); furi_string_free(new_name_fstr); furi_string_free(new_path_fstr); - return success; + return error; } void infrared_tx_start(InfraredApp* infrared) { @@ -382,15 +385,16 @@ void infrared_tx_start(InfraredApp* infrared) { infrared->app_state.is_transmitting = true; } -bool infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) { +InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) { furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote)); - bool result = + InfraredErrorCode error = infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index); - if(result) { + + if(!INFRARED_ERROR_PRESENT(error)) { infrared_tx_start(infrared); } - return result; + return error; } void infrared_tx_stop(InfraredApp* infrared) { @@ -413,7 +417,7 @@ void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback call furi_thread_start(infrared->task_thread); } -bool infrared_blocking_task_finalize(InfraredApp* infrared) { +InfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared) { furi_thread_join(infrared->task_thread); return furi_thread_get_return_code(infrared->task_thread); } @@ -563,10 +567,18 @@ int32_t infrared_app(void* p) { is_rpc_mode = true; } else { const char* file_path = (const char*)p; - is_remote_loaded = infrared_remote_load(infrared->remote, file_path); + InfraredErrorCode error = infrared_remote_load(infrared->remote, file_path); - if(!is_remote_loaded) { - infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); + if(!INFRARED_ERROR_PRESENT(error)) { + is_remote_loaded = true; + } else { + is_remote_loaded = false; + bool wrong_file_type = INFRARED_ERROR_CHECK(error, InfraredErrorCodeWrongFileType); + const char* format = wrong_file_type ? + "Library file\n\"%s\" can't be openned as a remote" : + "Failed to load\n\"%s\""; + + infrared_show_error_message(infrared, format, file_path); return -1; } diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 002a8c2ee..721771ef0 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -174,9 +174,9 @@ typedef enum { * @param[in] infrared pointer to the application instance. * @param[in] name pointer to a zero-terminated string containing the signal name. * @param[in] signal pointer to the signal to be added. - * @return true if the remote was successfully created, false otherwise. + * @return InfraredErrorCodeNone if the remote was successfully created, otherwise error code. */ -bool infrared_add_remote_with_button( +InfraredErrorCode infrared_add_remote_with_button( const InfraredApp* infrared, const char* name, const InfraredSignal* signal); @@ -186,9 +186,10 @@ bool infrared_add_remote_with_button( * * @param[in] infrared pointer to the application instance. * @param[in] new_name pointer to a zero-terminated string containing the new remote name. - * @return true if the remote was successfully renamed, false otherwise. + * @return InfraredErrorCodeNone if the remote was successfully renamed, otherwise error code. */ -bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name); +InfraredErrorCode + infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name); /** * @brief Begin transmission of the currently loaded signal. @@ -206,9 +207,9 @@ void infrared_tx_start(InfraredApp* infrared); * * @param[in,out] infrared pointer to the application instance. * @param[in] button_index index of the signal to be loaded. - * @returns true if the signal could be loaded, false otherwise. + * @returns InfraredErrorCodeNone if the signal could be loaded, otherwise error code. */ -bool infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); +InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); /** * @brief Stop transmission of the currently loaded signal. @@ -236,9 +237,9 @@ void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback call * (e.g. to display the results), the caller code MUST set it explicitly. * * @param[in,out] infrared pointer to the application instance. - * @return true if the blocking task finished successfully, false otherwise. + * @return InfraredErrorCodeNone if the blocking task finished successfully, otherwise error code. */ -bool infrared_blocking_task_finalize(InfraredApp* infrared); +InfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared); /** * @brief Set the internal text store with formatted text. diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index b28918489..8c7422d5e 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -50,10 +50,10 @@ void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const brute_force->db_filename = db_filename; } -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { +InfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { furi_assert(!brute_force->is_started); furi_assert(brute_force->db_filename); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); @@ -61,12 +61,15 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { InfraredSignal* signal = infrared_signal_alloc(); do { - if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) break; + if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } bool signals_valid = false; - while(infrared_signal_read_name(ff, signal_name)) { - signals_valid = infrared_signal_read_body(signal, ff) && - infrared_signal_is_valid(signal); + while(infrared_signal_read_name(ff, signal_name) == InfraredErrorCodeNone) { + error = infrared_signal_read_body(signal, ff); + signals_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal); if(!signals_valid) break; InfraredBruteForceRecord* record = @@ -75,9 +78,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { ++(record->count); } } - if(!signals_valid) break; - success = true; } while(false); infrared_signal_free(signal); @@ -85,7 +86,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } bool infrared_brute_force_start( @@ -139,10 +140,12 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) { bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { furi_assert(brute_force->is_started); + const bool success = infrared_signal_search_by_name_and_read( - brute_force->current_signal, - brute_force->ff, - furi_string_get_cstr(brute_force->current_record_name)); + brute_force->current_signal, + brute_force->ff, + furi_string_get_cstr(brute_force->current_record_name)) == + InfraredErrorCodeNone; if(success) { infrared_signal_transmit(brute_force->current_signal); } diff --git a/applications/main/infrared/infrared_brute_force.h b/applications/main/infrared/infrared_brute_force.h index 8eda08a63..879642257 100644 --- a/applications/main/infrared/infrared_brute_force.h +++ b/applications/main/infrared/infrared_brute_force.h @@ -10,6 +10,7 @@ #include #include +#include "infrared_error_code.h" /** * @brief InfraredBruteForce opaque type declaration. @@ -45,9 +46,9 @@ void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const * a infrared_brute_force_set_db_filename() call. * * @param[in,out] brute_force pointer to the instance to be updated. - * @returns true on success, false otherwise. + * @returns InfraredErrorCodeNone on success, otherwise error code. */ -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); +InfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); /** * @brief Start transmitting signals from a category stored in an InfraredBruteForce's instance dictionary. diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 00246a90f..b700cf121 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -232,9 +232,15 @@ static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { 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); + bool ret = true; + InfraredErrorCode error = infrared_signal_save(signal, file, name); + if(INFRARED_ERROR_PRESENT(error)) { + printf( + "Failed to save signal: \"%s\" code: 0x%X index: 0x%02X\r\n", + name, + INFRARED_ERROR_GET_CODE(error), + INFRARED_ERROR_GET_INDEX(error)); + ret = false; } return ret; } @@ -296,7 +302,7 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o FuriString* tmp; tmp = furi_string_alloc(); - while(infrared_signal_read(signal, input_file, tmp)) { + while(infrared_signal_read(signal, input_file, tmp) == InfraredErrorCodeNone) { ret = false; if(!infrared_signal_is_valid(signal)) { printf("Invalid signal\r\n"); @@ -464,7 +470,7 @@ static void printf("Missing signal name.\r\n"); break; } - if(!infrared_brute_force_calculate_messages(brute_force)) { + if(infrared_brute_force_calculate_messages(brute_force) != InfraredErrorCodeNone) { printf("Invalid remote name.\r\n"); break; } diff --git a/applications/main/infrared/infrared_error_code.h b/applications/main/infrared/infrared_error_code.h new file mode 100644 index 000000000..2af06bea1 --- /dev/null +++ b/applications/main/infrared/infrared_error_code.h @@ -0,0 +1,45 @@ +#pragma once + +typedef enum { + InfraredErrorCodeNone = 0, + InfraredErrorCodeFileOperationFailed = 0x800000, + InfraredErrorCodeWrongFileType = 0x80000100, + InfraredErrorCodeWrongFileVersion = 0x80000200, + + //Common signal errors + InfraredErrorCodeSignalTypeUnknown = 0x80000300, + InfraredErrorCodeSignalNameNotFound = 0x80000400, + InfraredErrorCodeSignalUnableToReadType = 0x80000500, + InfraredErrorCodeSignalUnableToWriteType = 0x80000600, + + //Raw signal errors + InfraredErrorCodeSignalRawUnableToReadFrequency = 0x80000700, + InfraredErrorCodeSignalRawUnableToReadDutyCycle = 0x80000800, + InfraredErrorCodeSignalRawUnableToReadTimingsSize = 0x80000900, + InfraredErrorCodeSignalRawUnableToReadTooLongData = 0x80000A00, + InfraredErrorCodeSignalRawUnableToReadData = 0x80000B00, + + InfraredErrorCodeSignalRawUnableToWriteFrequency = 0x80000C00, + InfraredErrorCodeSignalRawUnableToWriteDutyCycle = 0x80000D00, + InfraredErrorCodeSignalRawUnableToWriteData = 0x80000E00, + + //Message signal errors + InfraredErrorCodeSignalMessageUnableToReadProtocol = 0x80000F00, + InfraredErrorCodeSignalMessageUnableToReadAddress = 0x80001000, + InfraredErrorCodeSignalMessageUnableToReadCommand = 0x80001100, + InfraredErrorCodeSignalMessageIsInvalid = 0x80001200, + + InfraredErrorCodeSignalMessageUnableToWriteProtocol = 0x80001300, + InfraredErrorCodeSignalMessageUnableToWriteAddress = 0x80001400, + InfraredErrorCodeSignalMessageUnableToWriteCommand = 0x80001500, +} InfraredErrorCode; + +#define INFRARED_ERROR_CODE_MASK (0xFFFFFF00) +#define INFRARED_ERROR_INDEX_MASK (0x000000FF) + +#define INFRARED_ERROR_GET_CODE(error) (error & INFRARED_ERROR_CODE_MASK) +#define INFRARED_ERROR_GET_INDEX(error) (error & INFRARED_ERROR_INDEX_MASK) +#define INFRARED_ERROR_SET_INDEX(code, index) (code |= (index & INFRARED_ERROR_INDEX_MASK)) + +#define INFRARED_ERROR_PRESENT(error) (INFRARED_ERROR_GET_CODE(error) != InfraredErrorCodeNone) +#define INFRARED_ERROR_CHECK(error, test_code) (INFRARED_ERROR_GET_CODE(error) == test_code) diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index 11bbf197b..9da08cbd4 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -8,8 +8,9 @@ #define TAG "InfraredRemote" -#define INFRARED_FILE_HEADER "IR signals file" -#define INFRARED_FILE_VERSION (1) +#define INFRARED_FILE_HEADER "IR signals file" +#define INFRARED_LIBRARY_HEADER "IR library file" +#define INFRARED_FILE_VERSION (1) ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575 @@ -34,7 +35,7 @@ typedef struct { const InfraredSignal* signal; } InfraredBatchTarget; -typedef bool ( +typedef InfraredErrorCode ( *InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target); InfraredRemote* infrared_remote_alloc(void) { @@ -80,7 +81,7 @@ const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t return *StringArray_cget(remote->signal_names, index); } -bool infrared_remote_load_signal( +InfraredErrorCode infrared_remote_load_signal( const InfraredRemote* remote, InfraredSignal* signal, size_t index) { @@ -89,25 +90,27 @@ bool infrared_remote_load_signal( Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { const char* path = furi_string_get_cstr(remote->path); - if(!flipper_format_buffered_file_open_existing(ff, path)) break; + if(!flipper_format_buffered_file_open_existing(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } - if(!infrared_signal_search_by_index_and_read(signal, ff, index)) { + error = infrared_signal_search_by_index_and_read(signal, ff, index); + if(INFRARED_ERROR_PRESENT(error)) { const char* signal_name = infrared_remote_get_signal_name(remote, index); FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", signal_name, path); break; } - - success = true; } while(false); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } bool infrared_remote_get_signal_index( @@ -128,31 +131,35 @@ bool infrared_remote_get_signal_index( return false; } -bool infrared_remote_append_signal( +InfraredErrorCode infrared_remote_append_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; const char* path = furi_string_get_cstr(remote->path); do { - if(!flipper_format_file_open_append(ff, path)) break; - if(!infrared_signal_save(signal, ff, name)) break; + if(!flipper_format_file_open_append(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } + + error = infrared_signal_save(signal, ff, name); + if(INFRARED_ERROR_PRESENT(error)) break; StringArray_push_back(remote->signal_names, name); - success = true; } while(false); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } -static bool infrared_remote_batch_start( +static InfraredErrorCode infrared_remote_batch_start( InfraredRemote* remote, InfraredBatchCallback batch_callback, const InfraredBatchTarget* target) { @@ -179,33 +186,59 @@ static bool infrared_remote_batch_start( status = storage_common_stat(storage, path_out, NULL); } while(status == FSE_OK || status == FSE_EXIST); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; + StringArray_t buf_names; + StringArray_init_set(buf_names, remote->signal_names); do { - if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in)) break; - if(!flipper_format_buffered_file_open_always(batch_context.ff_out, path_out)) break; - if(!flipper_format_write_header_cstr( - batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) + if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in) || + !flipper_format_buffered_file_open_always(batch_context.ff_out, path_out) || + !flipper_format_write_header_cstr( + batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) { + error = InfraredErrorCodeFileOperationFailed; break; - + } const size_t signal_count = infrared_remote_get_signal_count(remote); for(; batch_context.signal_index < signal_count; ++batch_context.signal_index) { - if(!infrared_signal_read( - batch_context.signal, batch_context.ff_in, batch_context.signal_name)) + error = infrared_signal_read( + batch_context.signal, batch_context.ff_in, batch_context.signal_name); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index); break; - if(!batch_callback(&batch_context, target)) break; + } + + error = batch_callback(&batch_context, target); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index); + break; + } + } + if(INFRARED_ERROR_PRESENT(error)) break; + + if(!flipper_format_buffered_file_close(batch_context.ff_out) || + !flipper_format_buffered_file_close(batch_context.ff_in)) { + error = InfraredErrorCodeFileOperationFailed; + break; } - if(batch_context.signal_index != signal_count) break; - - if(!flipper_format_buffered_file_close(batch_context.ff_out)) break; - if(!flipper_format_buffered_file_close(batch_context.ff_in)) break; - const FS_Error status = storage_common_rename(storage, path_out, path_in); - success = (status == FSE_OK || status == FSE_EXIST); + error = (status == FSE_OK || status == FSE_EXIST) ? InfraredErrorCodeNone : + InfraredErrorCodeFileOperationFailed; } while(false); + if(INFRARED_ERROR_PRESENT(error)) { + //Remove all temp data and rollback signal names + flipper_format_buffered_file_close(batch_context.ff_out); + flipper_format_buffered_file_close(batch_context.ff_in); + status = storage_common_stat(storage, path_out, NULL); + if(status == FSE_OK || status == FSE_EXIST) storage_common_remove(storage, path_out); + + StringArray_reset(remote->signal_names); + StringArray_set(remote->signal_names, buf_names); + } + + StringArray_clear(buf_names); infrared_signal_free(batch_context.signal); furi_string_free(batch_context.signal_name); flipper_format_free(batch_context.ff_out); @@ -214,15 +247,18 @@ static bool infrared_remote_batch_start( furi_record_close(RECORD_STORAGE); - return success; + return error; } -static bool infrared_remote_insert_signal_callback( +static InfraredErrorCode infrared_remote_insert_signal_callback( const InfraredBatch* batch, const InfraredBatchTarget* target) { // Insert a signal under the specified index if(batch->signal_index == target->signal_index) { - if(!infrared_signal_save(target->signal, batch->ff_out, target->signal_name)) return false; + InfraredErrorCode error = + infrared_signal_save(target->signal, batch->ff_out, target->signal_name); + if(INFRARED_ERROR_PRESENT(error)) return error; + StringArray_push_at( batch->remote->signal_names, target->signal_index, target->signal_name); } @@ -232,7 +268,7 @@ static bool infrared_remote_insert_signal_callback( batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); } -bool infrared_remote_insert_signal( +InfraredErrorCode infrared_remote_insert_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name, @@ -251,7 +287,7 @@ bool infrared_remote_insert_signal( remote, infrared_remote_insert_signal_callback, &insert_target); } -static bool infrared_remote_rename_signal_callback( +static InfraredErrorCode infrared_remote_rename_signal_callback( const InfraredBatch* batch, const InfraredBatchTarget* target) { const char* signal_name; @@ -268,7 +304,8 @@ static bool infrared_remote_rename_signal_callback( return infrared_signal_save(batch->signal, batch->ff_out, signal_name); } -bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) { +InfraredErrorCode + infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) { furi_assert(index < infrared_remote_get_signal_count(remote)); const InfraredBatchTarget rename_target = { @@ -281,7 +318,7 @@ bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const c remote, infrared_remote_rename_signal_callback, &rename_target); } -static bool infrared_remote_delete_signal_callback( +static InfraredErrorCode infrared_remote_delete_signal_callback( const InfraredBatch* batch, const InfraredBatchTarget* target) { if(batch->signal_index == target->signal_index) { @@ -294,10 +331,10 @@ static bool infrared_remote_delete_signal_callback( batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); } - return true; + return InfraredErrorCodeNone; } -bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { +InfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { furi_assert(index < infrared_remote_get_signal_count(remote)); const InfraredBatchTarget delete_target = { @@ -310,33 +347,35 @@ bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { remote, infrared_remote_delete_signal_callback, &delete_target); } -bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) { +InfraredErrorCode + infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) { const size_t signal_count = infrared_remote_get_signal_count(remote); furi_assert(index < signal_count); furi_assert(new_index < signal_count); - if(index == new_index) return true; + InfraredErrorCode error = InfraredErrorCodeNone; + if(index == new_index) return error; InfraredSignal* signal = infrared_signal_alloc(); char* signal_name = strdup(infrared_remote_get_signal_name(remote, index)); - bool success = false; - do { - if(!infrared_remote_load_signal(remote, signal, index)) break; - if(!infrared_remote_delete_signal(remote, index)) break; - if(!infrared_remote_insert_signal(remote, signal, signal_name, new_index)) break; + error = infrared_remote_load_signal(remote, signal, index); + if(INFRARED_ERROR_PRESENT(error)) break; - success = true; + error = infrared_remote_delete_signal(remote, index); + if(INFRARED_ERROR_PRESENT(error)) break; + + error = infrared_remote_insert_signal(remote, signal, signal_name, new_index); } while(false); free(signal_name); infrared_signal_free(signal); - return success; + return error; } -bool infrared_remote_create(InfraredRemote* remote, const char* path) { +InfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path) { FURI_LOG_I(TAG, "Creating new file: '%s'", path); infrared_remote_reset(remote); @@ -358,45 +397,64 @@ bool infrared_remote_create(InfraredRemote* remote, const char* path) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } -bool infrared_remote_load(InfraredRemote* remote, const char* path) { +InfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path) { FURI_LOG_I(TAG, "Loading file: '%s'", path); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); FuriString* tmp = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_buffered_file_open_existing(ff, path)) break; + if(!flipper_format_buffered_file_open_existing(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } uint32_t version; - if(!flipper_format_read_header(ff, tmp, &version)) break; - - if(!furi_string_equal(tmp, INFRARED_FILE_HEADER) || (version != INFRARED_FILE_VERSION)) + if(!flipper_format_read_header(ff, tmp, &version)) { + error = InfraredErrorCodeFileOperationFailed; break; + } + + if(furi_string_equal(tmp, INFRARED_LIBRARY_HEADER)) { + FURI_LOG_E(TAG, "Library file can't be loaded in this context"); + error = InfraredErrorCodeWrongFileType; + break; + } + + if(!furi_string_equal(tmp, INFRARED_FILE_HEADER)) { + error = InfraredErrorCodeWrongFileType; + FURI_LOG_E(TAG, "Filetype unknown"); + break; + } + + if(version != INFRARED_FILE_VERSION) { + error = InfraredErrorCodeWrongFileVersion; + FURI_LOG_E(TAG, "Wrong file version"); + break; + } infrared_remote_set_path(remote, path); StringArray_reset(remote->signal_names); - while(infrared_signal_read_name(ff, tmp)) { + while(infrared_signal_read_name(ff, tmp) == InfraredErrorCodeNone) { StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp)); } - - success = true; } while(false); furi_string_free(tmp); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } -bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) { +InfraredErrorCode infrared_remote_rename(InfraredRemote* remote, const char* new_path) { const char* old_path = infrared_remote_get_path(remote); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -409,10 +467,10 @@ bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) { infrared_remote_set_path(remote, new_path); } - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } -bool infrared_remote_remove(InfraredRemote* remote) { +InfraredErrorCode infrared_remote_remove(InfraredRemote* remote) { Storage* storage = furi_record_open(RECORD_STORAGE); const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote)); furi_record_close(RECORD_STORAGE); @@ -423,5 +481,5 @@ bool infrared_remote_remove(InfraredRemote* remote) { infrared_remote_reset(remote); } - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h index 4604a25ef..14416cd65 100644 --- a/applications/main/infrared/infrared_remote.h +++ b/applications/main/infrared/infrared_remote.h @@ -105,12 +105,10 @@ bool infrared_remote_get_signal_index( * @param[in] remote pointer to the instance to load from. * @param[out] signal pointer to the signal to load into. Must be allocated. * @param[in] index index of the signal to be loaded. Must be less than the total signal count. - * @return true if the signal was successfully loaded, false otherwise. + * @return InfraredErrorCodeNone if the signal was successfully loaded, otherwise error code. */ -bool infrared_remote_load_signal( - const InfraredRemote* remote, - InfraredSignal* signal, - size_t index); +InfraredErrorCode + infrared_remote_load_signal(const InfraredRemote* remote, InfraredSignal* signal, size_t index); /** * @brief Append a signal to the file associated with an InfraredRemote instance. @@ -121,9 +119,9 @@ bool infrared_remote_load_signal( * @param[in,out] remote pointer to the instance to append to. * @param[in] signal pointer to the signal to be appended. * @param[in] name pointer to a zero-terminated string containing the name of the signal. - * @returns true if the signal was successfully appended, false otherwise. + * @returns InfraredErrorCodeNone if the signal was successfully appended, otherwise error code. */ -bool infrared_remote_append_signal( +InfraredErrorCode infrared_remote_append_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name); @@ -141,9 +139,10 @@ bool infrared_remote_append_signal( * @param[in] signal pointer to the signal to be inserted. * @param[in] name pointer to a zero-terminated string containing the name of the signal. * @param[in] index the index under which the signal shall be inserted. - * @returns true if the signal was successfully inserted, false otherwise. + * @returns InfraredErrorCodeNone if the signal was successfully inserted, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_insert_signal( +InfraredErrorCode infrared_remote_insert_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name, @@ -157,9 +156,10 @@ bool infrared_remote_insert_signal( * @param[in,out] remote pointer to the instance to be modified. * @param[in] index index of the signal to be renamed. Must be less than the total signal count. * @param[in] new_name pointer to a zero-terminated string containig the signal's new name. - * @returns true if the signal was successfully renamed, false otherwise. + * @returns InfraredErrorCodeNone if the signal was successfully renamed, otherwise error code. */ -bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name); +InfraredErrorCode + infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name); /** * @brief Change a signal's position in the file associated with an InfraredRemote instance. @@ -169,17 +169,21 @@ bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const c * @param[in,out] remote pointer to the instance to be modified. * @param[in] index index of the signal to be moved. Must be less than the total signal count. * @param[in] new_index index of the signal to be moved. Must be less than the total signal count. + * @returns InfraredErrorCodeNone if the signal was moved successfully, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index); +InfraredErrorCode + infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index); /** * @brief Delete a signal in the file associated with an InfraredRemote instance. * * @param[in,out] remote pointer to the instance to be modified. * @param[in] index index of the signal to be deleted. Must be less than the total signal count. - * @returns true if the signal was successfully deleted, false otherwise. + * @returns InfraredErrorCodeNone if the signal was successfully deleted, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index); +InfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index); /** * @brief Create a new file and associate it with an InfraredRemote instance. @@ -188,9 +192,9 @@ bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index); * * @param[in,out] remote pointer to the instance to be assigned with a new file. * @param[in] path pointer to a zero-terminated string containing the full file path. - * @returns true if the file was successfully created, false otherwise. + * @returns InfraredErrorCodeNone if the file was successfully created, otherwise error code. */ -bool infrared_remote_create(InfraredRemote* remote, const char* path); +InfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path); /** * @brief Associate an InfraredRemote instance with a file and load the signal names from it. @@ -200,9 +204,9 @@ bool infrared_remote_create(InfraredRemote* remote, const char* path); * * @param[in,out] remote pointer to the instance to be assigned with an existing file. * @param[in] path pointer to a zero-terminated string containing the full file path. - * @returns true if the file was successfully loaded, false otherwise. + * @returns InfraredErrorCodeNone if the file was successfully loaded, otherwise error code. */ -bool infrared_remote_load(InfraredRemote* remote, const char* path); +InfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path); /** * @brief Rename the file associated with an InfraredRemote instance. @@ -211,9 +215,9 @@ bool infrared_remote_load(InfraredRemote* remote, const char* path); * * @param[in,out] remote pointer to the instance to be modified. * @param[in] new_path pointer to a zero-terminated string containing the new full file path. - * @returns true if the file was successfully renamed, false otherwise. + * @returns InfraredErrorCodeNone if the file was successfully renamed, otherwise error code. */ -bool infrared_remote_rename(InfraredRemote* remote, const char* new_path); +InfraredErrorCode infrared_remote_rename(InfraredRemote* remote, const char* new_path); /** * @brief Remove the file associated with an InfraredRemote instance. @@ -224,6 +228,6 @@ bool infrared_remote_rename(InfraredRemote* remote, const char* new_path); * infrared_remote_create() or infrared_remote_load() are successfully executed. * * @param[in,out] remote pointer to the instance to be modified. - * @returns true if the file was successfully removed, false otherwise. + * @returns InfraredErrorCodeNone if the file was successfully removed, otherwise error code. */ -bool infrared_remote_remove(InfraredRemote* remote); +InfraredErrorCode infrared_remote_remove(InfraredRemote* remote); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index a4cb28bf0..75643f1d3 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -101,104 +101,177 @@ static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) { return true; } -static inline bool +static inline InfraredErrorCode infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) { const char* protocol_name = infrared_get_protocol_name(message->protocol); - return flipper_format_write_string_cstr( - ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED) && - flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name) && - flipper_format_write_hex( - ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4) && - flipper_format_write_hex( - ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4); + InfraredErrorCode error = InfraredErrorCodeNone; + do { + if(!flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED)) { + error = InfraredErrorCodeSignalUnableToWriteType; + break; + } + + if(!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name)) { + error = InfraredErrorCodeSignalMessageUnableToWriteProtocol; + break; + } + + if(!flipper_format_write_hex( + ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4)) { + error = InfraredErrorCodeSignalMessageUnableToWriteAddress; + break; + } + + if(!flipper_format_write_hex( + ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4)) { + error = InfraredErrorCodeSignalMessageUnableToWriteCommand; + break; + } + + } while(false); + + return error; } -static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { +static inline InfraredErrorCode + infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); - return flipper_format_write_string_cstr( - ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW) && - flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1) && - flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1) && - flipper_format_write_uint32( - ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size); + + InfraredErrorCode error = InfraredErrorCodeNone; + do { + if(!flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW)) { + error = InfraredErrorCodeSignalUnableToWriteType; + break; + } + + if(!flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1)) { + error = InfraredErrorCodeSignalRawUnableToWriteFrequency; + break; + } + + if(!flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1)) { + error = InfraredErrorCodeSignalRawUnableToWriteDutyCycle; + break; + } + + if(!flipper_format_write_uint32( + ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size)) { + error = InfraredErrorCodeSignalRawUnableToWriteData; + break; + } + } while(false); + return error; } -static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { +static inline InfraredErrorCode + infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { FuriString* buf; buf = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) break; + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) { + error = InfraredErrorCodeSignalMessageUnableToReadProtocol; + break; + } InfraredMessage message; message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); - if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) + if(!flipper_format_read_hex( + ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) { + error = InfraredErrorCodeSignalMessageUnableToReadAddress; break; - if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) + } + if(!flipper_format_read_hex( + ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) { + error = InfraredErrorCodeSignalMessageUnableToReadCommand; break; - if(!infrared_signal_is_message_valid(&message)) break; + } + + if(!infrared_signal_is_message_valid(&message)) { + error = InfraredErrorCodeSignalMessageIsInvalid; + break; + } infrared_signal_set_message(signal, &message); - success = true; } while(false); furi_string_free(buf); - return success; + return error; } -static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { - bool success = false; +static inline InfraredErrorCode + infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + InfraredErrorCode error = InfraredErrorCodeNone; do { uint32_t frequency; - if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) break; + if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) { + error = InfraredErrorCodeSignalRawUnableToReadFrequency; + break; + } float duty_cycle; - if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) break; + if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) { + error = InfraredErrorCodeSignalRawUnableToReadDutyCycle; + break; + } uint32_t timings_size; - if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) break; + if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) { + error = InfraredErrorCodeSignalRawUnableToReadTimingsSize; + break; + } - if(timings_size > MAX_TIMINGS_AMOUNT) break; + if(timings_size > MAX_TIMINGS_AMOUNT) { + error = InfraredErrorCodeSignalRawUnableToReadTooLongData; + break; + } uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_DATA_KEY, timings, timings_size)) { + error = InfraredErrorCodeSignalRawUnableToReadData; free(timings); break; } + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); free(timings); - success = true; + error = InfraredErrorCodeNone; } while(false); - return success; + return error; } -bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { +InfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { FuriString* tmp = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) break; - - if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) { - if(!infrared_signal_read_raw(signal, ff)) break; - } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) { - if(!infrared_signal_read_message(signal, ff)) break; - } else { - FURI_LOG_E(TAG, "Unknown signal type: %s", furi_string_get_cstr(tmp)); + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) { + error = InfraredErrorCodeSignalUnableToReadType; break; } - success = true; + if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) { + error = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) { + error = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type: %s", furi_string_get_cstr(tmp)); + error = InfraredErrorCodeSignalTypeUnknown; + break; + } } while(false); furi_string_free(tmp); - return success; + + return error; } InfraredSignal* infrared_signal_alloc(void) { @@ -270,68 +343,88 @@ const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) return &signal->payload.message; } -bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { +InfraredErrorCode + infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { + InfraredErrorCode error = InfraredErrorCodeNone; + if(!flipper_format_write_comment_cstr(ff, "") || !flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) { - return false; + error = InfraredErrorCodeFileOperationFailed; } else if(signal->is_raw) { - return infrared_signal_save_raw(&signal->payload.raw, ff); + error = infrared_signal_save_raw(&signal->payload.raw, ff); } else { - return infrared_signal_save_message(&signal->payload.message, ff); + error = infrared_signal_save_message(&signal->payload.message, ff); } + + return error; } -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { - bool success = false; +InfraredErrorCode + infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!infrared_signal_read_name(ff, name)) break; - if(!infrared_signal_read_body(signal, ff)) break; + error = infrared_signal_read_name(ff, name); + if(INFRARED_ERROR_PRESENT(error)) break; - success = true; //-V779 + error = infrared_signal_read_body(signal, ff); } while(false); - return success; + return error; } -bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { - return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name); +InfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { + return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name) ? + InfraredErrorCodeNone : + InfraredErrorCodeSignalNameNotFound; } -bool infrared_signal_search_by_name_and_read( +InfraredErrorCode infrared_signal_search_by_name_and_read( InfraredSignal* signal, FlipperFormat* ff, const char* name) { - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; FuriString* tmp = furi_string_alloc(); - while(infrared_signal_read_name(ff, tmp)) { + do { + error = infrared_signal_read_name(ff, tmp); + if(INFRARED_ERROR_PRESENT(error)) break; + if(furi_string_equal(tmp, name)) { - success = infrared_signal_read_body(signal, ff); + error = infrared_signal_read_body(signal, ff); break; } - } + } while(true); furi_string_free(tmp); - return success; + return error; } -bool infrared_signal_search_by_index_and_read( +InfraredErrorCode infrared_signal_search_by_index_and_read( InfraredSignal* signal, FlipperFormat* ff, size_t index) { - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; FuriString* tmp = furi_string_alloc(); - for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) { + for(uint32_t i = 0;; ++i) { + error = infrared_signal_read_name(ff, tmp); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, i); + break; + } + if(i == index) { - success = infrared_signal_read_body(signal, ff); + error = infrared_signal_read_body(signal, ff); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, i); + } break; } } furi_string_free(tmp); - return success; + return error; } void infrared_signal_transmit(const InfraredSignal* signal) { diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h index dcfde6e72..3fa7768b3 100644 --- a/applications/main/infrared/infrared_signal.h +++ b/applications/main/infrared/infrared_signal.h @@ -8,6 +8,7 @@ */ #pragma once +#include "infrared_error_code.h" #include #include @@ -136,9 +137,10 @@ const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) * @param[in,out] signal pointer to the instance to be read into. * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[out] name pointer to the string to hold the signal name. Must be properly allocated. - * @returns true if a signal was successfully read, false otherwise (e.g. no more signals to read). + * @returns InfraredErrorCodeNone if a signal was successfully read, otherwise error code */ -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); +InfraredErrorCode + infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); /** * @brief Read a signal name from a FlipperFormat file. @@ -147,9 +149,9 @@ bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* * * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[out] name pointer to the string to hold the signal name. Must be properly allocated. - * @returns true if a signal name was successfully read, false otherwise (e.g. no more signals to read). + * @returns InfraredErrorCodeNone if a signal name was successfully read, otherwise error code */ -bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); +InfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name); /** * @brief Read a signal from a FlipperFormat file. @@ -158,9 +160,9 @@ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); * * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[out] body pointer to the InfraredSignal instance to hold the signal body. Must be properly allocated. - * @returns true if a signal body was successfully read, false otherwise (e.g. syntax error). + * @returns InfraredErrorCodeNone if a signal body was successfully read, otherwise error code. */ -bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); +InfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); /** * @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance. @@ -171,9 +173,9 @@ bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); * @param[in,out] signal pointer to the instance to be read into. * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[in] name pointer to a zero-terminated string containing the requested signal name. - * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). + * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code. */ -bool infrared_signal_search_by_name_and_read( +InfraredErrorCode infrared_signal_search_by_name_and_read( InfraredSignal* signal, FlipperFormat* ff, const char* name); @@ -187,9 +189,9 @@ bool infrared_signal_search_by_name_and_read( * @param[in,out] signal pointer to the instance to be read into. * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[in] index the requested signal index. - * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). + * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code. */ -bool infrared_signal_search_by_index_and_read( +InfraredErrorCode infrared_signal_search_by_index_and_read( InfraredSignal* signal, FlipperFormat* ff, size_t index); @@ -203,8 +205,10 @@ bool infrared_signal_search_by_index_and_read( * @param[in] signal pointer to the instance holding the signal to be saved. * @param[in,out] ff pointer to the FlipperFormat file instance to write to. * @param[in] name pointer to a zero-terminated string contating the name of the signal. + * @returns InfraredErrorCodeNone if a signal was successfully saved, otherwise error code */ -bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name); +InfraredErrorCode + infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name); /** * @brief Transmit a signal contained in an InfraredSignal instance. diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 9bdcdc4a8..a52f141c4 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -34,12 +34,12 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { static int32_t infrared_scene_universal_common_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = infrared_brute_force_calculate_messages(infrared->brute_force); + const InfraredErrorCode error = infrared_brute_force_calculate_messages(infrared->brute_force); view_dispatcher_send_custom_event( infrared->view_dispatcher, infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0)); - return success; + return error; } void infrared_scene_universal_common_on_enter(void* context) { @@ -93,9 +93,9 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } } else if(event_type == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(!task_success) { + if(INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); } else { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 90a2633d3..63d71318c 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -11,12 +11,12 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; - bool success; + InfraredErrorCode error = InfraredErrorCodeNone; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); + error = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_remote_remove(infrared->remote); + error = infrared_remote_remove(infrared->remote); } else { furi_crash(); } @@ -24,7 +24,7 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_edit_delete_on_enter(void* context) { @@ -39,11 +39,15 @@ void infrared_scene_edit_delete_on_enter(void* context) { const int32_t current_button_index = infrared->app_state.current_button_index; furi_check(current_button_index != InfraredButtonIndexNone); - if(!infrared_remote_load_signal(remote, infrared->current_signal, current_button_index)) { + InfraredErrorCode error = + infrared_remote_load_signal(remote, infrared->current_signal, current_button_index); + if(INFRARED_ERROR_PRESENT(error)) { + const char* format = + (INFRARED_ERROR_CHECK(error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) ? + "Failed to delete\n\"%s\" is too long.\nTry to edit file from pc" : + "Failed to load\n\"%s\""; infrared_show_error_message( - infrared, - "Failed to load\n\"%s\"", - infrared_remote_get_signal_name(remote, current_button_index)); + infrared, format, infrared_remote_get_signal_name(remote, current_button_index)); scene_manager_previous_scene(infrared->scene_manager); return; } @@ -107,18 +111,30 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) infrared_blocking_task_start(infrared, infrared_scene_edit_delete_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); } else { - const char* edit_target_text = - app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; - infrared_show_error_message(infrared, "Failed to\ndelete %s", edit_target_text); + if(INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) { + const uint8_t index = INFRARED_ERROR_GET_INDEX(task_error); + const char* format = + "Failed to delete\n\"%s\" is too long.\nTry to edit file from pc"; + infrared_show_error_message( + infrared, + format, + infrared_remote_get_signal_name(infrared->remote, index)); + } else { + const char* edit_target_text = + app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; + infrared_show_error_message( + infrared, "Failed to\ndelete %s", edit_target_text); + } - const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index fcac1fc15..1c6415c24 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -2,14 +2,14 @@ static int32_t infrared_scene_edit_move_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = infrared_remote_move_signal( + const InfraredErrorCode error = infrared_remote_move_signal( infrared->remote, infrared->app_state.prev_button_index, infrared->app_state.current_button_index); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } static void infrared_scene_edit_move_button_callback( @@ -51,14 +51,26 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { infrared_blocking_task_start(infrared, infrared_scene_edit_move_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(!task_success) { - const char* signal_name = infrared_remote_get_signal_name( - infrared->remote, infrared->app_state.current_button_index); - infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name); - scene_manager_search_and_switch_to_previous_scene( - infrared->scene_manager, InfraredSceneRemoteList); + if(INFRARED_ERROR_PRESENT(task_error)) { + const char* format = "Failed to move\n\"%s\""; + uint8_t signal_index = infrared->app_state.prev_button_index; + + if(INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) { + signal_index = INFRARED_ERROR_GET_INDEX(task_error); + format = "Failed to move\n\"%s\" is too long.\nTry to edit file from pc"; + } + furi_assert(format); + + const char* signal_name = + infrared_remote_get_signal_name(infrared->remote, signal_index); + infrared_show_error_message(infrared, format, signal_name); + + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; + scene_manager_search_and_switch_to_previous_scene_one_of( + infrared->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } else { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index a546ad6e5..301e936d9 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -8,13 +8,13 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; - bool success; + InfraredErrorCode error = InfraredErrorCodeNone; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_rename_signal( + error = infrared_remote_rename_signal( infrared->remote, app_state->current_button_index, infrared->text_store[0]); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_rename_current_remote(infrared, infrared->text_store[0]); + error = infrared_rename_current_remote(infrared, infrared->text_store[0]); } else { furi_crash(); } @@ -22,7 +22,7 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_edit_rename_on_enter(void* context) { @@ -89,17 +89,30 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) infrared_blocking_task_start(infrared, infrared_scene_edit_rename_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); } else { - const char* edit_target_text = - app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; - infrared_show_error_message(infrared, "Failed to\nrename %s", edit_target_text); - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemoteList); + bool long_signal = INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData); + + const char* format = "Failed to rename\n%s"; + const char* target = infrared->app_state.edit_target == InfraredEditTargetButton ? + "button" : + "file"; + if(long_signal) { + format = "Failed to rename\n\"%s\" is too long.\nTry to edit file from pc"; + target = infrared_remote_get_signal_name( + infrared->remote, INFRARED_ERROR_GET_INDEX(task_error)); + } + + infrared_show_error_message(infrared, format, target); + + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } app_state->current_button_index = InfraredButtonIndexNone; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index be46a8691..7ffc9644b 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -41,12 +41,12 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTextEditDone) { const char* signal_name = infrared->text_store[0]; - const bool success = + const InfraredErrorCode error = infrared->app_state.is_learning_new_remote ? infrared_add_remote_with_button(infrared, signal_name, signal) : infrared_remote_append_signal(infrared->remote, signal, signal_name); - if(success) { + if(!INFRARED_ERROR_PRESENT(error)) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); dolphin_deed(DolphinDeedIrSave); } else { diff --git a/applications/main/infrared/scenes/infrared_scene_remote.c b/applications/main/infrared/scenes/infrared_scene_remote.c index e1195c516..6dd7aae8b 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote.c +++ b/applications/main/infrared/scenes/infrared_scene_remote.c @@ -85,7 +85,8 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(custom_type == InfraredCustomEventTypeTransmitStarted) { furi_assert(button_index >= 0); - if(!infrared_tx_start_button_index(infrared, button_index)) { + InfraredErrorCode error = infrared_tx_start_button_index(infrared, button_index); + if(INFRARED_ERROR_PRESENT(error)) { infrared_show_error_message( infrared, "Failed to load\n\"%s\"", diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 9c880ac2f..93665769c 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -2,11 +2,11 @@ static int32_t infrared_scene_remote_list_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = + const InfraredErrorCode error = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { @@ -38,13 +38,19 @@ bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { + bool wrong_file_type = + INFRARED_ERROR_CHECK(task_error, InfraredErrorCodeWrongFileType); + const char* format = wrong_file_type ? + "Library file\n\"%s\" can't be openned as a remote" : + "Failed to load\n\"%s\""; + infrared_show_error_message( - infrared, "Failed to load\n\"%s\"", furi_string_get_cstr(infrared->file_path)); + infrared, format, furi_string_get_cstr(infrared->file_path)); infrared_scene_remote_list_select_and_load(infrared); } } diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 167247668..8f9dc4338 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -11,11 +11,11 @@ typedef enum { static int32_t infrared_scene_rpc_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = + const InfraredErrorCode error = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_rpc_on_enter(void* context) { @@ -57,8 +57,11 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { } } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); - if(task_success) { + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); + + if(!INFRARED_ERROR_PRESENT(task_error)) { + const char* remote_name = infrared_remote_get_name(infrared->remote); + infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); } else { @@ -71,7 +74,7 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { furi_string_free(str); } - rpc_system_app_confirm(infrared->rpc_ctx, task_success); + rpc_system_app_confirm(infrared->rpc_ctx, !INFRARED_ERROR_PRESENT(task_error)); } else if( event.event == InfraredCustomEventTypeRpcButtonPressName || event.event == InfraredCustomEventTypeRpcButtonPressIndex) { @@ -90,7 +93,9 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { TAG, "Sending signal with index \"%ld\"", app_state->current_button_index); } if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { - if(infrared_tx_start_button_index(infrared, app_state->current_button_index)) { + InfraredErrorCode error = + infrared_tx_start_button_index(infrared, app_state->current_button_index); + if(!INFRARED_ERROR_PRESENT(error)) { const char* remote_name = infrared_remote_get_name(infrared->remote); infrared_text_store_set(infrared, 0, "emulating\n%s", remote_name);