[FL-1369, FL-1397, FL-1420] IRDA + SDcard (#513)
* Add saving to SD-Card (not ready yet) * Add saving to SD-card (done) * Select previous menu item * Fix central button * Fix current_button * Refactoring * Add notifications * [FL-1417] Add IRDA CLI CLI commands: 1) ir_rx Receives all IR-trafic, decodes and prints result to stdout 2) ir_tx <protocol> <address> <command> Transmits IR-signal. Address and command are hex-formatted * Fix BUG with random memory corruption at random time in random place in random universe in random unknown space and time forever amen * Fix submenu set_selected_item * Bring protocol order back * Add TODO sdcard check
This commit is contained in:
		
							parent
							
								
									498ffe8d2c
								
							
						
					
					
						commit
						6c74ea65c2
					
				| @ -41,6 +41,7 @@ int32_t app_archive(void* p); | ||||
| int32_t notification_app(void* p); | ||||
| 
 | ||||
| // On system start hooks declaration
 | ||||
| void irda_cli_init(); | ||||
| void nfc_cli_init(); | ||||
| void subghz_cli_init(); | ||||
| void bt_cli_init(); | ||||
| @ -104,7 +105,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { | ||||
| #endif | ||||
| 
 | ||||
| #ifdef SRV_IRDA | ||||
|     {.app = irda, .name = "irda", .stack_size = 1024, .icon = A_Plugins_14}, | ||||
|     {.app = irda, .name = "irda", .stack_size = 1024 * 3, .icon = A_Plugins_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef SRV_EXAMPLE_QRCODE | ||||
| @ -186,7 +187,7 @@ const FlipperApplication FLIPPER_APPS[] = { | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_IRDA | ||||
|     {.app = irda, .name = "Infrared", .stack_size = 1024, .icon = A_Infrared_14}, | ||||
|     {.app = irda, .name = "Infrared", .stack_size = 1024 * 3, .icon = A_Infrared_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_GPIO_DEMO | ||||
| @ -203,6 +204,7 @@ const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplicati | ||||
| 
 | ||||
| // On system start hooks
 | ||||
| const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | ||||
|     irda_cli_init, | ||||
| #ifdef APP_NFC | ||||
|     nfc_cli_init, | ||||
| #endif | ||||
|  | ||||
| @ -71,7 +71,7 @@ void elements_button_left(Canvas* canvas, const char* str) { | ||||
|     const uint8_t horizontal_offset = 3; | ||||
|     const uint8_t string_width = canvas_string_width(canvas, str); | ||||
|     const IconData* icon = assets_icons_get_data(I_ButtonLeft_4x7); | ||||
|     const uint8_t icon_offset = 6; | ||||
|     const uint8_t icon_offset = 3; | ||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||
| 
 | ||||
| @ -97,7 +97,7 @@ void elements_button_right(Canvas* canvas, const char* str) { | ||||
|     const uint8_t horizontal_offset = 3; | ||||
|     const uint8_t string_width = canvas_string_width(canvas, str); | ||||
|     const IconData* icon = assets_icons_get_data(I_ButtonRight_4x7); | ||||
|     const uint8_t icon_offset = 6; | ||||
|     const uint8_t icon_offset = 3; | ||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||
| 
 | ||||
| @ -122,10 +122,10 @@ void elements_button_right(Canvas* canvas, const char* str) { | ||||
| void elements_button_center(Canvas* canvas, const char* str) { | ||||
|     const uint8_t button_height = 13; | ||||
|     const uint8_t vertical_offset = 3; | ||||
|     const uint8_t horizontal_offset = 3; | ||||
|     const uint8_t horizontal_offset = 1; | ||||
|     const uint8_t string_width = canvas_string_width(canvas, str); | ||||
|     const IconData* icon = assets_icons_get_data(I_ButtonCenter_7x7); | ||||
|     const uint8_t icon_offset = 6; | ||||
|     const uint8_t icon_offset = 3; | ||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||
| 
 | ||||
|  | ||||
| @ -286,3 +286,21 @@ void button_menu_free(ButtonMenu* button_menu) { | ||||
|     view_free(button_menu->view); | ||||
|     free(button_menu); | ||||
| } | ||||
| 
 | ||||
| void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|             uint8_t item_position = 0; | ||||
|             ButtonMenuItemArray_it_t it; | ||||
|             for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); | ||||
|                 ButtonMenuItemArray_next(it), ++item_position) { | ||||
|                 if(ButtonMenuItemArray_cref(it)->index == index) { | ||||
|                     model->position = item_position; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| @ -68,6 +68,13 @@ void button_menu_free(ButtonMenu* button_menu); | ||||
|  */ | ||||
| void button_menu_set_header(ButtonMenu* button_menu, const char* header); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Set selected item | ||||
|  * @param button_menu   - ButtonMenu instance | ||||
|  * @param index         - index of ButtonMenu to be selected | ||||
|  */ | ||||
| void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -36,12 +36,12 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     const uint8_t item_width = 123; | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     uint8_t position = 0; | ||||
|     SubmenuItemArray_it_t it; | ||||
| 
 | ||||
|     if(model->header) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str(canvas, 4, 11, model->header); | ||||
|     } | ||||
| 
 | ||||
| @ -49,10 +49,10 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); | ||||
|         SubmenuItemArray_next(it)) { | ||||
|         uint8_t item_position = position - model->window_position; | ||||
|         uint8_t elements_on_screen = model->header ? 3 : 4; | ||||
|         uint8_t items_on_screen = model->header ? 3 : 4; | ||||
|         uint8_t y_offset = model->header ? 16 : 0; | ||||
| 
 | ||||
|         if(item_position < elements_on_screen) { | ||||
|         if(item_position < items_on_screen) { | ||||
|             if(position == model->position) { | ||||
|                 canvas_set_color(canvas, ColorBlack); | ||||
|                 elements_slightly_rounded_box( | ||||
| @ -202,11 +202,15 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | ||||
|                 model->window_position -= 1; | ||||
|             } | ||||
| 
 | ||||
|             if(SubmenuItemArray_size(model->items) <= 4) { | ||||
|             uint8_t items_on_screen = model->header ? 3 : 4; | ||||
| 
 | ||||
|             if(SubmenuItemArray_size(model->items) <= items_on_screen) { | ||||
|                 model->window_position = 0; | ||||
|             } else { | ||||
|                 if(model->window_position >= (SubmenuItemArray_size(model->items) - 4)) { | ||||
|                     model->window_position = (SubmenuItemArray_size(model->items) - 4); | ||||
|                 if(model->window_position >= | ||||
|                    (SubmenuItemArray_size(model->items) - items_on_screen)) { | ||||
|                     model->window_position = | ||||
|                         (SubmenuItemArray_size(model->items) - items_on_screen); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
| @ -217,7 +221,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | ||||
| void submenu_process_up(Submenu* submenu) { | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|             uint8_t elements_on_screen = model->header ? 3 : 4; | ||||
|             uint8_t items_on_screen = model->header ? 3 : 4; | ||||
|             if(model->position > 0) { | ||||
|                 model->position--; | ||||
|                 if(((model->position - model->window_position) < 1) && | ||||
| @ -226,8 +230,8 @@ void submenu_process_up(Submenu* submenu) { | ||||
|                 } | ||||
|             } else { | ||||
|                 model->position = SubmenuItemArray_size(model->items) - 1; | ||||
|                 if(model->position > (elements_on_screen - 1)) { | ||||
|                     model->window_position = model->position - (elements_on_screen - 1); | ||||
|                 if(model->position > (items_on_screen - 1)) { | ||||
|                     model->window_position = model->position - (items_on_screen - 1); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
| @ -237,12 +241,12 @@ void submenu_process_up(Submenu* submenu) { | ||||
| void submenu_process_down(Submenu* submenu) { | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|             uint8_t elements_on_screen = model->header ? 3 : 4; | ||||
|             uint8_t items_on_screen = model->header ? 3 : 4; | ||||
|             if(model->position < (SubmenuItemArray_size(model->items) - 1)) { | ||||
|                 model->position++; | ||||
|                 if((model->position - model->window_position) > (elements_on_screen - 2) && | ||||
|                 if((model->position - model->window_position) > (items_on_screen - 2) && | ||||
|                    model->window_position < | ||||
|                        (SubmenuItemArray_size(model->items) - elements_on_screen)) { | ||||
|                        (SubmenuItemArray_size(model->items) - items_on_screen)) { | ||||
|                     model->window_position++; | ||||
|                 } | ||||
|             } else { | ||||
|  | ||||
							
								
								
									
										104
									
								
								applications/irda/cli/irda-cli.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								applications/irda/cli/irda-cli.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | ||||
| #include "app-template.h" | ||||
| #include "cli/cli.h" | ||||
| #include "cmsis_os2.h" | ||||
| #include <furi.h> | ||||
| #include <api-hal-irda.h> | ||||
| #include "irda.h" | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| typedef struct IrdaCli { | ||||
|     IrdaHandler* handler; | ||||
|     osMessageQueueId_t message_queue; | ||||
| } IrdaCli; | ||||
| 
 | ||||
| static void irda_rx_callback(void* ctx, bool level, uint32_t duration) { | ||||
|     IrdaCli* irda_cli = (IrdaCli*)ctx; | ||||
|     const IrdaMessage* message; | ||||
|     message = irda_decode(irda_cli->handler, level, duration); | ||||
|     if(message) { | ||||
|         osMessageQueuePut(irda_cli->message_queue, message, 0, 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { | ||||
|     if(api_hal_irda_rx_irq_is_busy()) { | ||||
|         printf("IRDA is busy. Exit."); | ||||
|         return; | ||||
|     } | ||||
|     IrdaCli irda_cli; | ||||
|     irda_cli.handler = irda_alloc_decoder(); | ||||
|     irda_cli.message_queue = osMessageQueueNew(2, sizeof(IrdaMessage), NULL); | ||||
|     api_hal_irda_rx_irq_init(); | ||||
|     api_hal_irda_rx_irq_set_callback(irda_rx_callback, &irda_cli); | ||||
| 
 | ||||
|     printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); | ||||
|     while(!cli_cmd_interrupt_received(cli)) { | ||||
|         IrdaMessage message; | ||||
|         if(osOK == osMessageQueueGet(irda_cli.message_queue, &message, NULL, 50)) { | ||||
|             printf( | ||||
|                 "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", | ||||
|                 irda_get_protocol_name(message.protocol), | ||||
|                 irda_get_protocol_address_length(message.protocol), | ||||
|                 message.address, | ||||
|                 irda_get_protocol_command_length(message.protocol), | ||||
|                 message.command, | ||||
|                 message.repeat ? " R" : ""); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     api_hal_irda_rx_irq_deinit(); | ||||
|     irda_free_decoder(irda_cli.handler); | ||||
|     osMessageQueueDelete(irda_cli.message_queue); | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_print_usage(void) { | ||||
|     printf("Usage:\r\n\tir_tx <protocol> <command> <address>\r\n"); | ||||
|     printf("\t<command> and <address> are hex-formatted\r\n"); | ||||
|     printf("\tAvailable protocols:"); | ||||
|     for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) { | ||||
|         printf(" %s", irda_get_protocol_name((IrdaProtocol)i)); | ||||
|     } | ||||
|     printf("\r\n"); | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | ||||
|     if(api_hal_irda_rx_irq_is_busy()) { | ||||
|         printf("IRDA is busy. Exit."); | ||||
|         return; | ||||
|     } | ||||
|     auto ss = std::istringstream(string_get_cstr(args)); | ||||
|     uint32_t command = 0; | ||||
|     uint32_t address = 0; | ||||
|     std::string protocol_name; | ||||
| 
 | ||||
|     if(!(ss >> protocol_name) || !(ss >> std::hex >> address) || !(ss >> std::hex >> command)) { | ||||
|         printf("Wrong arguments.\r\n"); | ||||
|         irda_cli_print_usage(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name.c_str()); | ||||
| 
 | ||||
|     if(!irda_is_protocol_valid(protocol)) { | ||||
|         printf("Unknown protocol.\r\n"); | ||||
|         irda_cli_print_usage(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IrdaMessage message = { | ||||
|         .protocol = protocol, | ||||
|         .address = address, | ||||
|         .command = command, | ||||
|         .repeat = false, | ||||
|     }; | ||||
|     irda_send(&message, 1); | ||||
| } | ||||
| 
 | ||||
| extern "C" void irda_cli_init() { | ||||
|     Cli* cli = (Cli*)furi_record_open("cli"); | ||||
|     cli_add_command(cli, "ir_rx", irda_cli_start_ir_rx, NULL); | ||||
|     cli_add_command(cli, "ir_tx", irda_cli_start_ir_tx, NULL); | ||||
|     furi_record_close("cli"); | ||||
| } | ||||
| @ -1,51 +1,78 @@ | ||||
| #include "irda-app-remote-manager.hpp" | ||||
| #include "filesystem-api.h" | ||||
| #include "furi.h" | ||||
| #include "furi/check.h" | ||||
| #include "gui/modules/button_menu.h" | ||||
| #include "irda.h" | ||||
| #include "sys/_stdint.h" | ||||
| #include <cstdio> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| 
 | ||||
| IrdaAppRemoteManager::IrdaAppRemoteManager() { | ||||
|     // Read from api-hal-storage, and fill remotes
 | ||||
| } | ||||
| 
 | ||||
| const char* IrdaAppRemoteManager::irda_directory = "irda"; | ||||
| const char* IrdaAppRemoteManager::irda_extension = ".ir"; | ||||
| static const std::string default_remote_name = "remote"; | ||||
| 
 | ||||
| void IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | ||||
|     remotes[current_remote_index].buttons.emplace_back(button_name, message); | ||||
| static bool find_string(const std::vector<std::string>& strings, const std::string& match_string) { | ||||
|     for(const auto& string : strings) { | ||||
|         if(!string.compare(match_string)) return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::add_remote_with_button( | ||||
| static std::string | ||||
| find_vacant_name(const std::vector<std::string>& strings, const std::string& name) { | ||||
|     // if suggested name is occupied, try another one (name2, name3, etc)
 | ||||
|     if(find_string(strings, name)) { | ||||
|         int i = 1; | ||||
|         while(find_string(strings, name + std::to_string(++i))) | ||||
|             ; | ||||
|         return name + std::to_string(i); | ||||
|     } else { | ||||
|         return name; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppRemoteManager::IrdaAppRemoteManager() { | ||||
|     sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex")); | ||||
|     fs_api = static_cast<FS_Api*>(furi_record_open("sdcard")); | ||||
| } | ||||
| 
 | ||||
| IrdaAppRemoteManager::~IrdaAppRemoteManager() { | ||||
|     furi_record_close("sdcard"); | ||||
|     furi_record_close("sdcard-ex"); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | ||||
|     remote->buttons.emplace_back(button_name, message); | ||||
|     return store(); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::add_remote_with_button( | ||||
|     const char* button_name, | ||||
|     const IrdaMessage* message) { | ||||
|     bool found = true; | ||||
|     int i = 0; | ||||
|     furi_check(button_name != nullptr); | ||||
|     furi_check(message != nullptr); | ||||
| 
 | ||||
|     // find first free common name for remote
 | ||||
|     do { | ||||
|         found = false; | ||||
|         ++i; | ||||
|         for(const auto& it : remotes) { | ||||
|             if(it.name == (default_remote_name + std::to_string(i))) { | ||||
|                 found = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } while(found); | ||||
|     std::vector<std::string> remote_list; | ||||
|     bool result = get_remote_list(remote_list); | ||||
|     if(!result) return false; | ||||
| 
 | ||||
|     remotes.emplace_back(default_remote_name + std::to_string(i)); | ||||
|     current_remote_index = remotes.size() - 1; | ||||
|     add_button(button_name, message); | ||||
|     auto new_name = find_vacant_name(remote_list, default_remote_name); | ||||
| 
 | ||||
|     remote = std::make_unique<IrdaAppRemote>(new_name); | ||||
|     return add_button(button_name, message); | ||||
| } | ||||
| 
 | ||||
| IrdaAppRemote::IrdaAppRemote(std::string name) | ||||
| IrdaAppRemote::IrdaAppRemote(const std::string& name) | ||||
|     : name(name) { | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | ||||
|     std::vector<std::string> name_vector; | ||||
|     auto remote = remotes[current_remote_index]; | ||||
|     name_vector.reserve(remote.buttons.size()); | ||||
|     name_vector.reserve(remote->buttons.size()); | ||||
| 
 | ||||
|     for(const auto& it : remote.buttons) { | ||||
|     for(const auto& it : remote->buttons) { | ||||
|         name_vector.emplace_back(it.name); | ||||
|     } | ||||
| 
 | ||||
| @ -53,78 +80,289 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | ||||
|     return name_vector; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> IrdaAppRemoteManager::get_remote_list() const { | ||||
|     std::vector<std::string> name_vector; | ||||
|     name_vector.reserve(remotes.size()); | ||||
| const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t index) const { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
| 
 | ||||
|     for(const auto& it : remotes) { | ||||
|         name_vector.push_back(it.name); | ||||
|     return &buttons.at(index).message; | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { | ||||
|     return std::string("/") + irda_directory + "/" + name + irda_extension; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::delete_remote() { | ||||
|     FS_Error fs_res; | ||||
| 
 | ||||
|     fs_res = fs_api->common.remove(make_filename(remote->name).c_str()); | ||||
|     if(fs_res != FSE_OK) { | ||||
|         show_file_error_message("Error deleting file"); | ||||
|         return false; | ||||
|     } | ||||
|     remote.reset(); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::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 IrdaAppRemoteManager::get_button_name(uint32_t index) { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
|     return buttons[index].name; | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::get_remote_name() { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     return remote->name; | ||||
| } | ||||
| 
 | ||||
| int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) { | ||||
|     int i = 0; | ||||
|     for(const auto& str : strings) { | ||||
|         if(!str.compare(remote->name)) { | ||||
|             return i; | ||||
|         } | ||||
|         ++i; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::rename_remote(const char* str) { | ||||
|     furi_check(str != nullptr); | ||||
|     furi_check(remote.get() != nullptr); | ||||
| 
 | ||||
|     if(!remote->name.compare(str)) return true; | ||||
| 
 | ||||
|     std::vector<std::string> remote_list; | ||||
|     bool result = get_remote_list(remote_list); | ||||
|     if(!result) return false; | ||||
| 
 | ||||
|     auto new_name = find_vacant_name(remote_list, str); | ||||
|     FS_Error fs_err = fs_api->common.rename( | ||||
|         make_filename(remote->name).c_str(), make_filename(new_name).c_str()); | ||||
|     remote->name = new_name; | ||||
|     if(fs_err != FSE_OK) { | ||||
|         show_file_error_message("Error renaming\nremote file"); | ||||
|     } | ||||
|     return fs_err == FSE_OK; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::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 IrdaAppRemoteManager::get_number_of_buttons() { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     return remote->buttons.size(); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::show_file_error_message(const char* error_text) const { | ||||
|     sd_ex_api->show_error(sd_ex_api->context, error_text); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::store(void) { | ||||
|     File file; | ||||
|     uint16_t write_count; | ||||
|     std::string dirname(std::string("/") + irda_directory); | ||||
| 
 | ||||
|     FS_Error fs_err = fs_api->common.mkdir(dirname.c_str()); | ||||
|     if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) { | ||||
|         show_file_error_message("Can't create directory"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // copy elision
 | ||||
|     return name_vector; | ||||
|     std::string filename = dirname + "/" + remote->name + irda_extension; | ||||
|     bool res = fs_api->file.open(&file, filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||
| 
 | ||||
|     if(!res) { | ||||
|         show_file_error_message("Cannot create\nnew remote file"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     char content[128]; | ||||
| 
 | ||||
|     for(const auto& button : remote->buttons) { | ||||
|         auto protocol = button.message.protocol; | ||||
| 
 | ||||
|         sniprintf( | ||||
|             content, | ||||
|             sizeof(content), | ||||
|             "%.31s %.31s A:%0*lX C:%0*lX\n", | ||||
|             button.name.c_str(), | ||||
|             irda_get_protocol_name(protocol), | ||||
|             irda_get_protocol_address_length(protocol), | ||||
|             button.message.address, | ||||
|             irda_get_protocol_command_length(protocol), | ||||
|             button.message.command); | ||||
| 
 | ||||
|         auto content_len = strlen(content); | ||||
|         write_count = fs_api->file.write(&file, content, content_len); | ||||
|         if(file.error_id != FSE_OK || write_count != content_len) { | ||||
|             show_file_error_message("Cannot write\nto key file"); | ||||
|             fs_api->file.close(&file); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fs_api->file.close(&file); | ||||
|     sd_ex_api->check_error(sd_ex_api->context); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| size_t IrdaAppRemoteManager::get_current_remote(void) const { | ||||
|     return current_remote_index; | ||||
| bool IrdaAppRemoteManager::parse_button(std::string& str) { | ||||
|     char button_name[32]; | ||||
|     char protocol_name[32]; | ||||
|     uint32_t address; | ||||
|     uint32_t command; | ||||
| 
 | ||||
|     int parsed = std::sscanf( | ||||
|         str.c_str(), "%31s %31s A:%lX C:%lX", button_name, protocol_name, &address, &command); | ||||
| 
 | ||||
|     if(parsed != 4) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name); | ||||
| 
 | ||||
|     if(!irda_is_protocol_valid((IrdaProtocol)protocol)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int address_length = irda_get_protocol_address_length(protocol); | ||||
|     uint32_t address_mask = (1LU << (4 * address_length)) - 1; | ||||
|     if(address != (address & address_mask)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int command_length = irda_get_protocol_command_length(protocol); | ||||
|     uint32_t command_mask = (1LU << (4 * command_length)) - 1; | ||||
|     if(command != (command & command_mask)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     IrdaMessage irda_message = { | ||||
|         .protocol = protocol, | ||||
|         .address = address, | ||||
|         .command = command, | ||||
|         .repeat = false, | ||||
|     }; | ||||
|     remote->buttons.emplace_back(button_name, &irda_message); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| size_t IrdaAppRemoteManager::get_current_button(void) const { | ||||
|     return current_button_index; | ||||
| std::string getline( | ||||
|     const FS_Api* fs_api, | ||||
|     File& file, | ||||
|     char file_buf[], | ||||
|     size_t file_buf_size, | ||||
|     size_t& file_buf_cnt) { | ||||
|     std::string str; | ||||
|     size_t newline_index = 0; | ||||
|     bool found_eol = false; | ||||
| 
 | ||||
|     while(1) { | ||||
|         if(file_buf_cnt > 0) { | ||||
|             size_t end_index = 0; | ||||
|             char* endline_ptr = (char*)memchr(file_buf, '\n', file_buf_cnt); | ||||
|             newline_index = endline_ptr - file_buf; | ||||
| 
 | ||||
|             if(endline_ptr == 0) { | ||||
|                 end_index = file_buf_cnt; | ||||
|             } else if(newline_index < file_buf_cnt) { | ||||
|                 end_index = newline_index + 1; | ||||
|                 found_eol = true; | ||||
|             } else { | ||||
|                 furi_assert(0); | ||||
|             } | ||||
| 
 | ||||
|             str.append(file_buf, end_index); | ||||
|             memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index); | ||||
|             file_buf_cnt = file_buf_cnt - end_index; | ||||
|             if(found_eol) break; | ||||
|         } | ||||
| 
 | ||||
|         file_buf_cnt += | ||||
|             fs_api->file.read(&file, &file_buf[file_buf_cnt], file_buf_size - file_buf_cnt); | ||||
|         if(file_buf_cnt == 0) { | ||||
|             break; // end of reading
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return str; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemote::add_button( | ||||
|     size_t remote_index, | ||||
|     const char* button_name, | ||||
|     const IrdaMessage* message) { | ||||
|     buttons.emplace_back(button_name, message); | ||||
| bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const { | ||||
|     bool fs_res = false; | ||||
|     char name[128]; | ||||
|     File dir; | ||||
|     std::string dirname(std::string("/") + irda_directory); | ||||
|     remote_names.clear(); | ||||
| 
 | ||||
|     fs_res = fs_api->dir.open(&dir, dirname.c_str()); | ||||
|     if(!fs_res) { | ||||
|         if(!check_fs()) { | ||||
|             show_file_error_message("Cannot open\napplication directory"); | ||||
|             return false; | ||||
|         } else { | ||||
|             return true; // SD ok, but no files written yet
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     while(fs_api->dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) { | ||||
|         std::string filename(name); | ||||
|         auto extension_index = filename.rfind(irda_extension); | ||||
|         if((extension_index == std::string::npos) || | ||||
|            (extension_index + strlen(irda_extension) != filename.size())) { | ||||
|             continue; | ||||
|         } | ||||
|         remote_names.push_back(filename.erase(extension_index)); | ||||
|     } | ||||
|     fs_api->dir.close(&dir); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t button_index) const { | ||||
|     furi_check(remotes[current_remote_index].buttons.size() > button_index); | ||||
|     auto& b = remotes[current_remote_index].buttons.at(button_index); | ||||
|     return &b.message; | ||||
| bool IrdaAppRemoteManager::load(const std::string& name) { | ||||
|     bool fs_res = false; | ||||
|     File file; | ||||
| 
 | ||||
|     fs_res = fs_api->file.open(&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING); | ||||
|     if(!fs_res) { | ||||
|         show_file_error_message("Error opening file"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     remote = std::make_unique<IrdaAppRemote>(name); | ||||
| 
 | ||||
|     while(1) { | ||||
|         auto str = getline(fs_api, file, file_buf, sizeof(file_buf), file_buf_cnt); | ||||
|         if(str.empty()) break; | ||||
|         parse_button(str); | ||||
|     } | ||||
|     fs_api->file.close(&file); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::set_current_remote(size_t index) { | ||||
|     furi_check(index < remotes.size()); | ||||
|     current_remote_index = index; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::set_current_button(size_t index) { | ||||
|     furi_check(current_remote_index < remotes.size()); | ||||
|     furi_check(index < remotes[current_remote_index].buttons.size()); | ||||
|     current_button_index = index; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::delete_current_remote() { | ||||
|     remotes.erase(remotes.begin() + current_remote_index); | ||||
|     current_remote_index = 0; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::delete_current_button() { | ||||
|     auto& buttons = remotes[current_remote_index].buttons; | ||||
|     buttons.erase(buttons.begin() + current_button_index); | ||||
|     current_button_index = 0; | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::get_current_button_name() { | ||||
|     auto buttons = remotes[current_remote_index].buttons; | ||||
|     return buttons[current_button_index].name; | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::get_current_remote_name() { | ||||
|     return remotes[current_remote_index].name; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::rename_remote(const char* str) { | ||||
|     remotes[current_remote_index].name = str; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::rename_button(const char* str) { | ||||
|     remotes[current_remote_index].buttons[current_button_index].name = str; | ||||
| } | ||||
| 
 | ||||
| size_t IrdaAppRemoteManager::get_current_remote_buttons_number() { | ||||
|     return remotes[current_remote_index].buttons.size(); | ||||
| bool IrdaAppRemoteManager::check_fs() const { | ||||
|     // TODO: [FL-1431] Add return value to sd_ex_api->check_error() and replace get_fs_info().
 | ||||
|     auto fs_err = fs_api->common.get_fs_info(nullptr, nullptr); | ||||
|     if(fs_err != FSE_OK) show_file_error_message("SD card not found"); | ||||
|     return fs_err == FSE_OK; | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,14 @@ | ||||
| #pragma once | ||||
| #include "sys/_stdint.h" | ||||
| #include <algorithm> | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <list> | ||||
| #include <vector> | ||||
| #include <memory> | ||||
| #include <irda.h> | ||||
| #include <sd-card-api.h> | ||||
| #include <filesystem-api.h> | ||||
| 
 | ||||
| class IrdaAppRemoteButton { | ||||
|     friend class IrdaAppRemoteManager; | ||||
| @ -19,35 +24,50 @@ class IrdaAppRemote { | ||||
|     friend class IrdaAppRemoteManager; | ||||
|     std::vector<IrdaAppRemoteButton> buttons; | ||||
|     std::string name; | ||||
|     bool add(const IrdaMessage*); | ||||
|     void add_button(size_t remote_index, const char* button_name, const IrdaMessage* message); | ||||
| public: | ||||
|     IrdaAppRemote(std::string name); | ||||
|     IrdaAppRemote(const std::string& name); | ||||
|     IrdaAppRemote& operator=(std::string& new_name) noexcept | ||||
|     { | ||||
|         name = new_name; | ||||
|         buttons.clear(); | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppRemoteManager { | ||||
|     size_t current_remote_index; | ||||
|     size_t current_button_index; | ||||
|     std::vector<IrdaAppRemote> remotes; | ||||
| public: | ||||
|     std::vector<std::string> get_remote_list() const; | ||||
|     std::vector<std::string> get_button_list() const; | ||||
|     void add_remote_with_button(const char* button_name, const IrdaMessage* message); | ||||
|     void add_button(const char* button_name, const IrdaMessage* message); | ||||
|     static const char* irda_directory; | ||||
|     static const char* irda_extension; | ||||
|     std::unique_ptr<IrdaAppRemote> remote; | ||||
|     // TODO: make FS_Api and SdCard_Api unique_ptr
 | ||||
|     SdCard_Api* sd_ex_api; | ||||
|     FS_Api* fs_api; | ||||
|     void show_file_error_message(const char* error_text) const; | ||||
|     bool parse_button(std::string& str); | ||||
|     std::string make_filename(const std::string& name) const; | ||||
|     char file_buf[48]; | ||||
|     size_t file_buf_cnt = 0; | ||||
| 
 | ||||
|     size_t get_current_remote(void) const; | ||||
|     size_t get_current_button(void) const; | ||||
| public: | ||||
|     bool add_remote_with_button(const char* button_name, const IrdaMessage* message); | ||||
|     bool add_button(const char* button_name, const IrdaMessage* message); | ||||
| 
 | ||||
|     int find_remote_name(const std::vector<std::string>& strings); | ||||
|     bool rename_button(uint32_t index, const char* str); | ||||
|     bool rename_remote(const char* str); | ||||
| 
 | ||||
|     bool get_remote_list(std::vector<std::string>& remote_names) const; | ||||
|     std::vector<std::string> get_button_list() const; | ||||
|     std::string get_button_name(uint32_t index); | ||||
|     std::string get_remote_name(); | ||||
|     size_t get_number_of_buttons(); | ||||
|     const IrdaMessage* get_button_data(size_t button_index) const; | ||||
|     void set_current_remote(size_t index); | ||||
|     void set_current_button(size_t index); | ||||
|     void rename_button(const char* str); | ||||
|     void rename_remote(const char* str); | ||||
|     std::string get_current_button_name(); | ||||
|     std::string get_current_remote_name(); | ||||
|     size_t get_current_remote_buttons_number(); | ||||
|     void delete_current_button(); | ||||
|     void delete_current_remote(); | ||||
|     bool delete_button(uint32_t index); | ||||
|     bool delete_remote(); | ||||
|     IrdaAppRemoteManager(); | ||||
|     ~IrdaAppRemoteManager() {}; | ||||
|     ~IrdaAppRemoteManager(); | ||||
| 
 | ||||
|     bool store(); | ||||
|     bool load(const std::string& name); | ||||
|     bool check_fs() const; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "irda-app.hpp" | ||||
| #include "sys/_stdint.h" | ||||
| #include <furi.h> | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| @ -154,3 +155,70 @@ void IrdaApp::set_edit_action(IrdaApp::EditAction value) { | ||||
| IrdaApp::EditAction IrdaApp::get_edit_action(void) { | ||||
|     return action; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_current_button(int value) { | ||||
|     current_button = value; | ||||
| } | ||||
| 
 | ||||
| int IrdaApp::get_current_button() { | ||||
|     return current_button; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_success() { | ||||
|     notification_message(notification, &sequence_success); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_red_blink() { | ||||
|     notification_message(notification, &sequence_blink_red_10); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_space_blink() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_green_0, | ||||
|         &message_delay_50, | ||||
|         &message_green_255, | ||||
|         &message_do_not_reset, | ||||
|         NULL, | ||||
|     }; | ||||
| 
 | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_click() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_click, | ||||
|         &message_delay_1, | ||||
|         &message_sound_off, | ||||
|         NULL, | ||||
|     }; | ||||
| 
 | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_click_and_blink() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_click, | ||||
|         &message_delay_1, | ||||
|         &message_sound_off, | ||||
|         &message_red_0, | ||||
|         &message_green_255, | ||||
|         &message_blue_0, | ||||
|         &message_delay_10, | ||||
|         &message_green_0, | ||||
|         NULL, | ||||
|     }; | ||||
| 
 | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_double_vibro() { | ||||
|     notification_message(notification, &sequence_double_vibro); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_green_on() { | ||||
|     notification_message(notification, &sequence_set_only_green_255); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_green_off() { | ||||
|     notification_message(notification, &sequence_reset_green); | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #pragma once | ||||
| #include "sys/_stdint.h" | ||||
| #include <map> | ||||
| #include <irda.h> | ||||
| #include <furi.h> | ||||
| @ -9,6 +10,7 @@ | ||||
| #include "irda-app-receiver.hpp" | ||||
| #include <forward_list> | ||||
| #include <stdint.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| 
 | ||||
| class IrdaApp { | ||||
| @ -65,11 +67,29 @@ public: | ||||
|     bool get_learn_new_remote(); | ||||
|     void set_learn_new_remote(bool value); | ||||
| 
 | ||||
|     enum : int { | ||||
|            ButtonNA = -1, | ||||
|     }; | ||||
|     int get_current_button(); | ||||
|     void set_current_button(int value); | ||||
| 
 | ||||
|     void notify_success(); | ||||
|     void notify_red_blink(); | ||||
|     void notify_space_blink(); | ||||
|     void notify_double_vibro(); | ||||
|     void notify_green_on(); | ||||
|     void notify_green_off(); | ||||
|     void notify_click(); | ||||
|     void notify_click_and_blink(); | ||||
| 
 | ||||
|     static void text_input_callback(void* context, char* text); | ||||
|     static void popup_callback(void* context); | ||||
| 
 | ||||
|     IrdaApp() {} | ||||
|     IrdaApp() { | ||||
|         notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||
|     } | ||||
|     ~IrdaApp() { | ||||
|         furi_record_close("notification"); | ||||
|         for (auto &it : scenes) | ||||
|             delete it.second; | ||||
|     } | ||||
| @ -80,7 +100,9 @@ private: | ||||
|     bool learn_new_remote; | ||||
|     EditElement element; | ||||
|     EditAction action; | ||||
|     uint32_t current_button; | ||||
| 
 | ||||
|     NotificationApp* notification; | ||||
|     IrdaAppSignalReceiver receiver; | ||||
|     IrdaAppViewManager view_manager; | ||||
|     IrdaAppRemoteManager remote_manager; | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "../irda-app.hpp" | ||||
| #include "irda.h" | ||||
| #include "irda/scene/irda-app-scene.hpp" | ||||
| #include <string> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| @ -20,12 +21,12 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
| 
 | ||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|         auto message = remote_manager->get_button_data(remote_manager->get_current_button()); | ||||
|         auto message = remote_manager->get_button_data(app->get_current_button()); | ||||
|         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); | ||||
|         app->set_text_store( | ||||
|             0, | ||||
|             "%s\n%s\nA=0x%0*lX C=0x%0*lX", | ||||
|             remote_manager->get_current_button_name().c_str(), | ||||
|             remote_manager->get_button_name(app->get_current_button()).c_str(), | ||||
|             irda_get_protocol_name(message->protocol), | ||||
|             irda_get_protocol_address_length(message->protocol), | ||||
|             message->address, | ||||
| @ -36,8 +37,8 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|         app->set_text_store( | ||||
|             0, | ||||
|             "%s\n with %lu buttons", | ||||
|             remote_manager->get_current_remote_name().c_str(), | ||||
|             remote_manager->get_current_remote_buttons_number()); | ||||
|             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, 32, AlignCenter, AlignCenter); | ||||
| @ -63,13 +64,20 @@ bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|             break; | ||||
|         case DialogExResultRight: | ||||
|             auto remote_manager = app->get_remote_manager(); | ||||
|             bool result = false; | ||||
|             if(app->get_edit_element() == IrdaApp::EditElement::Remote) { | ||||
|                 remote_manager->delete_current_remote(); | ||||
|                 result = remote_manager->delete_remote(); | ||||
|             } else { | ||||
|                 remote_manager->delete_current_button(); | ||||
|                 result = remote_manager->delete_button(app->get_current_button()); | ||||
|                 app->set_current_button(IrdaApp::ButtonNA); | ||||
|             } | ||||
| 
 | ||||
|             if(!result) { | ||||
|                 app->search_and_switch_to_previous_scene( | ||||
|                     {IrdaApp::Scene::RemoteList, IrdaApp::Scene::Start}); | ||||
|             } else { | ||||
|                 app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -14,7 +14,7 @@ static void submenu_callback(void* context, uint32_t index) { | ||||
| void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     int i = 0; | ||||
|     int item_number = 0; | ||||
| 
 | ||||
|     const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : | ||||
|                                                                                  "Delete key:"; | ||||
| @ -23,7 +23,11 @@ void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | ||||
|     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(), i++, submenu_callback, app); | ||||
|         submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); | ||||
|     } | ||||
|     if((item_number > 0) && (app->get_current_button() != IrdaApp::ButtonNA)) { | ||||
|         submenu_set_selected_item(submenu, app->get_current_button()); | ||||
|         app->set_current_button(IrdaApp::ButtonNA); | ||||
|     } | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
| @ -33,8 +37,7 @@ bool IrdaAppSceneEditKeySelect::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|         auto remote_manager = app->get_remote_manager(); | ||||
|         remote_manager->set_current_button(event->payload.menu_index); | ||||
|         app->set_current_button(event->payload.menu_index); | ||||
|         consumed = true; | ||||
|         if(app->get_edit_action() == IrdaApp::EditAction::Rename) { | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditRename); | ||||
|  | ||||
| @ -7,10 +7,11 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | ||||
| 
 | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|         auto button_name = remote_manager->get_current_button_name(); | ||||
|         furi_assert(app->get_current_button() != IrdaApp::ButtonNA); | ||||
|         auto button_name = remote_manager->get_button_name(app->get_current_button()); | ||||
|         strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); | ||||
|     } else { | ||||
|         auto remote_name = remote_manager->get_current_remote_name(); | ||||
|         auto remote_name = remote_manager->get_remote_name(); | ||||
|         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); | ||||
|     } | ||||
| 
 | ||||
| @ -30,12 +31,20 @@ bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||
|         auto remote_manager = app->get_remote_manager(); | ||||
|         bool result = false; | ||||
|         if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|             remote_manager->rename_button(app->get_text_store(0)); | ||||
|             result = | ||||
|                 remote_manager->rename_button(app->get_current_button(), app->get_text_store(0)); | ||||
|             app->set_current_button(IrdaApp::ButtonNA); | ||||
|         } else { | ||||
|             remote_manager->rename_remote(app->get_text_store(0)); | ||||
|             result = remote_manager->rename_remote(app->get_text_store(0)); | ||||
|         } | ||||
|         if(!result) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "../irda-app.hpp" | ||||
| #include "gui/modules/submenu.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexAddKey, | ||||
| @ -27,6 +28,8 @@ void IrdaAppSceneEdit::on_enter(IrdaApp* 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(IrdaAppViewManager::ViewType::Submenu); | ||||
| } | ||||
| @ -35,8 +38,10 @@ bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::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(IrdaApp::Scene::Learn); | ||||
|             break; | ||||
|         case SubmenuIndexRenameKey: | ||||
|  | ||||
| @ -35,15 +35,22 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||
|         auto remote_manager = app->get_remote_manager(); | ||||
|         auto receiver = app->get_receiver(); | ||||
|         bool result = false; | ||||
|         if(app->get_learn_new_remote()) { | ||||
|             remote_manager->add_remote_with_button( | ||||
|             result = remote_manager->add_remote_with_button( | ||||
|                 app->get_text_store(0), receiver->get_last_message()); | ||||
|         } else { | ||||
|             result = | ||||
|                 remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); | ||||
|         } | ||||
| 
 | ||||
|         if(!result) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -17,6 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
| 
 | ||||
|     app->notify_green_on(); | ||||
| 
 | ||||
|     auto receiver = app->get_receiver(); | ||||
|     auto message = receiver->get_last_message(); | ||||
| 
 | ||||
| @ -32,6 +34,7 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | ||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(1), 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_DolphinExcited_64x63); | ||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); | ||||
|     dialog_ex_set_context(dialog_ex, app); | ||||
| @ -47,11 +50,20 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         case DialogExResultLeft: | ||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); | ||||
|             break; | ||||
|         case DialogExResultCenter: | ||||
|             furi_assert(0); | ||||
|         case DialogExResultCenter: { | ||||
|             app->notify_space_blink(); | ||||
|             auto receiver = app->get_receiver(); | ||||
|             auto message = receiver->get_last_message(); | ||||
|             irda_send(message, 1); | ||||
|             break; | ||||
|         } | ||||
|         case DialogExResultRight: | ||||
|             auto remote_manager = app->get_remote_manager(); | ||||
|             if(remote_manager->check_fs()) { | ||||
|                 app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); | ||||
|             } else { | ||||
|                 app->switch_to_previous_scene(); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| @ -60,4 +72,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
|     dialog_ex_set_center_button_text(dialog_ex, nullptr); | ||||
|     app->notify_green_off(); | ||||
| } | ||||
|  | ||||
| @ -14,13 +14,22 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | ||||
|         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); | ||||
|     popup_set_callback(popup, NULL); | ||||
| 
 | ||||
|     if(app->get_learn_new_remote()) { | ||||
|         app->notify_double_vibro(); | ||||
|     } | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::Tick) { | ||||
|         consumed = true; | ||||
|         app->notify_red_blink(); | ||||
|     } | ||||
|     if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { | ||||
|         app->notify_success(); | ||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -20,13 +20,26 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     int i = 0; | ||||
| 
 | ||||
|     remote_names = remote_manager->get_remote_list(); | ||||
|     for(auto& a : remote_names) { | ||||
|         submenu_add_item(submenu, a.c_str(), i++, submenu_callback, app); | ||||
|     bool result = remote_manager->get_remote_list(remote_names); | ||||
|     if(!result) { | ||||
|         app->switch_to_previous_scene(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     for(auto& name : remote_names) { | ||||
|         submenu_add_item(submenu, name.c_str(), i++, submenu_callback, app); | ||||
|     } | ||||
|     submenu_add_item( | ||||
|         submenu, "                           +", SubmenuIndexPlus, submenu_callback, app); | ||||
| 
 | ||||
|     if((SubmenuIndex)submenu_item_selected == SubmenuIndexPlus) { | ||||
|         submenu_set_selected_item(submenu, submenu_item_selected); | ||||
|     } else { | ||||
|         int remote_index = remote_manager->find_remote_name(remote_names); | ||||
|         submenu_set_selected_item(submenu, (remote_index >= 0) ? remote_index : 0); | ||||
|     } | ||||
| 
 | ||||
|     submenu_item_selected = 0; | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
| } | ||||
| 
 | ||||
| @ -38,11 +51,14 @@ bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         case SubmenuIndexPlus: | ||||
|             app->set_learn_new_remote(true); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||
|             submenu_item_selected = event->payload.menu_index; | ||||
|             break; | ||||
|         default: | ||||
|             auto remote_manager = app->get_remote_manager(); | ||||
|             remote_manager->set_current_remote(event->payload.menu_index); | ||||
|             bool result = remote_manager->load(remote_names.at(event->payload.menu_index)); | ||||
|             if(result) { | ||||
|                 app->switch_to_next_scene(IrdaApp::Scene::Remote); | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
| @ -4,6 +4,7 @@ | ||||
| typedef enum { | ||||
|     ButtonIndexPlus = -2, | ||||
|     ButtonIndexEdit = -1, | ||||
|     ButtonIndexNA = 0, | ||||
| } ButtonIndex; | ||||
| 
 | ||||
| static void button_menu_callback(void* context, int32_t index) { | ||||
| @ -35,8 +36,12 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) { | ||||
|     button_menu_add_item( | ||||
|         button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); | ||||
| 
 | ||||
|     app->set_text_store(0, "%s", remote_manager->get_current_remote_name().c_str()); | ||||
|     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(IrdaAppViewManager::ViewType::ButtonMenu); | ||||
| } | ||||
| 
 | ||||
| @ -46,12 +51,18 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|         switch(event->payload.menu_index) { | ||||
|         case ButtonIndexPlus: | ||||
|             app->notify_click(); | ||||
|             buttonmenu_item_selected = event->payload.menu_index; | ||||
|             app->set_learn_new_remote(false); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||
|             break; | ||||
|         case ButtonIndexEdit: | ||||
|             app->notify_click(); | ||||
|             buttonmenu_item_selected = event->payload.menu_index; | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Edit); | ||||
|             break; | ||||
|         default: | ||||
|             app->notify_click_and_blink(); | ||||
|             auto remote_manager = app->get_remote_manager(); | ||||
|             auto message = remote_manager->get_button_data(event->payload.menu_index); | ||||
|             app->get_receiver()->send_message(message); | ||||
|  | ||||
| @ -25,6 +25,8 @@ void IrdaAppSceneStart::on_enter(IrdaApp* 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(IrdaAppViewManager::ViewType::Submenu); | ||||
| } | ||||
| @ -33,6 +35,7 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexUniversalLibrary: | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Universal); | ||||
| @ -44,6 +47,9 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         case SubmenuIndexSavedRemotes: | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::RemoteList); | ||||
|             break; | ||||
|         default: | ||||
|             furi_assert(0); | ||||
|             break; | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
|  | ||||
| @ -24,6 +24,8 @@ void IrdaAppSceneUniversal::on_enter(IrdaApp* app) { | ||||
|     submenu_add_item(submenu, "Audio Players", SubmenuIndexUniversalAudio, submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, "Air Conditioners", SubmenuIndexUniversalAirConditioner, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
|     submenu_item_selected = 0; | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
| } | ||||
| @ -32,6 +34,7 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexUniversalTV: | ||||
|             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
 | ||||
|  | ||||
| @ -23,6 +23,8 @@ public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneUniversal : public IrdaAppScene { | ||||
| @ -30,6 +32,8 @@ public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneLearn : public IrdaAppScene { | ||||
| @ -74,6 +78,7 @@ public: | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| private: | ||||
|     std::vector<std::string> buttons_names; | ||||
|     uint32_t buttonmenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneRemoteList : public IrdaAppScene { | ||||
| @ -81,6 +86,8 @@ public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
|     std::vector<std::string> remote_names; | ||||
| }; | ||||
| 
 | ||||
| @ -89,6 +96,8 @@ public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditKeySelect : public IrdaAppScene { | ||||
|  | ||||
| @ -26,6 +26,11 @@ for octave in range(9): | ||||
|         print(f"extern const NotificationMessage message_note_{name}{octave};") | ||||
| */ | ||||
| 
 | ||||
| const NotificationMessage message_click = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
| }; | ||||
| const NotificationMessage message_note_c0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 16.35f, | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| extern const NotificationMessage message_click; | ||||
| extern const NotificationMessage message_note_c0; | ||||
| extern const NotificationMessage message_note_cs0; | ||||
| extern const NotificationMessage message_note_d0; | ||||
|  | ||||
| @ -48,6 +48,11 @@ const NotificationMessage message_blue_0 = { | ||||
| }; | ||||
| 
 | ||||
| // Delay
 | ||||
| const NotificationMessage message_delay_1 = { | ||||
|     .type = NotificationMessageTypeDelay, | ||||
|     .data.delay.length = 1, | ||||
| }; | ||||
| 
 | ||||
| const NotificationMessage message_delay_10 = { | ||||
|     .type = NotificationMessageTypeDelay, | ||||
|     .data.delay.length = 10, | ||||
| @ -113,12 +118,12 @@ const NotificationSequence sequence_reset_red = { | ||||
| }; | ||||
| 
 | ||||
| const NotificationSequence sequence_reset_green = { | ||||
|     &message_blue_0, | ||||
|     &message_green_0, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| const NotificationSequence sequence_reset_blue = { | ||||
|     &message_green_0, | ||||
|     &message_blue_0, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| @ -298,6 +303,17 @@ const NotificationSequence sequence_blink_white_100 = { | ||||
| }; | ||||
| 
 | ||||
| // General
 | ||||
| const NotificationSequence sequence_double_vibro = { | ||||
|     &message_vibro_on, | ||||
|     &message_delay_100, | ||||
|     &message_vibro_off, | ||||
|     &message_delay_100, | ||||
|     &message_vibro_on, | ||||
|     &message_delay_100, | ||||
|     &message_vibro_off, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| const NotificationSequence sequence_success = { | ||||
|     &message_display_on, | ||||
|     &message_green_255, | ||||
|  | ||||
| @ -23,6 +23,7 @@ extern const NotificationMessage message_green_0; | ||||
| extern const NotificationMessage message_blue_0; | ||||
| 
 | ||||
| // Delay
 | ||||
| extern const NotificationMessage message_delay_1; | ||||
| extern const NotificationMessage message_delay_10; | ||||
| extern const NotificationMessage message_delay_25; | ||||
| extern const NotificationMessage message_delay_50; | ||||
| @ -87,6 +88,7 @@ extern const NotificationSequence sequence_blink_magenta_100; | ||||
| extern const NotificationSequence sequence_blink_white_100; | ||||
| 
 | ||||
| // General
 | ||||
| extern const NotificationSequence sequence_double_vibro; | ||||
| extern const NotificationSequence sequence_success; | ||||
| extern const NotificationSequence sequence_error; | ||||
| 
 | ||||
|  | ||||
| @ -49,6 +49,12 @@ void api_hal_irda_pwm_set(float duty_cycle, float freq); | ||||
|  */ | ||||
| void api_hal_irda_pwm_stop(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Check if IRDA is in use now. | ||||
|  * @return  false - IRDA is busy, true otherwise. | ||||
|  */ | ||||
| bool api_hal_irda_rx_irq_is_busy(void); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -97,6 +97,10 @@ void api_hal_irda_rx_irq_deinit(void) { | ||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||
| } | ||||
| 
 | ||||
| bool api_hal_irda_rx_irq_is_busy(void) { | ||||
|     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||
| } | ||||
| 
 | ||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | ||||
|     furi_check(callback); | ||||
| 
 | ||||
|  | ||||
| @ -97,6 +97,10 @@ void api_hal_irda_rx_irq_deinit(void) { | ||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||
| } | ||||
| 
 | ||||
| bool api_hal_irda_rx_irq_is_busy(void) { | ||||
|     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||
| } | ||||
| 
 | ||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | ||||
|     furi_check(callback); | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| #include "irda.h" | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| @ -58,7 +59,7 @@ static const IrdaProtocolImplementation irda_protocols[] = { | ||||
|       .address_length = 2, | ||||
|       .command_length = 2, | ||||
|     }, | ||||
|     // #2
 | ||||
|     // #2 - have to be after NEC
 | ||||
|     { .protocol = IrdaProtocolNECext, | ||||
|       .name = "NECext", | ||||
|       .decoder = { | ||||
| @ -81,12 +82,14 @@ const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t durati | ||||
|     IrdaMessage* result = NULL; | ||||
| 
 | ||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||
|         if (irda_protocols[i].decoder.decode) { | ||||
|             message = irda_protocols[i].decoder.decode(handler->ctx[i], level, duration); | ||||
|             if (!result && message) { | ||||
|                 message->protocol = irda_protocols[i].protocol; | ||||
|                 result = message; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| @ -96,8 +99,9 @@ IrdaHandler* irda_alloc_decoder(void) { | ||||
|     handler->ctx = furi_alloc(sizeof(void*) * COUNT_OF(irda_protocols)); | ||||
| 
 | ||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||
|         handler->ctx[i] = 0; | ||||
|         if (irda_protocols[i].decoder.alloc) | ||||
|             handler->ctx[i] = irda_protocols[i].decoder.alloc(); | ||||
|         furi_check(handler->ctx[i]); | ||||
|     } | ||||
| 
 | ||||
|     return handler; | ||||
| @ -108,6 +112,7 @@ void irda_free_decoder(IrdaHandler* handler) { | ||||
|     furi_assert(handler->ctx); | ||||
| 
 | ||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||
|         if (irda_protocols[i].decoder.free) | ||||
|             irda_protocols[i].decoder.free(handler->ctx[i]); | ||||
|     } | ||||
| 
 | ||||
| @ -117,31 +122,54 @@ void irda_free_decoder(IrdaHandler* handler) { | ||||
| 
 | ||||
| void irda_reset_decoder(IrdaHandler* handler) { | ||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||
|         if (irda_protocols[i].decoder.reset) | ||||
|             irda_protocols[i].decoder.reset(handler->ctx[i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void irda_send(const IrdaMessage* message, int times) { | ||||
|     furi_assert(message); | ||||
|     furi_assert(irda_is_protocol_valid(message->protocol)); | ||||
| 
 | ||||
|     for (int i = 0; i < times; ++i) { | ||||
|         osKernelLock(); | ||||
|         if(irda_protocols[message->protocol].encoder.encode) { | ||||
|             __disable_irq(); | ||||
|             irda_protocols[message->protocol].encoder.encode(message->address, message->command, !!i); | ||||
|             __enable_irq(); | ||||
|         osKernelUnlock(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool irda_is_protocol_valid(IrdaProtocol protocol) { | ||||
|     return (protocol >= 0) && (protocol < COUNT_OF(irda_protocols)); | ||||
| } | ||||
| 
 | ||||
| IrdaProtocol irda_get_protocol_by_name(const char* protocol_name) { | ||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||
|         if (!strcmp(irda_protocols[i].name, protocol_name)) | ||||
|             return i; | ||||
|     } | ||||
|     return IrdaProtocolUnknown; | ||||
| } | ||||
| 
 | ||||
| const char* irda_get_protocol_name(IrdaProtocol protocol) { | ||||
|     if (irda_is_protocol_valid(protocol)) | ||||
|         return irda_protocols[protocol].name; | ||||
|     else | ||||
|         return "Invalid"; | ||||
| } | ||||
| 
 | ||||
| uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { | ||||
|     if (irda_is_protocol_valid(protocol)) | ||||
|         return irda_protocols[protocol].address_length; | ||||
|     else | ||||
|         return 0; | ||||
| } | ||||
| 
 | ||||
| uint8_t irda_get_protocol_command_length(IrdaProtocol protocol) { | ||||
|     if (irda_is_protocol_valid(protocol)) | ||||
|         return irda_protocols[protocol].command_length; | ||||
|     else | ||||
|         return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -11,6 +11,7 @@ typedef struct IrdaHandler IrdaHandler; | ||||
| 
 | ||||
| // Do not change protocol order, as it can be saved into memory and fw update can be performed!
 | ||||
| typedef enum { | ||||
|     IrdaProtocolUnknown = -1, | ||||
|     IrdaProtocolSamsung32 = 0, | ||||
|     IrdaProtocolNEC = 1, | ||||
|     IrdaProtocolNECext = 2, | ||||
| @ -74,6 +75,14 @@ void irda_send(const IrdaMessage* message, int times); | ||||
|  */ | ||||
| const char* irda_get_protocol_name(IrdaProtocol protocol); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get protocol enum by protocol name. | ||||
|  * | ||||
|  * \param[in]   protocol_name   - string to protocol name. | ||||
|  * \return      protocol identifier. | ||||
|  */ | ||||
| IrdaProtocol irda_get_protocol_by_name(const char* protocol_name); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get address length by protocol enum. | ||||
|  * | ||||
| @ -90,6 +99,14 @@ uint8_t irda_get_protocol_address_length(IrdaProtocol protocol); | ||||
|  */ | ||||
| uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); | ||||
| 
 | ||||
| /**
 | ||||
|  * Checks whether protocol valid. | ||||
|  * | ||||
|  * \param[in]   protocol    - protocol identifier. | ||||
|  * \return      true if protocol is valid, false otherwise. | ||||
|  */ | ||||
| bool irda_is_protocol_valid(IrdaProtocol protocol); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov