[FL-2279] IR doxygen, rename irda -> infrared (#1010)
* IR: Doxygen docs, some rename * Rename irda -> infrared * Rollback collateral renames Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
196
applications/infrared/cli/infrared_cli.cpp
Normal file
196
applications/infrared/cli/infrared_cli.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#include <furi_hal_delay.h>
|
||||
#include <infrared.h>
|
||||
#include <app_template.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cmsis_os2.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal_infrared.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <m-string.h>
|
||||
#include <infrared_transmit.h>
|
||||
#include <sys/types.h>
|
||||
#include "../helpers/infrared_parser.h"
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
|
||||
|
||||
static const struct {
|
||||
const char* cmd;
|
||||
void (*process_function)(Cli* cli, string_t args);
|
||||
} infrared_cli_commands[] = {
|
||||
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
||||
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
||||
};
|
||||
|
||||
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
furi_assert(received_signal);
|
||||
char buf[100];
|
||||
size_t buf_cnt;
|
||||
Cli* cli = (Cli*)context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
|
||||
buf_cnt = sniprintf(
|
||||
buf,
|
||||
sizeof(buf),
|
||||
"%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_cnt;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
|
||||
|
||||
buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
for(size_t i = 0; i < timings_cnt; ++i) {
|
||||
buf_cnt = sniprintf(buf, sizeof(buf), "%lu ", timings[i]);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
}
|
||||
buf_cnt = sniprintf(buf, sizeof(buf), "\r\n");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, string_t args) {
|
||||
InfraredWorker* worker = infrared_worker_alloc();
|
||||
infrared_worker_rx_start(worker);
|
||||
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
|
||||
|
||||
printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
delay(50);
|
||||
}
|
||||
|
||||
infrared_worker_rx_stop(worker);
|
||||
infrared_worker_free(worker);
|
||||
}
|
||||
|
||||
static void infrared_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("\tir rx\r\n");
|
||||
printf("\tir tx <protocol> <address> <command>\r\n");
|
||||
printf("\t<command> and <address> are hex-formatted\r\n");
|
||||
printf("\tAvailable protocols:");
|
||||
for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) {
|
||||
printf(" %s", infrared_get_protocol_name((InfraredProtocol)i));
|
||||
}
|
||||
printf("\r\n");
|
||||
printf("\tRaw format:\r\n");
|
||||
printf("\tir_tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n");
|
||||
printf(
|
||||
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
|
||||
INFRARED_MIN_FREQUENCY,
|
||||
INFRARED_MAX_FREQUENCY);
|
||||
}
|
||||
|
||||
static bool parse_message(const char* str, InfraredMessage* message) {
|
||||
char protocol_name[32];
|
||||
int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message->address, &message->command);
|
||||
|
||||
if(parsed != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
message->protocol = infrared_get_protocol_by_name(protocol_name);
|
||||
message->repeat = false;
|
||||
|
||||
return infrared_parser_is_parsed_signal_valid(message);
|
||||
}
|
||||
|
||||
static bool parse_signal_raw(
|
||||
const char* str,
|
||||
uint32_t* timings,
|
||||
uint32_t* timings_cnt,
|
||||
float* duty_cycle,
|
||||
uint32_t* frequency) {
|
||||
char frequency_str[10];
|
||||
char duty_cycle_str[10];
|
||||
int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
|
||||
if(parsed != 2) return false;
|
||||
|
||||
*frequency = atoi(frequency_str);
|
||||
*duty_cycle = (float)atoi(duty_cycle_str) / 100;
|
||||
str += strlen(frequency_str) + strlen(duty_cycle_str) + 10;
|
||||
|
||||
uint32_t timings_cnt_max = *timings_cnt;
|
||||
*timings_cnt = 0;
|
||||
|
||||
while(1) {
|
||||
char timing_str[10];
|
||||
for(; *str == ' '; ++str)
|
||||
;
|
||||
if(1 != sscanf(str, "%9s", timing_str)) break;
|
||||
str += strlen(timing_str);
|
||||
uint32_t timing = atoi(timing_str);
|
||||
if(timing <= 0) break;
|
||||
if(*timings_cnt >= timings_cnt_max) break;
|
||||
timings[*timings_cnt] = timing;
|
||||
++*timings_cnt;
|
||||
}
|
||||
|
||||
return infrared_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
|
||||
InfraredMessage message;
|
||||
const char* str = string_get_cstr(args);
|
||||
uint32_t frequency;
|
||||
float duty_cycle;
|
||||
uint32_t timings_cnt = MAX_TIMINGS_AMOUNT;
|
||||
uint32_t* timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt);
|
||||
|
||||
if(parse_message(str, &message)) {
|
||||
infrared_send(&message, 1);
|
||||
} else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) {
|
||||
infrared_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle);
|
||||
} else {
|
||||
printf("Wrong arguments.\r\n");
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
|
||||
free(timings);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
if(furi_hal_infrared_is_busy()) {
|
||||
printf("INFRARED is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
for(; i < COUNT_OF(infrared_cli_commands); ++i) {
|
||||
size_t size = strlen(infrared_cli_commands[i].cmd);
|
||||
bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size);
|
||||
if(cmd_found) {
|
||||
if(string_size(args) == size) {
|
||||
break;
|
||||
}
|
||||
if(string_get_cstr(args)[size] == ' ') {
|
||||
string_right(args, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(i < COUNT_OF(infrared_cli_commands)) {
|
||||
infrared_cli_commands[i].process_function(cli, args);
|
||||
} else {
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void infrared_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = (Cli*)furi_record_open("cli");
|
||||
cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
|
||||
furi_record_close("cli");
|
||||
#endif
|
||||
}
|
||||
157
applications/infrared/helpers/infrared_parser.cpp
Normal file
157
applications/infrared/helpers/infrared_parser.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
#include "../infrared_app_signal.h"
|
||||
#include "infrared.h"
|
||||
#include "infrared/helpers/infrared_parser.h"
|
||||
#include "infrared_worker.h"
|
||||
#include "m-string.h"
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <furi_hal_infrared.h>
|
||||
|
||||
#define TAG "InfraredParser"
|
||||
|
||||
bool infrared_parser_save_signal(
|
||||
FlipperFormat* ff,
|
||||
const InfraredAppSignal& signal,
|
||||
const std::string& name) {
|
||||
furi_assert(ff);
|
||||
furi_assert(!name.empty());
|
||||
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(ff, "")) break;
|
||||
if(!flipper_format_write_string_cstr(ff, "name", name.c_str())) break;
|
||||
if(signal.is_raw()) {
|
||||
furi_assert(signal.get_raw_signal().timings_cnt <= MAX_TIMINGS_AMOUNT);
|
||||
auto raw_signal = signal.get_raw_signal();
|
||||
if(!flipper_format_write_string_cstr(ff, "type", "raw")) break;
|
||||
if(!flipper_format_write_uint32(ff, "frequency", &raw_signal.frequency, 1)) break;
|
||||
if(!flipper_format_write_float(ff, "duty_cycle", &raw_signal.duty_cycle, 1)) break;
|
||||
if(!flipper_format_write_uint32(ff, "data", raw_signal.timings, raw_signal.timings_cnt))
|
||||
break;
|
||||
} else {
|
||||
auto parsed_signal = signal.get_message();
|
||||
const char* protocol_name = infrared_get_protocol_name(parsed_signal.protocol);
|
||||
if(!flipper_format_write_string_cstr(ff, "type", "parsed")) break;
|
||||
if(!flipper_format_write_string_cstr(ff, "protocol", protocol_name)) break;
|
||||
if(!flipper_format_write_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4))
|
||||
break;
|
||||
if(!flipper_format_write_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4))
|
||||
break;
|
||||
}
|
||||
result = true;
|
||||
} while(0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name) {
|
||||
furi_assert(ff);
|
||||
|
||||
bool result = false;
|
||||
string_t read_string;
|
||||
string_init(read_string);
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "name", read_string)) break;
|
||||
name = string_get_cstr(read_string);
|
||||
if(!flipper_format_read_string(ff, "type", read_string)) break;
|
||||
if(!string_cmp_str(read_string, "raw")) {
|
||||
uint32_t* timings = nullptr;
|
||||
uint32_t timings_cnt = 0;
|
||||
uint32_t frequency = 0;
|
||||
float duty_cycle = 0;
|
||||
|
||||
if(!flipper_format_read_uint32(ff, "frequency", &frequency, 1)) break;
|
||||
if(!flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1)) break;
|
||||
if(!flipper_format_get_value_count(ff, "data", &timings_cnt)) break;
|
||||
if(timings_cnt > MAX_TIMINGS_AMOUNT) break;
|
||||
timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt);
|
||||
if(flipper_format_read_uint32(ff, "data", timings, timings_cnt)) {
|
||||
signal.set_raw_signal(timings, timings_cnt, frequency, duty_cycle);
|
||||
result = true;
|
||||
}
|
||||
free(timings);
|
||||
} else if(!string_cmp_str(read_string, "parsed")) {
|
||||
InfraredMessage parsed_signal;
|
||||
if(!flipper_format_read_string(ff, "protocol", read_string)) break;
|
||||
parsed_signal.protocol = infrared_get_protocol_by_name(string_get_cstr(read_string));
|
||||
if(!flipper_format_read_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4)) break;
|
||||
if(!flipper_format_read_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4)) break;
|
||||
if(!infrared_parser_is_parsed_signal_valid(&parsed_signal)) break;
|
||||
signal.set_message(&parsed_signal);
|
||||
result = true;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) ");
|
||||
}
|
||||
} while(0);
|
||||
|
||||
string_clear(read_string);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal) {
|
||||
furi_assert(signal);
|
||||
bool result = true;
|
||||
|
||||
if(!infrared_is_protocol_valid(signal->protocol)) {
|
||||
FURI_LOG_E(TAG, "Unknown protocol");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(result) {
|
||||
uint32_t address_length = infrared_get_protocol_address_length(signal->protocol);
|
||||
uint32_t address_mask = (1LU << address_length) - 1;
|
||||
if(signal->address != (signal->address & address_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Address is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
address_mask,
|
||||
signal->address);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(result) {
|
||||
uint32_t command_length = infrared_get_protocol_command_length(signal->protocol);
|
||||
uint32_t command_mask = (1LU << command_length) - 1;
|
||||
if(signal->command != (signal->command & command_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Command is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
command_mask,
|
||||
signal->command);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool infrared_parser_is_raw_signal_valid(
|
||||
uint32_t frequency,
|
||||
float duty_cycle,
|
||||
uint32_t timings_cnt) {
|
||||
bool result = true;
|
||||
|
||||
if((frequency > INFRARED_MAX_FREQUENCY) || (frequency < INFRARED_MIN_FREQUENCY)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Frequency is out of range (%lX - %lX): %lX",
|
||||
INFRARED_MIN_FREQUENCY,
|
||||
INFRARED_MAX_FREQUENCY,
|
||||
frequency);
|
||||
result = false;
|
||||
} else if((duty_cycle <= 0) || (duty_cycle > 1)) {
|
||||
FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", duty_cycle);
|
||||
result = false;
|
||||
} else if((timings_cnt <= 0) || (timings_cnt > MAX_TIMINGS_AMOUNT)) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Timings amount is out of range (0 - %lX): %lX", MAX_TIMINGS_AMOUNT, timings_cnt);
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
48
applications/infrared/helpers/infrared_parser.h
Normal file
48
applications/infrared/helpers/infrared_parser.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file infrared_parser.h
|
||||
* Infrared: Helper file for conversion Flipper File Format
|
||||
* to Infrared signal class, and backwards
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../infrared_app_signal.h"
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <string>
|
||||
|
||||
/** Save Infrared signal into file
|
||||
*
|
||||
* @param ff - Flipper File Format instance
|
||||
* @param signal - Infrared signal to save
|
||||
* @param name - name for saved signal. Every
|
||||
* signal on disk has name.
|
||||
*/
|
||||
bool infrared_parser_save_signal(
|
||||
FlipperFormat* ff,
|
||||
const InfraredAppSignal& signal,
|
||||
const std::string& name);
|
||||
|
||||
/** Read Infrared signal from file
|
||||
*
|
||||
* @param ff - Flipper File Format instance
|
||||
* @param signal - Infrared signal to read to
|
||||
* @param name - name for saved signal. Every
|
||||
* signal in file has name.
|
||||
*/
|
||||
bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name);
|
||||
|
||||
/** Validate parsed signal
|
||||
*
|
||||
* @signal - signal to validate
|
||||
* @retval true if valid, false otherwise
|
||||
*/
|
||||
bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal);
|
||||
|
||||
/** Validate raw signal
|
||||
*
|
||||
* @signal - signal to validate
|
||||
* @retval true if valid, false otherwise
|
||||
*/
|
||||
bool infrared_parser_is_raw_signal_valid(
|
||||
uint32_t frequency,
|
||||
float duty_cycle,
|
||||
uint32_t timings_cnt);
|
||||
273
applications/infrared/infrared_app.cpp
Normal file
273
applications/infrared/infrared_app.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "infrared_app.h"
|
||||
#include <infrared_worker.h>
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdio.h>
|
||||
#include <callback-connector.h>
|
||||
|
||||
int32_t InfraredApp::run(void* args) {
|
||||
InfraredAppEvent event;
|
||||
bool consumed;
|
||||
bool exit = false;
|
||||
|
||||
if(args) {
|
||||
std::string full_name = static_cast<const char*>(args);
|
||||
std::string remote_name(full_name, full_name.find_last_of('/') + 1, full_name.size());
|
||||
remote_name.erase(remote_name.find_last_of('.'));
|
||||
bool result = remote_manager.load(remote_name);
|
||||
if(result) {
|
||||
current_scene = InfraredApp::Scene::Remote;
|
||||
} else {
|
||||
printf("Failed to load remote \'%s\'\r\n", remote_name.c_str());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
scenes[current_scene]->on_enter(this);
|
||||
|
||||
while(!exit) {
|
||||
view_manager.receive_event(&event);
|
||||
|
||||
if(event.type == InfraredAppEvent::Type::Exit) break;
|
||||
|
||||
consumed = scenes[current_scene]->on_event(this, &event);
|
||||
|
||||
if(!consumed) {
|
||||
if(event.type == InfraredAppEvent::Type::Back) {
|
||||
exit = switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scenes[current_scene]->on_exit(this);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
InfraredApp::InfraredApp() {
|
||||
furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size());
|
||||
notification = static_cast<NotificationApp*>(furi_record_open("notification"));
|
||||
infrared_worker = infrared_worker_alloc();
|
||||
}
|
||||
|
||||
InfraredApp::~InfraredApp() {
|
||||
infrared_worker_free(infrared_worker);
|
||||
furi_record_close("notification");
|
||||
for(auto& [key, scene] : scenes) delete scene;
|
||||
}
|
||||
|
||||
InfraredAppViewManager* InfraredApp::get_view_manager() {
|
||||
return &view_manager;
|
||||
}
|
||||
|
||||
void InfraredApp::set_learn_new_remote(bool value) {
|
||||
learn_new_remote = value;
|
||||
}
|
||||
|
||||
bool InfraredApp::get_learn_new_remote() {
|
||||
return learn_new_remote;
|
||||
}
|
||||
|
||||
void InfraredApp::switch_to_next_scene(Scene next_scene) {
|
||||
previous_scenes_list.push_front(current_scene);
|
||||
switch_to_next_scene_without_saving(next_scene);
|
||||
}
|
||||
|
||||
void InfraredApp::switch_to_next_scene_without_saving(Scene next_scene) {
|
||||
if(next_scene != Scene::Exit) {
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = next_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
view_manager.clear_events();
|
||||
}
|
||||
}
|
||||
|
||||
void InfraredApp::search_and_switch_to_previous_scene(
|
||||
const std::initializer_list<Scene>& scenes_list) {
|
||||
Scene previous_scene = Scene::Start;
|
||||
bool scene_found = false;
|
||||
|
||||
while(!scene_found) {
|
||||
previous_scene = get_previous_scene();
|
||||
|
||||
if(previous_scene == Scene::Exit) break;
|
||||
|
||||
for(Scene element : scenes_list) {
|
||||
if(previous_scene == element) {
|
||||
scene_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(previous_scene == Scene::Exit) {
|
||||
InfraredAppEvent event;
|
||||
event.type = InfraredAppEvent::Type::Exit;
|
||||
view_manager.send_event(&event);
|
||||
} else {
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = previous_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
view_manager.clear_events();
|
||||
}
|
||||
}
|
||||
|
||||
bool InfraredApp::switch_to_previous_scene(uint8_t count) {
|
||||
Scene previous_scene = Scene::Start;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) previous_scene = get_previous_scene();
|
||||
|
||||
if(previous_scene == Scene::Exit) return true;
|
||||
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = previous_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
view_manager.clear_events();
|
||||
return false;
|
||||
}
|
||||
|
||||
InfraredApp::Scene InfraredApp::get_previous_scene() {
|
||||
Scene scene = Scene::Exit;
|
||||
|
||||
if(!previous_scenes_list.empty()) {
|
||||
scene = previous_scenes_list.front();
|
||||
previous_scenes_list.pop_front();
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
InfraredAppRemoteManager* InfraredApp::get_remote_manager() {
|
||||
return &remote_manager;
|
||||
}
|
||||
|
||||
void InfraredApp::set_text_store(uint8_t index, const char* text...) {
|
||||
furi_check(index < text_store_max);
|
||||
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(text_store[index], text_store_size, text, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* InfraredApp::get_text_store(uint8_t index) {
|
||||
furi_check(index < text_store_max);
|
||||
|
||||
return text_store[index];
|
||||
}
|
||||
|
||||
uint8_t InfraredApp::get_text_store_size() {
|
||||
return text_store_size;
|
||||
}
|
||||
|
||||
void InfraredApp::text_input_callback(void* context) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
event.type = InfraredAppEvent::Type::TextEditDone;
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredApp::popup_callback(void* context) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
event.type = InfraredAppEvent::Type::PopupTimer;
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredApp::set_edit_element(InfraredApp::EditElement value) {
|
||||
element = value;
|
||||
}
|
||||
|
||||
InfraredApp::EditElement InfraredApp::get_edit_element(void) {
|
||||
return element;
|
||||
}
|
||||
|
||||
void InfraredApp::set_edit_action(InfraredApp::EditAction value) {
|
||||
action = value;
|
||||
}
|
||||
|
||||
InfraredApp::EditAction InfraredApp::get_edit_action(void) {
|
||||
return action;
|
||||
}
|
||||
|
||||
void InfraredApp::set_current_button(int value) {
|
||||
current_button = value;
|
||||
}
|
||||
|
||||
int InfraredApp::get_current_button() {
|
||||
return current_button;
|
||||
}
|
||||
|
||||
void InfraredApp::notify_success() {
|
||||
notification_message(notification, &sequence_success);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_red_blink() {
|
||||
notification_message(notification, &sequence_blink_red_10);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_click() {
|
||||
static const NotificationSequence sequence = {
|
||||
&message_click,
|
||||
&message_delay_1,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
notification_message_block(notification, &sequence);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_click_and_green_blink() {
|
||||
static const NotificationSequence sequence = {
|
||||
&message_click,
|
||||
&message_delay_1,
|
||||
&message_sound_off,
|
||||
&message_green_255,
|
||||
&message_delay_10,
|
||||
&message_green_0,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
notification_message_block(notification, &sequence);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_blink_green() {
|
||||
static const NotificationSequence sequence = {
|
||||
&message_green_255,
|
||||
&message_delay_10,
|
||||
&message_green_0,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
notification_message(notification, &sequence);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_green_on() {
|
||||
notification_message(notification, &sequence_set_only_green_255);
|
||||
}
|
||||
|
||||
void InfraredApp::notify_green_off() {
|
||||
notification_message(notification, &sequence_reset_green);
|
||||
}
|
||||
|
||||
InfraredWorker* InfraredApp::get_infrared_worker() {
|
||||
return infrared_worker;
|
||||
}
|
||||
|
||||
const InfraredAppSignal& InfraredApp::get_received_signal() const {
|
||||
return received_signal;
|
||||
}
|
||||
|
||||
void InfraredApp::set_received_signal(const InfraredAppSignal& signal) {
|
||||
received_signal = signal;
|
||||
}
|
||||
|
||||
void InfraredApp::signal_sent_callback(void* context) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
app->notify_blink_green();
|
||||
}
|
||||
322
applications/infrared/infrared_app.h
Normal file
322
applications/infrared/infrared_app.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* @file infrared_app.h
|
||||
* Infrared: Main infrared application class
|
||||
*/
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <infrared.h>
|
||||
#include <furi.h>
|
||||
#include <forward_list>
|
||||
#include <stdint.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <infrared_worker.h>
|
||||
|
||||
#include "scene/infrared_app_scene.h"
|
||||
#include "scene/infrared_app_scene.h"
|
||||
#include "infrared_app_view_manager.h"
|
||||
#include "infrared_app_remote_manager.h"
|
||||
#include "infrared_app_view_manager.h"
|
||||
|
||||
/** Main Infrared application class */
|
||||
class InfraredApp {
|
||||
public:
|
||||
/** Enum to save scene state: edit element */
|
||||
enum class EditElement : uint8_t {
|
||||
Button,
|
||||
Remote,
|
||||
};
|
||||
/** Enum to save scene state: edit action */
|
||||
enum class EditAction : uint8_t {
|
||||
Rename,
|
||||
Delete,
|
||||
};
|
||||
/** List of scenes for Infrared application */
|
||||
enum class Scene : uint8_t {
|
||||
Exit,
|
||||
Start,
|
||||
Universal,
|
||||
UniversalTV,
|
||||
UniversalAudio,
|
||||
UniversalAirConditioner,
|
||||
Learn,
|
||||
LearnSuccess,
|
||||
LearnEnterName,
|
||||
LearnDone,
|
||||
AskBack,
|
||||
Remote,
|
||||
RemoteList,
|
||||
Edit,
|
||||
EditKeySelect,
|
||||
EditRename,
|
||||
EditDelete,
|
||||
EditRenameDone,
|
||||
EditDeleteDone,
|
||||
};
|
||||
|
||||
/** Start application
|
||||
*
|
||||
* @param args - application arguments.
|
||||
* Allowed argument is path to remote file.
|
||||
* @retval 0 on success, error code otherwise
|
||||
*/
|
||||
int32_t run(void* args);
|
||||
|
||||
/** Switch to next scene. Put current scene number on stack.
|
||||
* Doesn't save scene state.
|
||||
*
|
||||
* @param index - next scene index
|
||||
*/
|
||||
void switch_to_next_scene(Scene index);
|
||||
|
||||
/** Switch to next scene, but don't put current scene on
|
||||
* stack. Thus calling switch_to_previous_scene() doesn't return
|
||||
* to current scene.
|
||||
*
|
||||
* @param index - next scene index
|
||||
*/
|
||||
void switch_to_next_scene_without_saving(Scene index);
|
||||
|
||||
/** Switch to previous scene. Pop scenes from stack and switch to last one.
|
||||
*
|
||||
* @param count - how many scenes should be popped
|
||||
* @retval false on failed, true on success
|
||||
*/
|
||||
bool switch_to_previous_scene(uint8_t count = 1);
|
||||
|
||||
/** Get previous scene in scene stack
|
||||
*
|
||||
* @retval previous scene
|
||||
*/
|
||||
Scene get_previous_scene();
|
||||
|
||||
/** Get view manager instance
|
||||
*
|
||||
* @retval view manager instance
|
||||
*/
|
||||
InfraredAppViewManager* get_view_manager();
|
||||
|
||||
/** Set one of text stores
|
||||
*
|
||||
* @param index - index of text store
|
||||
* @param text - text to set
|
||||
*/
|
||||
void set_text_store(uint8_t index, const char* text...);
|
||||
|
||||
/** Get value in text store
|
||||
*
|
||||
* @param index - index of text store
|
||||
* @retval value in text_store
|
||||
*/
|
||||
char* get_text_store(uint8_t index);
|
||||
|
||||
/** Get text store size
|
||||
*
|
||||
* @retval size of text store
|
||||
*/
|
||||
uint8_t get_text_store_size();
|
||||
|
||||
/** Get remote manager instance
|
||||
*
|
||||
* @retval remote manager instance
|
||||
*/
|
||||
InfraredAppRemoteManager* get_remote_manager();
|
||||
|
||||
/** Get infrared worker instance
|
||||
*
|
||||
* @retval infrared worker instance
|
||||
*/
|
||||
InfraredWorker* get_infrared_worker();
|
||||
|
||||
/** Get signal, previously got on Learn scene
|
||||
*
|
||||
* @retval received signal
|
||||
*/
|
||||
const InfraredAppSignal& get_received_signal() const;
|
||||
|
||||
/** Set received signal
|
||||
*
|
||||
* @param signal - signal
|
||||
*/
|
||||
void set_received_signal(const InfraredAppSignal& signal);
|
||||
|
||||
/** Switch to previous scene in one of provided in list.
|
||||
* Pop scene stack, and find first scene from list.
|
||||
*
|
||||
* @param scenes_list - list of scenes
|
||||
*/
|
||||
void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list);
|
||||
|
||||
/** Set edit element value. It is used on edit scene to determine
|
||||
* what should be deleted - remote or button.
|
||||
*
|
||||
* @param value - value to set
|
||||
*/
|
||||
void set_edit_element(EditElement value);
|
||||
|
||||
/** Get edit element
|
||||
*
|
||||
* @retval edit element value
|
||||
*/
|
||||
EditElement get_edit_element(void);
|
||||
|
||||
/** Set edit action value. It is used on edit scene to determine
|
||||
* what action to perform - deletion or renaming.
|
||||
*
|
||||
* @param value - value to set
|
||||
*/
|
||||
void set_edit_action(EditAction value);
|
||||
|
||||
/** Get edit action
|
||||
*
|
||||
* @retval edit action value
|
||||
*/
|
||||
EditAction get_edit_action(void);
|
||||
|
||||
/** Get state of learning new signal.
|
||||
* Adding new remote with 1 button from start scene and
|
||||
* learning 1 additional button to remote have very similar
|
||||
* flow, so they are joined. Difference in flow is handled
|
||||
* by this boolean flag.
|
||||
*
|
||||
* @retval false if flow is in learning new remote, true if
|
||||
* adding signal to created remote
|
||||
*
|
||||
*/
|
||||
bool get_learn_new_remote();
|
||||
|
||||
/** Set state of learning new signal.
|
||||
* Adding new remote with 1 button from start scene and
|
||||
* learning 1 additional button to remote have very similar
|
||||
* flow, so they are joined. Difference in flow is handled
|
||||
* by this boolean flag.
|
||||
*
|
||||
* @param value - false if flow is in learning new remote, true if
|
||||
* adding signal to created remote
|
||||
*/
|
||||
void set_learn_new_remote(bool value);
|
||||
|
||||
/** Button is not assigned value
|
||||
*/
|
||||
enum : int {
|
||||
ButtonNA = -1,
|
||||
};
|
||||
|
||||
/** Get current button index
|
||||
*
|
||||
* @retval current button index
|
||||
*/
|
||||
int get_current_button();
|
||||
|
||||
/** Set current button index
|
||||
*
|
||||
* @param current button index
|
||||
*/
|
||||
void set_current_button(int value);
|
||||
|
||||
/** Play success notification */
|
||||
void notify_success();
|
||||
/** Play red blink notification */
|
||||
void notify_red_blink();
|
||||
/** Light green */
|
||||
void notify_green_on();
|
||||
/** Disable green light */
|
||||
void notify_green_off();
|
||||
/** Play click sound */
|
||||
void notify_click();
|
||||
/** Play click and green notification */
|
||||
void notify_click_and_green_blink();
|
||||
/** Blink green light */
|
||||
void notify_blink_green();
|
||||
|
||||
/** Text input callback
|
||||
*
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
static void text_input_callback(void* context);
|
||||
|
||||
/** Popup callback
|
||||
*
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
static void popup_callback(void* context);
|
||||
|
||||
/** Signal sent callback
|
||||
*
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
static void signal_sent_callback(void* context);
|
||||
|
||||
/** Main class constructor, initializes all critical objects */
|
||||
InfraredApp();
|
||||
/** Main class destructor, deinitializes all critical objects */
|
||||
~InfraredApp();
|
||||
|
||||
/** Path to Infrared directory */
|
||||
static constexpr const char* infrared_directory = "/any/infrared";
|
||||
/** Infrared files extension (remote files and universal databases) */
|
||||
static constexpr const char* infrared_extension = ".ir";
|
||||
/** Max Raw timings in signal */
|
||||
static constexpr const uint32_t max_raw_timings_in_signal = 512;
|
||||
/** Max line length in Infrared file */
|
||||
static constexpr const uint32_t max_line_length =
|
||||
(9 + 1) * InfraredApp::max_raw_timings_in_signal + 100;
|
||||
|
||||
private:
|
||||
/** Text store size */
|
||||
static constexpr const uint8_t text_store_size = 128;
|
||||
/** Amount of text stores */
|
||||
static constexpr const uint8_t text_store_max = 2;
|
||||
/** Store text here, for some views, because they doesn't
|
||||
* hold ownership of text */
|
||||
char text_store[text_store_max][text_store_size + 1];
|
||||
/**
|
||||
* Flag to control adding new signal flow.
|
||||
* Adding new remote with 1 button from start scene and
|
||||
* learning 1 additional button to remote have very similar
|
||||
* flow, so they are joined. Difference in flow is handled
|
||||
* by this boolean flag.
|
||||
*/
|
||||
bool learn_new_remote;
|
||||
/** Value to control edit scene */
|
||||
EditElement element;
|
||||
/** Value to control edit scene */
|
||||
EditAction action;
|
||||
/** Selected button index */
|
||||
uint32_t current_button;
|
||||
|
||||
/** Notification instance */
|
||||
NotificationApp* notification;
|
||||
/** View manager instance */
|
||||
InfraredAppViewManager view_manager;
|
||||
/** Remote manager instance */
|
||||
InfraredAppRemoteManager remote_manager;
|
||||
/** Infrared worker instance */
|
||||
InfraredWorker* infrared_worker;
|
||||
/** Signal received on Learn scene */
|
||||
InfraredAppSignal received_signal;
|
||||
|
||||
/** Stack of previous scenes */
|
||||
std::forward_list<Scene> previous_scenes_list;
|
||||
/** Now acting scene */
|
||||
Scene current_scene = Scene::Start;
|
||||
|
||||
/** Map of index/scene objects */
|
||||
std::map<Scene, InfraredAppScene*> scenes = {
|
||||
{Scene::Start, new InfraredAppSceneStart()},
|
||||
{Scene::Universal, new InfraredAppSceneUniversal()},
|
||||
{Scene::UniversalTV, new InfraredAppSceneUniversalTV()},
|
||||
{Scene::Learn, new InfraredAppSceneLearn()},
|
||||
{Scene::LearnSuccess, new InfraredAppSceneLearnSuccess()},
|
||||
{Scene::LearnEnterName, new InfraredAppSceneLearnEnterName()},
|
||||
{Scene::LearnDone, new InfraredAppSceneLearnDone()},
|
||||
{Scene::AskBack, new InfraredAppSceneAskBack()},
|
||||
{Scene::Remote, new InfraredAppSceneRemote()},
|
||||
{Scene::RemoteList, new InfraredAppSceneRemoteList()},
|
||||
{Scene::Edit, new InfraredAppSceneEdit()},
|
||||
{Scene::EditKeySelect, new InfraredAppSceneEditKeySelect()},
|
||||
{Scene::EditRename, new InfraredAppSceneEditRename()},
|
||||
{Scene::EditDelete, new InfraredAppSceneEditDelete()},
|
||||
{Scene::EditRenameDone, new InfraredAppSceneEditRenameDone()},
|
||||
{Scene::EditDeleteDone, new InfraredAppSceneEditDeleteDone()},
|
||||
};
|
||||
};
|
||||
94
applications/infrared/infrared_app_brute_force.cpp
Normal file
94
applications/infrared/infrared_app_brute_force.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
#include "helpers/infrared_parser.h"
|
||||
#include "infrared_app_brute_force.h"
|
||||
#include "infrared_app_signal.h"
|
||||
#include <memory>
|
||||
#include <m-string.h>
|
||||
#include <furi.h>
|
||||
#include <file_worker_cpp.h>
|
||||
|
||||
void InfraredAppBruteForce::add_record(int index, const char* name) {
|
||||
records[name].index = index;
|
||||
records[name].amount = 0;
|
||||
}
|
||||
|
||||
bool InfraredAppBruteForce::calculate_messages() {
|
||||
bool result = false;
|
||||
|
||||
Storage* storage = static_cast<Storage*>(furi_record_open("storage"));
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
result = flipper_format_file_open_existing(ff, universal_db_filename);
|
||||
|
||||
if(result) {
|
||||
InfraredAppSignal signal;
|
||||
|
||||
string_t signal_name;
|
||||
string_init(signal_name);
|
||||
while(flipper_format_read_string(ff, "name", signal_name)) {
|
||||
auto element = records.find(string_get_cstr(signal_name));
|
||||
if(element != records.cend()) {
|
||||
++element->second.amount;
|
||||
}
|
||||
}
|
||||
string_clear(signal_name);
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close("storage");
|
||||
return result;
|
||||
}
|
||||
|
||||
void InfraredAppBruteForce::stop_bruteforce() {
|
||||
furi_assert((current_record.size()));
|
||||
|
||||
if(current_record.size()) {
|
||||
furi_assert(ff);
|
||||
current_record.clear();
|
||||
flipper_format_free(ff);
|
||||
furi_record_close("storage");
|
||||
}
|
||||
}
|
||||
|
||||
bool InfraredAppBruteForce::send_next_bruteforce(void) {
|
||||
furi_assert(current_record.size());
|
||||
furi_assert(ff);
|
||||
|
||||
InfraredAppSignal signal;
|
||||
std::string signal_name;
|
||||
bool result = false;
|
||||
do {
|
||||
result = infrared_parser_read_signal(ff, signal, signal_name);
|
||||
} while(result && current_record.compare(signal_name));
|
||||
|
||||
if(result) {
|
||||
signal.transmit();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InfraredAppBruteForce::start_bruteforce(int index, int& record_amount) {
|
||||
bool result = false;
|
||||
record_amount = 0;
|
||||
|
||||
for(const auto& it : records) {
|
||||
if(it.second.index == index) {
|
||||
record_amount = it.second.amount;
|
||||
if(record_amount) {
|
||||
current_record = it.first;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(record_amount) {
|
||||
Storage* storage = static_cast<Storage*>(furi_record_open("storage"));
|
||||
ff = flipper_format_file_alloc(storage);
|
||||
result = flipper_format_file_open_existing(ff, universal_db_filename);
|
||||
if(!result) {
|
||||
flipper_format_free(ff);
|
||||
furi_record_close("storage");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
67
applications/infrared/infrared_app_brute_force.h
Normal file
67
applications/infrared/infrared_app_brute_force.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file infrared_app_brute_force.h
|
||||
* Infrared: Brute Force class description
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
/** Class handles brute force mechanic */
|
||||
class InfraredAppBruteForce {
|
||||
/** Universal database filename */
|
||||
const char* universal_db_filename;
|
||||
|
||||
/** Current record name (POWER, MUTE, VOL+, etc).
|
||||
* This is the name of signal to brute force. */
|
||||
std::string current_record;
|
||||
|
||||
/** Flipper File Format instance */
|
||||
FlipperFormat* ff;
|
||||
|
||||
/** Data about every record - index in button panel view
|
||||
* and amount of signals, which is need for correct
|
||||
* progress bar displaying. */
|
||||
typedef struct {
|
||||
/** Index of record in button panel view model */
|
||||
int index;
|
||||
/** Amount of signals of that type (POWER, MUTE, etc) */
|
||||
int amount;
|
||||
} Record;
|
||||
|
||||
/** Container to hold Record info.
|
||||
* 'key' is record name, because we have to search by both, index and name,
|
||||
* but index search has place once per button press, and should not be
|
||||
* noticed, but name search should occur during entering universal menu,
|
||||
* and will go through container for every record in file, that's why
|
||||
* more critical to have faster search by record name.
|
||||
*/
|
||||
std::unordered_map<std::string, Record> records;
|
||||
|
||||
public:
|
||||
/** Calculate messages. Walk through the file ('universal_db_name')
|
||||
* and calculate amount of records of certain type. */
|
||||
bool calculate_messages();
|
||||
|
||||
/** Start brute force */
|
||||
bool start_bruteforce(int index, int& record_amount);
|
||||
|
||||
/** Stop brute force */
|
||||
void stop_bruteforce();
|
||||
|
||||
/** Send next signal during brute force */
|
||||
bool send_next_bruteforce();
|
||||
|
||||
/** Add record to container of records */
|
||||
void add_record(int index, const char* name);
|
||||
|
||||
/** Initialize class, set db file */
|
||||
InfraredAppBruteForce(const char* filename)
|
||||
: universal_db_filename(filename) {
|
||||
}
|
||||
|
||||
/** Deinitialize class */
|
||||
~InfraredAppBruteForce() {
|
||||
}
|
||||
};
|
||||
47
applications/infrared/infrared_app_event.h
Normal file
47
applications/infrared/infrared_app_event.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file infrared_app_event.h
|
||||
* Infrared: Scene events description
|
||||
*/
|
||||
#pragma once
|
||||
#include <infrared.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
/** Infrared events class */
|
||||
class InfraredAppEvent {
|
||||
public:
|
||||
/** Type of event enum */
|
||||
enum class Type : uint8_t {
|
||||
/** Tick event come after no other events came in 100 ms */
|
||||
Tick,
|
||||
/** Exit application event */
|
||||
Exit,
|
||||
/** Back event */
|
||||
Back,
|
||||
/** Menu selected event type. Provided with payload value. */
|
||||
MenuSelected,
|
||||
/** Button press event. Need for continuous signal sending. */
|
||||
MenuSelectedPress,
|
||||
/** Button release event. Need for continuous signal sending. */
|
||||
MenuSelectedRelease,
|
||||
/** Events from DialogEx view module */
|
||||
DialogExSelected,
|
||||
/** Infrared signal received event */
|
||||
InfraredMessageReceived,
|
||||
/** Text edit done event */
|
||||
TextEditDone,
|
||||
/** Popup timer finished event */
|
||||
PopupTimer,
|
||||
/** Button panel pressed event */
|
||||
ButtonPanelPressed,
|
||||
};
|
||||
|
||||
union {
|
||||
/** Menu selected event type payload. Selected index. */
|
||||
int32_t menu_index;
|
||||
/** DialogEx view module event type payload */
|
||||
DialogExResult dialog_ex_result;
|
||||
} payload;
|
||||
|
||||
/** Type of event */
|
||||
Type type;
|
||||
};
|
||||
211
applications/infrared/infrared_app_remote_manager.cpp
Normal file
211
applications/infrared/infrared_app_remote_manager.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include <file_worker_cpp.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include "infrared_app_remote_manager.h"
|
||||
#include "infrared/helpers/infrared_parser.h"
|
||||
#include "infrared/infrared_app_signal.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <infrared.h>
|
||||
#include <cstdio>
|
||||
#include <furi.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <storage/storage.h>
|
||||
#include "infrared_app.h"
|
||||
|
||||
static const std::string default_remote_name = "remote";
|
||||
|
||||
std::string InfraredAppRemoteManager::make_full_name(const std::string& remote_name) const {
|
||||
return std::string("") + InfraredApp::infrared_directory + "/" + remote_name +
|
||||
InfraredApp::infrared_extension;
|
||||
}
|
||||
|
||||
std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) {
|
||||
bool exist = true;
|
||||
FileWorkerCpp file_worker;
|
||||
|
||||
if(!file_worker.is_file_exist(make_full_name(name).c_str(), &exist)) {
|
||||
return std::string();
|
||||
} else if(!exist) {
|
||||
return name;
|
||||
}
|
||||
|
||||
/* if suggested name is occupied, try another one (name2, name3, etc) */
|
||||
uint32_t i = 1;
|
||||
bool file_worker_result = false;
|
||||
std::string new_name;
|
||||
do {
|
||||
new_name = make_full_name(name + std::to_string(++i));
|
||||
file_worker_result = file_worker.is_file_exist(new_name.c_str(), &exist);
|
||||
} while(file_worker_result && exist);
|
||||
|
||||
return !exist ? name + std::to_string(i) : std::string();
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) {
|
||||
remote->buttons.emplace_back(button_name, signal);
|
||||
return store();
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::add_remote_with_button(
|
||||
const char* button_name,
|
||||
const InfraredAppSignal& signal) {
|
||||
furi_check(button_name != nullptr);
|
||||
|
||||
auto new_name = find_vacant_remote_name(default_remote_name);
|
||||
if(new_name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
remote = std::make_unique<InfraredAppRemote>(new_name);
|
||||
return add_button(button_name, signal);
|
||||
}
|
||||
|
||||
std::vector<std::string> InfraredAppRemoteManager::get_button_list(void) const {
|
||||
std::vector<std::string> name_vector;
|
||||
name_vector.reserve(remote->buttons.size());
|
||||
|
||||
for(const auto& it : remote->buttons) {
|
||||
name_vector.emplace_back(it.name);
|
||||
}
|
||||
|
||||
// copy elision
|
||||
return name_vector;
|
||||
}
|
||||
|
||||
const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) const {
|
||||
furi_check(remote.get() != nullptr);
|
||||
auto& buttons = remote->buttons;
|
||||
furi_check(index < buttons.size());
|
||||
|
||||
return buttons.at(index).signal;
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::delete_remote() {
|
||||
bool result;
|
||||
FileWorkerCpp file_worker;
|
||||
result = file_worker.remove(make_full_name(remote->name).c_str());
|
||||
|
||||
reset_remote();
|
||||
return result;
|
||||
}
|
||||
|
||||
void InfraredAppRemoteManager::reset_remote() {
|
||||
remote.reset();
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::delete_button(uint32_t index) {
|
||||
furi_check(remote.get() != nullptr);
|
||||
auto& buttons = remote->buttons;
|
||||
furi_check(index < buttons.size());
|
||||
|
||||
buttons.erase(buttons.begin() + index);
|
||||
return store();
|
||||
}
|
||||
|
||||
std::string InfraredAppRemoteManager::get_button_name(uint32_t index) {
|
||||
furi_check(remote.get() != nullptr);
|
||||
auto& buttons = remote->buttons;
|
||||
furi_check(index < buttons.size());
|
||||
return buttons[index].name.c_str();
|
||||
}
|
||||
|
||||
std::string InfraredAppRemoteManager::get_remote_name() {
|
||||
return remote.get() ? remote->name : std::string();
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::rename_remote(const char* str) {
|
||||
furi_check(str != nullptr);
|
||||
furi_check(remote.get() != nullptr);
|
||||
|
||||
if(!remote->name.compare(str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto new_name = find_vacant_remote_name(str);
|
||||
if(new_name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileWorkerCpp file_worker;
|
||||
std::string old_filename = make_full_name(remote->name);
|
||||
std::string new_filename = make_full_name(new_name);
|
||||
bool result = file_worker.rename(old_filename.c_str(), new_filename.c_str());
|
||||
|
||||
remote->name = new_name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::rename_button(uint32_t index, const char* str) {
|
||||
furi_check(remote.get() != nullptr);
|
||||
auto& buttons = remote->buttons;
|
||||
furi_check(index < buttons.size());
|
||||
|
||||
buttons[index].name = str;
|
||||
return store();
|
||||
}
|
||||
|
||||
size_t InfraredAppRemoteManager::get_number_of_buttons() {
|
||||
furi_check(remote.get() != nullptr);
|
||||
return remote->buttons.size();
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::store(void) {
|
||||
bool result = false;
|
||||
FileWorkerCpp file_worker;
|
||||
|
||||
if(!file_worker.mkdir(InfraredApp::infrared_directory)) return false;
|
||||
|
||||
Storage* storage = static_cast<Storage*>(furi_record_open("storage"));
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
FURI_LOG_I("RemoteManager", "store file: \'%s\'", make_full_name(remote->name).c_str());
|
||||
result = flipper_format_file_open_always(ff, make_full_name(remote->name).c_str());
|
||||
if(result) {
|
||||
result = flipper_format_write_header_cstr(ff, "IR signals file", 1);
|
||||
}
|
||||
if(result) {
|
||||
for(const auto& button : remote->buttons) {
|
||||
result = infrared_parser_save_signal(ff, button.signal, button.name.c_str());
|
||||
if(!result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close("storage");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InfraredAppRemoteManager::load(const std::string& remote_name) {
|
||||
bool result = false;
|
||||
Storage* storage = static_cast<Storage*>(furi_record_open("storage"));
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
FURI_LOG_I("RemoteManager", "load file: \'%s\'", make_full_name(remote_name).c_str());
|
||||
result = flipper_format_file_open_existing(ff, make_full_name(remote_name).c_str());
|
||||
if(result) {
|
||||
string_t header;
|
||||
string_init(header);
|
||||
uint32_t version;
|
||||
result = flipper_format_read_header(ff, header, &version);
|
||||
if(result) {
|
||||
result = !string_cmp_str(header, "IR signals file") && (version == 1);
|
||||
}
|
||||
string_clear(header);
|
||||
}
|
||||
if(result) {
|
||||
remote = std::make_unique<InfraredAppRemote>(remote_name);
|
||||
InfraredAppSignal signal;
|
||||
std::string signal_name;
|
||||
while(infrared_parser_read_signal(ff, signal, signal_name)) {
|
||||
remote->buttons.emplace_back(signal_name.c_str(), std::move(signal));
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close("storage");
|
||||
return result;
|
||||
}
|
||||
188
applications/infrared/infrared_app_remote_manager.h
Normal file
188
applications/infrared/infrared_app_remote_manager.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @file infrared_app_remote_manager.h
|
||||
* Infrared: Remote manager class.
|
||||
* It holds remote, can load/save/rename remote,
|
||||
* add/remove/rename buttons.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "infrared_app_signal.h"
|
||||
|
||||
#include <infrared_worker.h>
|
||||
#include <infrared.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/** Class to handle remote button */
|
||||
class InfraredAppRemoteButton {
|
||||
/** Allow field access */
|
||||
friend class InfraredAppRemoteManager;
|
||||
/** Name of signal */
|
||||
std::string name;
|
||||
/** Signal data */
|
||||
InfraredAppSignal signal;
|
||||
|
||||
public:
|
||||
/** Initialize remote button
|
||||
*
|
||||
* @param name - button name
|
||||
* @param signal - signal to copy for remote button
|
||||
*/
|
||||
InfraredAppRemoteButton(const char* name, const InfraredAppSignal& signal)
|
||||
: name(name)
|
||||
, signal(signal) {
|
||||
}
|
||||
|
||||
/** Initialize remote button
|
||||
*
|
||||
* @param name - button name
|
||||
* @param signal - signal to move for remote button
|
||||
*/
|
||||
InfraredAppRemoteButton(const char* name, InfraredAppSignal&& signal)
|
||||
: name(name)
|
||||
, signal(std::move(signal)) {
|
||||
}
|
||||
|
||||
/** Deinitialize remote button */
|
||||
~InfraredAppRemoteButton() {
|
||||
}
|
||||
};
|
||||
|
||||
/** Class to handle remote */
|
||||
class InfraredAppRemote {
|
||||
/** Allow field access */
|
||||
friend class InfraredAppRemoteManager;
|
||||
/** Button container */
|
||||
std::vector<InfraredAppRemoteButton> buttons;
|
||||
/** Name of remote */
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
/** Initialize new remote
|
||||
*
|
||||
* @param name - new remote name
|
||||
*/
|
||||
InfraredAppRemote(const std::string& name)
|
||||
: name(name) {
|
||||
}
|
||||
};
|
||||
|
||||
/** Class to handle remote manager */
|
||||
class InfraredAppRemoteManager {
|
||||
/** Remote instance. There can be 1 remote loaded at a time. */
|
||||
std::unique_ptr<InfraredAppRemote> remote;
|
||||
/** Make full name from remote name
|
||||
*
|
||||
* @param remote_name name of remote
|
||||
* @retval full name of remote on disk
|
||||
*/
|
||||
std::string make_full_name(const std::string& remote_name) const;
|
||||
|
||||
public:
|
||||
/** Restriction to button name length. Buttons larger are ignored. */
|
||||
static constexpr const uint32_t max_button_name_length = 22;
|
||||
|
||||
/** Restriction to remote name length. Remotes larger are ignored. */
|
||||
static constexpr const uint32_t max_remote_name_length = 22;
|
||||
|
||||
/** Construct button from signal, and create remote
|
||||
*
|
||||
* @param button_name - name of button to create
|
||||
* @param signal - signal to create button from
|
||||
* @retval true for success, false otherwise
|
||||
* */
|
||||
bool add_remote_with_button(const char* button_name, const InfraredAppSignal& signal);
|
||||
|
||||
/** Add button to current remote
|
||||
*
|
||||
* @param button_name - name of button to create
|
||||
* @param signal - signal to create button from
|
||||
* @retval true for success, false otherwise
|
||||
* */
|
||||
bool add_button(const char* button_name, const InfraredAppSignal& signal);
|
||||
|
||||
/** Rename button in current remote
|
||||
*
|
||||
* @param index - index of button to rename
|
||||
* @param str - new button name
|
||||
*/
|
||||
bool rename_button(uint32_t index, const char* str);
|
||||
|
||||
/** Rename current remote
|
||||
*
|
||||
* @param str - new remote name
|
||||
*/
|
||||
bool rename_remote(const char* str);
|
||||
|
||||
/** Find vacant remote name. If suggested name is occupied,
|
||||
* incremented digit(2,3,4,etc) added to name and check repeated.
|
||||
*
|
||||
* @param name - suggested remote name
|
||||
* @retval garanteed free remote name, prefixed with suggested
|
||||
*/
|
||||
std::string find_vacant_remote_name(const std::string& name);
|
||||
|
||||
/** Get button list
|
||||
*
|
||||
* @retval container of button names
|
||||
*/
|
||||
std::vector<std::string> get_button_list() const;
|
||||
|
||||
/** Get button name by index
|
||||
*
|
||||
* @param index - index of button to get name from
|
||||
* @retval button name
|
||||
*/
|
||||
std::string get_button_name(uint32_t index);
|
||||
|
||||
/** Get remote name
|
||||
*
|
||||
* @retval remote name
|
||||
*/
|
||||
std::string get_remote_name();
|
||||
|
||||
/** Get number of buttons
|
||||
*
|
||||
* @retval number of buttons
|
||||
*/
|
||||
size_t get_number_of_buttons();
|
||||
|
||||
/** Get button's signal
|
||||
*
|
||||
* @param index - index of interested button
|
||||
* @retval signal
|
||||
*/
|
||||
const InfraredAppSignal& get_button_data(size_t index) const;
|
||||
|
||||
/** Delete button
|
||||
*
|
||||
* @param index - index of interested button
|
||||
* @retval true if success, false otherwise
|
||||
*/
|
||||
bool delete_button(uint32_t index);
|
||||
|
||||
/** Delete remote
|
||||
*
|
||||
* @retval true if success, false otherwise
|
||||
*/
|
||||
bool delete_remote();
|
||||
|
||||
/** Clean all loaded info in current remote */
|
||||
void reset_remote();
|
||||
|
||||
/** Store current remote data on disk
|
||||
*
|
||||
* @retval true if success, false otherwise
|
||||
*/
|
||||
bool store();
|
||||
|
||||
/** Load data from disk into current remote
|
||||
*
|
||||
* @param name - name of remote to load
|
||||
* @retval true if success, false otherwise
|
||||
*/
|
||||
bool load(const std::string& name);
|
||||
};
|
||||
116
applications/infrared/infrared_app_signal.cpp
Normal file
116
applications/infrared/infrared_app_signal.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "infrared_app_signal.h"
|
||||
#include <infrared_transmit.h>
|
||||
|
||||
void InfraredAppSignal::copy_raw_signal(
|
||||
const uint32_t* timings,
|
||||
size_t size,
|
||||
uint32_t frequency,
|
||||
float duty_cycle) {
|
||||
furi_assert(size);
|
||||
furi_assert(timings);
|
||||
|
||||
payload.raw.frequency = frequency;
|
||||
payload.raw.duty_cycle = duty_cycle;
|
||||
payload.raw.timings_cnt = size;
|
||||
if(size) {
|
||||
payload.raw.timings = new uint32_t[size];
|
||||
memcpy(payload.raw.timings, timings, size * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
void InfraredAppSignal::clear_timings() {
|
||||
if(raw_signal) {
|
||||
delete[] payload.raw.timings;
|
||||
payload.raw.timings_cnt = 0;
|
||||
payload.raw.timings = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
InfraredAppSignal::InfraredAppSignal(
|
||||
const uint32_t* timings,
|
||||
size_t timings_cnt,
|
||||
uint32_t frequency,
|
||||
float duty_cycle) {
|
||||
raw_signal = true;
|
||||
copy_raw_signal(timings, timings_cnt, frequency, duty_cycle);
|
||||
}
|
||||
|
||||
InfraredAppSignal::InfraredAppSignal(const InfraredMessage* infrared_message) {
|
||||
raw_signal = false;
|
||||
payload.message = *infrared_message;
|
||||
}
|
||||
|
||||
InfraredAppSignal& InfraredAppSignal::operator=(const InfraredAppSignal& other) {
|
||||
clear_timings();
|
||||
raw_signal = other.raw_signal;
|
||||
if(!raw_signal) {
|
||||
payload.message = other.payload.message;
|
||||
} else {
|
||||
copy_raw_signal(
|
||||
other.payload.raw.timings,
|
||||
other.payload.raw.timings_cnt,
|
||||
other.payload.raw.frequency,
|
||||
other.payload.raw.duty_cycle);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
InfraredAppSignal::InfraredAppSignal(const InfraredAppSignal& other) {
|
||||
raw_signal = other.raw_signal;
|
||||
if(!raw_signal) {
|
||||
payload.message = other.payload.message;
|
||||
} else {
|
||||
copy_raw_signal(
|
||||
other.payload.raw.timings,
|
||||
other.payload.raw.timings_cnt,
|
||||
other.payload.raw.frequency,
|
||||
other.payload.raw.duty_cycle);
|
||||
}
|
||||
}
|
||||
|
||||
InfraredAppSignal::InfraredAppSignal(InfraredAppSignal&& other) {
|
||||
raw_signal = other.raw_signal;
|
||||
if(!raw_signal) {
|
||||
payload.message = other.payload.message;
|
||||
} else {
|
||||
furi_assert(other.payload.raw.timings_cnt > 0);
|
||||
|
||||
payload.raw.timings = other.payload.raw.timings;
|
||||
payload.raw.timings_cnt = other.payload.raw.timings_cnt;
|
||||
payload.raw.frequency = other.payload.raw.frequency;
|
||||
payload.raw.duty_cycle = other.payload.raw.duty_cycle;
|
||||
other.payload.raw.timings = nullptr;
|
||||
other.payload.raw.timings_cnt = 0;
|
||||
other.raw_signal = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InfraredAppSignal::set_message(const InfraredMessage* infrared_message) {
|
||||
clear_timings();
|
||||
raw_signal = false;
|
||||
payload.message = *infrared_message;
|
||||
}
|
||||
|
||||
void InfraredAppSignal::set_raw_signal(
|
||||
uint32_t* timings,
|
||||
size_t timings_cnt,
|
||||
uint32_t frequency,
|
||||
float duty_cycle) {
|
||||
clear_timings();
|
||||
raw_signal = true;
|
||||
copy_raw_signal(timings, timings_cnt, frequency, duty_cycle);
|
||||
}
|
||||
|
||||
void InfraredAppSignal::transmit() const {
|
||||
if(!raw_signal) {
|
||||
infrared_send(&payload.message, 1);
|
||||
} else {
|
||||
infrared_send_raw_ext(
|
||||
payload.raw.timings,
|
||||
payload.raw.timings_cnt,
|
||||
true,
|
||||
payload.raw.frequency,
|
||||
payload.raw.duty_cycle);
|
||||
}
|
||||
}
|
||||
134
applications/infrared/infrared_app_signal.h
Normal file
134
applications/infrared/infrared_app_signal.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file infrared_app_signal.h
|
||||
* Infrared: Signal class
|
||||
*/
|
||||
#pragma once
|
||||
#include <infrared_worker.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <infrared.h>
|
||||
|
||||
/** Infrared application signal class */
|
||||
class InfraredAppSignal {
|
||||
public:
|
||||
/** Raw signal structure */
|
||||
typedef struct {
|
||||
/** Timings amount */
|
||||
size_t timings_cnt;
|
||||
/** Samples of raw signal in ms */
|
||||
uint32_t* timings;
|
||||
/** PWM Frequency of raw signal */
|
||||
uint32_t frequency;
|
||||
/** PWM Duty cycle of raw signal */
|
||||
float duty_cycle;
|
||||
} RawSignal;
|
||||
|
||||
private:
|
||||
/** if true - signal is raw, if false - signal is parsed */
|
||||
bool raw_signal;
|
||||
/** signal data, either raw or parsed */
|
||||
union {
|
||||
/** signal data for parsed signal */
|
||||
InfraredMessage message;
|
||||
/** raw signal data */
|
||||
RawSignal raw;
|
||||
} payload;
|
||||
|
||||
/** Copy raw signal into object
|
||||
*
|
||||
* @param timings - timings (samples) of raw signal
|
||||
* @param size - number of timings
|
||||
* @frequency - PWM frequency of raw signal
|
||||
* @duty_cycle - PWM duty cycle
|
||||
*/
|
||||
void
|
||||
copy_raw_signal(const uint32_t* timings, size_t size, uint32_t frequency, float duty_cycle);
|
||||
/** Clear and free timings data */
|
||||
void clear_timings();
|
||||
|
||||
public:
|
||||
/** Construct Infrared signal class */
|
||||
InfraredAppSignal() {
|
||||
raw_signal = false;
|
||||
payload.message.protocol = InfraredProtocolUnknown;
|
||||
}
|
||||
|
||||
/** Destruct signal class and free all allocated data */
|
||||
~InfraredAppSignal() {
|
||||
clear_timings();
|
||||
}
|
||||
|
||||
/** Construct object with raw signal
|
||||
*
|
||||
* @param timings - timings (samples) of raw signal
|
||||
* @param size - number of timings
|
||||
* @frequency - PWM frequency of raw signal
|
||||
* @duty_cycle - PWM duty cycle
|
||||
*/
|
||||
InfraredAppSignal(
|
||||
const uint32_t* timings,
|
||||
size_t timings_cnt,
|
||||
uint32_t frequency,
|
||||
float duty_cycle);
|
||||
|
||||
/** Construct object with parsed signal
|
||||
*
|
||||
* @param infrared_message - parsed_signal to construct from
|
||||
*/
|
||||
InfraredAppSignal(const InfraredMessage* infrared_message);
|
||||
|
||||
/** Copy constructor */
|
||||
InfraredAppSignal(const InfraredAppSignal& other);
|
||||
/** Move constructor */
|
||||
InfraredAppSignal(InfraredAppSignal&& other);
|
||||
|
||||
/** Assignment operator */
|
||||
InfraredAppSignal& operator=(const InfraredAppSignal& signal);
|
||||
|
||||
/** Set object to parsed signal
|
||||
*
|
||||
* @param infrared_message - parsed_signal to construct from
|
||||
*/
|
||||
void set_message(const InfraredMessage* infrared_message);
|
||||
|
||||
/** Set object to raw signal
|
||||
*
|
||||
* @param timings - timings (samples) of raw signal
|
||||
* @param size - number of timings
|
||||
* @frequency - PWM frequency of raw signal
|
||||
* @duty_cycle - PWM duty cycle
|
||||
*/
|
||||
void
|
||||
set_raw_signal(uint32_t* timings, size_t timings_cnt, uint32_t frequency, float duty_cycle);
|
||||
|
||||
/** Transmit held signal (???) */
|
||||
void transmit() const;
|
||||
|
||||
/** Show is held signal raw
|
||||
*
|
||||
* @retval true if signal is raw, false if signal is parsed
|
||||
*/
|
||||
bool is_raw(void) const {
|
||||
return raw_signal;
|
||||
}
|
||||
|
||||
/** Get parsed signal.
|
||||
* User must check is_raw() signal before calling this function.
|
||||
*
|
||||
* @retval parsed signal pointer
|
||||
*/
|
||||
const InfraredMessage& get_message(void) const {
|
||||
furi_assert(!raw_signal);
|
||||
return payload.message;
|
||||
}
|
||||
|
||||
/** Get raw signal.
|
||||
* User must check is_raw() signal before calling this function.
|
||||
*
|
||||
* @retval raw signal
|
||||
*/
|
||||
const RawSignal& get_raw_signal(void) const {
|
||||
furi_assert(raw_signal);
|
||||
return payload.raw;
|
||||
}
|
||||
};
|
||||
163
applications/infrared/infrared_app_view_manager.cpp
Normal file
163
applications/infrared/infrared_app_view_manager.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <furi.h>
|
||||
#include <callback-connector.h>
|
||||
|
||||
#include "infrared/infrared_app_view_manager.h"
|
||||
#include "infrared/view/infrared_progress_view.h"
|
||||
#include "infrared_app.h"
|
||||
#include "infrared/infrared_app_event.h"
|
||||
|
||||
InfraredAppViewManager::InfraredAppViewManager() {
|
||||
event_queue = osMessageQueueNew(10, sizeof(InfraredAppEvent), NULL);
|
||||
|
||||
view_dispatcher = view_dispatcher_alloc();
|
||||
auto callback = cbc::obtain_connector(this, &InfraredAppViewManager::previous_view_callback);
|
||||
|
||||
gui = static_cast<Gui*>(furi_record_open("gui"));
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
button_menu = button_menu_alloc();
|
||||
submenu = submenu_alloc();
|
||||
popup = popup_alloc();
|
||||
dialog_ex = dialog_ex_alloc();
|
||||
text_input = text_input_alloc();
|
||||
button_panel = button_panel_alloc();
|
||||
progress_view = infrared_progress_view_alloc();
|
||||
loading_view = loading_alloc();
|
||||
universal_view_stack = view_stack_alloc();
|
||||
view_stack_add_view(universal_view_stack, button_panel_get_view(button_panel));
|
||||
view_set_orientation(view_stack_get_view(universal_view_stack), ViewOrientationVertical);
|
||||
|
||||
add_view(ViewId::UniversalRemote, view_stack_get_view(universal_view_stack));
|
||||
add_view(ViewId::ButtonMenu, button_menu_get_view(button_menu));
|
||||
add_view(ViewId::Submenu, submenu_get_view(submenu));
|
||||
add_view(ViewId::Popup, popup_get_view(popup));
|
||||
add_view(ViewId::DialogEx, dialog_ex_get_view(dialog_ex));
|
||||
add_view(ViewId::TextInput, text_input_get_view(text_input));
|
||||
|
||||
view_set_previous_callback(view_stack_get_view(universal_view_stack), callback);
|
||||
view_set_previous_callback(button_menu_get_view(button_menu), callback);
|
||||
view_set_previous_callback(submenu_get_view(submenu), callback);
|
||||
view_set_previous_callback(popup_get_view(popup), callback);
|
||||
view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback);
|
||||
view_set_previous_callback(text_input_get_view(text_input), callback);
|
||||
}
|
||||
|
||||
InfraredAppViewManager::~InfraredAppViewManager() {
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::UniversalRemote));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::ButtonMenu));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::TextInput));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::DialogEx));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Submenu));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Popup));
|
||||
|
||||
view_stack_remove_view(universal_view_stack, button_panel_get_view(button_panel));
|
||||
view_stack_free(universal_view_stack);
|
||||
button_panel_free(button_panel);
|
||||
submenu_free(submenu);
|
||||
popup_free(popup);
|
||||
button_menu_free(button_menu);
|
||||
dialog_ex_free(dialog_ex);
|
||||
text_input_free(text_input);
|
||||
infrared_progress_view_free(progress_view);
|
||||
loading_free(loading_view);
|
||||
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
furi_record_close("gui");
|
||||
osMessageQueueDelete(event_queue);
|
||||
}
|
||||
|
||||
void InfraredAppViewManager::switch_to(ViewId type) {
|
||||
view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
|
||||
}
|
||||
|
||||
TextInput* InfraredAppViewManager::get_text_input() {
|
||||
return text_input;
|
||||
}
|
||||
|
||||
DialogEx* InfraredAppViewManager::get_dialog_ex() {
|
||||
return dialog_ex;
|
||||
}
|
||||
|
||||
Submenu* InfraredAppViewManager::get_submenu() {
|
||||
return submenu;
|
||||
}
|
||||
|
||||
Popup* InfraredAppViewManager::get_popup() {
|
||||
return popup;
|
||||
}
|
||||
|
||||
ButtonMenu* InfraredAppViewManager::get_button_menu() {
|
||||
return button_menu;
|
||||
}
|
||||
|
||||
ButtonPanel* InfraredAppViewManager::get_button_panel() {
|
||||
return button_panel;
|
||||
}
|
||||
|
||||
InfraredProgressView* InfraredAppViewManager::get_progress() {
|
||||
return progress_view;
|
||||
}
|
||||
|
||||
Loading* InfraredAppViewManager::get_loading() {
|
||||
return loading_view;
|
||||
}
|
||||
|
||||
ViewStack* InfraredAppViewManager::get_universal_view_stack() {
|
||||
return universal_view_stack;
|
||||
}
|
||||
|
||||
osMessageQueueId_t InfraredAppViewManager::get_event_queue() {
|
||||
return event_queue;
|
||||
}
|
||||
|
||||
void InfraredAppViewManager::clear_events() {
|
||||
InfraredAppEvent event;
|
||||
while(osMessageQueueGet(event_queue, &event, NULL, 0) == osOK)
|
||||
;
|
||||
}
|
||||
|
||||
void InfraredAppViewManager::receive_event(InfraredAppEvent* event) {
|
||||
if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
|
||||
event->type = InfraredAppEvent::Type::Tick;
|
||||
}
|
||||
}
|
||||
|
||||
void InfraredAppViewManager::send_event(InfraredAppEvent* event) {
|
||||
uint32_t timeout = 0;
|
||||
/* Rapid button hammering on signal send scenes causes queue overflow - ignore it,
|
||||
* but try to keep button release event - it switches off INFRARED DMA sending. */
|
||||
if(event->type == InfraredAppEvent::Type::MenuSelectedRelease) {
|
||||
timeout = 200;
|
||||
}
|
||||
if((event->type == InfraredAppEvent::Type::DialogExSelected) &&
|
||||
(event->payload.dialog_ex_result == DialogExReleaseCenter)) {
|
||||
timeout = 200;
|
||||
}
|
||||
|
||||
osMessageQueuePut(event_queue, event, 0, timeout);
|
||||
}
|
||||
|
||||
uint32_t InfraredAppViewManager::previous_view_callback(void* context) {
|
||||
if(event_queue != NULL) {
|
||||
InfraredAppEvent event;
|
||||
event.type = InfraredAppEvent::Type::Back;
|
||||
send_event(&event);
|
||||
}
|
||||
|
||||
return VIEW_IGNORE;
|
||||
}
|
||||
|
||||
void InfraredAppViewManager::add_view(ViewId view_type, View* view) {
|
||||
view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view);
|
||||
}
|
||||
164
applications/infrared/infrared_app_view_manager.h
Normal file
164
applications/infrared/infrared_app_view_manager.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @file infrared_app_view_manager.h
|
||||
* Infrared: Scene events description
|
||||
*/
|
||||
#pragma once
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
#include <furi.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
|
||||
#include "infrared_app_event.h"
|
||||
#include "view/infrared_progress_view.h"
|
||||
|
||||
/** Infrared View manager class */
|
||||
class InfraredAppViewManager {
|
||||
public:
|
||||
/** Infrared View Id enum, it is used
|
||||
* to identify added views */
|
||||
enum class ViewId : uint8_t {
|
||||
DialogEx,
|
||||
TextInput,
|
||||
Submenu,
|
||||
ButtonMenu,
|
||||
UniversalRemote,
|
||||
Popup,
|
||||
};
|
||||
|
||||
/** Class constructor */
|
||||
InfraredAppViewManager();
|
||||
/** Class destructor */
|
||||
~InfraredAppViewManager();
|
||||
|
||||
/** Switch to another view
|
||||
*
|
||||
* @param id - view id to switch to
|
||||
*/
|
||||
void switch_to(ViewId id);
|
||||
|
||||
/** Receive event from queue
|
||||
*
|
||||
* @param event - received event
|
||||
*/
|
||||
void receive_event(InfraredAppEvent* event);
|
||||
|
||||
/** Send event to queue
|
||||
*
|
||||
* @param event - event to send
|
||||
*/
|
||||
void send_event(InfraredAppEvent* event);
|
||||
|
||||
/** Clear events that already in queue
|
||||
*
|
||||
* @param event - event to send
|
||||
*/
|
||||
void clear_events();
|
||||
|
||||
/** Get dialog_ex view module
|
||||
*
|
||||
* @retval dialog_ex view module
|
||||
*/
|
||||
DialogEx* get_dialog_ex();
|
||||
|
||||
/** Get submenu view module
|
||||
*
|
||||
* @retval submenu view module
|
||||
*/
|
||||
Submenu* get_submenu();
|
||||
|
||||
/** Get popup view module
|
||||
*
|
||||
* @retval popup view module
|
||||
*/
|
||||
Popup* get_popup();
|
||||
|
||||
/** Get text_input view module
|
||||
*
|
||||
* @retval text_input view module
|
||||
*/
|
||||
TextInput* get_text_input();
|
||||
|
||||
/** Get button_menu view module
|
||||
*
|
||||
* @retval button_menu view module
|
||||
*/
|
||||
ButtonMenu* get_button_menu();
|
||||
|
||||
/** Get button_panel view module
|
||||
*
|
||||
* @retval button_panel view module
|
||||
*/
|
||||
ButtonPanel* get_button_panel();
|
||||
|
||||
/** Get view_stack view module used in universal remote
|
||||
*
|
||||
* @retval view_stack view module
|
||||
*/
|
||||
ViewStack* get_universal_view_stack();
|
||||
|
||||
/** Get progress view module
|
||||
*
|
||||
* @retval progress view module
|
||||
*/
|
||||
InfraredProgressView* get_progress();
|
||||
|
||||
/** Get loading view module
|
||||
*
|
||||
* @retval loading view module
|
||||
*/
|
||||
Loading* get_loading();
|
||||
|
||||
/** Get event queue
|
||||
*
|
||||
* @retval event queue
|
||||
*/
|
||||
osMessageQueueId_t get_event_queue();
|
||||
|
||||
/** Callback to handle back button
|
||||
*
|
||||
* @param context - context to pass to callback
|
||||
* @retval always returns VIEW_IGNORE
|
||||
*/
|
||||
uint32_t previous_view_callback(void* context);
|
||||
|
||||
private:
|
||||
/** View Dispatcher instance.
|
||||
* It handles view switching */
|
||||
ViewDispatcher* view_dispatcher;
|
||||
/** Gui instance */
|
||||
Gui* gui;
|
||||
/** Text input view module instance */
|
||||
TextInput* text_input;
|
||||
/** DialogEx view module instance */
|
||||
DialogEx* dialog_ex;
|
||||
/** Submenu view module instance */
|
||||
Submenu* submenu;
|
||||
/** Popup view module instance */
|
||||
Popup* popup;
|
||||
/** ButtonMenu view module instance */
|
||||
ButtonMenu* button_menu;
|
||||
/** ButtonPanel view module instance */
|
||||
ButtonPanel* button_panel;
|
||||
/** ViewStack view module instance */
|
||||
ViewStack* universal_view_stack;
|
||||
/** ProgressView view module instance */
|
||||
InfraredProgressView* progress_view;
|
||||
/** Loading view module instance */
|
||||
Loading* loading_view;
|
||||
|
||||
/** Queue to handle events, which are processed in scenes */
|
||||
osMessageQueueId_t event_queue;
|
||||
|
||||
/** Add View to pull of views
|
||||
*
|
||||
* @param view_id - id to identify view
|
||||
* @param view - view to add
|
||||
*/
|
||||
void add_view(ViewId view_id, View* view);
|
||||
};
|
||||
9
applications/infrared/infrared_runner.cpp
Normal file
9
applications/infrared/infrared_runner.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "infrared_app.h"
|
||||
|
||||
extern "C" int32_t infrared_app(void* p) {
|
||||
InfraredApp* app = new InfraredApp();
|
||||
int32_t result = app->run(p);
|
||||
delete app;
|
||||
|
||||
return result;
|
||||
}
|
||||
305
applications/infrared/scene/infrared_app_scene.h
Normal file
305
applications/infrared/scene/infrared_app_scene.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* @file infrared_app_scene.h
|
||||
* Infrared: Application scenes
|
||||
*/
|
||||
#pragma once
|
||||
#include "../infrared_app_event.h"
|
||||
#include <furi_hal_infrared.h>
|
||||
#include "infrared.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../infrared_app_brute_force.h"
|
||||
|
||||
/** Anonymous class */
|
||||
class InfraredApp;
|
||||
|
||||
/** Base Scene class */
|
||||
class InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
virtual void on_enter(InfraredApp* app) = 0;
|
||||
/** Events handler callback */
|
||||
virtual bool on_event(InfraredApp* app, InfraredAppEvent* event) = 0;
|
||||
/** Called when exit scene */
|
||||
virtual void on_exit(InfraredApp* app) = 0;
|
||||
/** Virtual destructor of base class */
|
||||
virtual ~InfraredAppScene(){};
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
/** Start scene
|
||||
* Main Infrared application menu
|
||||
*/
|
||||
class InfraredAppSceneStart : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** Save previously selected submenu index
|
||||
* to highlight it when get back */
|
||||
uint32_t submenu_item_selected = 0;
|
||||
};
|
||||
|
||||
/** Universal menu scene
|
||||
* Scene to select universal remote
|
||||
*/
|
||||
class InfraredAppSceneUniversal : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** Save previously selected submenu index
|
||||
* to highlight it when get back */
|
||||
uint32_t submenu_item_selected = 0;
|
||||
};
|
||||
|
||||
/** Learn new signal scene
|
||||
* On this scene catching new IR signal performed.
|
||||
*/
|
||||
class InfraredAppSceneLearn : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
/** New signal learn succeeded scene
|
||||
*/
|
||||
class InfraredAppSceneLearnSuccess : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
bool button_pressed = false;
|
||||
};
|
||||
|
||||
/** Scene to enter name for new button in remote
|
||||
*/
|
||||
class InfraredAppSceneLearnEnterName : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
/** Scene where signal is learnt
|
||||
*/
|
||||
class InfraredAppSceneLearnDone : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
/** Remote interface scene
|
||||
* On this scene you can send IR signals from selected remote
|
||||
*/
|
||||
class InfraredAppSceneRemote : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** container of button names in current remote. */
|
||||
std::vector<std::string> buttons_names;
|
||||
/** Save previously selected index
|
||||
* to highlight it when get back */
|
||||
uint32_t buttonmenu_item_selected = 0;
|
||||
/** state flag to show button is pressed.
|
||||
* As long as send-signal button pressed no other button
|
||||
* events are handled. */
|
||||
bool button_pressed = false;
|
||||
};
|
||||
|
||||
/** List of remotes scene
|
||||
* Every remote is a file, located on internal/external storage.
|
||||
* Every file has same format, and same extension.
|
||||
* Files are parsed as you enter 'Remote scene' and showed
|
||||
* as a buttons.
|
||||
*/
|
||||
class InfraredAppSceneRemoteList : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** Save previously selected index
|
||||
* to highlight it when get back */
|
||||
uint32_t submenu_item_selected = 0;
|
||||
/** Remote names to show them in submenu */
|
||||
std::vector<std::string> remote_names;
|
||||
};
|
||||
|
||||
class InfraredAppSceneAskBack : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEdit : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** Save previously selected index
|
||||
* to highlight it when get back */
|
||||
uint32_t submenu_item_selected = 0;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEditKeySelect : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
private:
|
||||
/** Button names to show them in submenu */
|
||||
std::vector<std::string> buttons_names;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEditRename : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEditDelete : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEditRenameDone : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
class InfraredAppSceneEditDeleteDone : public InfraredAppScene {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
};
|
||||
|
||||
class InfraredAppSceneUniversalCommon : public InfraredAppScene {
|
||||
/** Brute force started flag */
|
||||
bool brute_force_started = false;
|
||||
|
||||
protected:
|
||||
/** Events handler callback */
|
||||
bool on_event(InfraredApp* app, InfraredAppEvent* event) final;
|
||||
/** Called when exit scene */
|
||||
void on_exit(InfraredApp* app) final;
|
||||
|
||||
/** Show popup window
|
||||
*
|
||||
* @param app - application instance
|
||||
*/
|
||||
void show_popup(InfraredApp* app, int record_amount);
|
||||
|
||||
/** Hide popup window
|
||||
*
|
||||
* @param app - application instance
|
||||
*/
|
||||
void hide_popup(InfraredApp* app);
|
||||
|
||||
/** Propagate progress in popup window
|
||||
*
|
||||
* @param app - application instance
|
||||
*/
|
||||
bool progress_popup(InfraredApp* app);
|
||||
|
||||
/** Item selected callback
|
||||
*
|
||||
* @param context - context
|
||||
* @param index - selected item index
|
||||
*/
|
||||
static void infrared_app_item_callback(void* context, uint32_t index);
|
||||
|
||||
/** Brute Force instance */
|
||||
InfraredAppBruteForce brute_force;
|
||||
|
||||
/** Constructor */
|
||||
InfraredAppSceneUniversalCommon(const char* filename)
|
||||
: brute_force(filename) {
|
||||
}
|
||||
|
||||
/** Destructor */
|
||||
~InfraredAppSceneUniversalCommon() {
|
||||
}
|
||||
};
|
||||
|
||||
class InfraredAppSceneUniversalTV : public InfraredAppSceneUniversalCommon {
|
||||
public:
|
||||
/** Called when enter scene */
|
||||
void on_enter(InfraredApp* app) final;
|
||||
|
||||
/** Constructor
|
||||
* Specifies path to brute force db library */
|
||||
InfraredAppSceneUniversalTV()
|
||||
: InfraredAppSceneUniversalCommon("/ext/infrared/assets/tv.ir") {
|
||||
}
|
||||
|
||||
/** Destructor */
|
||||
~InfraredAppSceneUniversalTV() {
|
||||
}
|
||||
};
|
||||
73
applications/infrared/scene/infrared_app_scene_ask_back.cpp
Normal file
73
applications/infrared/scene/infrared_app_scene_ask_back.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "gui/modules/dialog_ex.h"
|
||||
#include "infrared.h"
|
||||
#include "infrared/scene/infrared_app_scene.h"
|
||||
#include <string>
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::DialogExSelected;
|
||||
event.payload.dialog_ex_result = result;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneAskBack::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
|
||||
if(app->get_learn_new_remote()) {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop);
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, nullptr);
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Stay");
|
||||
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, app);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneAskBack::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::DialogExSelected) {
|
||||
switch(event->payload.dialog_ex_result) {
|
||||
case DialogExResultLeft:
|
||||
consumed = true;
|
||||
if(app->get_learn_new_remote()) {
|
||||
app->search_and_switch_to_previous_scene({InfraredApp::Scene::Start});
|
||||
} else {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::Edit, InfraredApp::Scene::Remote});
|
||||
}
|
||||
break;
|
||||
case DialogExResultCenter:
|
||||
furi_assert(0);
|
||||
break;
|
||||
case DialogExResultRight:
|
||||
app->switch_to_previous_scene();
|
||||
consumed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::Back) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneAskBack::on_exit(InfraredApp* app) {
|
||||
}
|
||||
79
applications/infrared/scene/infrared_app_scene_edit.cpp
Normal file
79
applications/infrared/scene/infrared_app_scene_edit.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "gui/modules/submenu.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexAddKey,
|
||||
SubmenuIndexRenameKey,
|
||||
SubmenuIndexDeleteKey,
|
||||
SubmenuIndexRenameRemote,
|
||||
SubmenuIndexDeleteRemote,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void submenu_callback(void* context, uint32_t index) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::MenuSelected;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneEdit::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_add_item(submenu, "Add key", SubmenuIndexAddKey, submenu_callback, app);
|
||||
submenu_add_item(submenu, "Rename key", SubmenuIndexRenameKey, submenu_callback, app);
|
||||
submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app);
|
||||
submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app);
|
||||
submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app);
|
||||
submenu_set_selected_item(submenu, submenu_item_selected);
|
||||
submenu_item_selected = 0;
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEdit::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::MenuSelected) {
|
||||
submenu_item_selected = event->payload.menu_index;
|
||||
switch(event->payload.menu_index) {
|
||||
case SubmenuIndexAddKey:
|
||||
app->set_learn_new_remote(false);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Learn);
|
||||
break;
|
||||
case SubmenuIndexRenameKey:
|
||||
app->set_edit_action(InfraredApp::EditAction::Rename);
|
||||
app->set_edit_element(InfraredApp::EditElement::Button);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect);
|
||||
break;
|
||||
case SubmenuIndexDeleteKey:
|
||||
app->set_edit_action(InfraredApp::EditAction::Delete);
|
||||
app->set_edit_element(InfraredApp::EditElement::Button);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect);
|
||||
break;
|
||||
case SubmenuIndexRenameRemote:
|
||||
app->set_edit_action(InfraredApp::EditAction::Rename);
|
||||
app->set_edit_element(InfraredApp::EditElement::Remote);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditRename);
|
||||
break;
|
||||
case SubmenuIndexDeleteRemote:
|
||||
app->set_edit_action(InfraredApp::EditAction::Delete);
|
||||
app->set_edit_element(InfraredApp::EditElement::Remote);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditDelete);
|
||||
break;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEdit::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_reset(submenu);
|
||||
}
|
||||
100
applications/infrared/scene/infrared_app_scene_edit_delete.cpp
Normal file
100
applications/infrared/scene/infrared_app_scene_edit_delete.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "infrared.h"
|
||||
#include "infrared/scene/infrared_app_scene.h"
|
||||
#include <string>
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::DialogExSelected;
|
||||
event.payload.dialog_ex_result = result;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditDelete::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
|
||||
if(app->get_edit_element() == InfraredApp::EditElement::Button) {
|
||||
auto signal = remote_manager->get_button_data(app->get_current_button());
|
||||
dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop);
|
||||
if(!signal.is_raw()) {
|
||||
auto message = &signal.get_message();
|
||||
app->set_text_store(
|
||||
0,
|
||||
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
|
||||
remote_manager->get_button_name(app->get_current_button()).c_str(),
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
} else {
|
||||
app->set_text_store(
|
||||
0,
|
||||
"%s\nRAW\n%ld samples",
|
||||
remote_manager->get_button_name(app->get_current_button()).c_str(),
|
||||
signal.get_raw_signal().timings_cnt);
|
||||
}
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop);
|
||||
app->set_text_store(
|
||||
0,
|
||||
"%s\n with %lu buttons",
|
||||
remote_manager->get_remote_name().c_str(),
|
||||
remote_manager->get_number_of_buttons());
|
||||
}
|
||||
|
||||
dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Back");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Delete");
|
||||
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, app);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEditDelete::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::DialogExSelected) {
|
||||
switch(event->payload.dialog_ex_result) {
|
||||
case DialogExResultLeft:
|
||||
app->switch_to_previous_scene();
|
||||
break;
|
||||
case DialogExResultCenter:
|
||||
furi_assert(0);
|
||||
break;
|
||||
case DialogExResultRight: {
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
bool result = false;
|
||||
if(app->get_edit_element() == InfraredApp::EditElement::Remote) {
|
||||
result = remote_manager->delete_remote();
|
||||
} else {
|
||||
result = remote_manager->delete_button(app->get_current_button());
|
||||
app->set_current_button(InfraredApp::ButtonNA);
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::RemoteList, InfraredApp::Scene::Start});
|
||||
} else {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditDeleteDone);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditDelete::on_exit(InfraredApp* app) {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "../infrared_app.h"
|
||||
|
||||
void InfraredAppSceneEditDeleteDone::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
popup_set_callback(popup, InfraredApp::popup_callback);
|
||||
popup_set_context(popup, app);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Popup);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEditDeleteDone::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::PopupTimer) {
|
||||
if(app->get_edit_element() == InfraredApp::EditElement::Remote) {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList});
|
||||
} else {
|
||||
app->search_and_switch_to_previous_scene({InfraredApp::Scene::Remote});
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditDeleteDone::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "gui/modules/submenu.h"
|
||||
|
||||
static void submenu_callback(void* context, uint32_t index) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::MenuSelected;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditKeySelect::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
int item_number = 0;
|
||||
|
||||
const char* header =
|
||||
app->get_edit_action() == InfraredApp::EditAction::Rename ? "Rename key:" : "Delete key:";
|
||||
submenu_set_header(submenu, header);
|
||||
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
buttons_names = remote_manager->get_button_list();
|
||||
for(const auto& it : buttons_names) {
|
||||
submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app);
|
||||
}
|
||||
if((item_number > 0) && (app->get_current_button() != InfraredApp::ButtonNA)) {
|
||||
submenu_set_selected_item(submenu, app->get_current_button());
|
||||
app->set_current_button(InfraredApp::ButtonNA);
|
||||
}
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEditKeySelect::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::MenuSelected) {
|
||||
app->set_current_button(event->payload.menu_index);
|
||||
consumed = true;
|
||||
if(app->get_edit_action() == InfraredApp::EditAction::Rename) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditRename);
|
||||
} else {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::EditDelete);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditKeySelect::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_reset(submenu);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "../infrared_app.h"
|
||||
|
||||
void InfraredAppSceneEditRename::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
TextInput* text_input = view_manager->get_text_input();
|
||||
size_t enter_name_length = 0;
|
||||
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
if(app->get_edit_element() == InfraredApp::EditElement::Button) {
|
||||
furi_assert(app->get_current_button() != InfraredApp::ButtonNA);
|
||||
auto button_name = remote_manager->get_button_name(app->get_current_button());
|
||||
char* buffer_str = app->get_text_store(0);
|
||||
size_t max_len = InfraredAppRemoteManager::max_button_name_length;
|
||||
strncpy(buffer_str, button_name.c_str(), max_len);
|
||||
buffer_str[max_len + 1] = 0;
|
||||
enter_name_length = max_len;
|
||||
text_input_set_header_text(text_input, "Name the key");
|
||||
} else {
|
||||
auto remote_name = remote_manager->get_remote_name();
|
||||
strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size());
|
||||
enter_name_length = InfraredAppRemoteManager::max_remote_name_length;
|
||||
text_input_set_header_text(text_input, "Name the remote");
|
||||
|
||||
ValidatorIsFile* validator_is_file =
|
||||
validator_is_file_alloc_init(app->infrared_directory, app->infrared_extension);
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
}
|
||||
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
InfraredApp::text_input_callback,
|
||||
app,
|
||||
app->get_text_store(0),
|
||||
enter_name_length,
|
||||
false);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEditRename::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::TextEditDone) {
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
bool result = false;
|
||||
if(app->get_edit_element() == InfraredApp::EditElement::Button) {
|
||||
result =
|
||||
remote_manager->rename_button(app->get_current_button(), app->get_text_store(0));
|
||||
app->set_current_button(InfraredApp::ButtonNA);
|
||||
} else {
|
||||
result = remote_manager->rename_remote(app->get_text_store(0));
|
||||
}
|
||||
if(!result) {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList});
|
||||
} else {
|
||||
app->switch_to_next_scene_without_saving(InfraredApp::Scene::EditRenameDone);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditRename::on_exit(InfraredApp* app) {
|
||||
TextInput* text_input = app->get_view_manager()->get_text_input();
|
||||
|
||||
void* validator_context = text_input_get_validator_callback_context(text_input);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
|
||||
if(validator_context != NULL) validator_is_file_free((ValidatorIsFile*)validator_context);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../infrared_app.h"
|
||||
|
||||
void InfraredAppSceneEditRenameDone::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
popup_set_callback(popup, InfraredApp::popup_callback);
|
||||
popup_set_context(popup, app);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Popup);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneEditRenameDone::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::PopupTimer) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Remote);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneEditRenameDone::on_exit(InfraredApp* app) {
|
||||
}
|
||||
75
applications/infrared/scene/infrared_app_scene_learn.cpp
Normal file
75
applications/infrared/scene/infrared_app_scene_learn.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "../infrared_app_event.h"
|
||||
#include "infrared.h"
|
||||
#include <infrared_worker.h>
|
||||
|
||||
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
furi_assert(context);
|
||||
furi_assert(received_signal);
|
||||
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
InfraredAppSignal signal(infrared_worker_get_decoded_signal(received_signal));
|
||||
app->set_received_signal(signal);
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_cnt;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
|
||||
InfraredAppSignal signal(
|
||||
timings, timings_cnt, INFRARED_COMMON_CARRIER_FREQUENCY, INFRARED_COMMON_DUTY_CYCLE);
|
||||
app->set_received_signal(signal);
|
||||
}
|
||||
|
||||
infrared_worker_rx_set_received_signal_callback(app->get_infrared_worker(), NULL, NULL);
|
||||
InfraredAppEvent event;
|
||||
event.type = InfraredAppEvent::Type::InfraredMessageReceived;
|
||||
auto view_manager = app->get_view_manager();
|
||||
view_manager->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearn::on_enter(InfraredApp* app) {
|
||||
auto view_manager = app->get_view_manager();
|
||||
auto popup = view_manager->get_popup();
|
||||
|
||||
auto worker = app->get_infrared_worker();
|
||||
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, app);
|
||||
infrared_worker_rx_start(worker);
|
||||
|
||||
popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31);
|
||||
popup_set_text(
|
||||
popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
|
||||
popup_set_callback(popup, NULL);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Popup);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
switch(event->type) {
|
||||
case InfraredAppEvent::Type::Tick:
|
||||
consumed = true;
|
||||
app->notify_red_blink();
|
||||
break;
|
||||
case InfraredAppEvent::Type::InfraredMessageReceived:
|
||||
app->notify_success();
|
||||
app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnSuccess);
|
||||
break;
|
||||
case InfraredAppEvent::Type::Back:
|
||||
consumed = true;
|
||||
app->switch_to_previous_scene();
|
||||
break;
|
||||
default:
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearn::on_exit(InfraredApp* app) {
|
||||
infrared_worker_rx_stop(app->get_infrared_worker());
|
||||
auto view_manager = app->get_view_manager();
|
||||
auto popup = view_manager->get_popup();
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "../infrared_app.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void InfraredAppSceneLearnDone::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
DOLPHIN_DEED(DolphinDeedIrSave);
|
||||
|
||||
if(app->get_learn_new_remote()) {
|
||||
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
|
||||
} else {
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
popup_set_callback(popup, InfraredApp::popup_callback);
|
||||
popup_set_context(popup, app);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Popup);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneLearnDone::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::PopupTimer) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Remote);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearnDone::on_exit(InfraredApp* app) {
|
||||
app->set_learn_new_remote(false);
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "gui/modules/text_input.h"
|
||||
|
||||
void InfraredAppSceneLearnEnterName::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
TextInput* text_input = view_manager->get_text_input();
|
||||
|
||||
auto signal = app->get_received_signal();
|
||||
|
||||
if(!signal.is_raw()) {
|
||||
auto message = &signal.get_message();
|
||||
app->set_text_store(
|
||||
0,
|
||||
"%.4s_%0*lX",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
} else {
|
||||
auto raw_signal = signal.get_raw_signal();
|
||||
app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt);
|
||||
}
|
||||
|
||||
text_input_set_header_text(text_input, "Name the key");
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
InfraredApp::text_input_callback,
|
||||
app,
|
||||
app->get_text_store(0),
|
||||
InfraredAppRemoteManager::max_button_name_length,
|
||||
true);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneLearnEnterName::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::TextEditDone) {
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
bool result = false;
|
||||
if(app->get_learn_new_remote()) {
|
||||
result = remote_manager->add_remote_with_button(
|
||||
app->get_text_store(0), app->get_received_signal());
|
||||
} else {
|
||||
result =
|
||||
remote_manager->add_button(app->get_text_store(0), app->get_received_signal());
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList});
|
||||
} else {
|
||||
app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnDone);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearnEnterName::on_exit(InfraredApp* app) {
|
||||
}
|
||||
149
applications/infrared/scene/infrared_app_scene_learn_success.cpp
Normal file
149
applications/infrared/scene/infrared_app_scene_learn_success.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <file_worker_cpp.h>
|
||||
#include <memory>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#include "../infrared_app.h"
|
||||
#include "infrared.h"
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::DialogExSelected;
|
||||
event.payload.dialog_ex_result = result;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearnSuccess::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedIrLearnSuccess);
|
||||
app->notify_green_on();
|
||||
|
||||
infrared_worker_tx_set_get_signal_callback(
|
||||
app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app);
|
||||
infrared_worker_tx_set_signal_sent_callback(
|
||||
app->get_infrared_worker(), InfraredApp::signal_sent_callback, app);
|
||||
|
||||
auto signal = app->get_received_signal();
|
||||
|
||||
if(!signal.is_raw()) {
|
||||
auto message = &signal.get_message();
|
||||
uint8_t adr_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
|
||||
uint8_t cmd_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4);
|
||||
uint8_t max_digits = MAX(adr_digits, cmd_digits);
|
||||
max_digits = MIN(max_digits, 7);
|
||||
size_t label_x_offset = 63 + (7 - max_digits) * 3;
|
||||
|
||||
app->set_text_store(0, "%s", infrared_get_protocol_name(message->protocol));
|
||||
app->set_text_store(
|
||||
1,
|
||||
"A: 0x%0*lX\nC: 0x%0*lX\n",
|
||||
adr_digits,
|
||||
message->address,
|
||||
cmd_digits,
|
||||
message->command);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 7, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, app->get_text_store(1), label_x_offset, 34, AlignLeft, AlignCenter);
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
|
||||
app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt);
|
||||
dialog_ex_set_text(dialog_ex, app->get_text_store(0), 75, 23, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Save");
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Send");
|
||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
|
||||
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, app);
|
||||
dialog_ex_enable_extended_events(dialog_ex);
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
if(event->type == InfraredAppEvent::Type::Tick) {
|
||||
/* Send event every tick to suppress any switching off green light */
|
||||
if(!button_pressed) {
|
||||
app->notify_green_on();
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::DialogExSelected) {
|
||||
switch(event->payload.dialog_ex_result) {
|
||||
case DialogExResultLeft:
|
||||
consumed = true;
|
||||
if(!button_pressed) {
|
||||
app->switch_to_next_scene_without_saving(InfraredApp::Scene::Learn);
|
||||
}
|
||||
break;
|
||||
case DialogExResultRight: {
|
||||
consumed = true;
|
||||
FileWorkerCpp file_worker;
|
||||
if(!button_pressed) {
|
||||
if(file_worker.check_errors()) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName);
|
||||
} else {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DialogExPressCenter:
|
||||
if(!button_pressed) {
|
||||
button_pressed = true;
|
||||
app->notify_click_and_green_blink();
|
||||
|
||||
auto signal = app->get_received_signal();
|
||||
if(signal.is_raw()) {
|
||||
infrared_worker_set_raw_signal(
|
||||
app->get_infrared_worker(),
|
||||
signal.get_raw_signal().timings,
|
||||
signal.get_raw_signal().timings_cnt);
|
||||
} else {
|
||||
infrared_worker_set_decoded_signal(
|
||||
app->get_infrared_worker(), &signal.get_message());
|
||||
}
|
||||
|
||||
infrared_worker_tx_start(app->get_infrared_worker());
|
||||
}
|
||||
break;
|
||||
case DialogExReleaseCenter:
|
||||
if(button_pressed) {
|
||||
button_pressed = false;
|
||||
infrared_worker_tx_stop(app->get_infrared_worker());
|
||||
app->notify_green_off();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::Back) {
|
||||
if(!button_pressed) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::AskBack);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneLearnSuccess::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
dialog_ex_reset(dialog_ex);
|
||||
app->notify_green_off();
|
||||
infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr);
|
||||
infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr);
|
||||
}
|
||||
134
applications/infrared/scene/infrared_app_scene_remote.cpp
Normal file
134
applications/infrared/scene/infrared_app_scene_remote.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <input/input.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include "../infrared_app.h"
|
||||
#include "../infrared_app_view_manager.h"
|
||||
|
||||
typedef enum {
|
||||
ButtonIndexPlus = -2,
|
||||
ButtonIndexEdit = -1,
|
||||
ButtonIndexNA = 0,
|
||||
} ButtonIndex;
|
||||
|
||||
static void button_menu_callback(void* context, int32_t index, InputType type) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
if(type == InputTypePress) {
|
||||
event.type = InfraredAppEvent::Type::MenuSelectedPress;
|
||||
} else if(type == InputTypeRelease) {
|
||||
event.type = InfraredAppEvent::Type::MenuSelectedRelease;
|
||||
} else if(type == InputTypeShort) {
|
||||
event.type = InfraredAppEvent::Type::MenuSelected;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneRemote::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonMenu* button_menu = view_manager->get_button_menu();
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
int i = 0;
|
||||
button_pressed = false;
|
||||
|
||||
infrared_worker_tx_set_get_signal_callback(
|
||||
app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app);
|
||||
infrared_worker_tx_set_signal_sent_callback(
|
||||
app->get_infrared_worker(), InfraredApp::signal_sent_callback, app);
|
||||
buttons_names = remote_manager->get_button_list();
|
||||
|
||||
i = 0;
|
||||
for(auto& name : buttons_names) {
|
||||
button_menu_add_item(
|
||||
button_menu, name.c_str(), i++, button_menu_callback, ButtonMenuItemTypeCommon, app);
|
||||
}
|
||||
|
||||
button_menu_add_item(
|
||||
button_menu, "+", ButtonIndexPlus, button_menu_callback, ButtonMenuItemTypeControl, app);
|
||||
button_menu_add_item(
|
||||
button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app);
|
||||
|
||||
app->set_text_store(0, "%s", remote_manager->get_remote_name().c_str());
|
||||
button_menu_set_header(button_menu, app->get_text_store(0));
|
||||
if(buttonmenu_item_selected != ButtonIndexNA) {
|
||||
button_menu_set_selected_item(button_menu, buttonmenu_item_selected);
|
||||
buttonmenu_item_selected = ButtonIndexNA;
|
||||
}
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = true;
|
||||
|
||||
if((event->type == InfraredAppEvent::Type::MenuSelected) ||
|
||||
(event->type == InfraredAppEvent::Type::MenuSelectedPress) ||
|
||||
(event->type == InfraredAppEvent::Type::MenuSelectedRelease)) {
|
||||
switch(event->payload.menu_index) {
|
||||
case ButtonIndexPlus:
|
||||
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
||||
app->notify_click();
|
||||
buttonmenu_item_selected = event->payload.menu_index;
|
||||
app->set_learn_new_remote(false);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Learn);
|
||||
break;
|
||||
case ButtonIndexEdit:
|
||||
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
||||
app->notify_click();
|
||||
buttonmenu_item_selected = event->payload.menu_index;
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Edit);
|
||||
break;
|
||||
default:
|
||||
furi_assert(event->type != InfraredAppEvent::Type::MenuSelected);
|
||||
bool pressed = (event->type == InfraredAppEvent::Type::MenuSelectedPress);
|
||||
|
||||
if(pressed && !button_pressed) {
|
||||
button_pressed = true;
|
||||
app->notify_click_and_green_blink();
|
||||
|
||||
auto button_signal =
|
||||
app->get_remote_manager()->get_button_data(event->payload.menu_index);
|
||||
if(button_signal.is_raw()) {
|
||||
infrared_worker_set_raw_signal(
|
||||
app->get_infrared_worker(),
|
||||
button_signal.get_raw_signal().timings,
|
||||
button_signal.get_raw_signal().timings_cnt);
|
||||
} else {
|
||||
infrared_worker_set_decoded_signal(
|
||||
app->get_infrared_worker(), &button_signal.get_message());
|
||||
}
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedIrSend);
|
||||
infrared_worker_tx_start(app->get_infrared_worker());
|
||||
} else if(!pressed && button_pressed) {
|
||||
button_pressed = false;
|
||||
infrared_worker_tx_stop(app->get_infrared_worker());
|
||||
app->notify_green_off();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if(event->type == InfraredAppEvent::Type::Back) {
|
||||
if(!button_pressed) {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList});
|
||||
}
|
||||
} else {
|
||||
consumed = false;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneRemote::on_exit(InfraredApp* app) {
|
||||
infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr);
|
||||
infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr);
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonMenu* button_menu = view_manager->get_button_menu();
|
||||
|
||||
button_menu_reset(button_menu);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "../infrared_app.h"
|
||||
#include "infrared/infrared_app_event.h"
|
||||
#include <text_store.h>
|
||||
#include <file_worker_cpp.h>
|
||||
|
||||
void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
FileWorkerCpp file_worker;
|
||||
bool result = false;
|
||||
bool file_select_result;
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
auto last_selected_remote = remote_manager->get_remote_name();
|
||||
const char* last_selected_remote_name =
|
||||
last_selected_remote.size() ? last_selected_remote.c_str() : nullptr;
|
||||
auto filename_ts =
|
||||
std::make_unique<TextStore>(InfraredAppRemoteManager::max_remote_name_length);
|
||||
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonMenu* button_menu = view_manager->get_button_menu();
|
||||
button_menu_reset(button_menu);
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu);
|
||||
|
||||
file_select_result = file_worker.file_select(
|
||||
InfraredApp::infrared_directory,
|
||||
InfraredApp::infrared_extension,
|
||||
filename_ts->text,
|
||||
filename_ts->text_size,
|
||||
last_selected_remote_name);
|
||||
|
||||
if(file_select_result) {
|
||||
if(remote_manager->load(std::string(filename_ts->text))) {
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Remote);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
|
||||
bool InfraredAppSceneRemoteList::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneRemoteList::on_exit(InfraredApp* app) {
|
||||
}
|
||||
66
applications/infrared/scene/infrared_app_scene_start.cpp
Normal file
66
applications/infrared/scene/infrared_app_scene_start.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "../infrared_app.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexUniversalLibrary,
|
||||
SubmenuIndexLearnNewRemote,
|
||||
SubmenuIndexSavedRemotes,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void submenu_callback(void* context, uint32_t index) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::MenuSelected;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneStart::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Universal library", SubmenuIndexUniversalLibrary, submenu_callback, app);
|
||||
submenu_add_item(
|
||||
submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app);
|
||||
submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app);
|
||||
submenu_set_selected_item(submenu, submenu_item_selected);
|
||||
submenu_item_selected = 0;
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneStart::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::MenuSelected) {
|
||||
submenu_item_selected = event->payload.menu_index;
|
||||
switch(event->payload.menu_index) {
|
||||
case SubmenuIndexUniversalLibrary:
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Universal);
|
||||
break;
|
||||
case SubmenuIndexLearnNewRemote:
|
||||
app->set_learn_new_remote(true);
|
||||
app->switch_to_next_scene(InfraredApp::Scene::Learn);
|
||||
break;
|
||||
case SubmenuIndexSavedRemotes:
|
||||
app->switch_to_next_scene(InfraredApp::Scene::RemoteList);
|
||||
break;
|
||||
default:
|
||||
furi_assert(0);
|
||||
break;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneStart::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
app->get_remote_manager()->reset_remote();
|
||||
submenu_reset(submenu);
|
||||
}
|
||||
57
applications/infrared/scene/infrared_app_scene_universal.cpp
Normal file
57
applications/infrared/scene/infrared_app_scene_universal.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "../infrared_app.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexUniversalTV,
|
||||
SubmenuIndexUniversalAudio,
|
||||
SubmenuIndexUniversalAirConditioner,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void submenu_callback(void* context, uint32_t index) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::MenuSelected;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneUniversal::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_add_item(submenu, "TV's", SubmenuIndexUniversalTV, submenu_callback, app);
|
||||
submenu_set_selected_item(submenu, submenu_item_selected);
|
||||
submenu_item_selected = 0;
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneUniversal::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InfraredAppEvent::Type::MenuSelected) {
|
||||
submenu_item_selected = event->payload.menu_index;
|
||||
switch(event->payload.menu_index) {
|
||||
case SubmenuIndexUniversalTV:
|
||||
app->switch_to_next_scene(InfraredApp::Scene::UniversalTV);
|
||||
break;
|
||||
case SubmenuIndexUniversalAudio:
|
||||
// app->switch_to_next_scene(InfraredApp::Scene::UniversalAudio);
|
||||
break;
|
||||
case SubmenuIndexUniversalAirConditioner:
|
||||
// app->switch_to_next_scene(InfraredApp::Scene::UniversalAirConditioner);
|
||||
break;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneUniversal::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
Submenu* submenu = view_manager->get_submenu();
|
||||
|
||||
submenu_reset(submenu);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_stack.h>
|
||||
|
||||
#include "../infrared_app.h"
|
||||
#include "infrared/infrared_app_event.h"
|
||||
#include "infrared/infrared_app_view_manager.h"
|
||||
#include "infrared/scene/infrared_app_scene.h"
|
||||
#include "../view/infrared_progress_view.h"
|
||||
|
||||
void InfraredAppSceneUniversalCommon::infrared_app_item_callback(void* context, uint32_t index) {
|
||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||
InfraredAppEvent event;
|
||||
|
||||
event.type = InfraredAppEvent::Type::ButtonPanelPressed;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
static void infrared_progress_back_callback(void* context) {
|
||||
furi_assert(context);
|
||||
auto app = static_cast<InfraredApp*>(context);
|
||||
|
||||
InfraredAppEvent infrared_event = {
|
||||
.type = InfraredAppEvent::Type::Back,
|
||||
};
|
||||
app->get_view_manager()->clear_events();
|
||||
app->get_view_manager()->send_event(&infrared_event);
|
||||
}
|
||||
|
||||
void InfraredAppSceneUniversalCommon::hide_popup(InfraredApp* app) {
|
||||
auto stack_view = app->get_view_manager()->get_universal_view_stack();
|
||||
auto progress_view = app->get_view_manager()->get_progress();
|
||||
view_stack_remove_view(stack_view, infrared_progress_view_get_view(progress_view));
|
||||
}
|
||||
|
||||
void InfraredAppSceneUniversalCommon::show_popup(InfraredApp* app, int record_amount) {
|
||||
auto stack_view = app->get_view_manager()->get_universal_view_stack();
|
||||
auto progress_view = app->get_view_manager()->get_progress();
|
||||
infrared_progress_view_set_progress_total(progress_view, record_amount);
|
||||
infrared_progress_view_set_back_callback(progress_view, infrared_progress_back_callback, app);
|
||||
view_stack_add_view(stack_view, infrared_progress_view_get_view(progress_view));
|
||||
}
|
||||
|
||||
bool InfraredAppSceneUniversalCommon::progress_popup(InfraredApp* app) {
|
||||
auto progress_view = app->get_view_manager()->get_progress();
|
||||
return infrared_progress_view_increase_progress(progress_view);
|
||||
}
|
||||
|
||||
bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(brute_force_started) {
|
||||
if(event->type == InfraredAppEvent::Type::Tick) {
|
||||
auto view_manager = app->get_view_manager();
|
||||
InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick};
|
||||
view_manager->send_event(&tick_event);
|
||||
bool result = brute_force.send_next_bruteforce();
|
||||
if(result) {
|
||||
result = progress_popup(app);
|
||||
}
|
||||
if(!result) {
|
||||
brute_force.stop_bruteforce();
|
||||
brute_force_started = false;
|
||||
hide_popup(app);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->type == InfraredAppEvent::Type::Back) {
|
||||
brute_force_started = false;
|
||||
brute_force.stop_bruteforce();
|
||||
hide_popup(app);
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
if(event->type == InfraredAppEvent::Type::ButtonPanelPressed) {
|
||||
int record_amount = 0;
|
||||
if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) {
|
||||
DOLPHIN_DEED(DolphinDeedIrBruteForce);
|
||||
brute_force_started = true;
|
||||
show_popup(app, record_amount);
|
||||
} else {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->type == InfraredAppEvent::Type::Back) {
|
||||
app->switch_to_previous_scene();
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void InfraredAppSceneUniversalCommon::on_exit(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonPanel* button_panel = view_manager->get_button_panel();
|
||||
button_panel_reset(button_panel);
|
||||
}
|
||||
123
applications/infrared/scene/infrared_app_scene_universal_tv.cpp
Normal file
123
applications/infrared/scene/infrared_app_scene_universal_tv.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <stdint.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include "infrared/scene/infrared_app_scene.h"
|
||||
#include "infrared/infrared_app.h"
|
||||
|
||||
void InfraredAppSceneUniversalTV::on_enter(InfraredApp* app) {
|
||||
InfraredAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonPanel* button_panel = view_manager->get_button_panel();
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
|
||||
int i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
19,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "POWER");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
19,
|
||||
&I_Mute_25x27,
|
||||
&I_Mute_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "MUTE");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "VOL+");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
66,
|
||||
&I_Up_25x27,
|
||||
&I_Up_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "CH+");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "VOL-");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
98,
|
||||
&I_Down_25x27,
|
||||
&I_Down_hvr_25x27,
|
||||
infrared_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "CH-");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
||||
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
||||
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
|
||||
|
||||
view_manager->switch_to(InfraredAppViewManager::ViewId::UniversalRemote);
|
||||
|
||||
auto stack_view = app->get_view_manager()->get_universal_view_stack();
|
||||
auto loading_view = app->get_view_manager()->get_loading();
|
||||
view_stack_add_view(stack_view, loading_get_view(loading_view));
|
||||
|
||||
/**
|
||||
* Problem: Update events are not handled in Loading View, because:
|
||||
* 1) Timer task has least prio
|
||||
* 2) Storage service uses drivers that capture whole CPU time
|
||||
* to handle SD communication
|
||||
*
|
||||
* Ugly workaround, but it works for current situation:
|
||||
* raise timer task prio for DB scanning period.
|
||||
*/
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
TaskHandle_t storage_task = xTaskGetHandle("StorageSrv");
|
||||
uint32_t timer_prio = uxTaskPriorityGet(timer_task);
|
||||
uint32_t storage_prio = uxTaskPriorityGet(storage_task);
|
||||
vTaskPrioritySet(timer_task, storage_prio + 1);
|
||||
bool result = brute_force.calculate_messages();
|
||||
vTaskPrioritySet(timer_task, timer_prio);
|
||||
|
||||
view_stack_remove_view(stack_view, loading_get_view(loading_view));
|
||||
|
||||
if(!result) {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
121
applications/infrared/view/infrared_progress_view.c
Normal file
121
applications/infrared/view/infrared_progress_view.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "furi/check.h"
|
||||
#include "furi_hal_resources.h"
|
||||
#include "assets_icons.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/view.h"
|
||||
#include "input/input.h"
|
||||
#include "m-string.h"
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include "infrared_progress_view.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct InfraredProgressView {
|
||||
View* view;
|
||||
InfraredProgressViewBackCallback back_callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t progress;
|
||||
size_t progress_total;
|
||||
} InfraredProgressViewModel;
|
||||
|
||||
bool infrared_progress_view_increase_progress(InfraredProgressView* progress) {
|
||||
furi_assert(progress);
|
||||
bool result = false;
|
||||
|
||||
InfraredProgressViewModel* model = view_get_model(progress->view);
|
||||
if(model->progress < model->progress_total) {
|
||||
++model->progress;
|
||||
result = model->progress < model->progress_total;
|
||||
}
|
||||
view_commit_model(progress->view, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
InfraredProgressViewModel* model = (InfraredProgressViewModel*)_model;
|
||||
|
||||
uint8_t x = 0;
|
||||
uint8_t y = 36;
|
||||
uint8_t width = 63;
|
||||
uint8_t height = 59;
|
||||
|
||||
elements_bold_rounded_frame(canvas, x, y, width, height);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, x + 34, y + 9, AlignCenter, AlignCenter, "Sending ...");
|
||||
|
||||
float progress_value = (float)model->progress / model->progress_total;
|
||||
elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value);
|
||||
|
||||
uint8_t percent_value = 100 * model->progress / model->progress_total;
|
||||
char percents_string[10] = {0};
|
||||
snprintf(percents_string, sizeof(percents_string), "%d%%", percent_value);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, x + 33, y + 37, AlignCenter, AlignCenter, percents_string);
|
||||
|
||||
canvas_draw_icon(canvas, x + 11, y + height - 15, &I_Back_15x10);
|
||||
canvas_draw_str(canvas, x + 30, y + height - 6, "= stop");
|
||||
}
|
||||
|
||||
void infrared_progress_view_set_progress_total(
|
||||
InfraredProgressView* progress,
|
||||
uint16_t progress_total) {
|
||||
furi_assert(progress);
|
||||
InfraredProgressViewModel* model = view_get_model(progress->view);
|
||||
model->progress = 0;
|
||||
model->progress_total = progress_total;
|
||||
view_commit_model(progress->view, false);
|
||||
}
|
||||
|
||||
bool infrared_progress_view_input_callback(InputEvent* event, void* context) {
|
||||
InfraredProgressView* instance = context;
|
||||
|
||||
if((event->type == InputTypeShort) && (event->key == InputKeyBack)) {
|
||||
if(instance->back_callback) {
|
||||
instance->back_callback(instance->context);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InfraredProgressView* infrared_progress_view_alloc(void) {
|
||||
InfraredProgressView* instance = malloc(sizeof(InfraredProgressView));
|
||||
instance->view = view_alloc();
|
||||
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(InfraredProgressViewModel));
|
||||
InfraredProgressViewModel* model = view_get_model(instance->view);
|
||||
model->progress = 0;
|
||||
model->progress_total = 0;
|
||||
view_commit_model(instance->view, false);
|
||||
view_set_draw_callback(instance->view, infrared_progress_view_draw_callback);
|
||||
view_set_input_callback(instance->view, infrared_progress_view_input_callback);
|
||||
view_set_context(instance->view, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void infrared_progress_view_free(InfraredProgressView* progress) {
|
||||
view_free(progress->view);
|
||||
free(progress);
|
||||
}
|
||||
|
||||
void infrared_progress_view_set_back_callback(
|
||||
InfraredProgressView* instance,
|
||||
InfraredProgressViewBackCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
instance->back_callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
View* infrared_progress_view_get_view(InfraredProgressView* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->view);
|
||||
return instance->view;
|
||||
}
|
||||
68
applications/infrared/view/infrared_progress_view.h
Normal file
68
applications/infrared/view/infrared_progress_view.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file infrared_progress_view.h
|
||||
* Infrared: Custom Infrared view module.
|
||||
* It shows popup progress bar during brute force.
|
||||
*/
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Anonumous instance */
|
||||
typedef struct InfraredProgressView InfraredProgressView;
|
||||
|
||||
/** Callback for back button handling */
|
||||
typedef void (*InfraredProgressViewBackCallback)(void*);
|
||||
|
||||
/** Allocate and initialize Infrared view
|
||||
*
|
||||
* @retval new allocated instance
|
||||
*/
|
||||
InfraredProgressView* infrared_progress_view_alloc();
|
||||
|
||||
/** Free previously allocated Progress view module instance
|
||||
*
|
||||
* @param instance to free
|
||||
*/
|
||||
void infrared_progress_view_free(InfraredProgressView* instance);
|
||||
|
||||
/** Get progress view module view
|
||||
*
|
||||
* @param instance view module
|
||||
* @retval view
|
||||
*/
|
||||
View* infrared_progress_view_get_view(InfraredProgressView* instance);
|
||||
|
||||
/** Increase progress on progress view module
|
||||
*
|
||||
* @param instance view module
|
||||
* @retval true - value is incremented and maximum is reached,
|
||||
* false - value is incremented and maximum is not reached
|
||||
*/
|
||||
bool infrared_progress_view_increase_progress(InfraredProgressView* instance);
|
||||
|
||||
/** Set maximum progress value
|
||||
*
|
||||
* @param instance - view module
|
||||
* @param progress_max - maximum value of progress
|
||||
*/
|
||||
void infrared_progress_view_set_progress_total(
|
||||
InfraredProgressView* instance,
|
||||
uint16_t progress_max);
|
||||
|
||||
/** Set back button callback
|
||||
*
|
||||
* @param instance - view module
|
||||
* @param callback - callback to call for back button
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
void infrared_progress_view_set_back_callback(
|
||||
InfraredProgressView* instance,
|
||||
InfraredProgressViewBackCallback callback,
|
||||
void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user