[FL-3890] Infrared button operation fails now shows more informative messages (#3859)

* 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: あく <alleteam@gmail.com>
This commit is contained in:
RebornedBrain 2024-09-06 12:52:00 +03:00 committed by GitHub
parent 6a1c27ef93
commit e0654fe409
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 541 additions and 261 deletions

View file

@ -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;
}

View file

@ -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.

View file

@ -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);
}

View file

@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdbool.h>
#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.

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -8,6 +8,7 @@
*/
#pragma once
#include "infrared_error_code.h"
#include <flipper_format/flipper_format.h>
#include <infrared/encoder_decoder/infrared.h>
@ -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.

View file

@ -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);

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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 {

View file

@ -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\"",

View file

@ -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);
}
}

View file

@ -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);