From 4a1695ba1cc787c73a6f135c8a352fe300cdea72 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 4 Jul 2022 16:09:46 +0300 Subject: [PATCH] [FL-2589] RPC App control commands (#1350) * RPC App control commands * Button release timeout * SubGhz tx fix Co-authored-by: Aleksandr Kutuzov --- applications/gpio/usb_uart_bridge.c | 2 - applications/ibutton/ibutton.c | 60 +++++- applications/ibutton/ibutton_custom_event.h | 2 + applications/ibutton/ibutton_i.h | 2 + .../ibutton/scenes/ibutton_scene_config.h | 1 + .../ibutton/scenes/ibutton_scene_rpc.c | 36 ++++ applications/infrared/infrared.c | 76 +++++++- applications/infrared/infrared_i.h | 4 + applications/infrared/infrared_remote.c | 13 ++ applications/infrared/infrared_remote.h | 1 + .../infrared/scenes/infrared_scene_config.h | 1 + .../infrared/scenes/infrared_scene_rpc.c | 37 ++++ applications/lfrfid/lfrfid_app.cpp | 63 ++++++- applications/lfrfid/lfrfid_app.h | 9 +- .../lfrfid/scene/lfrfid_app_scene_rpc.cpp | 37 ++++ .../lfrfid/scene/lfrfid_app_scene_rpc.h | 12 ++ applications/nfc/nfc.c | 78 +++++++- applications/nfc/nfc_device.c | 12 +- applications/nfc/nfc_device.h | 2 +- applications/nfc/nfc_i.h | 13 ++ applications/nfc/scenes/nfc_scene_config.h | 1 + applications/nfc/scenes/nfc_scene_rpc.c | 34 ++++ applications/rpc/rpc.c | 2 +- applications/rpc/rpc_app.c | 175 +++++++++++++++++- applications/rpc/rpc_app.h | 24 +++ applications/rpc/rpc_i.h | 1 + .../subghz/scenes/subghz_scene_config.h | 1 + applications/subghz/scenes/subghz_scene_rpc.c | 34 ++++ applications/subghz/subghz.c | 60 +++++- applications/subghz/subghz_i.c | 12 +- applications/subghz/subghz_i.h | 6 +- assets/protobuf | 2 +- 32 files changed, 768 insertions(+), 45 deletions(-) create mode 100644 applications/ibutton/scenes/ibutton_scene_rpc.c create mode 100644 applications/infrared/scenes/infrared_scene_rpc.c create mode 100644 applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp create mode 100644 applications/lfrfid/scene/lfrfid_app_scene_rpc.h mode change 100755 => 100644 applications/nfc/nfc.c create mode 100644 applications/nfc/scenes/nfc_scene_rpc.c create mode 100644 applications/rpc/rpc_app.h create mode 100644 applications/subghz/scenes/subghz_scene_rpc.c diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index cf7e7687..9c6ba0a6 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -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) { furi_hal_usb_unlock(); - FURI_LOG_I("", "Init %d", vcp_ch); if(vcp_ch == 0) { Cli* cli = furi_record_open("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) { UNUSED(usb_uart); furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); - FURI_LOG_I("", "Deinit %d", vcp_ch); if(vcp_ch != 0) { Cli* cli = furi_record_open("cli"); cli_session_close(cli); diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c index ae93f972..0f54dc3e 100644 --- a/applications/ibutton/ibutton.c +++ b/applications/ibutton/ibutton.c @@ -5,6 +5,9 @@ #include "m-string.h" #include #include +#include "rpc/rpc_app.h" + +#define TAG "iButtonApp" static const NotificationSequence sequence_blink_start_cyan = { &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); bool result = false; string_t data; @@ -89,13 +92,40 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) { flipper_format_free(file); string_clear(data); - if(!result) { + if((!result) && (show_dialog)) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); } 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) { furi_assert(context); iButton* ibutton = context; @@ -226,7 +256,7 @@ bool ibutton_file_select(iButton* ibutton) { true); if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path); + success = ibutton_load_key_data(ibutton, ibutton->file_path, true); } return success; @@ -334,16 +364,27 @@ int32_t ibutton_app(void* p) { ibutton_make_app_folder(ibutton); bool key_loaded = false; + bool rpc_mode = false; if(p) { - string_set_str(ibutton->file_path, (const char*)p); - if(ibutton_load_key_data(ibutton, ibutton->file_path)) { - key_loaded = true; - // TODO: Display an error if the key from p could not be loaded + 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); + if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { + key_loaded = true; + // 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); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); @@ -351,6 +392,9 @@ int32_t ibutton_app(void* p) { view_dispatcher_run(ibutton->view_dispatcher); + if(ibutton->rpc_ctx) { + rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + } ibutton_free(ibutton); return 0; } diff --git a/applications/ibutton/ibutton_custom_event.h b/applications/ibutton/ibutton_custom_event.h index 2be42d66..1706e00f 100644 --- a/applications/ibutton/ibutton_custom_event.h +++ b/applications/ibutton/ibutton_custom_event.h @@ -9,4 +9,6 @@ enum iButtonCustomEvent { iButtonCustomEventByteEditResult, iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerRead, + + iButtonCustomEventRpcExit, }; diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h index a85dd5f6..889d5a67 100644 --- a/applications/ibutton/ibutton_i.h +++ b/applications/ibutton/ibutton_i.h @@ -50,6 +50,8 @@ struct iButton { Popup* popup; Widget* widget; DialogEx* dialog_ex; + + void* rpc_ctx; }; typedef enum { diff --git a/applications/ibutton/scenes/ibutton_scene_config.h b/applications/ibutton/scenes/ibutton_scene_config.h index d30b43be..87fa1a03 100644 --- a/applications/ibutton/scenes/ibutton_scene_config.h +++ b/applications/ibutton/scenes/ibutton_scene_config.h @@ -18,3 +18,4 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm) ADD_SCENE(ibutton, delete_success, DeleteSuccess) ADD_SCENE(ibutton, retry_confirm, RetryConfirm) ADD_SCENE(ibutton, exit_confirm, ExitConfirm) +ADD_SCENE(ibutton, rpc, Rpc) diff --git a/applications/ibutton/scenes/ibutton_scene_rpc.c b/applications/ibutton/scenes/ibutton_scene_rpc.c new file mode 100644 index 00000000..ceeca017 --- /dev/null +++ b/applications/ibutton/scenes/ibutton_scene_rpc.c @@ -0,0 +1,36 @@ +#include "../ibutton_i.h" +#include + +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); +} diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c index e641302c..62206116 100644 --- a/applications/infrared/infrared.c +++ b/applications/infrared/infrared.c @@ -36,6 +36,52 @@ static void infrared_tick_event_callback(void* context) { 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) { Storage* storage = furi_record_open("storage"); @@ -154,6 +200,11 @@ static void infrared_free(Infrared* infrared) { ViewDispatcher* view_dispatcher = infrared->view_dispatcher; 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); submenu_free(infrared->submenu); @@ -375,18 +426,29 @@ int32_t infrared_app(void* p) { infrared_make_app_folder(infrared); bool is_remote_loaded = false; + bool is_rpc_mode = false; if(p) { - string_set_str(infrared->file_path, (const char*)p); - is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); - if(!is_remote_loaded) { - dialog_message_show_storage_error( - infrared->dialogs, "Failed to load\nselected remote"); - return -1; + 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); + is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); + if(!is_remote_loaded) { + dialog_message_show_storage_error( + infrared->dialogs, "Failed to load\nselected remote"); + 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); } else { scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h index 5c447adc..c47753f8 100644 --- a/applications/infrared/infrared_i.h +++ b/applications/infrared/infrared_i.h @@ -30,6 +30,8 @@ #include "views/infrared_progress_view.h" #include "views/infrared_debug_view.h" +#include "rpc/rpc_app.h" + #define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_SIZE 128 @@ -95,6 +97,8 @@ struct Infrared { string_t file_path; char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; + + void* rpc_ctx; }; typedef enum { diff --git a/applications/infrared/infrared_remote.c b/applications/infrared/infrared_remote.c index ad75efe7..94658035 100644 --- a/applications/infrared/infrared_remote.c +++ b/applications/infrared/infrared_remote.c @@ -1,5 +1,7 @@ #include "infrared_remote.h" +#include +#include #include #include #include @@ -73,6 +75,17 @@ InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t 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) { InfraredRemoteButton* button = infrared_remote_button_alloc(); infrared_remote_button_set_name(button, name); diff --git a/applications/infrared/infrared_remote.h b/applications/infrared/infrared_remote.h index 1336383f..b6f63a19 100644 --- a/applications/infrared/infrared_remote.h +++ b/applications/infrared/infrared_remote.h @@ -18,6 +18,7 @@ const char* infrared_remote_get_path(InfraredRemote* remote); size_t infrared_remote_get_button_count(InfraredRemote* remote); 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_rename_button(InfraredRemote* remote, const char* new_name, size_t index); diff --git a/applications/infrared/scenes/infrared_scene_config.h b/applications/infrared/scenes/infrared_scene_config.h index c4e083c1..8ff3b167 100644 --- a/applications/infrared/scenes/infrared_scene_config.h +++ b/applications/infrared/scenes/infrared_scene_config.h @@ -16,3 +16,4 @@ ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) +ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/infrared/scenes/infrared_scene_rpc.c b/applications/infrared/scenes/infrared_scene_rpc.c new file mode 100644 index 00000000..3cab9f80 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_rpc.c @@ -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); +} diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index 4027a07e..d2c92d24 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -20,10 +20,13 @@ #include "scene/lfrfid_app_scene_saved_info.h" #include "scene/lfrfid_app_scene_delete_confirm.h" #include "scene/lfrfid_app_scene_delete_success.h" +#include "scene/lfrfid_app_scene_rpc.h" #include #include +#include "rpc/rpc_app.h" + const char* LfRfidApp::app_folder = "/any/lfrfid"; const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_filetype = "Flipper RFID key"; @@ -39,6 +42,43 @@ LfRfidApp::LfRfidApp() LfRfidApp::~LfRfidApp() { 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(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) { @@ -47,10 +87,19 @@ void LfRfidApp::run(void* _args) { make_app_folder(); if(strlen(args)) { - string_set_str(file_path, args); - load_key_data(file_path, &worker.key); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.process(100, SceneType::Emulate); + 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); + load_key_data(file_path, &worker.key, true); + scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); + scene_controller.process(100, SceneType::Emulate); + } + } else { scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); 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); if(result) { - result = load_key_data(file_path, &worker.key); + result = load_key_data(file_path, &worker.key, true); } return result; @@ -110,7 +159,7 @@ bool LfRfidApp::delete_key(RfidKey* key) { 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); bool result = false; string_t str_result; @@ -149,7 +198,7 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { flipper_format_free(file); string_clear(str_result); - if(!result) { + if((!result) && (show_dialog)) { dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); } diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index 3f8209a1..3372552f 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -21,6 +21,7 @@ #include #include "helpers/rfid_worker.h" +#include "rpc/rpc_app.h" class LfRfidApp { public: @@ -30,6 +31,8 @@ public: MenuSelected, Stay, Retry, + Exit, + EmulateStart, }; enum class SceneType : uint8_t { @@ -51,6 +54,7 @@ public: SavedInfo, DeleteConfirm, DeleteSuccess, + Rpc, }; class Event { @@ -79,6 +83,8 @@ public: string_t file_path; + RpcAppSystem* rpc_ctx; + void run(void* args); static const char* app_folder; @@ -89,8 +95,9 @@ public: bool load_key_from_file_select(bool need_restore); 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); void make_app_folder(); + //bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context); }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp new file mode 100644 index 00000000..012c96ac --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -0,0 +1,37 @@ +#include "lfrfid_app_scene_rpc.h" +#include "furi/common_defines.h" +#include + +void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { + auto popup = app->view_controller.get(); + + popup->set_header("RPC Mode", 64, 30, AlignCenter, AlignTop); + + app->view_controller.switch_to(); + + 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()->clean(); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h new file mode 100644 index 00000000..f630dfd3 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRpc : public GenericScene { +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; +}; diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c old mode 100755 new mode 100644 index 1999ba6a..e98b5d9a --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -19,6 +19,77 @@ void nfc_tick_event_callback(void* context) { 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 = malloc(sizeof(Nfc)); @@ -193,7 +264,12 @@ int32_t nfc_app(void* p) { // Check argument and run corresponding scene 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) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index 092d0089..155cc3f8 100644 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -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); } -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; FlipperFormat* file = flipper_format_file_alloc(dev->storage); FuriHalNfcDevData* data = &dev->dev_data.nfc_data; @@ -887,7 +887,7 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { parsed = true; } while(false); - if(!parsed) { + if((!parsed) && (show_dialog)) { if(deprecated_version) { dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); } else { @@ -900,13 +900,13 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { 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(file_path); // Load device data 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) { // Set device name string_t filename; @@ -933,7 +933,7 @@ bool nfc_file_select(NfcDevice* dev) { string_init(filename); path_extract_filename(dev->load_path, filename, true); 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) { nfc_device_set_name(dev, dev->dev_name); } @@ -1017,7 +1017,7 @@ bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { } else { 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; } while(0); diff --git a/applications/nfc/nfc_device.h b/applications/nfc/nfc_device.h index 400f6c36..3b2875c0 100644 --- a/applications/nfc/nfc_device.h +++ b/applications/nfc/nfc_device.h @@ -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_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); diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index cdbe3bea..c42ddced 100755 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -29,10 +29,18 @@ #include #include +#include "rpc/rpc_app.h" + #define NFC_SEND_NOTIFICATION_FALSE (0UL) #define NFC_SEND_NOTIFICATION_TRUE (1UL) #define NFC_TEXT_STORE_SIZE 128 +typedef enum { + NfcRpcStateIdle, + NfcRpcStateEmulating, + NfcRpcStateEmulated, +} NfcRpcState; + // Forward declaration due to circular dependency typedef struct NfcGenerator NfcGenerator; @@ -48,6 +56,9 @@ struct Nfc { char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; + void* rpc_ctx; + NfcRpcState rpc_state; + // Common Views Submenu* submenu; DialogEx* dialog_ex; @@ -85,3 +96,5 @@ void nfc_text_store_clear(Nfc* nfc); void nfc_blink_start(Nfc* nfc); void nfc_blink_stop(Nfc* nfc); + +void nfc_rpc_exit_callback(Nfc* nfc); diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index 5cf2c86f..ffd757de 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -37,4 +37,5 @@ ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) ADD_SCENE(nfc, dict_not_found, DictNotFound) +ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/nfc/scenes/nfc_scene_rpc.c b/applications/nfc/scenes/nfc_scene_rpc.c new file mode 100644 index 00000000..b94bf424 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_rpc.c @@ -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); +} diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index c974a0e5..4832afe3 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -45,7 +45,7 @@ static RpcSystemCallbacks rpc_systems[] = { }, { .alloc = rpc_system_app_alloc, - .free = NULL, + .free = rpc_system_app_free, }, { .alloc = rpc_system_gui_alloc, diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c index 728c2052..f6678c3b 100644 --- a/applications/rpc/rpc_app.c +++ b/applications/rpc/rpc_app.c @@ -1,16 +1,39 @@ +#include "cmsis_os2.h" #include "flipper.pb.h" #include "furi/record.h" #include "rpc_i.h" #include #include +#include "rpc_app.h" #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) { furi_assert(request); + furi_assert(context); + 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); + char args_temp[16]; 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; if(app_name) { 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); if(status == LoaderStatusErrorAppStarted) { 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) { furi_assert(request); + furi_assert(context); + 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_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); } +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) { 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 = { .message_handler = NULL, .decode_submessage = NULL, - .context = session, + .context = rpc_app, }; 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_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); } diff --git a/applications/rpc/rpc_app.h b/applications/rpc/rpc_app.h new file mode 100644 index 00000000..396eef1a --- /dev/null +++ b/applications/rpc/rpc_app.h @@ -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 diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index f84cc991..d0c6cf76 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -29,6 +29,7 @@ void* rpc_system_system_alloc(RpcSession* session); void* rpc_system_storage_alloc(RpcSession* session); void rpc_system_storage_free(void* ctx); 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_free(void* ctx); diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/subghz/scenes/subghz_scene_config.h index 1cb217ad..fd1271c8 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/subghz/scenes/subghz_scene_config.h @@ -22,3 +22,4 @@ ADD_SCENE(subghz, read_raw, ReadRAW) ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) +ADD_SCENE(subghz, rpc, Rpc) diff --git a/applications/subghz/scenes/subghz_scene_rpc.c b/applications/subghz/scenes/subghz_scene_rpc.c new file mode 100644 index 00000000..c7573fda --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_rpc.c @@ -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); +} diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index ade7a844..984ce472 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -23,6 +23,54 @@ void subghz_tick_event_callback(void* context) { 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 = malloc(sizeof(SubGhz)); @@ -168,6 +216,11 @@ SubGhz* subghz_alloc() { void subghz_free(SubGhz* subghz) { furi_assert(subghz); + if(subghz->rpc_ctx) { + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; + } + // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); 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"); // Check argument and run corresponding scene 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); if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 33ceb9bb..0593cc6f 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -218,7 +218,7 @@ void subghz_dialog_message_show_only_rx(SubGhz* subghz) { 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(file_path); @@ -308,11 +308,15 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { switch(load_key_state) { case SubGhzLoadKeyStateParseErr: - dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); + if(show_dialog) { + dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); + } return false; case SubGhzLoadKeyStateOnlyRx: - subghz_dialog_message_show_only_rx(subghz); + if(show_dialog) { + subghz_dialog_message_show_only_rx(subghz); + } return false; case SubGhzLoadKeyStateOK: @@ -427,7 +431,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { true); 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); diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index d75e92a7..0a4bcf0b 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -36,6 +36,8 @@ #include #include +#include "rpc/rpc_app.h" + #define SUBGHZ_MAX_LEN_NAME 64 struct SubGhzTxRx { @@ -91,6 +93,8 @@ struct SubGhz { string_t error_str; SubGhzSetting* setting; SubGhzLock lock; + + void* rpc_ctx; }; 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); void subghz_tx_stop(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_save_protocol_to_file( SubGhz* subghz, diff --git a/assets/protobuf b/assets/protobuf index ffa62429..e3d9cdb6 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit ffa62429f3c678537e0e883a3a8c3ae5f1398ed4 +Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425