Merge branch 'dev' into keeloq_move_mf_to_keystore

This commit is contained in:
MX
2023-05-31 10:38:28 +03:00
378 changed files with 23484 additions and 8573 deletions

View File

@@ -12,6 +12,6 @@ App(
"dialogs",
],
icon="A_SubGHzRemote_14",
stack_size=4 * 1024,
stack_size=2 * 1024,
order=11,
)

View File

@@ -1,12 +1,14 @@
#pragma once
typedef enum {
//SubmenuIndex
SubmenuIndexSubRemOpenMapFile,
// StartSubmenuIndex
SubmenuIndexSubRemOpenMapFile = 0,
#if FURI_DEBUG
SubmenuIndexSubRemRemoteView,
SubmenuIndexSubRemAbout,
#endif
// SubmenuIndexSubRemAbout,
//SubRemCustomEvent
// SubRemCustomEvent
SubRemCustomEventViewRemoteStartUP = 100,
SubRemCustomEventViewRemoteStartDOWN,
SubRemCustomEventViewRemoteStartLEFT,

View File

@@ -0,0 +1,179 @@
#include "subrem_presets.h"
#define TAG "SubRemPresets"
SubRemSubFilePreset* subrem_sub_file_preset_alloc() {
SubRemSubFilePreset* sub_preset = malloc(sizeof(SubRemSubFilePreset));
sub_preset->fff_data = flipper_format_string_alloc();
sub_preset->file_path = furi_string_alloc();
sub_preset->protocaol_name = furi_string_alloc();
sub_preset->label = furi_string_alloc();
sub_preset->freq_preset.name = furi_string_alloc();
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
return sub_preset;
}
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_free(sub_preset->label);
furi_string_free(sub_preset->protocaol_name);
furi_string_free(sub_preset->file_path);
flipper_format_free(sub_preset->fff_data);
furi_string_free(sub_preset->freq_preset.name);
free(sub_preset);
}
void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_set_str(sub_preset->label, "");
furi_string_reset(sub_preset->protocaol_name);
furi_string_reset(sub_preset->file_path);
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
stream_clean(fff_data_stream);
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
}
SubRemLoadSubState subrem_sub_preset_load(
SubRemSubFilePreset* sub_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file) {
furi_assert(sub_preset);
furi_assert(txrx);
furi_assert(fff_data_file);
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
SubRemLoadSubState ret;
FuriString* temp_str = furi_string_alloc();
uint32_t temp_data32;
uint32_t repeat = 200;
ret = SubRemLoadSubStateError;
do {
stream_clean(fff_data_stream);
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(TAG, "Missing or incorrect header");
break;
}
if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
} else {
FURI_LOG_E(TAG, "Type or version mismatch");
break;
}
SubGhzSetting* setting = subghz_txrx_get_setting(txrx);
//Load frequency or using default from settings
ret = SubRemLoadSubStateErrorFreq;
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency");
sub_preset->freq_preset.frequency = subghz_setting_get_default_frequency(setting);
} else if(!furi_hal_subghz_is_tx_allowed(temp_data32)) {
FURI_LOG_E(TAG, "This frequency can only be used for RX");
break;
}
sub_preset->freq_preset.frequency = temp_data32;
//Load preset
ret = SubRemLoadSubStateErrorMod;
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset");
break;
}
furi_string_set_str(
temp_str, subghz_txrx_get_preset_name(txrx, furi_string_get_cstr(temp_str)));
if(!strcmp(furi_string_get_cstr(temp_str), "")) {
break;
}
if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
FURI_LOG_E(TAG, "CUSTOM preset is not supported");
break;
// TODO Custom preset loading logic if need
// sub_preset->freq_preset.preset_index =
// subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));
}
furi_string_set(sub_preset->freq_preset.name, temp_str);
// Load protocol
ret = SubRemLoadSubStateErrorProtocol;
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
FlipperFormat* fff_data = sub_preset->fff_data;
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
//if RAW
subghz_protocol_raw_gen_fff_data(
fff_data, furi_string_get_cstr(sub_preset->file_path));
} else {
stream_copy_full(
flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(fff_data));
}
if(subghz_txrx_load_decoder_by_name_protocol(txrx, furi_string_get_cstr(temp_str))) {
SubGhzProtocolStatus status =
subghz_protocol_decoder_base_deserialize(subghz_txrx_get_decoder(txrx), fff_data);
if(status != SubGhzProtocolStatusOk) {
break;
}
} else {
FURI_LOG_E(TAG, "Protocol not found");
break;
}
const SubGhzProtocol* protocol = subghz_txrx_get_decoder(txrx)->protocol;
if(protocol->flag & SubGhzProtocolFlag_Send) {
if((protocol->type == SubGhzProtocolTypeStatic) ||
(protocol->type == SubGhzProtocolTypeDynamic) ||
(protocol->type == SubGhzProtocolTypeBinRAW) ||
(protocol->type == SubGhzProtocolTypeRAW)) {
sub_preset->type = protocol->type;
} else {
FURI_LOG_E(TAG, "Unsuported Protocol");
break;
}
furi_string_set(sub_preset->protocaol_name, temp_str);
} else {
FURI_LOG_E(TAG, "Protocol does not support transmission");
break;
}
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubRemLoadSubStateOK;
#if FURI_DEBUG
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
#endif
} while(false);
furi_string_free(temp_str);
sub_preset->load_state = ret;
return ret;
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include "subrem_types.h"
#include "txrx/subghz_txrx.h"
#include <flipper_format/flipper_format_i.h>
#include <lib/subghz/types.h>
typedef struct {
FuriString* name;
uint32_t frequency;
// size_t preset_index; // Need for custom preset
} FreqPreset;
// Sub File preset
typedef struct {
FlipperFormat* fff_data;
FreqPreset freq_preset;
FuriString* file_path;
FuriString* protocaol_name;
FuriString* label;
SubGhzProtocolType type;
SubRemLoadSubState load_state;
} SubRemSubFilePreset;
typedef struct {
SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount];
} SubRemMapPreset;
SubRemSubFilePreset* subrem_sub_file_preset_alloc();
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset);
void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset);
SubRemLoadSubState subrem_sub_preset_load(
SubRemSubFilePreset* sub_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file);

View File

@@ -3,9 +3,8 @@
#include <furi.h>
#include <furi_hal.h>
// TODO: File version/type logic
// #define SUBREM_APP_APP_FILE_VERSION 1
// #define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file"
#define SUBREM_APP_APP_FILE_VERSION 1
#define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file"
#define SUBREM_APP_EXTENSION ".txt"
typedef enum {
@@ -18,17 +17,15 @@ typedef enum {
} SubRemSubKeyName;
typedef enum {
SubRemViewSubmenu,
SubRemViewWidget,
SubRemViewPopup,
SubRemViewTextInput,
SubRemViewIDSubmenu,
SubRemViewIDRemote,
} SubRemViewID;
typedef enum {
SubRemLoadSubStateNotSet,
SubRemLoadSubStateNotSet = 0,
SubRemLoadSubStatePreloaded,
SubRemLoadSubStateError,
SubRemLoadSubStateErrorIncorectPath,
SubRemLoadSubStateErrorNoFile,
SubRemLoadSubStateErrorFreq,
SubRemLoadSubStateErrorMod,

View File

@@ -0,0 +1,4 @@
This is part of the official `SubGhz` app from [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware/tree/3217f286f03da119398586daf94c0723d28b872a/applications/main/subghz)
With changes from [unleashed-firmware
](https://github.com/DarkFlippers/unleashed-firmware/tree/3eac6ccd48a3851cf5d63bf7899b387a293e5319/applications/main/subghz)

View File

@@ -0,0 +1,3 @@
#pragma once
#include "../../../subghz/helpers/subghz_txrx.h"

View File

@@ -1,3 +1,5 @@
#ifndef SUBREM_LIGHT
ADD_SCENE(subrem, start, Start)
ADD_SCENE(subrem, openmapfile, OpenMapFile)
#endif
ADD_SCENE(subrem, open_map_file, OpenMapFile)
ADD_SCENE(subrem, remote, Remote)

View File

@@ -0,0 +1,43 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_open_map_file_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
SubRemLoadMapState load_state = subrem_load_from_file(app);
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
} else {
if(load_state != SubRemLoadMapStateBack) {
#ifdef SUBREM_LIGHT
dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file");
#else
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter);
dialog_message_set_text(
message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, "Back", NULL, NULL);
dialog_message_show(app->dialogs, message);
dialog_message_free(message);
#endif
}
// TODO: Map Preset Reset
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
}
}
bool subrem_scene_open_map_file_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void subrem_scene_open_map_file_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -1,41 +0,0 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_openmapfile_on_enter(void* context) {
SubGhzRemoteApp* app = context;
SubRemLoadMapState load_state = subrem_load_from_file(app);
if(load_state == SubRemLoadMapStateError) {
#ifdef SUBREM_LIGHT
dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file");
#else
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter);
dialog_message_set_text(message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, "Back", NULL, NULL);
dialog_message_show(app->dialogs, message);
dialog_message_free(message);
#endif
}
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
} else {
// TODO: Map Preset Reset
if(!scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SubRemSceneStart)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
}
}
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

@@ -35,25 +35,10 @@ static uint8_t subrem_scene_remote_event_to_index(SubRemCustomEvent event_id) {
return ret;
}
static bool subrem_scene_remote_update_data_show(void* context) {
SubGhzRemoteApp* app = context;
bool ret = false;
subrem_view_remote_add_data_to_show(
app->subrem_remote_view,
furi_string_get_cstr(app->subs_preset[0]->label),
furi_string_get_cstr(app->subs_preset[1]->label),
furi_string_get_cstr(app->subs_preset[2]->label),
furi_string_get_cstr(app->subs_preset[3]->label),
furi_string_get_cstr(app->subs_preset[4]->label));
return ret;
}
void subrem_scene_remote_on_enter(void* context) {
SubGhzRemoteApp* app = context;
subrem_scene_remote_update_data_show(app);
subrem_view_remote_update_data_labels(app->subrem_remote_view, app->map_preset->subs_preset);
subrem_view_remote_set_callback(app->subrem_remote_view, subrem_scene_remote_callback, app);
@@ -64,13 +49,9 @@ bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventViewRemoteBack) {
if(!scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SubRemSceneOpenMapFile)) {
if(!scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SubRemSceneStart)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
return true;
} else if(
@@ -81,32 +62,37 @@ bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) {
event.event == SubRemCustomEventViewRemoteStartOK) {
// Start sending sub
subrem_tx_stop_sub(app, true);
app->chusen_sub = subrem_scene_remote_event_to_index(event.event);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateLoading);
if(subrem_tx_start_sub(
app,
app->subs_preset[app->chusen_sub],
subrem_scene_remote_raw_callback_end_tx)) {
subrem_view_remote_set_presed_btn(app->subrem_remote_view, app->chusen_sub);
uint8_t chusen_sub = subrem_scene_remote_event_to_index(event.event);
app->chusen_sub = chusen_sub;
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateLoading, chusen_sub);
if(subrem_tx_start_sub(app, app->map_preset->subs_preset[chusen_sub])) {
if(app->map_preset->subs_preset[chusen_sub]->type == SubGhzProtocolTypeRAW) {
subghz_txrx_set_raw_file_encoder_worker_callback_end(
app->txrx, subrem_scene_remote_raw_callback_end_tx, app);
}
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateSending);
app->subrem_remote_view, SubRemViewRemoteStateSending, chusen_sub);
notification_message(app->notifications, &sequence_blink_start_magenta);
} else {
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
}
return true;
} else if(event.event == SubRemCustomEventViewRemoteForcedStop) {
subrem_tx_stop_sub(app, true);
subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
return true;
} else if(event.event == SubRemCustomEventViewRemoteStop) {
if(subrem_tx_stop_sub(app, false)) {
subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
}
@@ -123,8 +109,7 @@ void subrem_scene_remote_on_exit(void* context) {
subrem_tx_stop_sub(app, true);
subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
}

View File

@@ -33,13 +33,11 @@ void subrem_scene_start_on_enter(void* context) {
// SubmenuIndexSubGhzRemoteAbout,
// subrem_scene_start_submenu_callback,
// app);
// TODO: set scene state in subrem alloc
// submenu_set_selected_item(
// submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
submenu_set_selected_item(submenu, SubmenuIndexSubRemOpenMapFile);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewSubmenu);
#ifndef SUBREM_LIGHT
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
#endif
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
}
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
@@ -50,6 +48,10 @@ bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSubRemOpenMapFile) {
#ifndef SUBREM_LIGHT
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
#endif
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
consumed = true;
}

View File

@@ -1,7 +1,5 @@
#include "subghz_remote_app_i.h"
#include <lib/subghz/protocols/protocol_items.h>
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
@@ -50,6 +48,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
// 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);
@@ -69,9 +68,9 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewSubmenu, submenu_get_view(app->submenu));
app->view_dispatcher, SubRemViewIDSubmenu, submenu_get_view(app->submenu));
//Dialog
// Dialog
app->dialogs = furi_record_open(RECORD_DIALOGS);
// Remote view
@@ -81,34 +80,21 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
SubRemViewIDRemote,
subrem_view_remote_get_view(app->subrem_remote_view));
app->map_preset = malloc(sizeof(SubRemMapPreset));
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
app->subs_preset[i] = subrem_sub_file_preset_alloc();
app->map_preset->subs_preset[i] = subrem_sub_file_preset_alloc();
}
app->setting = subghz_setting_alloc();
subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
app->txrx = subghz_txrx_alloc();
app->environment = subghz_environment_alloc();
subghz_environment_load_keystore(app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
subghz_environment_load_keystore(
app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
subghz_environment_set_came_atomo_rainbow_table_file_name(
app->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
app->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
app->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry);
app->receiver = subghz_receiver_alloc_init(app->environment);
app->tx_running = false;
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
#ifdef SUBREM_LIGHT
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
#else
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
#endif
return app;
@@ -125,23 +111,25 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewSubmenu);
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
submenu_free(app->submenu);
//Dialog
// Dialog
furi_record_close(RECORD_DIALOGS);
// Remote view
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
subrem_view_remote_free(app->subrem_remote_view);
subghz_receiver_free(app->receiver);
subghz_environment_free(app->environment);
subghz_setting_free(app->setting);
scene_manager_free(app->scene_manager);
view_dispatcher_free(app->view_dispatcher);
subghz_txrx_free(app->txrx);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
subrem_sub_file_preset_free(app->subs_preset[i]);
subrem_sub_file_preset_free(app->map_preset->subs_preset[i]);
}
free(app->map_preset);
// Notifications
furi_record_close(RECORD_NOTIFICATION);

View File

@@ -2,12 +2,15 @@
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/subghz/protocols/protocol_items.h>
#include "helpers/txrx/subghz_txrx.h"
// #include <lib/subghz/protocols/keeloq.h>
// #include <lib/subghz/protocols/star_line.h>
#ifdef APP_SUBGHZREMOTE
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/blocks/custom_btn.h>
#endif
#define TAG "SubGhzRemote"
@@ -19,383 +22,46 @@ static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
};
static bool
subrem_set_preset_data(SubGhzSetting* setting, FreqPreset* freq_preset, const char* preset) {
const char* preset_name = "";
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
preset_name = "AM270";
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
preset_name = "AM650";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name = "FM238";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name = "FM476";
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
// preset_name = "CUSTOM";
return false;
} else {
FURI_LOG_E(TAG, "Unknown preset");
return false;
}
size_t preset_index = subghz_setting_get_inx_preset_by_name(setting, preset_name);
freq_preset->data = subghz_setting_get_preset_data(setting, preset_index);
return true;
}
SubRemSubFilePreset* subrem_sub_file_preset_alloc() {
SubRemSubFilePreset* sub_preset = malloc(sizeof(SubRemSubFilePreset));
sub_preset->fff_data = flipper_format_string_alloc();
sub_preset->file_path = furi_string_alloc();
sub_preset->protocaol_name = furi_string_alloc();
sub_preset->label = furi_string_alloc_set_str("N/A");
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
return sub_preset;
}
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_free(sub_preset->label);
furi_string_free(sub_preset->protocaol_name);
furi_string_free(sub_preset->file_path);
flipper_format_free(sub_preset->fff_data);
free(sub_preset);
}
static void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_set_str(sub_preset->label, "N/A");
furi_string_reset(sub_preset->protocaol_name);
furi_string_reset(sub_preset->file_path);
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
stream_clean(fff_data_stream);
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
}
void subrem_map_preset_reset(SubGhzRemoteApp* app) {
furi_assert(app);
static void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
furi_assert(map_preset);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
subrem_sub_file_preset_reset(app->subs_preset[i]);
subrem_sub_file_preset_reset(map_preset->subs_preset[i]);
}
}
static bool subrem_map_preset_load(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) {
furi_assert(app);
bool ret = false;
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = app->subs_preset[i];
if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][0], sub_preset->file_path)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No file patch for %s", map_file_labels[i][0]);
#endif
sub_preset->type = SubGhzProtocolTypeUnknown;
} else if(!flipper_format_rewind(fff_data_file)) {
// Rewind error
} else if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][1], sub_preset->label)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]);
#endif
path_extract_filename(sub_preset->file_path, sub_preset->label, true);
} else {
// Preload seccesful
FURI_LOG_I(
TAG,
"%-5s: %s %s",
map_file_labels[i][0],
furi_string_get_cstr(sub_preset->label),
furi_string_get_cstr(sub_preset->file_path));
ret = true;
sub_preset->load_state = SubRemLoadSubStatePreloaded;
}
flipper_format_rewind(fff_data_file);
}
return ret;
}
static SubRemLoadMapState subrem_map_preset_check(
SubRemMapPreset* map_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file) {
furi_assert(map_preset);
furi_assert(txrx);
bool subrem_save_protocol_to_file(FlipperFormat* flipper_format, const char* dev_file_name) {
furi_assert(flipper_format);
furi_assert(dev_file_name);
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
bool saved = false;
uint32_t repeat = 200;
FuriString* file_dir = furi_string_alloc();
path_extract_dirname(dev_file_name, file_dir);
do {
// removing additional fields
flipper_format_delete_key(flipper_format, "Repeat");
// flipper_format_delete_key(flipper_format, "Manufacture");
if(!storage_simply_remove(storage, dev_file_name)) {
break;
}
//ToDo check Write
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS);
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
saved = true;
} while(0);
furi_string_free(file_dir);
furi_record_close(RECORD_STORAGE);
return saved;
}
bool subrem_tx_start_sub(
SubGhzRemoteApp* app,
SubRemSubFilePreset* sub_preset,
SubGhzProtocolEncoderRAWCallbackEnd callback) {
furi_assert(app);
furi_assert(sub_preset);
bool ret = false;
subrem_tx_stop_sub(app, true);
if(sub_preset->type == SubGhzProtocolTypeUnknown) {
return false;
}
FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label));
subghz_custom_btns_reset();
do {
flipper_format_rewind(sub_preset->fff_data); //
app->transmitter = subghz_transmitter_alloc_init(
app->environment, furi_string_get_cstr(sub_preset->protocaol_name));
if(app->transmitter) {
if(subghz_transmitter_deserialize(app->transmitter, sub_preset->fff_data) !=
SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Deserialize error!");
break;
}
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(sub_preset->freq_preset.data);
furi_hal_gpio_init(
furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(sub_preset->freq_preset.frequency);
furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
furi_hal_gpio_init(
furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
if(!furi_hal_subghz_tx()) {
FURI_LOG_E(TAG, "Sending not allowed");
break;
}
if(sub_preset->type == SubGhzProtocolTypeRAW) {
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(
app->transmitter),
callback,
app);
}
furi_hal_subghz_start_async_tx(subghz_transmitter_yield, app->transmitter);
ret = true;
}
} while(false);
app->tx_running = ret;
return ret;
}
static void subghz_tx_stop(SubGhzRemoteApp* app) {
furi_assert(app);
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_transmitter_stop(app->transmitter);
subghz_transmitter_free(app->transmitter);
furi_hal_subghz_idle();
}
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
furi_assert(app);
SubRemSubFilePreset* sub_preset = app->subs_preset[app->chusen_sub];
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
// SubRemSubKeyTypeRawKey)) {
if(app->tx_running) {
subghz_tx_stop(app);
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
subrem_save_protocol_to_file(
sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path));
subghz_environment_reset_keeloq(app->environment);
subghz_custom_btns_reset();
}
app->tx_running = false;
return true;
}
}
return false;
}
static SubRemLoadMapState
subrem_map_preset_check(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) {
furi_assert(app);
FuriString* temp_str = furi_string_alloc();
uint32_t temp_data32;
bool all_loaded = true;
SubRemLoadMapState ret = SubRemLoadMapStateErrorBrokenFile;
SubRemLoadSubState sub_preset_loaded;
SubRemLoadSubState sub_loadig_state;
SubRemSubFilePreset* sub_preset;
uint32_t repeat = 200;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = app->subs_preset[i];
sub_preset = map_preset->subs_preset[i];
sub_loadig_state = SubRemLoadSubStateErrorNoFile;
if(furi_string_empty(sub_preset->file_path)) {
// FURI_LOG_I(TAG, "Empty file path");
continue;
} else if(!flipper_format_file_open_existing(
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
sub_preset->load_state = SubRemLoadSubStateErrorNoFile;
FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path));
} else {
sub_loadig_state = subrem_sub_preset_load(sub_preset, txrx, fff_data_file);
}
sub_preset_loaded = SubRemLoadSubStateErrorNoFile;
repeat = 200;
do {
if(!flipper_format_file_open_existing(
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path));
break;
}
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(TAG, "Missing or incorrect header");
break;
}
if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
} else {
FURI_LOG_E(TAG, "Type or version mismatch");
break;
}
//Load frequency
sub_preset_loaded = SubRemLoadSubStateErrorFreq;
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency");
sub_preset->freq_preset.frequency =
subghz_setting_get_default_frequency(app->setting);
} else if(!furi_hal_subghz_is_tx_allowed(temp_data32)) {
FURI_LOG_E(TAG, "This frequency can only be used for RX");
break;
} else {
sub_preset->freq_preset.frequency = temp_data32;
}
//Load preset
sub_preset_loaded = SubRemLoadSubStateErrorMod;
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset");
break;
}
if(!subrem_set_preset_data(
app->setting, &sub_preset->freq_preset, furi_string_get_cstr(temp_str))) {
FURI_LOG_E(TAG, "Cannot load preset.");
break;
}
//Load protocol
sub_preset_loaded = SubRemLoadSubStateErrorProtocol;
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
FlipperFormat* fff_data = sub_preset->fff_data;
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
//if RAW
subghz_protocol_raw_gen_fff_data(
fff_data, furi_string_get_cstr(sub_preset->file_path));
} else {
stream_copy_full(
flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(fff_data));
}
const SubGhzProtocolRegistry* protocol_registry_items =
subghz_environment_get_protocol_registry(app->environment);
const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(
protocol_registry_items, furi_string_get_cstr(temp_str));
if(!protocol) {
FURI_LOG_E(TAG, "Protocol not found");
break;
} else if(protocol->flag & SubGhzProtocolFlag_Send) {
if((protocol->type == SubGhzProtocolTypeStatic) ||
(protocol->type == SubGhzProtocolTypeDynamic) ||
// (protocol->type == SubGhzProtocolTypeBinRAW) || // TODO: BINRAW
(protocol->type == SubGhzProtocolTypeRAW)) {
sub_preset->type = protocol->type;
} else {
FURI_LOG_E(TAG, "Unsuported Protocol");
break;
}
furi_string_set(sub_preset->protocaol_name, temp_str);
} else {
FURI_LOG_E(TAG, "Protocol does not support transmission");
}
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
sub_preset_loaded = SubRemLoadSubStateOK;
ret = SubRemLoadMapStateNotAllOK;
#if FURI_DEBUG
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
#endif
} while(false);
// TODO:
// Load file state logic
// Label depending on the state
// Move to remote scene
if(sub_preset_loaded != SubRemLoadSubStateOK) {
furi_string_set_str(sub_preset->label, "N/A");
if(sub_loadig_state != SubRemLoadSubStateOK) {
all_loaded = false;
} else {
ret = SubRemLoadMapStateNotAllOK;
}
if(ret != SubRemLoadMapStateErrorBrokenFile && all_loaded) {
@@ -404,11 +70,52 @@ static SubRemLoadMapState
flipper_format_file_close(fff_data_file);
}
furi_string_free(temp_str);
return ret;
}
static bool subrem_map_preset_load(SubRemMapPreset* map_preset, FlipperFormat* fff_data_file) {
furi_assert(map_preset);
bool ret = false;
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = map_preset->subs_preset[i];
if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][0], sub_preset->file_path)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No file patch for %s", map_file_labels[i][0]);
#endif
sub_preset->type = SubGhzProtocolTypeUnknown;
} else if(!path_contains_only_ascii(furi_string_get_cstr(sub_preset->file_path))) {
FURI_LOG_E(TAG, "Incorrect characters in [%s] file path", map_file_labels[i][0]);
sub_preset->type = SubGhzProtocolTypeUnknown;
} else if(!flipper_format_rewind(fff_data_file)) {
// Rewind error
} else if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][1], sub_preset->label)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]);
#endif
ret = true;
} else {
ret = true;
}
if(ret) {
// Preload seccesful
FURI_LOG_I(
TAG,
"%-5s: %s %s",
map_file_labels[i][0],
furi_string_get_cstr(sub_preset->label),
furi_string_get_cstr(sub_preset->file_path));
sub_preset->load_state = SubRemLoadSubStatePreloaded;
}
flipper_format_rewind(fff_data_file);
}
return ret;
}
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path) {
furi_assert(app);
furi_assert(file_path);
@@ -421,19 +128,19 @@ SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_p
#if FURI_DEBUG
FURI_LOG_I(TAG, "Open Map File..");
#endif
subrem_map_preset_reset(app);
subrem_map_preset_reset(app->map_preset);
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
ret = SubRemLoadMapStateErrorOpenError;
} else {
if(!subrem_map_preset_load(app, fff_data_file)) {
if(!subrem_map_preset_load(app->map_preset, fff_data_file)) {
FURI_LOG_E(TAG, "Could no Sub file path in MAP file");
// ret = // error for popup
} else if(!flipper_format_file_close(fff_data_file)) {
ret = SubRemLoadMapStateErrorOpenError;
} else {
ret = subrem_map_preset_check(app, fff_data_file);
ret = subrem_map_preset_check(app->map_preset, app->txrx, fff_data_file);
}
}
@@ -452,6 +159,109 @@ SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_p
return ret;
}
bool subrem_save_protocol_to_file(FlipperFormat* flipper_format, const char* sub_file_name) {
furi_assert(flipper_format);
furi_assert(sub_file_name);
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
bool saved = false;
uint32_t repeat = 200;
FuriString* file_dir = furi_string_alloc();
path_extract_dirname(sub_file_name, file_dir);
do {
// removing additional fields
flipper_format_delete_key(flipper_format, "Repeat");
// flipper_format_delete_key(flipper_format, "Manufacture");
if(!storage_simply_remove(storage, sub_file_name)) {
break;
}
//ToDo check Write
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
stream_save_to_file(flipper_format_stream, storage, sub_file_name, FSOM_CREATE_ALWAYS);
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
saved = true;
} while(0);
furi_string_free(file_dir);
furi_record_close(RECORD_STORAGE);
return saved;
}
void subrem_save_active_sub(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
subrem_save_protocol_to_file(
sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path));
}
bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset) {
furi_assert(app);
furi_assert(sub_preset);
bool ret = false;
subrem_tx_stop_sub(app, true);
if(sub_preset->type == SubGhzProtocolTypeUnknown) {
ret = false;
} else {
FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label));
subghz_txrx_load_decoder_by_name_protocol(
app->txrx, furi_string_get_cstr(sub_preset->protocaol_name));
subghz_custom_btns_reset();
subghz_txrx_set_preset(
app->txrx,
furi_string_get_cstr(sub_preset->freq_preset.name),
sub_preset->freq_preset.frequency,
NULL,
0);
#ifdef APP_SUBGHZREMOTE
subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK);
keeloq_reset_original_btn();
subghz_custom_btns_reset();
#endif
if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) {
ret = true;
}
}
return ret;
}
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
furi_assert(app);
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
subghz_txrx_stop(app->txrx);
#ifdef APP_SUBGHZREMOTE
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
subghz_environment_reset_keeloq(app->environment);
}
keeloq_reset_original_btn();
subghz_custom_btns_reset();
#endif
return true;
}
return false;
}
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
furi_assert(app);
@@ -459,7 +269,7 @@ SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
SubRemLoadMapState ret = SubRemLoadMapStateBack;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_sub1_10px);
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_subrem_10px);
browser_options.base_path = SUBREM_APP_FOLDER;
// Input events and views are managed by file_select

View File

@@ -1,54 +1,35 @@
#pragma once
#include "helpers/subrem_types.h"
#include "helpers/subrem_presets.h"
#include "scenes/subrem_scene.h"
#include "helpers/txrx/subghz_txrx.h"
#ifdef APP_SUBGHZREMOTE
#include <assets_icons.h>
#else
#include <subrem_remote_fap_icons.h>
#endif
#include "views/remote.h"
#include "scenes/subrem_scene.h"
#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 <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <storage/storage.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/subghz/protocols/raw.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote")
#define SUBREM_MAX_LEN_NAME 64
typedef struct {
uint32_t frequency;
uint8_t* data;
} FreqPreset;
// Sub File preset
typedef struct {
FlipperFormat* fff_data;
FreqPreset freq_preset;
FuriString* file_path;
FuriString* protocaol_name;
FuriString* label;
SubGhzProtocolType type;
SubRemLoadSubState load_state;
} SubRemSubFilePreset;
SubRemSubFilePreset* subrem_sub_file_preset_alloc();
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset);
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
@@ -56,28 +37,22 @@ typedef struct {
NotificationApp* notifications;
DialogsApp* dialogs;
Submenu* submenu;
FuriString* file_path;
char file_name_tmp[SUBREM_MAX_LEN_NAME];
SubRemViewRemote* subrem_remote_view;
SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount];
SubRemMapPreset* map_preset;
SubGhzSetting* setting;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
bool tx_running;
SubGhzTxRx* txrx;
uint8_t chusen_sub;
} SubGhzRemoteApp;
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app);
bool subrem_tx_start_sub(
SubGhzRemoteApp* app,
SubRemSubFilePreset* sub_preset,
SubGhzProtocolEncoderRAWCallbackEnd callback);
bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset);
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
void subrem_save_active_sub(void* context);

View File

@@ -4,7 +4,11 @@
#include <input/input.h>
#include <gui/elements.h>
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 12
#include <lib/toolbox/path.h>
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 30
#define SUBREM_VIEW_REMOTE_LEFT_OFFSET 10
#define SUBREM_VIEW_REMOTE_RIGHT_OFFSET 22
struct SubRemViewRemote {
View* view;
@@ -12,19 +16,8 @@ struct SubRemViewRemote {
void* context;
};
// 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;
char* labels[SubRemSubKeyNameMaxCount];
SubRemViewRemoteState state;
@@ -41,49 +34,76 @@ void subrem_view_remote_set_callback(
subrem_view_remote->context = context;
}
void subrem_view_remote_add_data_to_show(
void subrem_view_remote_update_data_labels(
SubRemViewRemote* subrem_view_remote,
const char* up_label,
const char* down_label,
const char* left_label,
const char* right_label,
const char* ok_label) {
SubRemSubFilePreset** subs_presets) {
furi_assert(subrem_view_remote);
furi_assert(subs_presets);
FuriString* labels[SubRemSubKeyNameMaxCount];
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = subs_presets[i];
switch(sub_preset->load_state) {
case SubRemLoadSubStateOK:
if(!furi_string_empty(sub_preset->label)) {
labels[i] = furi_string_alloc_set(sub_preset->label);
} else if(!furi_string_empty(sub_preset->file_path)) {
labels[i] = furi_string_alloc();
path_extract_filename(sub_preset->file_path, labels[i], true);
} else {
labels[i] = furi_string_alloc_set("Empty Label");
}
break;
case SubRemLoadSubStateErrorNoFile:
labels[i] = furi_string_alloc_set("[X] Can't open file");
break;
case SubRemLoadSubStateErrorFreq:
case SubRemLoadSubStateErrorMod:
case SubRemLoadSubStateErrorProtocol:
labels[i] = furi_string_alloc_set("[X] Error in .sub file");
break;
default:
labels[i] = furi_string_alloc_set("");
break;
}
}
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);
// 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);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
strncpy(
model->labels[i],
furi_string_get_cstr(labels[i]),
SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
}
},
true);
}
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn) {
furi_assert(subrem_view_remote);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{ model->pressed_btn = presed_btn; },
true);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
furi_string_free(labels[i]);
}
}
void subrem_view_remote_set_state(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteState state) {
SubRemViewRemoteState state,
uint8_t presed_btn) {
furi_assert(subrem_view_remote);
with_view_model(
subrem_view_remote->view, SubRemViewRemoteModel * model, { model->state = state; }, true);
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
model->state = state;
model->pressed_btn = presed_btn;
},
true);
}
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
@@ -103,24 +123,32 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
//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);
uint8_t y = 0;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
elements_text_box(
canvas,
SUBREM_VIEW_REMOTE_LEFT_OFFSET,
y + 2,
126 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET,
12,
AlignLeft,
AlignBottom,
model->labels[i],
false);
y += 10;
}
// 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.");
if(model->state == SubRemViewRemoteStateOFF) {
elements_button_left(canvas, "Back");
elements_button_right(canvas, "Save");
} else {
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
}
//Status text and indicator
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
if(model->state == SubRemViewRemoteStateIdle) {
if(model->state == SubRemViewRemoteStateIdle || model->state == SubRemViewRemoteStateOFF) {
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle");
} else {
switch(model->state) {
@@ -155,10 +183,6 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
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) {
@@ -166,17 +190,6 @@ bool subrem_view_remote_input(InputEvent* event, void* 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");
},
false);
subrem_view_remote->callback(SubRemCustomEventViewRemoteBack, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
@@ -248,23 +261,10 @@ SubRemViewRemote* subrem_view_remote_alloc() {
{
model->state = SubRemViewRemoteStateIdle;
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_set_str("N/A");
// model->down_label = furi_string_alloc_set_str("N/A");
// model->left_label = furi_string_alloc_set_str("N/A");
// model->right_label = furi_string_alloc_set_str("N/A");
// model->ok_label = furi_string_alloc_set_str("N/A");
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
model->labels[i] = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
strcpy(model->labels[i], "");
}
model->pressed_btn = 0;
},
@@ -279,17 +279,9 @@ void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
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);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
free(model->labels[i]);
}
},
true);
view_free(subghz_remote->view);

View File

@@ -2,11 +2,13 @@
#include <gui/view.h>
#include "../helpers/subrem_custom_event.h"
#include "../helpers/subrem_presets.h"
typedef enum {
SubRemViewRemoteStateIdle,
SubRemViewRemoteStateLoading,
SubRemViewRemoteStateSending,
SubRemViewRemoteStateOFF,
} SubRemViewRemoteState;
typedef struct SubRemViewRemote SubRemViewRemote;
@@ -24,15 +26,11 @@ 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(
void subrem_view_remote_update_data_labels(
SubRemViewRemote* subrem_view_remote,
const char* up_label,
const char* down_label,
const char* left_label,
const char* right_label,
const char* ok_label);
SubRemSubFilePreset** subs_presets);
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn);
void subrem_view_remote_set_state(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteState state);
SubRemViewRemoteState state,
uint8_t presed_btn);