From f61a8fda536656ee4900e24ed1b5a029b0de533f Mon Sep 17 00:00:00 2001 From: Travis Montoya <99630881+sqlsquirreltm@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:07:05 -0600 Subject: [PATCH] Feature/infrared add remote to cli (#1856) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial testing of remote using cli * More fixes for cli ir remote * Fixes. Turns off power now * Finished adding other tv remote commands * Changed if-else formatting * Cleaned up unused variables * Updating cli unviersal remote to accept tv, ac and more modular. Listing signals still does not work properly * Using mlib dictionary to get unique signals from files for ir universal list * Fixing progress bar * Added error checking for invalid signal to stop freezing cli * Added error checking for arg length * Api symbols was changed somehow.. changed back and updated the argument check to account for newline * Fixing string compares and argument length issue * Freeing InfraredBruteForce in cli brute force signals Co-authored-by: sqlsquirreltm Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5ec57c75..54e3e251 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,12 +7,24 @@ #include #include "infrared_signal.h" +#include "infrared_brute_force.h" + +#include "m-dict.h" +#include "m-string.h" #define INFRARED_CLI_BUF_SIZE 10 +DICT_DEF2(dict_signals, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST) + +enum RemoteTypes { TV = 0, AC = 1 }; + static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args); +static void infrared_cli_process_universal(Cli* cli, FuriString* args); +static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type); +static void + infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal); static const struct { const char* cmd; @@ -20,6 +33,7 @@ static const struct { {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, {.cmd = "decode", .process_function = infrared_cli_process_decode}, + {.cmd = "universal", .process_function = infrared_cli_process_universal}, }; static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { @@ -90,6 +104,8 @@ static void infrared_cli_print_usage(void) { INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); printf("\tir decode []\r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); } static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { @@ -328,6 +344,168 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) { furi_record_close(RECORD_STORAGE); } +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + enum RemoteTypes Remote; + + FuriString* command; + FuriString* remote; + FuriString* signal; + command = furi_string_alloc(); + remote = furi_string_alloc(); + signal = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, command)) { + infrared_cli_print_usage(); + break; + } + + if(furi_string_cmp_str(command, "list") == 0) { + args_read_string_and_trim(args, remote); + if(furi_string_cmp_str(remote, "tv") == 0) { + Remote = TV; + } else if(furi_string_cmp_str(remote, "ac") == 0) { + Remote = AC; + } else { + printf("Invalid remote type.\r\n"); + break; + } + infrared_cli_list_remote_signals(Remote); + break; + } + + if(furi_string_cmp_str(command, "tv") == 0) { + Remote = TV; + } else if(furi_string_cmp_str(command, "ac") == 0) { + Remote = AC; + } else { + printf("Invalid remote type.\r\n"); + break; + } + + args_read_string_and_trim(args, signal); + if(furi_string_empty(signal)) { + printf("Must supply a valid signal for type of remote selected.\r\n"); + break; + } + + infrared_cli_brute_force_signals(cli, Remote, signal); + break; + + } while(false); + + furi_string_free(command); + furi_string_free(remote); + furi_string_free(signal); +} + +static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + dict_signals_t signals_dict; + string_t key; + const char* remote_file = NULL; + bool success = false; + int max = 1; + + switch(remote_type) { + case TV: + remote_file = EXT_PATH("infrared/assets/tv.ir"); + break; + case AC: + remote_file = EXT_PATH("infrared/assets/ac.ir"); + break; + default: + break; + } + + dict_signals_init(signals_dict); + string_init(key); + + success = flipper_format_buffered_file_open_existing(ff, remote_file); + if(success) { + FuriString* signal_name; + signal_name = furi_string_alloc(); + printf("Valid signals:\r\n"); + while(flipper_format_read_string(ff, "name", signal_name)) { + string_set_str(key, furi_string_get_cstr(signal_name)); + int* v = dict_signals_get(signals_dict, key); + if(v != NULL) { + (*v)++; + max = M_MAX(*v, max); + } else { + dict_signals_set_at(signals_dict, key, 1); + } + } + dict_signals_it_t it; + for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { + const struct dict_signals_pair_s* pair = dict_signals_cref(it); + printf("\t%s\r\n", string_get_cstr(pair->key)); + } + furi_string_free(signal_name); + } + + string_clear(key); + dict_signals_clear(signals_dict); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); +} + +static void + infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + InfraredBruteForce* brute_force = infrared_brute_force_alloc(); + const char* remote_file = NULL; + uint32_t i = 0; + bool success = false; + + switch(remote_type) { + case TV: + remote_file = EXT_PATH("infrared/assets/tv.ir"); + break; + case AC: + remote_file = EXT_PATH("infrared/assets/ac.ir"); + break; + default: + break; + } + + infrared_brute_force_set_db_filename(brute_force, remote_file); + infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal)); + + success = infrared_brute_force_calculate_messages(brute_force); + if(success) { + uint32_t record_count; + uint32_t index = 0; + int records_sent = 0; + bool running = false; + + running = infrared_brute_force_start(brute_force, index, &record_count); + if(record_count <= 0) { + printf("Invalid signal.\n"); + infrared_brute_force_reset(brute_force); + return; + } + + printf("Sending %ld codes to the tv.\r\n", record_count); + printf("Press Ctrl-C to stop.\r\n"); + while(running) { + running = infrared_brute_force_send_next(brute_force); + + if(cli_cmd_interrupt_received(cli)) break; + + printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + fflush(stdout); + } + + infrared_brute_force_stop(brute_force); + } else { + printf("Invalid signal.\r\n"); + } + + infrared_brute_force_reset(brute_force); + infrared_brute_force_free(brute_force); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) {