|
@@ -1,4 +1,5 @@
|
|
|
#include <cli/cli.h>
|
|
#include <cli/cli.h>
|
|
|
|
|
+#include <cli/cli_i.h>
|
|
|
#include <infrared.h>
|
|
#include <infrared.h>
|
|
|
#include <infrared_worker.h>
|
|
#include <infrared_worker.h>
|
|
|
#include <furi_hal_infrared.h>
|
|
#include <furi_hal_infrared.h>
|
|
@@ -6,12 +7,24 @@
|
|
|
#include <toolbox/args.h>
|
|
#include <toolbox/args.h>
|
|
|
|
|
|
|
|
#include "infrared_signal.h"
|
|
#include "infrared_signal.h"
|
|
|
|
|
+#include "infrared_brute_force.h"
|
|
|
|
|
+
|
|
|
|
|
+#include "m-dict.h"
|
|
|
|
|
+#include "m-string.h"
|
|
|
|
|
|
|
|
#define INFRARED_CLI_BUF_SIZE 10
|
|
#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_rx(Cli* cli, FuriString* args);
|
|
|
static void infrared_cli_start_ir_tx(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_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 {
|
|
static const struct {
|
|
|
const char* cmd;
|
|
const char* cmd;
|
|
@@ -20,6 +33,7 @@ static const struct {
|
|
|
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
|
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
|
|
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
|
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
|
|
{.cmd = "decode", .process_function = infrared_cli_process_decode},
|
|
{.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) {
|
|
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_MIN_FREQUENCY,
|
|
|
INFRARED_MAX_FREQUENCY);
|
|
INFRARED_MAX_FREQUENCY);
|
|
|
printf("\tir decode <input_file> [<output_file>]\r\n");
|
|
printf("\tir decode <input_file> [<output_file>]\r\n");
|
|
|
|
|
+ printf("\tir universal <tv, ac> <signal name>\r\n");
|
|
|
|
|
+ printf("\tir universal list <tv, ac>\r\n");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
|
|
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);
|
|
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) {
|
|
static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) {
|
|
|
UNUSED(context);
|
|
UNUSED(context);
|
|
|
if(furi_hal_infrared_is_busy()) {
|
|
if(furi_hal_infrared_is_busy()) {
|