[FL-2589] RPC App control commands (#1350)
* RPC App control commands * Button release timeout * SubGhz tx fix Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									0e78f38404
								
							
						
					
					
						commit
						4a1695ba1c
					
				| @ -85,7 +85,6 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | |||||||
| 
 | 
 | ||||||
| static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     furi_hal_usb_unlock(); |     furi_hal_usb_unlock(); | ||||||
|     FURI_LOG_I("", "Init %d", vcp_ch); |  | ||||||
|     if(vcp_ch == 0) { |     if(vcp_ch == 0) { | ||||||
|         Cli* cli = furi_record_open("cli"); |         Cli* cli = furi_record_open("cli"); | ||||||
|         cli_session_close(cli); |         cli_session_close(cli); | ||||||
| @ -103,7 +102,6 @@ static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | |||||||
| static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     UNUSED(usb_uart); |     UNUSED(usb_uart); | ||||||
|     furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); |     furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); | ||||||
|     FURI_LOG_I("", "Deinit %d", vcp_ch); |  | ||||||
|     if(vcp_ch != 0) { |     if(vcp_ch != 0) { | ||||||
|         Cli* cli = furi_record_open("cli"); |         Cli* cli = furi_record_open("cli"); | ||||||
|         cli_session_close(cli); |         cli_session_close(cli); | ||||||
|  | |||||||
| @ -5,6 +5,9 @@ | |||||||
| #include "m-string.h" | #include "m-string.h" | ||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "iButtonApp" | ||||||
| 
 | 
 | ||||||
| static const NotificationSequence sequence_blink_start_cyan = { | static const NotificationSequence sequence_blink_start_cyan = { | ||||||
|     &message_blink_start_10, |     &message_blink_start_10, | ||||||
| @ -55,7 +58,7 @@ static void ibutton_make_app_folder(iButton* ibutton) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) { | static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { | ||||||
|     FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); |     FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     string_t data; |     string_t data; | ||||||
| @ -89,13 +92,40 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) { | |||||||
|     flipper_format_free(file); |     flipper_format_free(file); | ||||||
|     string_clear(data); |     string_clear(data); | ||||||
| 
 | 
 | ||||||
|     if(!result) { |     if((!result) && (show_dialog)) { | ||||||
|         dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); |         dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     iButton* ibutton = context; | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(event == RpcAppEventSessionClose) { | ||||||
|  |         rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); | ||||||
|  |         ibutton->rpc_ctx = NULL; | ||||||
|  |         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventAppExit) { | ||||||
|  |         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventLoadFile) { | ||||||
|  |         if(arg) { | ||||||
|  |             string_set_str(ibutton->file_path, arg); | ||||||
|  |             if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { | ||||||
|  |                 ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool ibutton_custom_event_callback(void* context, uint32_t event) { | bool ibutton_custom_event_callback(void* context, uint32_t event) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     iButton* ibutton = context; |     iButton* ibutton = context; | ||||||
| @ -226,7 +256,7 @@ bool ibutton_file_select(iButton* ibutton) { | |||||||
|         true); |         true); | ||||||
| 
 | 
 | ||||||
|     if(success) { |     if(success) { | ||||||
|         success = ibutton_load_key_data(ibutton, ibutton->file_path); |         success = ibutton_load_key_data(ibutton, ibutton->file_path, true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return success; |     return success; | ||||||
| @ -334,16 +364,27 @@ int32_t ibutton_app(void* p) { | |||||||
|     ibutton_make_app_folder(ibutton); |     ibutton_make_app_folder(ibutton); | ||||||
| 
 | 
 | ||||||
|     bool key_loaded = false; |     bool key_loaded = false; | ||||||
|  |     bool rpc_mode = false; | ||||||
| 
 | 
 | ||||||
|     if(p) { |     if(p) { | ||||||
|  |         uint32_t rpc_ctx = 0; | ||||||
|  |         if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { | ||||||
|  |             FURI_LOG_D(TAG, "Running in RPC mode"); | ||||||
|  |             ibutton->rpc_ctx = (void*)rpc_ctx; | ||||||
|  |             rpc_mode = true; | ||||||
|  |             rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton); | ||||||
|  |         } else { | ||||||
|             string_set_str(ibutton->file_path, (const char*)p); |             string_set_str(ibutton->file_path, (const char*)p); | ||||||
|         if(ibutton_load_key_data(ibutton, ibutton->file_path)) { |             if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { | ||||||
|                 key_loaded = true; |                 key_loaded = true; | ||||||
|                 // TODO: Display an error if the key from p could not be loaded
 |                 // TODO: Display an error if the key from p could not be loaded
 | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if(key_loaded) { |     if(rpc_mode) { | ||||||
|  |         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); | ||||||
|  |     } else if(key_loaded) { | ||||||
|         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); |         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); | ||||||
|     } else { |     } else { | ||||||
|         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); |         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); | ||||||
| @ -351,6 +392,9 @@ int32_t ibutton_app(void* p) { | |||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(ibutton->view_dispatcher); |     view_dispatcher_run(ibutton->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|  |     if(ibutton->rpc_ctx) { | ||||||
|  |         rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); | ||||||
|  |     } | ||||||
|     ibutton_free(ibutton); |     ibutton_free(ibutton); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,4 +9,6 @@ enum iButtonCustomEvent { | |||||||
|     iButtonCustomEventByteEditResult, |     iButtonCustomEventByteEditResult, | ||||||
|     iButtonCustomEventWorkerEmulated, |     iButtonCustomEventWorkerEmulated, | ||||||
|     iButtonCustomEventWorkerRead, |     iButtonCustomEventWorkerRead, | ||||||
|  | 
 | ||||||
|  |     iButtonCustomEventRpcExit, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -50,6 +50,8 @@ struct iButton { | |||||||
|     Popup* popup; |     Popup* popup; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|     DialogEx* dialog_ex; |     DialogEx* dialog_ex; | ||||||
|  | 
 | ||||||
|  |     void* rpc_ctx; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|  | |||||||
| @ -18,3 +18,4 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm) | |||||||
| ADD_SCENE(ibutton, delete_success, DeleteSuccess) | ADD_SCENE(ibutton, delete_success, DeleteSuccess) | ||||||
| ADD_SCENE(ibutton, retry_confirm, RetryConfirm) | ADD_SCENE(ibutton, retry_confirm, RetryConfirm) | ||||||
| ADD_SCENE(ibutton, exit_confirm, ExitConfirm) | ADD_SCENE(ibutton, exit_confirm, ExitConfirm) | ||||||
|  | ADD_SCENE(ibutton, rpc, Rpc) | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								applications/ibutton/scenes/ibutton_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								applications/ibutton/scenes/ibutton_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | #include "../ibutton_i.h" | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | 
 | ||||||
|  | void ibutton_scene_rpc_on_enter(void* context) { | ||||||
|  |     iButton* ibutton = context; | ||||||
|  |     Widget* widget = ibutton->widget; | ||||||
|  | 
 | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); | ||||||
|  | 
 | ||||||
|  |     notification_message(ibutton->notifications, &sequence_display_backlight_on); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     iButton* ibutton = context; | ||||||
|  | 
 | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         consumed = true; | ||||||
|  |         if(event.event == iButtonCustomEventRpcExit) { | ||||||
|  |             view_dispatcher_stop(ibutton->view_dispatcher); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_scene_rpc_on_exit(void* context) { | ||||||
|  |     iButton* ibutton = context; | ||||||
|  |     widget_reset(ibutton->widget); | ||||||
|  | } | ||||||
| @ -36,6 +36,52 @@ static void infrared_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(infrared->scene_manager); |     scene_manager_handle_tick_event(infrared->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool | ||||||
|  |     infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  | 
 | ||||||
|  |     if(!infrared->rpc_ctx) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(event == RpcAppEventSessionClose) { | ||||||
|  |         rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); | ||||||
|  |         infrared->rpc_ctx = NULL; | ||||||
|  |         view_dispatcher_send_custom_event( | ||||||
|  |             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventAppExit) { | ||||||
|  |         view_dispatcher_send_custom_event( | ||||||
|  |             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventLoadFile) { | ||||||
|  |         if(arg) { | ||||||
|  |             string_set_str(infrared->file_path, arg); | ||||||
|  |             result = infrared_remote_load(infrared->remote, infrared->file_path); | ||||||
|  |             infrared_worker_tx_set_get_signal_callback( | ||||||
|  |                 infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); | ||||||
|  |             infrared_worker_tx_set_signal_sent_callback( | ||||||
|  |                 infrared->worker, infrared_signal_sent_callback, infrared); | ||||||
|  |         } | ||||||
|  |     } else if(event == RpcAppEventButtonPress) { | ||||||
|  |         if(arg) { | ||||||
|  |             size_t button_index = 0; | ||||||
|  |             if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { | ||||||
|  |                 infrared_tx_start_button_index(infrared, button_index); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if(event == RpcAppEventButtonRelease) { | ||||||
|  |         infrared_tx_stop(infrared); | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void infrared_find_vacant_remote_name(string_t name, const char* path) { | static void infrared_find_vacant_remote_name(string_t name, const char* path) { | ||||||
|     Storage* storage = furi_record_open("storage"); |     Storage* storage = furi_record_open("storage"); | ||||||
| 
 | 
 | ||||||
| @ -154,6 +200,11 @@ static void infrared_free(Infrared* infrared) { | |||||||
|     ViewDispatcher* view_dispatcher = infrared->view_dispatcher; |     ViewDispatcher* view_dispatcher = infrared->view_dispatcher; | ||||||
|     InfraredAppState* app_state = &infrared->app_state; |     InfraredAppState* app_state = &infrared->app_state; | ||||||
| 
 | 
 | ||||||
|  |     if(infrared->rpc_ctx) { | ||||||
|  |         rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); | ||||||
|  |         infrared->rpc_ctx = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); |     view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); | ||||||
|     submenu_free(infrared->submenu); |     submenu_free(infrared->submenu); | ||||||
| 
 | 
 | ||||||
| @ -375,8 +426,16 @@ int32_t infrared_app(void* p) { | |||||||
|     infrared_make_app_folder(infrared); |     infrared_make_app_folder(infrared); | ||||||
| 
 | 
 | ||||||
|     bool is_remote_loaded = false; |     bool is_remote_loaded = false; | ||||||
|  |     bool is_rpc_mode = false; | ||||||
| 
 | 
 | ||||||
|     if(p) { |     if(p) { | ||||||
|  |         uint32_t rpc_ctx = 0; | ||||||
|  |         if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { | ||||||
|  |             infrared->rpc_ctx = (void*)rpc_ctx; | ||||||
|  |             rpc_system_app_set_callback( | ||||||
|  |                 infrared->rpc_ctx, infrared_rpc_command_callback, infrared); | ||||||
|  |             is_rpc_mode = true; | ||||||
|  |         } else { | ||||||
|             string_set_str(infrared->file_path, (const char*)p); |             string_set_str(infrared->file_path, (const char*)p); | ||||||
|             is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); |             is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); | ||||||
|             if(!is_remote_loaded) { |             if(!is_remote_loaded) { | ||||||
| @ -385,8 +444,11 @@ int32_t infrared_app(void* p) { | |||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if(is_remote_loaded) { |     if(is_rpc_mode) { | ||||||
|  |         scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc); | ||||||
|  |     } else if(is_remote_loaded) { | ||||||
|         scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); |         scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); | ||||||
|     } else { |     } else { | ||||||
|         scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); |         scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); | ||||||
|  | |||||||
| @ -30,6 +30,8 @@ | |||||||
| #include "views/infrared_progress_view.h" | #include "views/infrared_progress_view.h" | ||||||
| #include "views/infrared_debug_view.h" | #include "views/infrared_debug_view.h" | ||||||
| 
 | 
 | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
|  | 
 | ||||||
| #define INFRARED_FILE_NAME_SIZE 100 | #define INFRARED_FILE_NAME_SIZE 100 | ||||||
| #define INFRARED_TEXT_STORE_NUM 2 | #define INFRARED_TEXT_STORE_NUM 2 | ||||||
| #define INFRARED_TEXT_STORE_SIZE 128 | #define INFRARED_TEXT_STORE_SIZE 128 | ||||||
| @ -95,6 +97,8 @@ struct Infrared { | |||||||
|     string_t file_path; |     string_t file_path; | ||||||
|     char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; |     char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; | ||||||
|     InfraredAppState app_state; |     InfraredAppState app_state; | ||||||
|  | 
 | ||||||
|  |     void* rpc_ctx; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| #include "infrared_remote.h" | #include "infrared_remote.h" | ||||||
| 
 | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stddef.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <m-array.h> | #include <m-array.h> | ||||||
| @ -73,6 +75,17 @@ InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t | |||||||
|     return *InfraredButtonArray_get(remote->buttons, index); |     return *InfraredButtonArray_get(remote->buttons, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) { | ||||||
|  |     for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) { | ||||||
|  |         InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i); | ||||||
|  |         if(!strcmp(infrared_remote_button_get_name(button), name)) { | ||||||
|  |             *index = i; | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { | bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { | ||||||
|     InfraredRemoteButton* button = infrared_remote_button_alloc(); |     InfraredRemoteButton* button = infrared_remote_button_alloc(); | ||||||
|     infrared_remote_button_set_name(button, name); |     infrared_remote_button_set_name(button, name); | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ const char* infrared_remote_get_path(InfraredRemote* remote); | |||||||
| 
 | 
 | ||||||
| size_t infrared_remote_get_button_count(InfraredRemote* remote); | size_t infrared_remote_get_button_count(InfraredRemote* remote); | ||||||
| InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); | InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); | ||||||
|  | bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index); | ||||||
| 
 | 
 | ||||||
| bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); | bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); | ||||||
| bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); | bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); | ||||||
|  | |||||||
| @ -16,3 +16,4 @@ ADD_SCENE(infrared, universal, Universal) | |||||||
| ADD_SCENE(infrared, universal_tv, UniversalTV) | ADD_SCENE(infrared, universal_tv, UniversalTV) | ||||||
| ADD_SCENE(infrared, debug, Debug) | ADD_SCENE(infrared, debug, Debug) | ||||||
| ADD_SCENE(infrared, error_databases, ErrorDatabases) | ADD_SCENE(infrared, error_databases, ErrorDatabases) | ||||||
|  | ADD_SCENE(infrared, rpc, Rpc) | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								applications/infrared/scenes/infrared_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								applications/infrared/scenes/infrared_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | #include "../infrared_i.h" | ||||||
|  | #include "gui/canvas.h" | ||||||
|  | 
 | ||||||
|  | void infrared_scene_rpc_on_enter(void* context) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     Popup* popup = infrared->popup; | ||||||
|  | 
 | ||||||
|  |     popup_set_text(popup, "Rpc mode", 64, 28, AlignCenter, AlignCenter); | ||||||
|  | 
 | ||||||
|  |     popup_set_context(popup, context); | ||||||
|  |     popup_set_callback(popup, infrared_popup_closed_callback); | ||||||
|  | 
 | ||||||
|  |     infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn); | ||||||
|  |     view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); | ||||||
|  | 
 | ||||||
|  |     notification_message(infrared->notifications, &sequence_display_backlight_on); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         consumed = true; | ||||||
|  |         if(event.event == InfraredCustomEventTypeBackPressed) { | ||||||
|  |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|  |         } else if(event.event == InfraredCustomEventTypePopupClosed) { | ||||||
|  |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_scene_rpc_on_exit(void* context) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     popup_reset(infrared->popup); | ||||||
|  | } | ||||||
| @ -20,10 +20,13 @@ | |||||||
| #include "scene/lfrfid_app_scene_saved_info.h" | #include "scene/lfrfid_app_scene_saved_info.h" | ||||||
| #include "scene/lfrfid_app_scene_delete_confirm.h" | #include "scene/lfrfid_app_scene_delete_confirm.h" | ||||||
| #include "scene/lfrfid_app_scene_delete_success.h" | #include "scene/lfrfid_app_scene_delete_success.h" | ||||||
|  | #include "scene/lfrfid_app_scene_rpc.h" | ||||||
| 
 | 
 | ||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| 
 | 
 | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
|  | 
 | ||||||
| const char* LfRfidApp::app_folder = "/any/lfrfid"; | const char* LfRfidApp::app_folder = "/any/lfrfid"; | ||||||
| const char* LfRfidApp::app_extension = ".rfid"; | const char* LfRfidApp::app_extension = ".rfid"; | ||||||
| const char* LfRfidApp::app_filetype = "Flipper RFID key"; | const char* LfRfidApp::app_filetype = "Flipper RFID key"; | ||||||
| @ -39,6 +42,43 @@ LfRfidApp::LfRfidApp() | |||||||
| 
 | 
 | ||||||
| LfRfidApp::~LfRfidApp() { | LfRfidApp::~LfRfidApp() { | ||||||
|     string_clear(file_path); |     string_clear(file_path); | ||||||
|  |     if(rpc_ctx) { | ||||||
|  |         rpc_system_app_set_callback(rpc_ctx, NULL, NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(event == RpcAppEventSessionClose) { | ||||||
|  |         rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); | ||||||
|  |         app->rpc_ctx = NULL; | ||||||
|  |         LfRfidApp::Event event; | ||||||
|  |         event.type = LfRfidApp::EventType::Exit; | ||||||
|  |         app->view_controller.send_event(&event); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventAppExit) { | ||||||
|  |         LfRfidApp::Event event; | ||||||
|  |         event.type = LfRfidApp::EventType::Exit; | ||||||
|  |         app->view_controller.send_event(&event); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventLoadFile) { | ||||||
|  |         if(arg) { | ||||||
|  |             string_set_str(app->file_path, arg); | ||||||
|  |             if(app->load_key_data(app->file_path, &(app->worker.key), false)) { | ||||||
|  |                 LfRfidApp::Event event; | ||||||
|  |                 event.type = LfRfidApp::EventType::EmulateStart; | ||||||
|  |                 app->view_controller.send_event(&event); | ||||||
|  |                 app->worker.start_emulate(); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LfRfidApp::run(void* _args) { | void LfRfidApp::run(void* _args) { | ||||||
| @ -47,10 +87,19 @@ void LfRfidApp::run(void* _args) { | |||||||
|     make_app_folder(); |     make_app_folder(); | ||||||
| 
 | 
 | ||||||
|     if(strlen(args)) { |     if(strlen(args)) { | ||||||
|  |         uint32_t rpc_ctx_ptr = 0; | ||||||
|  |         if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { | ||||||
|  |             rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; | ||||||
|  |             rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); | ||||||
|  |             scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); | ||||||
|  |             scene_controller.process(100, SceneType::Rpc); | ||||||
|  |         } else { | ||||||
|             string_set_str(file_path, args); |             string_set_str(file_path, args); | ||||||
|         load_key_data(file_path, &worker.key); |             load_key_data(file_path, &worker.key, true); | ||||||
|             scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); |             scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); | ||||||
|             scene_controller.process(100, SceneType::Emulate); |             scene_controller.process(100, SceneType::Emulate); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     } else { |     } else { | ||||||
|         scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); |         scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); | ||||||
|         scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); |         scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); | ||||||
| @ -99,7 +148,7 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) { | |||||||
|         dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); |         dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); | ||||||
| 
 | 
 | ||||||
|     if(result) { |     if(result) { | ||||||
|         result = load_key_data(file_path, &worker.key); |         result = load_key_data(file_path, &worker.key, true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| @ -110,7 +159,7 @@ bool LfRfidApp::delete_key(RfidKey* key) { | |||||||
|     return storage_simply_remove(storage, string_get_cstr(file_path)); |     return storage_simply_remove(storage, string_get_cstr(file_path)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { | bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { | ||||||
|     FlipperFormat* file = flipper_format_file_alloc(storage); |     FlipperFormat* file = flipper_format_file_alloc(storage); | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     string_t str_result; |     string_t str_result; | ||||||
| @ -149,7 +198,7 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { | |||||||
|     flipper_format_free(file); |     flipper_format_free(file); | ||||||
|     string_clear(str_result); |     string_clear(str_result); | ||||||
| 
 | 
 | ||||||
|     if(!result) { |     if((!result) && (show_dialog)) { | ||||||
|         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); |         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ | |||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include "helpers/rfid_worker.h" | #include "helpers/rfid_worker.h" | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
| 
 | 
 | ||||||
| class LfRfidApp { | class LfRfidApp { | ||||||
| public: | public: | ||||||
| @ -30,6 +31,8 @@ public: | |||||||
|         MenuSelected, |         MenuSelected, | ||||||
|         Stay, |         Stay, | ||||||
|         Retry, |         Retry, | ||||||
|  |         Exit, | ||||||
|  |         EmulateStart, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     enum class SceneType : uint8_t { |     enum class SceneType : uint8_t { | ||||||
| @ -51,6 +54,7 @@ public: | |||||||
|         SavedInfo, |         SavedInfo, | ||||||
|         DeleteConfirm, |         DeleteConfirm, | ||||||
|         DeleteSuccess, |         DeleteSuccess, | ||||||
|  |         Rpc, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     class Event { |     class Event { | ||||||
| @ -79,6 +83,8 @@ public: | |||||||
| 
 | 
 | ||||||
|     string_t file_path; |     string_t file_path; | ||||||
| 
 | 
 | ||||||
|  |     RpcAppSystem* rpc_ctx; | ||||||
|  | 
 | ||||||
|     void run(void* args); |     void run(void* args); | ||||||
| 
 | 
 | ||||||
|     static const char* app_folder; |     static const char* app_folder; | ||||||
| @ -89,8 +95,9 @@ public: | |||||||
|     bool load_key_from_file_select(bool need_restore); |     bool load_key_from_file_select(bool need_restore); | ||||||
|     bool delete_key(RfidKey* key); |     bool delete_key(RfidKey* key); | ||||||
| 
 | 
 | ||||||
|     bool load_key_data(string_t path, RfidKey* key); |     bool load_key_data(string_t path, RfidKey* key, bool show_dialog); | ||||||
|     bool save_key_data(string_t path, RfidKey* key); |     bool save_key_data(string_t path, RfidKey* key); | ||||||
| 
 | 
 | ||||||
|     void make_app_folder(); |     void make_app_folder(); | ||||||
|  |     //bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context);
 | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | #include "lfrfid_app_scene_rpc.h" | ||||||
|  | #include "furi/common_defines.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { | ||||||
|  |     auto popup = app->view_controller.get<PopupVM>(); | ||||||
|  | 
 | ||||||
|  |     popup->set_header("RPC Mode", 64, 30, AlignCenter, AlignTop); | ||||||
|  | 
 | ||||||
|  |     app->view_controller.switch_to<PopupVM>(); | ||||||
|  | 
 | ||||||
|  |     notification_message(app->notification, &sequence_display_backlight_on); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||||
|  |     UNUSED(app); | ||||||
|  |     UNUSED(event); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == LfRfidApp::EventType::Exit) { | ||||||
|  |         consumed = true; | ||||||
|  |         LfRfidApp::Event view_event; | ||||||
|  |         view_event.type = LfRfidApp::EventType::Back; | ||||||
|  |         app->view_controller.send_event(&view_event); | ||||||
|  |     } else if(event->type == LfRfidApp::EventType::EmulateStart) { | ||||||
|  |         consumed = true; | ||||||
|  |         emulating = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { | ||||||
|  |     if(emulating) { | ||||||
|  |         app->worker.stop_emulate(); | ||||||
|  |     } | ||||||
|  |     app->view_controller.get<PopupVM>()->clean(); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_rpc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_rpc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "../lfrfid_app.h" | ||||||
|  | 
 | ||||||
|  | class LfRfidAppSceneRpc : public GenericScene<LfRfidApp> { | ||||||
|  | public: | ||||||
|  |     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||||
|  |     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||||
|  |     void on_exit(LfRfidApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     bool emulating = false; | ||||||
|  | }; | ||||||
							
								
								
									
										78
									
								
								applications/nfc/nfc.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										78
									
								
								applications/nfc/nfc.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -19,6 +19,77 @@ void nfc_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(nfc->scene_manager); |     scene_manager_handle_tick_event(nfc->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nfc_rpc_exit_callback(Nfc* nfc) { | ||||||
|  |     if(nfc->rpc_state == NfcRpcStateEmulating) { | ||||||
|  |         // Stop worker
 | ||||||
|  |         nfc_worker_stop(nfc->worker); | ||||||
|  |     } else if(nfc->rpc_state == NfcRpcStateEmulated) { | ||||||
|  |         // Stop worker
 | ||||||
|  |         nfc_worker_stop(nfc->worker); | ||||||
|  |         // Save data in shadow file
 | ||||||
|  |         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||||
|  |     } | ||||||
|  |     if(nfc->rpc_ctx) { | ||||||
|  |         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||||
|  |         nfc->rpc_ctx = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     UNUSED(event); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     nfc->rpc_state = NfcRpcStateEmulated; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     if(!nfc->rpc_ctx) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(event == RpcAppEventSessionClose) { | ||||||
|  |         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||||
|  |         nfc->rpc_ctx = NULL; | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventAppExit) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventLoadFile) { | ||||||
|  |         if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { | ||||||
|  |             if(nfc_device_load(nfc->dev, arg, false)) { | ||||||
|  |                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|  |                     nfc_worker_start( | ||||||
|  |                         nfc->worker, | ||||||
|  |                         NfcWorkerStateEmulateMifareUltralight, | ||||||
|  |                         &nfc->dev->dev_data, | ||||||
|  |                         nfc_rpc_emulate_callback, | ||||||
|  |                         nfc); | ||||||
|  |                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|  |                     nfc_worker_start( | ||||||
|  |                         nfc->worker, | ||||||
|  |                         NfcWorkerStateEmulateMifareClassic, | ||||||
|  |                         &nfc->dev->dev_data, | ||||||
|  |                         nfc_rpc_emulate_callback, | ||||||
|  |                         nfc); | ||||||
|  |                 } else { | ||||||
|  |                     nfc_worker_start( | ||||||
|  |                         nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||||
|  |                 } | ||||||
|  |                 nfc->rpc_state = NfcRpcStateEmulating; | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Nfc* nfc_alloc() { | Nfc* nfc_alloc() { | ||||||
|     Nfc* nfc = malloc(sizeof(Nfc)); |     Nfc* nfc = malloc(sizeof(Nfc)); | ||||||
| 
 | 
 | ||||||
| @ -193,7 +264,12 @@ int32_t nfc_app(void* p) { | |||||||
| 
 | 
 | ||||||
|     // Check argument and run corresponding scene
 |     // Check argument and run corresponding scene
 | ||||||
|     if((*args != '\0')) { |     if((*args != '\0')) { | ||||||
|         if(nfc_device_load(nfc->dev, p)) { |         uint32_t rpc_ctx = 0; | ||||||
|  |         if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { | ||||||
|  |             nfc->rpc_ctx = (void*)rpc_ctx; | ||||||
|  |             rpc_system_app_set_callback(nfc->rpc_ctx, nfc_rpc_command_callback, nfc); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc); | ||||||
|  |         } else if(nfc_device_load(nfc->dev, p, true)) { | ||||||
|             if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |             if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); | ||||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|  | |||||||
| @ -837,7 +837,7 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { | |||||||
|     return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); |     return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | static bool nfc_device_load_data(NfcDevice* dev, string_t path, bool show_dialog) { | ||||||
|     bool parsed = false; |     bool parsed = false; | ||||||
|     FlipperFormat* file = flipper_format_file_alloc(dev->storage); |     FlipperFormat* file = flipper_format_file_alloc(dev->storage); | ||||||
|     FuriHalNfcDevData* data = &dev->dev_data.nfc_data; |     FuriHalNfcDevData* data = &dev->dev_data.nfc_data; | ||||||
| @ -887,7 +887,7 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | |||||||
|         parsed = true; |         parsed = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     if(!parsed) { |     if((!parsed) && (show_dialog)) { | ||||||
|         if(deprecated_version) { |         if(deprecated_version) { | ||||||
|             dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); |             dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); | ||||||
|         } else { |         } else { | ||||||
| @ -900,13 +900,13 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | |||||||
|     return parsed; |     return parsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_device_load(NfcDevice* dev, const char* file_path) { | bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog) { | ||||||
|     furi_assert(dev); |     furi_assert(dev); | ||||||
|     furi_assert(file_path); |     furi_assert(file_path); | ||||||
| 
 | 
 | ||||||
|     // Load device data
 |     // Load device data
 | ||||||
|     string_set_str(dev->load_path, file_path); |     string_set_str(dev->load_path, file_path); | ||||||
|     bool dev_load = nfc_device_load_data(dev, dev->load_path); |     bool dev_load = nfc_device_load_data(dev, dev->load_path, show_dialog); | ||||||
|     if(dev_load) { |     if(dev_load) { | ||||||
|         // Set device name
 |         // Set device name
 | ||||||
|         string_t filename; |         string_t filename; | ||||||
| @ -933,7 +933,7 @@ bool nfc_file_select(NfcDevice* dev) { | |||||||
|         string_init(filename); |         string_init(filename); | ||||||
|         path_extract_filename(dev->load_path, filename, true); |         path_extract_filename(dev->load_path, filename, true); | ||||||
|         strncpy(dev->dev_name, string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); |         strncpy(dev->dev_name, string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); | ||||||
|         res = nfc_device_load_data(dev, dev->load_path); |         res = nfc_device_load_data(dev, dev->load_path, true); | ||||||
|         if(res) { |         if(res) { | ||||||
|             nfc_device_set_name(dev, dev->dev_name); |             nfc_device_set_name(dev, dev->dev_name); | ||||||
|         } |         } | ||||||
| @ -1017,7 +1017,7 @@ bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { | |||||||
|         } else { |         } else { | ||||||
|             string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); |             string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); | ||||||
|         } |         } | ||||||
|         if(!nfc_device_load_data(dev, path)) break; |         if(!nfc_device_load_data(dev, path, true)) break; | ||||||
|         restored = true; |         restored = true; | ||||||
|     } while(0); |     } while(0); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -71,7 +71,7 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name); | |||||||
| 
 | 
 | ||||||
| bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); | bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); | ||||||
| 
 | 
 | ||||||
| bool nfc_device_load(NfcDevice* dev, const char* file_path); | bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); | ||||||
| 
 | 
 | ||||||
| bool nfc_file_select(NfcDevice* dev); | bool nfc_file_select(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,10 +29,18 @@ | |||||||
| #include <nfc/scenes/nfc_scene.h> | #include <nfc/scenes/nfc_scene.h> | ||||||
| #include <nfc/helpers/nfc_custom_event.h> | #include <nfc/helpers/nfc_custom_event.h> | ||||||
| 
 | 
 | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
|  | 
 | ||||||
| #define NFC_SEND_NOTIFICATION_FALSE (0UL) | #define NFC_SEND_NOTIFICATION_FALSE (0UL) | ||||||
| #define NFC_SEND_NOTIFICATION_TRUE (1UL) | #define NFC_SEND_NOTIFICATION_TRUE (1UL) | ||||||
| #define NFC_TEXT_STORE_SIZE 128 | #define NFC_TEXT_STORE_SIZE 128 | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     NfcRpcStateIdle, | ||||||
|  |     NfcRpcStateEmulating, | ||||||
|  |     NfcRpcStateEmulated, | ||||||
|  | } NfcRpcState; | ||||||
|  | 
 | ||||||
| // Forward declaration due to circular dependency
 | // Forward declaration due to circular dependency
 | ||||||
| typedef struct NfcGenerator NfcGenerator; | typedef struct NfcGenerator NfcGenerator; | ||||||
| 
 | 
 | ||||||
| @ -48,6 +56,9 @@ struct Nfc { | |||||||
|     char text_store[NFC_TEXT_STORE_SIZE + 1]; |     char text_store[NFC_TEXT_STORE_SIZE + 1]; | ||||||
|     string_t text_box_store; |     string_t text_box_store; | ||||||
| 
 | 
 | ||||||
|  |     void* rpc_ctx; | ||||||
|  |     NfcRpcState rpc_state; | ||||||
|  | 
 | ||||||
|     // Common Views
 |     // Common Views
 | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     DialogEx* dialog_ex; |     DialogEx* dialog_ex; | ||||||
| @ -85,3 +96,5 @@ void nfc_text_store_clear(Nfc* nfc); | |||||||
| void nfc_blink_start(Nfc* nfc); | void nfc_blink_start(Nfc* nfc); | ||||||
| 
 | 
 | ||||||
| void nfc_blink_stop(Nfc* nfc); | void nfc_blink_stop(Nfc* nfc); | ||||||
|  | 
 | ||||||
|  | void nfc_rpc_exit_callback(Nfc* nfc); | ||||||
|  | |||||||
| @ -37,4 +37,5 @@ ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) | |||||||
| ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) | ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) | ||||||
| ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) | ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) | ||||||
| ADD_SCENE(nfc, dict_not_found, DictNotFound) | ADD_SCENE(nfc, dict_not_found, DictNotFound) | ||||||
|  | ADD_SCENE(nfc, rpc, Rpc) | ||||||
| ADD_SCENE(nfc, generate_info, GenerateInfo) | ADD_SCENE(nfc, generate_info, GenerateInfo) | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								applications/nfc/scenes/nfc_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/nfc/scenes/nfc_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_rpc_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  | 
 | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); | ||||||
|  | 
 | ||||||
|  |     notification_message(nfc->notifications, &sequence_display_backlight_on); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         consumed = true; | ||||||
|  |         if(event.event == NfcCustomEventViewExit) { | ||||||
|  |             view_dispatcher_stop(nfc->view_dispatcher); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_rpc_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     nfc_rpc_exit_callback(nfc); | ||||||
|  | 
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  | } | ||||||
| @ -45,7 +45,7 @@ static RpcSystemCallbacks rpc_systems[] = { | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .alloc = rpc_system_app_alloc, |         .alloc = rpc_system_app_alloc, | ||||||
|         .free = NULL, |         .free = rpc_system_app_free, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .alloc = rpc_system_gui_alloc, |         .alloc = rpc_system_gui_alloc, | ||||||
|  | |||||||
| @ -1,16 +1,39 @@ | |||||||
|  | #include "cmsis_os2.h" | ||||||
| #include "flipper.pb.h" | #include "flipper.pb.h" | ||||||
| #include "furi/record.h" | #include "furi/record.h" | ||||||
| #include "rpc_i.h" | #include "rpc_i.h" | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
|  | #include "rpc_app.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "RpcSystemApp" | #define TAG "RpcSystemApp" | ||||||
|  | #define APP_BUTTON_TIMEOUT 1000 | ||||||
|  | 
 | ||||||
|  | struct RpcAppSystem { | ||||||
|  |     RpcSession* session; | ||||||
|  |     RpcAppSystemCallback app_callback; | ||||||
|  |     void* app_context; | ||||||
|  |     osTimerId_t timer; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void rpc_system_app_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  | 
 | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_start_process(const PB_Main* request, void* context) { | static void rpc_system_app_start_process(const PB_Main* request, void* context) { | ||||||
|     furi_assert(request); |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|     furi_assert(request->which_content == PB_Main_app_start_request_tag); |     furi_assert(request->which_content == PB_Main_app_start_request_tag); | ||||||
|     RpcSession* session = (RpcSession*)context; |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
|  |     char args_temp[16]; | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_D(TAG, "Start"); |     FURI_LOG_D(TAG, "Start"); | ||||||
| 
 | 
 | ||||||
| @ -20,6 +43,11 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
|     const char* app_name = request->content.app_start_request.name; |     const char* app_name = request->content.app_start_request.name; | ||||||
|     if(app_name) { |     if(app_name) { | ||||||
|         const char* app_args = request->content.app_start_request.args; |         const char* app_args = request->content.app_start_request.args; | ||||||
|  |         if(strcmp(app_args, "RPC") == 0) { | ||||||
|  |             // If app is being started in RPC mode - pass RPC context via args string
 | ||||||
|  |             snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); | ||||||
|  |             app_args = args_temp; | ||||||
|  |         } | ||||||
|         LoaderStatus status = loader_start(loader, app_name, app_args); |         LoaderStatus status = loader_start(loader, app_name, app_args); | ||||||
|         if(status == LoaderStatusErrorAppStarted) { |         if(status == LoaderStatusErrorAppStarted) { | ||||||
|             result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; |             result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; | ||||||
| @ -43,8 +71,11 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
| 
 | 
 | ||||||
| static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { | static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { | ||||||
|     furi_assert(request); |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|     furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); |     furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); | ||||||
|     RpcSession* session = (RpcSession*)context; |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_D(TAG, "LockStatus"); |     FURI_LOG_D(TAG, "LockStatus"); | ||||||
| @ -66,13 +97,123 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con | |||||||
|     pb_release(&PB_Main_msg, &response); |     pb_release(&PB_Main_msg, &response); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void rpc_system_app_exit(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_exit_request_tag); | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus status; | ||||||
|  | 
 | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { | ||||||
|  |             status = PB_CommandStatus_OK; | ||||||
|  |             osTimerStop(rpc_app->timer); | ||||||
|  |         } else { | ||||||
|  |             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_system_app_load_file(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_load_file_request_tag); | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus status; | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         const char* file_path = request->content.app_load_file_request.path; | ||||||
|  |         if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { | ||||||
|  |             status = PB_CommandStatus_OK; | ||||||
|  |         } else { | ||||||
|  |             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_system_app_button_press(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_button_press_request_tag); | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus status; | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         const char* args = request->content.app_button_press_request.args; | ||||||
|  |         if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { | ||||||
|  |             status = PB_CommandStatus_OK; | ||||||
|  |             osTimerStart(rpc_app->timer, APP_BUTTON_TIMEOUT); | ||||||
|  |         } else { | ||||||
|  |             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_system_app_button_release(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_button_release_request_tag); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus status; | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { | ||||||
|  |             status = PB_CommandStatus_OK; | ||||||
|  |             osTimerStop(rpc_app->timer); | ||||||
|  |         } else { | ||||||
|  |             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { | ||||||
|  |     furi_assert(rpc_app); | ||||||
|  | 
 | ||||||
|  |     rpc_app->app_callback = callback; | ||||||
|  |     rpc_app->app_context = ctx; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void* rpc_system_app_alloc(RpcSession* session) { | void* rpc_system_app_alloc(RpcSession* session) { | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|  |     RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); | ||||||
|  |     rpc_app->session = session; | ||||||
|  | 
 | ||||||
|  |     rpc_app->timer = osTimerNew(rpc_system_app_timer_callback, osTimerOnce, rpc_app, NULL); | ||||||
|  | 
 | ||||||
|     RpcHandler rpc_handler = { |     RpcHandler rpc_handler = { | ||||||
|         .message_handler = NULL, |         .message_handler = NULL, | ||||||
|         .decode_submessage = NULL, |         .decode_submessage = NULL, | ||||||
|         .context = session, |         .context = rpc_app, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     rpc_handler.message_handler = rpc_system_app_start_process; |     rpc_handler.message_handler = rpc_system_app_start_process; | ||||||
| @ -81,5 +222,31 @@ void* rpc_system_app_alloc(RpcSession* session) { | |||||||
|     rpc_handler.message_handler = rpc_system_app_lock_status_process; |     rpc_handler.message_handler = rpc_system_app_lock_status_process; | ||||||
|     rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); |     rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|     return NULL; |     rpc_handler.message_handler = rpc_system_app_exit; | ||||||
|  |     rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_app_load_file; | ||||||
|  |     rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_app_button_press; | ||||||
|  |     rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_app_button_release; | ||||||
|  |     rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     return rpc_app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_free(void* context) { | ||||||
|  |     RpcAppSystem* rpc_app = context; | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     osTimerDelete(rpc_app->timer); | ||||||
|  | 
 | ||||||
|  |     if(rpc_app->app_callback) { | ||||||
|  |         rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     free(rpc_app); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								applications/rpc/rpc_app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								applications/rpc/rpc_app.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "rpc.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     RpcAppEventSessionClose, | ||||||
|  |     RpcAppEventAppExit, | ||||||
|  |     RpcAppEventLoadFile, | ||||||
|  |     RpcAppEventButtonPress, | ||||||
|  |     RpcAppEventButtonRelease, | ||||||
|  | } RpcAppSystemEvent; | ||||||
|  | 
 | ||||||
|  | typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); | ||||||
|  | 
 | ||||||
|  | typedef struct RpcAppSystem RpcAppSystem; | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -29,6 +29,7 @@ void* rpc_system_system_alloc(RpcSession* session); | |||||||
| void* rpc_system_storage_alloc(RpcSession* session); | void* rpc_system_storage_alloc(RpcSession* session); | ||||||
| void rpc_system_storage_free(void* ctx); | void rpc_system_storage_free(void* ctx); | ||||||
| void* rpc_system_app_alloc(RpcSession* session); | void* rpc_system_app_alloc(RpcSession* session); | ||||||
|  | void rpc_system_app_free(void* ctx); | ||||||
| void* rpc_system_gui_alloc(RpcSession* session); | void* rpc_system_gui_alloc(RpcSession* session); | ||||||
| void rpc_system_gui_free(void* ctx); | void rpc_system_gui_free(void* ctx); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,3 +22,4 @@ ADD_SCENE(subghz, read_raw, ReadRAW) | |||||||
| ADD_SCENE(subghz, more_raw, MoreRAW) | ADD_SCENE(subghz, more_raw, MoreRAW) | ||||||
| ADD_SCENE(subghz, delete_raw, DeleteRAW) | ADD_SCENE(subghz, delete_raw, DeleteRAW) | ||||||
| ADD_SCENE(subghz, need_saving, NeedSaving) | ADD_SCENE(subghz, need_saving, NeedSaving) | ||||||
|  | ADD_SCENE(subghz, rpc, Rpc) | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								applications/subghz/scenes/subghz_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/subghz/scenes/subghz_scene_rpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #include "../subghz_i.h" | ||||||
|  | 
 | ||||||
|  | void subghz_scene_rpc_on_enter(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     Widget* widget = subghz->widget; | ||||||
|  | 
 | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); | ||||||
|  | 
 | ||||||
|  |     notification_message(subghz->notifications, &sequence_display_backlight_on); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         consumed = true; | ||||||
|  |         if(event.event == SubGhzCustomEventSceneExit) { | ||||||
|  |             view_dispatcher_stop(subghz->view_dispatcher); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_scene_rpc_on_exit(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  | 
 | ||||||
|  |     //subghz_rpc_exit_callback(subghz);
 | ||||||
|  | 
 | ||||||
|  |     widget_reset(subghz->widget); | ||||||
|  | } | ||||||
| @ -23,6 +23,54 @@ void subghz_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(subghz->scene_manager); |     scene_manager_handle_tick_event(subghz->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  | 
 | ||||||
|  |     if(!subghz->rpc_ctx) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(event == RpcAppEventSessionClose) { | ||||||
|  |         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||||
|  |         subghz->rpc_ctx = NULL; | ||||||
|  |         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||||
|  |         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |             subghz_tx_stop(subghz); | ||||||
|  |             subghz_sleep(subghz); | ||||||
|  |         } | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventAppExit) { | ||||||
|  |         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||||
|  |         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |             subghz_tx_stop(subghz); | ||||||
|  |             subghz_sleep(subghz); | ||||||
|  |         } | ||||||
|  |         result = true; | ||||||
|  |     } else if(event == RpcAppEventLoadFile) { | ||||||
|  |         if(arg) { | ||||||
|  |             if(subghz_key_load(subghz, arg, false)) { | ||||||
|  |                 string_set_str(subghz->file_path, arg); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if(event == RpcAppEventButtonPress) { | ||||||
|  |         if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||||
|  |             result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||||
|  |         } | ||||||
|  |     } else if(event == RpcAppEventButtonRelease) { | ||||||
|  |         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |             subghz_tx_stop(subghz); | ||||||
|  |             subghz_sleep(subghz); | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| SubGhz* subghz_alloc() { | SubGhz* subghz_alloc() { | ||||||
|     SubGhz* subghz = malloc(sizeof(SubGhz)); |     SubGhz* subghz = malloc(sizeof(SubGhz)); | ||||||
| 
 | 
 | ||||||
| @ -168,6 +216,11 @@ SubGhz* subghz_alloc() { | |||||||
| void subghz_free(SubGhz* subghz) { | void subghz_free(SubGhz* subghz) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
| 
 | 
 | ||||||
|  |     if(subghz->rpc_ctx) { | ||||||
|  |         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||||
|  |         subghz->rpc_ctx = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Packet Test
 |     // Packet Test
 | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); | ||||||
|     subghz_test_packet_free(subghz->subghz_test_packet); |     subghz_test_packet_free(subghz->subghz_test_packet); | ||||||
| @ -265,7 +318,12 @@ int32_t subghz_app(void* p) { | |||||||
|         subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); |         subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); | ||||||
|     // Check argument and run corresponding scene
 |     // Check argument and run corresponding scene
 | ||||||
|     if(p) { |     if(p) { | ||||||
|         if(subghz_key_load(subghz, p)) { |         uint32_t rpc_ctx = 0; | ||||||
|  |         if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { | ||||||
|  |             subghz->rpc_ctx = (void*)rpc_ctx; | ||||||
|  |             rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); | ||||||
|  |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc); | ||||||
|  |         } else if(subghz_key_load(subghz, p, true)) { | ||||||
|             string_set_str(subghz->file_path, p); |             string_set_str(subghz->file_path, p); | ||||||
| 
 | 
 | ||||||
|             if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { |             if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { | ||||||
|  | |||||||
| @ -218,7 +218,7 @@ void subghz_dialog_message_show_only_rx(SubGhz* subghz) { | |||||||
|     dialog_message_free(message); |     dialog_message_free(message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path) { | bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
|     furi_assert(file_path); |     furi_assert(file_path); | ||||||
| 
 | 
 | ||||||
| @ -308,11 +308,15 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
| 
 | 
 | ||||||
|     switch(load_key_state) { |     switch(load_key_state) { | ||||||
|     case SubGhzLoadKeyStateParseErr: |     case SubGhzLoadKeyStateParseErr: | ||||||
|  |         if(show_dialog) { | ||||||
|             dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); |             dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); | ||||||
|  |         } | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     case SubGhzLoadKeyStateOnlyRx: |     case SubGhzLoadKeyStateOnlyRx: | ||||||
|  |         if(show_dialog) { | ||||||
|             subghz_dialog_message_show_only_rx(subghz); |             subghz_dialog_message_show_only_rx(subghz); | ||||||
|  |         } | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     case SubGhzLoadKeyStateOK: |     case SubGhzLoadKeyStateOK: | ||||||
| @ -427,7 +431,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { | |||||||
|         true); |         true); | ||||||
| 
 | 
 | ||||||
|     if(res) { |     if(res) { | ||||||
|         res = subghz_key_load(subghz, string_get_cstr(subghz->file_path)); |         res = subghz_key_load(subghz, string_get_cstr(subghz->file_path), true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(file_path); |     string_clear(file_path); | ||||||
|  | |||||||
| @ -36,6 +36,8 @@ | |||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
| 
 | 
 | ||||||
|  | #include "rpc/rpc_app.h" | ||||||
|  | 
 | ||||||
| #define SUBGHZ_MAX_LEN_NAME 64 | #define SUBGHZ_MAX_LEN_NAME 64 | ||||||
| 
 | 
 | ||||||
| struct SubGhzTxRx { | struct SubGhzTxRx { | ||||||
| @ -91,6 +93,8 @@ struct SubGhz { | |||||||
|     string_t error_str; |     string_t error_str; | ||||||
|     SubGhzSetting* setting; |     SubGhzSetting* setting; | ||||||
|     SubGhzLock lock; |     SubGhzLock lock; | ||||||
|  | 
 | ||||||
|  |     void* rpc_ctx; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool subghz_set_preset(SubGhz* subghz, const char* preset); | bool subghz_set_preset(SubGhz* subghz, const char* preset); | ||||||
| @ -102,7 +106,7 @@ void subghz_sleep(SubGhz* subghz); | |||||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||||
| void subghz_tx_stop(SubGhz* subghz); | void subghz_tx_stop(SubGhz* subghz); | ||||||
| void subghz_dialog_message_show_only_rx(SubGhz* subghz); | void subghz_dialog_message_show_only_rx(SubGhz* subghz); | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path); | bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); | ||||||
| bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); | bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); | ||||||
| bool subghz_save_protocol_to_file( | bool subghz_save_protocol_to_file( | ||||||
|     SubGhz* subghz, |     SubGhz* subghz, | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit ffa62429f3c678537e0e883a3a8c3ae5f1398ed4 | Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nikolay Minaylov
						Nikolay Minaylov