New SubGhz Remote GUI

This commit is contained in:
gid9798 2023-05-12 16:47:39 +03:00
parent 3881de74eb
commit fe212228e2
25 changed files with 1320 additions and 0 deletions

View File

@ -0,0 +1,15 @@
App(
appid="subghz_remote_new",
name="SubRem new",
apptype=FlipperAppType.EXTERNAL,
entry_point="subghz_remote_app",
requires=[
"gui",
"dialogs",
],
icon="A_SubGHzRemote_14",
stack_size=4 * 1024,
order=12,
fap_category="Debug",
fap_icon_assets="icons",
)

View File

@ -0,0 +1,67 @@
#pragma once
typedef enum {
// SubRemCustomEventManagerNoSet = 0,
// SubRemCustomEventManagerSet,
// SubRemCustomEventManagerSetRAW,
//SubmenuIndex
SubmenuIndexOpenMapFile,
SubmenuIndexOpenView, // TODO: temp debug
//SubRemCustomEvent
SubRemCustomEventViewRemoteBack = 100,
// SubRemCustomEventSceneDeleteSuccess = 100,
// SubRemCustomEventSceneDelete,
// SubRemCustomEventSceneDeleteRAW,
// SubRemCustomEventSceneDeleteRAWBack,
// SubRemCustomEventSceneReceiverInfoTxStart,
// SubRemCustomEventSceneReceiverInfoTxStop,
// SubRemCustomEventSceneReceiverInfoSave,
// SubRemCustomEventSceneSaveName,
// SubRemCustomEventSceneSaveSuccess,
// SubRemCustomEventSceneShowErrorBack,
// SubRemCustomEventSceneShowErrorOk,
// SubRemCustomEventSceneShowErrorSub,
// SubRemCustomEventSceneShowOnlyRX,
// SubRemCustomEventSceneAnalyzerLock,
// SubRemCustomEventSceneAnalyzerUnlock,
// SubRemCustomEventSceneSettingLock,
// SubRemCustomEventSceneExit,
// SubRemCustomEventSceneStay,
// SubRemCustomEventSceneRpcLoad,
// SubRemCustomEventSceneRpcButtonPress,
// SubRemCustomEventSceneRpcButtonRelease,
// SubRemCustomEventSceneRpcSessionClose,
// SubRemCustomEventViewReceiverOK,
// SubRemCustomEventViewReceiverConfig,
// SubRemCustomEventViewReceiverBack,
// SubRemCustomEventViewReceiverOffDisplay,
// SubRemCustomEventViewReceiverUnlock,
// SubRemCustomEventViewReceiverDeleteItem,
// SubRemCustomEventViewReadRAWBack,
// SubRemCustomEventViewReadRAWIDLE,
// SubRemCustomEventViewReadRAWREC,
// SubRemCustomEventViewReadRAWConfig,
// SubRemCustomEventViewReadRAWErase,
// SubRemCustomEventViewReadRAWSendStart,
// SubRemCustomEventViewReadRAWSendStop,
// SubRemCustomEventViewReadRAWSave,
// SubRemCustomEventViewReadRAWTXRXStop,
// SubRemCustomEventViewReadRAWMore,
// SubRemCustomEventViewTransmitterBack,
// SubRemCustomEventViewTransmitterSendStart,
// SubRemCustomEventViewTransmitterSendStop,
// SubRemCustomEventViewTransmitterError,
// SubRemCustomEventViewFreqAnalOkShort,
// SubRemCustomEventViewFreqAnalOkLong,
// SubRemCustomEventByteInputDone,
} SubRemCustomEvent;

View File

@ -0,0 +1,39 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
/*
#define AVR_ISP_VERSION_APP "0.1"
#define AVR_ISP_DEVELOPED "SkorP"
#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
#define AVR_ISP_APP_FILE_VERSION 1
#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR"
#define AVR_ISP_APP_EXTENSION ".avr"
*/
// TODO: rename Filepath
//#define SUBREMOTEMAP_FOLDER "/ext/subghz_remote"
#define SUBGHZ_REMOTE_APP_EXTENSION ".txt"
#define SUBGHZ_REMOTE_APP_PATH_PREFIX "/ext/subghz_remote"
typedef enum {
//SubRemViewVariableItemList,
SubRemViewSubmenu,
//SubRemViewProgrammer,
//SubRemViewReader,
//SubRemViewWriter,
SubRemViewWidget,
SubRemViewPopup,
SubRemViewTextInput,
SubRemViewIDRemote,
//SubRemViewChipDetect,
} SubRemViewID;
/*
typedef enum {
SubRemErrorNoError,
SubRemErrorReading,
SubRemErrorWriting,
SubRemErrorVerification,
SubRemErrorWritingFuse,
} SubRemError;*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

View File

@ -0,0 +1,30 @@
#include "../subghz_remote_app_i.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const subrem_scene_on_enter_handlers[])(void*) = {
#include "subrem_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const subrem_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "subrem_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const subrem_scene_on_exit_handlers[])(void* context) = {
#include "subrem_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers subrem_scene_handlers = {
.on_enter_handlers = subrem_scene_on_enter_handlers,
.on_event_handlers = subrem_scene_on_event_handlers,
.on_exit_handlers = subrem_scene_on_exit_handlers,
.scene_num = SubRemSceneNum,
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) SubRemScene##id,
typedef enum {
#include "subrem_scene_config.h"
SubRemSceneNum,
} SubRemScene;
#undef ADD_SCENE
extern const SceneManagerHandlers subrem_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "subrem_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "subrem_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "subrem_scene_config.h"
#undef ADD_SCENE

View File

@ -0,0 +1,3 @@
ADD_SCENE(subrem, start, Start)
ADD_SCENE(subrem, openmapfile, OpenMapFile)
ADD_SCENE(subrem, remote, Remote)

View File

@ -0,0 +1,26 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_openmapfile_on_enter(void* context) {
SubGhzRemoteApp* app = context;
if(subrem_load_from_file(app)) {
// if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {
// subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
// scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
// } else {
// scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu);
// }
} else {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SubRemSceneStart);
}
}
bool subrem_scene_openmapfile_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void subrem_scene_openmapfile_on_exit(void* context) {
UNUSED(context);
}

View File

@ -0,0 +1,135 @@
#include "../subghz_remote_app_i.h"
#include "../views/transmitter.h"
// TODO:
// #include <lib/subghz/protocols/keeloq.h>
// #include <lib/subghz/protocols/star_line.h>
// #include <lib/subghz/blocks/custom_btn.h>
void subrem_scene_remote_callback(SubRemCustomEvent event, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
bool subrem_scene_remote_update_data_show(void* context) {
SubGhzRemoteApp* app = context;
//UNUSED(app);
bool ret = false;
subrem_view_remote_add_data_to_show(
//app->subrem_remote_view, "N/A", "N/A", "N/A", "N/A", "N/A");
app->subrem_remote_view,
"UP",
"DOWN",
"LEFT",
"RIGHT",
"OK");
// SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(app->txrx);
// if(decoder) {
// FuriString* key_str = furi_string_alloc();
// FuriString* frequency_str = furi_string_alloc();
// FuriString* modulation_str = furi_string_alloc();
// if(subghz_protocol_decoder_base_deserialize(
// decoder, subghz_txrx_get_fff_data(app->txrx)) == SubGhzProtocolStatusOk) {
// subghz_protocol_decoder_base_get_string(decoder, key_str);
// subghz_txrx_get_frequency_and_modulation(
// app->txrx, frequency_str, modulation_str, false);
// subghz_view_transmitter_add_data_to_show(
// app->subghz_transmitter,
// furi_string_get_cstr(key_str),
// furi_string_get_cstr(frequency_str),
// furi_string_get_cstr(modulation_str),
// subghz_txrx_protocol_is_transmittable(app->txrx, false));
// ret = true;
// }
// furi_string_free(frequency_str);
// furi_string_free(modulation_str);
// furi_string_free(key_str);
// }
return ret;
}
void subrem_scene_remote_on_enter(void* context) {
SubGhzRemoteApp* app = context;
// TODO: reset custom btns
// keeloq_reset_original_btn();
// subghz_custom_btns_reset();
// TODO: init view data
if(!subrem_scene_remote_update_data_show(app)) {
// view_dispatcher_send_custom_event(
// app->view_dispatcher, SubGhzCustomEventViewTransmitterError);
}
subrem_view_remote_set_callback(app->subrem_remote_view, subrem_scene_remote_callback, app);
// TODO: notifications
// app->state_notifications = SubGhzNotificationStateIDLE;
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDRemote);
}
bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
// if(event.event == SubGhzCustomEventViewTransmitterSendStart) {
// app->state_notifications = SubGhzNotificationStateIDLE;
// if(subghz_tx_start(app, subghz_txrx_get_fff_data(app->txrx))) {
// app->state_notifications = SubGhzNotificationStateTx;
// subrem_scene_remote_update_data_show(app);
// DOLPHIN_DEED(DolphinDeedSubGhzSend);
// }
// return true;
// } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {
// app->state_notifications = SubGhzNotificationStateIDLE;
// subghz_txrx_stop(app->txrx);
// if(subghz_custom_btn_get() != 0) {
// subghz_custom_btn_set(0);
// uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
// furi_hal_subghz_set_rolling_counter_mult(0);
// // Calling restore!
// subghz_tx_start(app, subghz_txrx_get_fff_data(app->txrx));
// subghz_txrx_stop(app->txrx);
// furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
// }
// return true;
// } else
if(event.event == SubRemCustomEventViewRemoteBack) {
// app->state_notifications = SubGhzNotificationStateIDLE; //TODO: notification
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SubRemSceneStart);
return true;
}
// else if(event.event == SubGhzCustomEventViewTransmitterError) {
// furi_string_set(app->error_str, "Protocol not\nfound!");
// scene_manager_next_scene(app->scene_manager, SubGhzSceneShowErrorSub);
// }
} else if(event.type == SceneManagerEventTypeTick) {
// if(app->state_notifications == SubGhzNotificationStateTx) {
// notification_message(app->notifications, &sequence_blink_magenta_10);
// }
// return true;
}
return false;
}
void subrem_scene_remote_on_exit(void* context) {
SubGhzRemoteApp* app = context;
UNUSED(app);
// TODO: notifications and reset KL
//app->state_notifications = SubGhzNotificationStateIDLE;
// keeloq_reset_mfname();
// keeloq_reset_kl_type();
// keeloq_reset_original_btn();
// subghz_custom_btns_reset();
// star_line_reset_mfname();
// star_line_reset_kl_type();
}

View File

@ -0,0 +1,93 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
void subrem_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void subrem_scene_start_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Open Map File",
SubmenuIndexOpenMapFile,
subrem_scene_start_submenu_callback,
app);
submenu_add_item(
submenu, "Remote", SubmenuIndexOpenView, subrem_scene_start_submenu_callback, app);
// submenu_add_item(
// submenu,
// "ISP Programmer",
// SubmenuIndexSubGhzRemoteProgrammer,
// subrem_scene_start_submenu_callback,
// app);
// submenu_add_item(
// submenu,
// "Wiring",
// SubmenuIndexAvrIsWiring,
// subrem_scene_start_submenu_callback,
// app);
// submenu_add_item(
// submenu,
// "About",
// SubmenuIndexSubGhzRemoteAbout,
// subrem_scene_start_submenu_callback,
// app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewSubmenu);
}
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexOpenMapFile) {
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
consumed = true;
} else if(event.event == SubmenuIndexOpenView) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
consumed = true;
}
// } else if(event.event == SubmenuIndexSubGhzRemoteProgrammer) {
// scene_manager_set_scene_state(
// app->scene_manager, SubRemSceneChipDetect, SubGhzRemoteViewProgrammer);
// scene_manager_next_scene(app->scene_manager, SubRemSceneChipDetect);
// consumed = true;
// } else if(event.event == SubmenuIndexSubGhzRemoteReader) {
// scene_manager_set_scene_state(
// app->scene_manager, SubRemSceneChipDetect, SubGhzRemoteViewReader);
// scene_manager_next_scene(app->scene_manager, SubRemSceneChipDetect);
// consumed = true;
// } else if(event.event == SubmenuIndexSubGhzRemoteWriter) {
// scene_manager_set_scene_state(
// app->scene_manager, SubRemSceneChipDetect, SubGhzRemoteViewWriter);
// scene_manager_next_scene(app->scene_manager, SubRemSceneChipDetect);
// consumed = true;
// } else if(event.event == SubmenuIndexAvrIsWiring) {
// scene_manager_next_scene(app->scene_manager, SubRemSceneWiring);
// consumed = true;
// }
scene_manager_set_scene_state(app->scene_manager, SubRemSceneStart, event.event);
}
return consumed;
}
void subrem_scene_start_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -0,0 +1,181 @@
#include "subghz_remote_app_i.h"
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool subghz_remote_app_back_event_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void subghz_remote_app_tick_event_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
// // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
// uint8_t attempts = 0;
// while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
// furi_hal_power_enable_otg();
// furi_delay_ms(10);
// }
app->file_path = furi_string_alloc();
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
//app->error = SubGhzRemoteErrorNoError;
// GUI
app->gui = furi_record_open(RECORD_GUI);
// View Dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&subrem_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, subghz_remote_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, subghz_remote_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, subghz_remote_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewSubmenu, submenu_get_view(app->submenu));
// Widget
app->widget = widget_alloc();
view_dispatcher_add_view(app->view_dispatcher, SubRemViewWidget, widget_get_view(app->widget));
// Text Input
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewTextInput, text_input_get_view(app->text_input));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, SubRemViewPopup, popup_get_view(app->popup));
//Dialog
app->dialogs = furi_record_open(RECORD_DIALOGS);
// Remote view
app->subrem_remote_view = subrem_view_remote_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewIDRemote,
subrem_view_remote_get_view(app->subrem_remote_view));
/*
// Reader view
app->subghz_remote_reader_view = subghz_remote_reader_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewReader,
subghz_remote_reader_view_get_view(app->subghz_remote_reader_view));
// Writer view
app->subghz_remote_writer_view = subghz_remote_writer_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewWriter,
subghz_remote_writer_view_get_view(app->subghz_remote_writer_view));
// Chip detect view
app->subghz_remote_chip_detect_view = subghz_remote_chip_detect_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewChipDetect,
subghz_remote_chip_detect_view_get_view(app->subghz_remote_chip_detect_view));
*/
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
return app;
}
void subghz_remote_app_free(SubGhzRemoteApp* app) {
furi_assert(app);
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewSubmenu);
submenu_free(app->submenu);
// Widget
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewWidget);
widget_free(app->widget);
// TextInput
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewTextInput);
text_input_free(app->text_input);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewPopup);
popup_free(app->popup);
//Dialog
furi_record_close(RECORD_DIALOGS);
// Remote view
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
subrem_view_remote_free(app->subrem_remote_view);
// // Reader view
// view_dispatcher_remove_view(app->view_dispatcher, SubRemViewReader);
// subghz_remote_reader_view_free(app->subghz_remote_reader_view);
// // Writer view
// view_dispatcher_remove_view(app->view_dispatcher, SubRemViewWriter);
// subghz_remote_writer_view_free(app->subghz_remote_writer_view);
// // Chip detect view
// view_dispatcher_remove_view(app->view_dispatcher, SubRemViewChipDetect);
// subghz_remote_chip_detect_view_free(app->subghz_remote_chip_detect_view);
// // View dispatcher
// view_dispatcher_free(app->view_dispatcher);
// scene_manager_free(app->scene_manager);
// Notifications
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Close records
furi_record_close(RECORD_GUI);
// Path strings
furi_string_free(app->file_path);
// Disable 5v power
// if(furi_hal_power_is_otg_enabled()) {
// furi_hal_power_disable_otg();
// }
free(app);
}
int32_t subghz_remote_app(void* p) {
UNUSED(p);
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
view_dispatcher_run(subghz_remote_app->view_dispatcher);
subghz_remote_app_free(subghz_remote_app);
return 0;
}

View File

@ -0,0 +1,28 @@
#include "subghz_remote_app_i.h"
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format_i.h>
#define TAG "SubGhzRemote"
bool subrem_load_from_file(SubGhzRemoteApp* app) {
furi_assert(app);
FuriString* file_path = furi_string_alloc();
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_sub1_10px);
browser_options.base_path = SUBREM_APP_FOLDER;
// Input events and views are managed by file_select
bool res =
dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
if(res) {
// res = subghz_key_load(app, furi_string_get_cstr(app->file_path), true);
res = false;
}
furi_string_free(file_path);
return res;
}

View File

@ -0,0 +1,53 @@
#pragma once
#include "helpers/subrem_types.h"
#include "views/transmitter.h"
#include "scenes/subrem_scene.h"
#include <subghz_remote_new_icons.h> // TODO:
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/widget.h>
#include <notification/notification_messages.h>
#include <gui/modules/text_input.h>
#include <dialogs/dialogs.h>
#include <storage/storage.h>
#include <gui/modules/popup.h>
// #include "views/subghz_remote_view_programmer.h"
// #include "views/subghz_remote_view_reader.h"
// #include "views/subghz_remote_view_writer.h"
// #include "views/subghz_remote_view_chip_detect.h"
#define SUBREM_APP_EXTENSION ".txt"
#define SUBREM_APP_FOLDER "/ext/subghz_remote"
#define SUBGHZ_REMOTE_MAX_LEN_NAME 64
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Popup* popup;
Submenu* submenu;
Widget* widget;
TextInput* text_input;
FuriString* file_path;
char file_name_tmp[SUBGHZ_REMOTE_MAX_LEN_NAME];
SubRemViewRemote* subrem_remote_view;
// AvrIspProgrammerView* subghz_remote_programmer_view;
// AvrIspReaderView* subghz_remote_reader_view;
// AvrIspWriterView* subghz_remote_writer_view;
// AvrIspChipDetectView* subghz_remote_chip_detect_view;
// AvrIspError error;
} SubGhzRemoteApp;
bool subrem_load_from_file(SubGhzRemoteApp* app);

View File

@ -0,0 +1,277 @@
#include "transmitter.h"
#include "../subghz_remote_app_i.h"
#include <input/input.h>
#include <gui/elements.h>
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 16
struct SubRemViewRemote {
View* view;
SubRemViewRemoteCallback callback;
void* context;
};
// FIXME: drop
// static char* char_to_str(char* str, int i) {
// char* converted = malloc(sizeof(char) * i + 1);
// memcpy(converted, str, i);
// converted[i] = '\0';
// return converted;
// }
// TODO: model
typedef struct {
// FuriString* up_label;
// FuriString* down_label;
// FuriString* left_label;
// FuriString* right_label;
// FuriString* ok_label;
char* up_label;
char* down_label;
char* left_label;
char* right_label;
char* ok_label;
uint8_t pressed_btn;
// bool show_button;
// FuriString* temp_button_id;
// bool draw_temp_button;
} SubRemViewRemoteModel;
void subrem_view_remote_set_callback(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteCallback callback,
void* context) {
furi_assert(subrem_view_remote);
subrem_view_remote->callback = callback;
subrem_view_remote->context = context;
}
void subrem_view_remote_add_data_to_show(
SubRemViewRemote* subrem_view_remote,
const char* up_label,
const char* down_label,
const char* left_label,
const char* right_label,
const char* ok_label) {
furi_assert(subrem_view_remote);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
strncpy(model->up_label, up_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
strncpy(model->down_label, down_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
strncpy(model->left_label, left_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
strncpy(model->right_label, right_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
strncpy(model->ok_label, ok_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
// model->up_label = char_to_str((char*)up_label, 16);
// model->down_label = char_to_str((char*)down_label, 16);
// model->left_label = char_to_str((char*)left_label, 16);
// model->right_label = char_to_str((char*)right_label, 16);
// model->ok_label = char_to_str((char*)ok_label, 16);
// furi_string_set(model->up_label, up_label);
// furi_string_set(model->down_label, down_label);
// furi_string_set(model->left_label, left_label);
// furi_string_set(model->right_label, right_label);
// furi_string_set(model->ok_label, ok_label);
},
true);
}
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
//map found, draw all the things
canvas_clear(canvas);
//canvas_set_font(canvas, FontPrimary);
//canvas_draw_str(canvas, 0, 10, "U: ");
//canvas_draw_str(canvas, 0, 20, "L: ");
//canvas_draw_str(canvas, 0, 30, "R: ");
//canvas_draw_str(canvas, 0, 40, "D: ");
//canvas_draw_str(canvas, 0, 50, "Ok: ");
//PNGs are located in assets/icons/SubGHzRemote before compilation
//Icons for Labels
//canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64);
canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7);
canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 0, 53, &I_back_10px);
//Labels
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 10, 10, model->up_label);
canvas_draw_str(canvas, 10, 20, model->down_label);
canvas_draw_str(canvas, 10, 30, model->left_label);
canvas_draw_str(canvas, 10, 40, model->right_label);
canvas_draw_str(canvas, 10, 50, model->ok_label);
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
//Status text and indicator
// canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, app->send_status);
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
switch(model->pressed_btn) {
case 0:
break;
case 1:
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9);
break;
case 2:
canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180);
break;
case 3:
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90);
break;
case 4:
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270);
break;
case 5:
canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7);
break;
}
//Repeat indicator
//canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:");
//canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14);
//canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat));
}
bool subrem_view_remote_input(InputEvent* event, void* context) {
furi_assert(context);
SubRemViewRemote* subrem_view_remote = context;
if(event->key == InputKeyBack && event->type == InputTypeLong) {
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
strcpy(model->up_label, "N/A");
strcpy(model->down_label, "N/A");
strcpy(model->left_label, "N/A");
strcpy(model->right_label, "N/A");
strcpy(model->ok_label, "N/A");
// furi_string_reset(model->up_label);
// furi_string_reset(model->down_label);
// furi_string_reset(model->left_label);
// furi_string_reset(model->right_label);
// furi_string_reset(model->ok_label);
},
false);
return false;
} else if(event->key == InputKeyUp) {
if(event->type == InputTypePress) {
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{ model->pressed_btn = 1; },
true);
return true;
} else if(event->type == InputTypeRelease) {
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{ model->pressed_btn = 0; },
true);
return true;
}
}
return true;
}
void subrem_view_remote_enter(void* context) {
furi_assert(context);
}
void subrem_view_remote_exit(void* context) {
furi_assert(context);
}
SubRemViewRemote* subrem_view_remote_alloc() {
SubRemViewRemote* subrem_view_remote = malloc(sizeof(SubRemViewRemote));
// View allocation and configuration
subrem_view_remote->view = view_alloc();
view_allocate_model(
subrem_view_remote->view, ViewModelTypeLocking, sizeof(SubRemViewRemoteModel));
view_set_context(subrem_view_remote->view, subrem_view_remote);
view_set_draw_callback(subrem_view_remote->view, (ViewDrawCallback)subrem_view_remote_draw);
view_set_input_callback(subrem_view_remote->view, subrem_view_remote_input);
view_set_enter_callback(subrem_view_remote->view, subrem_view_remote_enter);
view_set_exit_callback(subrem_view_remote->view, subrem_view_remote_exit);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
model->up_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
model->down_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
model->left_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
model->right_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
model->ok_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
strcpy(model->up_label, "N/A");
strcpy(model->down_label, "N/A");
strcpy(model->left_label, "N/A");
strcpy(model->right_label, "N/A");
strcpy(model->ok_label, "N/A");
// model->up_label = furi_string_alloc();
// model->down_label = furi_string_alloc();
// model->left_label = furi_string_alloc();
// model->right_label = furi_string_alloc();
// model->ok_label = furi_string_alloc();
model->pressed_btn = 0;
},
true);
return subrem_view_remote;
}
void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
furi_assert(subghz_remote);
with_view_model(
subghz_remote->view,
SubRemViewRemoteModel * model,
{
free(model->up_label);
free(model->down_label);
free(model->left_label);
free(model->right_label);
free(model->ok_label);
// furi_string_free(model->up_label);
// furi_string_free(model->down_label);
// furi_string_free(model->left_label);
// furi_string_free(model->right_label);
// furi_string_free(model->ok_label);
},
true);
view_free(subghz_remote->view);
free(subghz_remote);
}
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote) {
furi_assert(subrem_view_remote);
return subrem_view_remote->view;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subrem_custom_event.h"
typedef struct SubRemViewRemote SubRemViewRemote;
typedef void (*SubRemViewRemoteCallback)(SubRemCustomEvent event, void* context);
void subrem_view_remote_set_callback(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteCallback callback,
void* context);
SubRemViewRemote* subrem_view_remote_alloc();
void subrem_view_remote_free(SubRemViewRemote* subrem_view_remote);
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote);
void subrem_view_remote_add_data_to_show(
SubRemViewRemote* subrem_view_remote,
const char* up_label,
const char* down_label,
const char* left_label,
const char* right_label,
const char* ok_label);

View File

@ -0,0 +1,317 @@
#include "transmitter.h"
#include "../subghz_remote_app_i.h"
#include <input/input.h>
#include <gui/elements.h>
#include <lib/subghz/blocks/custom_btn.h>
struct SubGhzRemoteViewRemote {
View* view;
SubGhzRemoteViewRemoteCallback callback;
void* context;
};
typedef struct {
FuriString* frequency_str;
FuriString* preset_str;
FuriString* key_str;
// bool show_button;
// FuriString* temp_button_id;
// bool draw_temp_button;
} SubGhzRemoteViewRemoteModel;
void subghz_view_transmitter_set_callback(
SubGhzRemoteViewRemote* subghz_transmitter,
SubGhzRemoteViewRemoteCallback callback,
void* context) {
furi_assert(subghz_transmitter);
subghz_transmitter->callback = callback;
subghz_transmitter->context = context;
}
void subghz_view_transmitter_add_data_to_show(
SubGhzRemoteViewRemote* subghz_transmitter,
const char* key_str,
const char* frequency_str,
const char* preset_str,
bool show_button) {
furi_assert(subghz_transmitter);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_set(model->key_str, key_str);
furi_string_set(model->frequency_str, frequency_str);
furi_string_set(model->preset_str, preset_str);
model->show_button = show_button;
},
true);
}
static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) {
const uint8_t button_height = 12;
const uint8_t vertical_offset = 3;
const uint8_t horizontal_offset = 1;
const uint8_t string_width = canvas_string_width(canvas, str);
const Icon* icon = &I_ButtonCenter_7x7;
const uint8_t icon_offset = 3;
const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset;
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40;
const uint8_t y = canvas_height(canvas);
canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0);
canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1);
canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2);
canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);
canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);
canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);
canvas_invert_color(canvas);
canvas_draw_icon(
canvas,
x + horizontal_offset,
y - button_height + vertical_offset - 1,
&I_ButtonCenter_7x7);
canvas_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas);
}
void subghz_view_transmitter_draw(Canvas* canvas, SubGhzRemoteViewRemoteModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str));
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
// if(model->draw_temp_button) {
// canvas_set_font(canvas, FontBatteryPercent);
// canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id));
// canvas_set_font(canvas, FontSecondary);
// }
// if(model->show_button) {
// canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
// subghz_view_transmitter_button_right(canvas, "Send");
// }
}
bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzRemoteViewRemote* subghz_transmitter = context;
bool can_be_sent = false;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str);
furi_string_reset(model->key_str);
furi_string_reset(model->temp_button_id);
model->show_button = false;
model->draw_temp_button = false;
},
false);
return false;
}
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
if(model->show_button) {
can_be_sent = true;
}
},
true);
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
subghz_custom_btn_set(0);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->temp_button_id);
model->draw_temp_button = false;
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Temp Buttons (UP)
if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
subghz_custom_btn_set(1);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->temp_button_id);
if(subghz_custom_btn_get_original() != 0) {
if(subghz_custom_btn_get() == 1) {
furi_string_printf(
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
model->draw_temp_button = true;
}
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Down
if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) {
subghz_custom_btn_set(2);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->temp_button_id);
if(subghz_custom_btn_get_original() != 0) {
if(subghz_custom_btn_get() == 2) {
furi_string_printf(
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
model->draw_temp_button = true;
}
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Left
if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) {
subghz_custom_btn_set(3);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->temp_button_id);
if(subghz_custom_btn_get_original() != 0) {
if(subghz_custom_btn_get() == 3) {
furi_string_printf(
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
model->draw_temp_button = true;
}
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Right
if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) {
subghz_custom_btn_set(4);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_reset(model->temp_button_id);
if(subghz_custom_btn_get_original() != 0) {
if(subghz_custom_btn_get() == 4) {
furi_string_printf(
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
model->draw_temp_button = true;
}
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
return true;
}
void subghz_view_transmitter_enter(void* context) {
furi_assert(context);
}
void subghz_view_transmitter_exit(void* context) {
furi_assert(context);
}
SubGhzRemoteViewRemote* subghz_view_transmitter_alloc() {
SubGhzRemoteViewRemote* subghz_transmitter = malloc(sizeof(SubGhzRemoteViewRemote));
// View allocation and configuration
subghz_transmitter->view = view_alloc();
view_allocate_model(
subghz_transmitter->view, ViewModelTypeLocking, sizeof(SubGhzRemoteViewRemoteModel));
view_set_context(subghz_transmitter->view, subghz_transmitter);
view_set_draw_callback(
subghz_transmitter->view, (ViewDrawCallback)subghz_view_transmitter_draw);
view_set_input_callback(subghz_transmitter->view, subghz_view_transmitter_input);
view_set_enter_callback(subghz_transmitter->view, subghz_view_transmitter_enter);
view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
model->frequency_str = furi_string_alloc();
model->preset_str = furi_string_alloc();
model->key_str = furi_string_alloc();
model->temp_button_id = furi_string_alloc();
},
true);
return subghz_transmitter;
}
void subghz_view_transmitter_free(SubGhzRemoteViewRemote* subghz_transmitter) {
furi_assert(subghz_transmitter);
with_view_model(
subghz_transmitter->view,
SubGhzRemoteViewRemoteModel * model,
{
furi_string_free(model->frequency_str);
furi_string_free(model->preset_str);
furi_string_free(model->key_str);
furi_string_free(model->temp_button_id);
},
true);
view_free(subghz_transmitter->view);
free(subghz_transmitter);
}
View* subghz_view_transmitter_get_view(SubGhzRemoteViewRemote* subghz_transmitter) {
furi_assert(subghz_transmitter);
return subghz_transmitter->view;
}