Merge branch 'subrem' into subrem_configurator
|
@ -3,17 +3,14 @@ App(
|
||||||
name="Sub-GHz Remote",
|
name="Sub-GHz Remote",
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
entry_point="subghz_remote_app",
|
entry_point="subghz_remote_app",
|
||||||
cdefines=[
|
|
||||||
"APP_SUBGHZREMOTE",
|
|
||||||
"SUBREM_LIGHT",
|
|
||||||
],
|
|
||||||
requires=[
|
requires=[
|
||||||
"gui",
|
"gui",
|
||||||
"dialogs",
|
"dialogs",
|
||||||
],
|
],
|
||||||
icon="A_SubGHzRemote_14",
|
stack_size=3 * 1024,
|
||||||
stack_size=4 * 1024,
|
|
||||||
order=50,
|
order=50,
|
||||||
fap_category="Debug",
|
fap_description="Remote control for transmission multiple *.sub files",
|
||||||
|
fap_category="Debug", #"Sub-Ghz"
|
||||||
fap_icon_assets="icons",
|
fap_icon_assets="icons",
|
||||||
|
fap_icon="icons/subrem_10px.png",
|
||||||
)
|
)
|
|
@ -1,11 +1,25 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
//SubmenuIndex
|
SubRemEditMenuStateUP = 0,
|
||||||
|
SubRemEditMenuStateDOWN,
|
||||||
|
SubRemEditMenuStateLEFT,
|
||||||
|
SubRemEditMenuStateRIGHT,
|
||||||
|
SubRemEditMenuStateOK,
|
||||||
|
} SubRemEditMenuState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// SubmenuIndex
|
||||||
|
SubmenuIndexSubRemEditMapFile = 0,
|
||||||
|
SubmenuIndexSubRemNewMapFile,
|
||||||
SubmenuIndexSubRemRemoteView,
|
SubmenuIndexSubRemRemoteView,
|
||||||
SubmenuIndexSubRemAbout,
|
SubmenuIndexSubRemAbout,
|
||||||
|
|
||||||
//SubRemCustomEvent
|
// EditSubmenuIndex
|
||||||
|
EditSubmenuIndexEditLabel,
|
||||||
|
EditSubmenuIndexEditFile,
|
||||||
|
|
||||||
|
// SubRemCustomEvent
|
||||||
SubRemCustomEventViewRemoteStartUP = 100,
|
SubRemCustomEventViewRemoteStartUP = 100,
|
||||||
SubRemCustomEventViewRemoteStartDOWN,
|
SubRemCustomEventViewRemoteStartDOWN,
|
||||||
SubRemCustomEventViewRemoteStartLEFT,
|
SubRemCustomEventViewRemoteStartLEFT,
|
||||||
|
@ -14,4 +28,25 @@ typedef enum {
|
||||||
SubRemCustomEventViewRemoteBack,
|
SubRemCustomEventViewRemoteBack,
|
||||||
SubRemCustomEventViewRemoteStop,
|
SubRemCustomEventViewRemoteStop,
|
||||||
SubRemCustomEventViewRemoteForcedStop,
|
SubRemCustomEventViewRemoteForcedStop,
|
||||||
|
|
||||||
|
SubRemCustomEventViewEditMenuBack,
|
||||||
|
SubRemCustomEventViewEditMenuUP,
|
||||||
|
SubRemCustomEventViewEditMenuDOWN,
|
||||||
|
SubRemCustomEventViewEditMenuEdit,
|
||||||
|
SubRemCustomEventViewEditMenuSave,
|
||||||
|
|
||||||
|
SubRemCustomEventSceneEditsubmenu,
|
||||||
|
SubRemCustomEventSceneEditLabelInputDone,
|
||||||
|
SubRemCustomEventSceneEditLabelWidgetAcces,
|
||||||
|
SubRemCustomEventSceneEditLabelWidgetBack,
|
||||||
|
|
||||||
|
SubRemCustomEventSceneEditOpenSubErrorPopup,
|
||||||
|
|
||||||
|
SubRemCustomEventSceneEditPreviewSaved,
|
||||||
|
|
||||||
|
SubRemCustomEventSceneNewName,
|
||||||
|
|
||||||
|
// // SceneStates
|
||||||
|
// SubRemSceneOpenMapFileStateOpen,
|
||||||
|
// SubRemSceneOpenMapFileStateEdit,
|
||||||
} SubRemCustomEvent;
|
} SubRemCustomEvent;
|
180
applications/external/subghz_remote_new/helpers/subrem_presets.c
vendored
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
#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) ||
|
||||||
|
// TODO: BINRAW It probably works, but checks are needed.
|
||||||
|
// (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;
|
||||||
|
}
|
39
applications/external/subghz_remote_new/helpers/subrem_presets.h
vendored
Normal 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);
|
|
@ -3,9 +3,8 @@
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
// TODO: File version/type logic
|
#define SUBREM_APP_APP_FILE_VERSION 1
|
||||||
// #define SUBREM_APP_APP_FILE_VERSION 1
|
#define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file"
|
||||||
// #define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file"
|
|
||||||
#define SUBREM_APP_EXTENSION ".txt"
|
#define SUBREM_APP_EXTENSION ".txt"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -18,17 +17,19 @@ typedef enum {
|
||||||
} SubRemSubKeyName;
|
} SubRemSubKeyName;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SubRemViewSubmenu,
|
SubRemViewIDSubmenu,
|
||||||
SubRemViewWidget,
|
SubRemViewIDWidget,
|
||||||
SubRemViewPopup,
|
SubRemViewIDPopup,
|
||||||
SubRemViewTextInput,
|
SubRemViewIDTextInput,
|
||||||
SubRemViewIDRemote,
|
SubRemViewIDRemote,
|
||||||
|
SubRemViewIDEditMenu,
|
||||||
} SubRemViewID;
|
} SubRemViewID;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SubRemLoadSubStateNotSet,
|
SubRemLoadSubStateNotSet = 0,
|
||||||
SubRemLoadSubStatePreloaded,
|
SubRemLoadSubStatePreloaded,
|
||||||
SubRemLoadSubStateError,
|
SubRemLoadSubStateError,
|
||||||
|
SubRemLoadSubStateErrorIncorectPath,
|
||||||
SubRemLoadSubStateErrorNoFile,
|
SubRemLoadSubStateErrorNoFile,
|
||||||
SubRemLoadSubStateErrorFreq,
|
SubRemLoadSubStateErrorFreq,
|
||||||
SubRemLoadSubStateErrorMod,
|
SubRemLoadSubStateErrorMod,
|
||||||
|
|
4
applications/external/subghz_remote_new/helpers/txrx/Readme.md
vendored
Normal 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)
|
563
applications/external/subghz_remote_new/helpers/txrx/subghz_txrx.c
vendored
Normal file
|
@ -0,0 +1,563 @@
|
||||||
|
#include "subghz_txrx_i.h"
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
|
||||||
|
#define TAG "SubGhz"
|
||||||
|
|
||||||
|
SubGhzTxRx* subghz_txrx_alloc() {
|
||||||
|
SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));
|
||||||
|
instance->setting = subghz_setting_alloc();
|
||||||
|
subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user"));
|
||||||
|
|
||||||
|
instance->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
|
instance->preset->name = furi_string_alloc();
|
||||||
|
subghz_txrx_set_preset(
|
||||||
|
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
|
||||||
|
|
||||||
|
instance->txrx_state = SubGhzTxRxStateSleep;
|
||||||
|
|
||||||
|
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
|
||||||
|
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
|
||||||
|
subghz_txrx_set_debug_pin_state(instance, false);
|
||||||
|
|
||||||
|
instance->worker = subghz_worker_alloc();
|
||||||
|
instance->fff_data = flipper_format_string_alloc();
|
||||||
|
|
||||||
|
instance->environment = subghz_environment_alloc();
|
||||||
|
instance->is_database_loaded = subghz_environment_load_keystore(
|
||||||
|
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
|
||||||
|
subghz_environment_load_keystore(
|
||||||
|
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
|
||||||
|
subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||||
|
instance->environment, EXT_PATH("subghz/assets/came_atomo"));
|
||||||
|
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||||
|
instance->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
|
||||||
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
|
instance->environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
||||||
|
subghz_environment_set_protocol_registry(
|
||||||
|
instance->environment, (void*)&subghz_protocol_registry);
|
||||||
|
instance->receiver = subghz_receiver_alloc_init(instance->environment);
|
||||||
|
|
||||||
|
subghz_worker_set_overrun_callback(
|
||||||
|
instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||||
|
subghz_worker_set_pair_callback(
|
||||||
|
instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||||
|
subghz_worker_set_context(instance->worker, instance->receiver);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_free(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
subghz_worker_free(instance->worker);
|
||||||
|
subghz_receiver_free(instance->receiver);
|
||||||
|
subghz_environment_free(instance->environment);
|
||||||
|
flipper_format_free(instance->fff_data);
|
||||||
|
furi_string_free(instance->preset->name);
|
||||||
|
subghz_setting_free(instance->setting);
|
||||||
|
free(instance->preset);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->is_database_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_set_preset(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint8_t* preset_data,
|
||||||
|
size_t preset_data_size) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_string_set(instance->preset->name, preset_name);
|
||||||
|
SubGhzRadioPreset* preset = instance->preset;
|
||||||
|
preset->frequency = frequency;
|
||||||
|
preset->data = preset_data;
|
||||||
|
preset->data_size = preset_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {
|
||||||
|
UNUSED(instance);
|
||||||
|
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";
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Unknown preset");
|
||||||
|
}
|
||||||
|
return preset_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return *instance->preset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_get_frequency_and_modulation(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
FuriString* frequency,
|
||||||
|
FuriString* modulation,
|
||||||
|
bool long_name) {
|
||||||
|
furi_assert(instance);
|
||||||
|
SubGhzRadioPreset* preset = instance->preset;
|
||||||
|
if(frequency != NULL) {
|
||||||
|
furi_string_printf(
|
||||||
|
frequency,
|
||||||
|
"%03ld.%02ld",
|
||||||
|
preset->frequency / 1000000 % 1000,
|
||||||
|
preset->frequency / 10000 % 100);
|
||||||
|
}
|
||||||
|
if(modulation != NULL) {
|
||||||
|
if(long_name) {
|
||||||
|
furi_string_printf(modulation, "%s", furi_string_get_cstr(preset->name));
|
||||||
|
} else {
|
||||||
|
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_hal_subghz_reset();
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
furi_hal_subghz_load_custom_preset(preset_data);
|
||||||
|
furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
|
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
|
||||||
|
furi_crash("SubGhz: Incorrect RX frequency.");
|
||||||
|
}
|
||||||
|
furi_assert(
|
||||||
|
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
|
||||||
|
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
|
||||||
|
furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
|
furi_hal_subghz_flush_rx();
|
||||||
|
subghz_txrx_speaker_on(instance);
|
||||||
|
furi_hal_subghz_rx();
|
||||||
|
|
||||||
|
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker);
|
||||||
|
subghz_worker_start(instance->worker);
|
||||||
|
instance->txrx_state = SubGhzTxRxStateRx;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subghz_txrx_idle(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
subghz_txrx_speaker_off(instance);
|
||||||
|
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(instance->txrx_state == SubGhzTxRxStateRx);
|
||||||
|
|
||||||
|
if(subghz_worker_is_running(instance->worker)) {
|
||||||
|
subghz_worker_stop(instance->worker);
|
||||||
|
furi_hal_subghz_stop_async_rx();
|
||||||
|
}
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
subghz_txrx_speaker_off(instance);
|
||||||
|
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_sleep(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_hal_subghz_sleep();
|
||||||
|
instance->txrx_state = SubGhzTxRxStateSleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
|
||||||
|
furi_crash("SubGhz: Incorrect TX frequency.");
|
||||||
|
}
|
||||||
|
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
furi_hal_subghz_set_frequency_and_path(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);
|
||||||
|
bool ret = furi_hal_subghz_tx();
|
||||||
|
if(ret) {
|
||||||
|
subghz_txrx_speaker_on(instance);
|
||||||
|
instance->txrx_state = SubGhzTxRxStateTx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(flipper_format);
|
||||||
|
|
||||||
|
subghz_txrx_stop(instance);
|
||||||
|
|
||||||
|
SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
uint32_t repeat = 200;
|
||||||
|
do {
|
||||||
|
if(!flipper_format_rewind(flipper_format)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Protocol");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable Repeat");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = SubGhzTxRxStartTxStateOk;
|
||||||
|
|
||||||
|
SubGhzRadioPreset* preset = instance->preset;
|
||||||
|
instance->transmitter =
|
||||||
|
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
if(instance->transmitter) {
|
||||||
|
if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==
|
||||||
|
SubGhzProtocolStatusOk) {
|
||||||
|
if(strcmp(furi_string_get_cstr(preset->name), "") != 0) {
|
||||||
|
subghz_txrx_begin(
|
||||||
|
instance,
|
||||||
|
subghz_setting_get_preset_data_by_name(
|
||||||
|
instance->setting, furi_string_get_cstr(preset->name)));
|
||||||
|
if(preset->frequency) {
|
||||||
|
if(!subghz_txrx_tx(instance, preset->frequency)) {
|
||||||
|
FURI_LOG_E(TAG, "Only Rx");
|
||||||
|
ret = SubGhzTxRxStartTxStateErrorOnlyRx;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(
|
||||||
|
TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name));
|
||||||
|
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ret == SubGhzTxRxStartTxStateOk) {
|
||||||
|
//Start TX
|
||||||
|
furi_hal_subghz_start_async_tx(
|
||||||
|
subghz_transmitter_yield, instance->transmitter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||||
|
}
|
||||||
|
if(ret != SubGhzTxRxStartTxStateOk) {
|
||||||
|
subghz_transmitter_free(instance->transmitter);
|
||||||
|
if(instance->txrx_state != SubGhzTxRxStateIDLE) {
|
||||||
|
subghz_txrx_idle(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
subghz_txrx_stop(instance);
|
||||||
|
subghz_txrx_begin(
|
||||||
|
instance,
|
||||||
|
subghz_setting_get_preset_data_by_name(
|
||||||
|
subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));
|
||||||
|
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_set_need_save_callback(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzTxRxNeedSaveCallback callback,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(instance);
|
||||||
|
instance->need_save_callback = callback;
|
||||||
|
instance->need_save_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(instance->txrx_state == SubGhzTxRxStateTx);
|
||||||
|
//Stop TX
|
||||||
|
furi_hal_subghz_stop_async_tx();
|
||||||
|
subghz_transmitter_stop(instance->transmitter);
|
||||||
|
subghz_transmitter_free(instance->transmitter);
|
||||||
|
|
||||||
|
//if protocol dynamic then we save the last upload
|
||||||
|
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
|
||||||
|
if(instance->need_save_callback) {
|
||||||
|
instance->need_save_callback(instance->need_save_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subghz_txrx_idle(instance);
|
||||||
|
subghz_txrx_speaker_off(instance);
|
||||||
|
//Todo: Show message
|
||||||
|
// notification_message(notifications, &sequence_reset_red);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->fff_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_stop(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
switch(instance->txrx_state) {
|
||||||
|
case SubGhzTxRxStateTx:
|
||||||
|
subghz_txrx_tx_stop(instance);
|
||||||
|
subghz_txrx_speaker_unmute(instance);
|
||||||
|
break;
|
||||||
|
case SubGhzTxRxStateRx:
|
||||||
|
subghz_txrx_rx_end(instance);
|
||||||
|
subghz_txrx_speaker_mute(instance);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
switch(instance->hopper_state) {
|
||||||
|
case SubGhzHopperStateOFF:
|
||||||
|
case SubGhzHopperStatePause:
|
||||||
|
return;
|
||||||
|
case SubGhzHopperStateRSSITimeOut:
|
||||||
|
if(instance->hopper_timeout != 0) {
|
||||||
|
instance->hopper_timeout--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
float rssi = -127.0f;
|
||||||
|
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
|
||||||
|
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||||
|
rssi = furi_hal_subghz_get_rssi();
|
||||||
|
|
||||||
|
// Stay if RSSI is high enough
|
||||||
|
if(rssi > -90.0f) {
|
||||||
|
instance->hopper_timeout = 10;
|
||||||
|
instance->hopper_state = SubGhzHopperStateRSSITimeOut;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->hopper_state = SubGhzHopperStateRunning;
|
||||||
|
}
|
||||||
|
// Select next frequency
|
||||||
|
if(instance->hopper_idx_frequency <
|
||||||
|
subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {
|
||||||
|
instance->hopper_idx_frequency++;
|
||||||
|
} else {
|
||||||
|
instance->hopper_idx_frequency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance->txrx_state == SubGhzTxRxStateRx) {
|
||||||
|
subghz_txrx_rx_end(instance);
|
||||||
|
};
|
||||||
|
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
|
||||||
|
subghz_receiver_reset(instance->receiver);
|
||||||
|
instance->preset->frequency =
|
||||||
|
subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);
|
||||||
|
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->hopper_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {
|
||||||
|
furi_assert(instance);
|
||||||
|
instance->hopper_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->hopper_state == SubGhzHopperStatePause) {
|
||||||
|
instance->hopper_state = SubGhzHopperStateRunning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->hopper_state == SubGhzHopperStateRunning) {
|
||||||
|
instance->hopper_state = SubGhzHopperStatePause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_speaker_on(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||||
|
if(furi_hal_speaker_acquire(30)) {
|
||||||
|
if(!instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->speaker_state = SubGhzSpeakerStateDisable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||||
|
}
|
||||||
|
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
|
||||||
|
if(furi_hal_speaker_is_mine()) {
|
||||||
|
if(!instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||||
|
}
|
||||||
|
furi_hal_speaker_release();
|
||||||
|
if(instance->speaker_state == SubGhzSpeakerStateShutdown)
|
||||||
|
instance->speaker_state = SubGhzSpeakerStateDisable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_speaker_mute(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||||
|
}
|
||||||
|
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||||
|
if(furi_hal_speaker_is_mine()) {
|
||||||
|
if(!instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton);
|
||||||
|
}
|
||||||
|
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||||
|
if(furi_hal_speaker_is_mine()) {
|
||||||
|
if(!instance->debug_pin_state) {
|
||||||
|
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {
|
||||||
|
furi_assert(instance);
|
||||||
|
instance->speaker_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->speaker_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(name_protocol);
|
||||||
|
bool res = false;
|
||||||
|
instance->decoder_result =
|
||||||
|
subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);
|
||||||
|
if(instance->decoder_result) {
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->decoder_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return (
|
||||||
|
(instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==
|
||||||
|
SubGhzProtocolFlag_Save);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {
|
||||||
|
furi_assert(instance);
|
||||||
|
const SubGhzProtocol* protocol = instance->decoder_result->protocol;
|
||||||
|
if(check_type) {
|
||||||
|
return (
|
||||||
|
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
|
||||||
|
protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
|
||||||
|
protocol->encoder->deserialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {
|
||||||
|
furi_assert(instance);
|
||||||
|
subghz_receiver_set_filter(instance->receiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_set_rx_calback(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzReceiverCallback callback,
|
||||||
|
void* context) {
|
||||||
|
subghz_receiver_set_rx_callback(instance->receiver, callback, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzProtocolEncoderRAWCallbackEnd callback,
|
||||||
|
void* context) {
|
||||||
|
subghz_protocol_raw_file_encoder_worker_set_callback_end(
|
||||||
|
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),
|
||||||
|
callback,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state) {
|
||||||
|
furi_assert(instance);
|
||||||
|
instance->debug_pin_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->debug_pin_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->receiver;
|
||||||
|
}
|
318
applications/external/subghz_remote_new/helpers/txrx/subghz_txrx.h
vendored
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lib/subghz/subghz_worker.h>
|
||||||
|
#include <lib/subghz/subghz_setting.h>
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/transmitter.h>
|
||||||
|
#include <lib/subghz/protocols/raw.h>
|
||||||
|
|
||||||
|
typedef struct SubGhzTxRx SubGhzTxRx;
|
||||||
|
|
||||||
|
typedef void (*SubGhzTxRxNeedSaveCallback)(void* context);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubGhzTxRxStartTxStateOk,
|
||||||
|
SubGhzTxRxStartTxStateErrorOnlyRx,
|
||||||
|
SubGhzTxRxStartTxStateErrorParserOthers,
|
||||||
|
} SubGhzTxRxStartTxState;
|
||||||
|
|
||||||
|
// Type from subghz_types.h need for txrx working
|
||||||
|
/** SubGhzTxRx state */
|
||||||
|
typedef enum {
|
||||||
|
SubGhzTxRxStateIDLE,
|
||||||
|
SubGhzTxRxStateRx,
|
||||||
|
SubGhzTxRxStateTx,
|
||||||
|
SubGhzTxRxStateSleep,
|
||||||
|
} SubGhzTxRxState;
|
||||||
|
|
||||||
|
/** SubGhzHopperState state */
|
||||||
|
typedef enum {
|
||||||
|
SubGhzHopperStateOFF,
|
||||||
|
SubGhzHopperStateRunning,
|
||||||
|
SubGhzHopperStatePause,
|
||||||
|
SubGhzHopperStateRSSITimeOut,
|
||||||
|
} SubGhzHopperState;
|
||||||
|
|
||||||
|
/** SubGhzSpeakerState state */
|
||||||
|
typedef enum {
|
||||||
|
SubGhzSpeakerStateDisable,
|
||||||
|
SubGhzSpeakerStateShutdown,
|
||||||
|
SubGhzSpeakerStateEnable,
|
||||||
|
} SubGhzSpeakerState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate SubGhzTxRx
|
||||||
|
*
|
||||||
|
* @return SubGhzTxRx* pointer to SubGhzTxRx
|
||||||
|
*/
|
||||||
|
SubGhzTxRx* subghz_txrx_alloc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free SubGhzTxRx
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_free(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the database is loaded
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return bool True if the database is loaded
|
||||||
|
*/
|
||||||
|
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set preset
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param preset_name Name of preset
|
||||||
|
* @param frequency Frequency in Hz
|
||||||
|
* @param preset_data Data of preset
|
||||||
|
* @param preset_data_size Size of preset data
|
||||||
|
*/
|
||||||
|
void subghz_txrx_set_preset(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint8_t* preset_data,
|
||||||
|
size_t preset_data_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of preset
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param preset String of preset
|
||||||
|
* @return const char* Name of preset
|
||||||
|
*/
|
||||||
|
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get of preset
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return SubGhzRadioPreset Preset
|
||||||
|
*/
|
||||||
|
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string frequency and modulation
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param frequency Pointer to a string frequency
|
||||||
|
* @param modulation Pointer to a string modulation
|
||||||
|
*/
|
||||||
|
void subghz_txrx_get_frequency_and_modulation(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
FuriString* frequency,
|
||||||
|
FuriString* modulation,
|
||||||
|
bool long_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start TX CC1101
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat
|
||||||
|
* @return SubGhzTxRxStartTxState
|
||||||
|
*/
|
||||||
|
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start RX CC1101
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_rx_start(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop TX/RX CC1101
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_stop(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set sleep mode CC1101
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_sleep(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update frequency CC1101 in automatic mode (hopper)
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_hopper_update(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get state hopper
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return SubGhzHopperState
|
||||||
|
*/
|
||||||
|
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set state hopper
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param state State hopper
|
||||||
|
*/
|
||||||
|
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpause hopper
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set pause hopper
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaker on
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_speaker_on(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaker off
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_speaker_off(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaker mute
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_speaker_mute(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaker unmute
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
*/
|
||||||
|
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set state speaker
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param state State speaker
|
||||||
|
*/
|
||||||
|
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get state speaker
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return SubGhzSpeakerState
|
||||||
|
*/
|
||||||
|
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load decoder by name protocol
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param name_protocol Name protocol
|
||||||
|
* @return bool True if the decoder is loaded
|
||||||
|
*/
|
||||||
|
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get decoder
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase
|
||||||
|
*/
|
||||||
|
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set callback for save data
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param callback Callback for save data
|
||||||
|
* @param context Context for callback
|
||||||
|
*/
|
||||||
|
void subghz_txrx_set_need_save_callback(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzTxRxNeedSaveCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get pointer to a load data key
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return FlipperFormat*
|
||||||
|
*/
|
||||||
|
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get pointer to a SugGhzSetting
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return SubGhzSetting*
|
||||||
|
*/
|
||||||
|
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it possible to save this protocol
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return bool True if it is possible to save this protocol
|
||||||
|
*/
|
||||||
|
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it possible to send this protocol
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @return bool True if it is possible to send this protocol
|
||||||
|
*/
|
||||||
|
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set filter, what types of decoder to use
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param filter Filter
|
||||||
|
*/
|
||||||
|
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set callback for receive data
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param callback Callback for receive data
|
||||||
|
* @param context Context for callback
|
||||||
|
*/
|
||||||
|
void subghz_txrx_set_rx_calback(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzReceiverCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set callback for Raw decoder, end of data transfer
|
||||||
|
*
|
||||||
|
* @param instance Pointer to a SubGhzTxRx
|
||||||
|
* @param callback Callback for Raw decoder, end of data transfer
|
||||||
|
* @param context Context for callback
|
||||||
|
*/
|
||||||
|
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
|
||||||
|
SubGhzTxRx* instance,
|
||||||
|
SubGhzProtocolEncoderRAWCallbackEnd callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state);
|
||||||
|
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance);
|
||||||
|
|
||||||
|
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw
|
29
applications/external/subghz_remote_new/helpers/txrx/subghz_txrx_i.h
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "subghz_txrx.h"
|
||||||
|
|
||||||
|
struct SubGhzTxRx {
|
||||||
|
SubGhzWorker* worker;
|
||||||
|
|
||||||
|
SubGhzEnvironment* environment;
|
||||||
|
SubGhzReceiver* receiver;
|
||||||
|
SubGhzTransmitter* transmitter;
|
||||||
|
SubGhzProtocolDecoderBase* decoder_result;
|
||||||
|
FlipperFormat* fff_data;
|
||||||
|
|
||||||
|
SubGhzRadioPreset* preset;
|
||||||
|
SubGhzSetting* setting;
|
||||||
|
|
||||||
|
uint8_t hopper_timeout;
|
||||||
|
uint8_t hopper_idx_frequency;
|
||||||
|
bool is_database_loaded;
|
||||||
|
SubGhzHopperState hopper_state;
|
||||||
|
|
||||||
|
SubGhzTxRxState txrx_state;
|
||||||
|
SubGhzSpeakerState speaker_state;
|
||||||
|
|
||||||
|
SubGhzTxRxNeedSaveCallback need_save_callback;
|
||||||
|
void* need_save_context;
|
||||||
|
|
||||||
|
bool debug_pin_state;
|
||||||
|
};
|
BIN
applications/external/subghz_remote_new/icons/DolphinNice_96x59.png
vendored
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 154 B After Width: | Height: | Size: 154 B |
BIN
applications/external/subghz_remote_new/icons/subrem_10px.png
vendored
Normal file
After Width: | Height: | Size: 4.9 KiB |
|
@ -1,2 +1,8 @@
|
||||||
ADD_SCENE(subrem, start, Start)
|
ADD_SCENE(subrem, start, Start)
|
||||||
ADD_SCENE(subrem, openmapfile, OpenMapFile)
|
ADD_SCENE(subrem, open_map_file, OpenMapFile)
|
||||||
|
ADD_SCENE(subrem, edit_menu, EditMenu)
|
||||||
|
ADD_SCENE(subrem, edit_submenu, EditSubMenu)
|
||||||
|
ADD_SCENE(subrem, edit_label, EditLabel)
|
||||||
|
ADD_SCENE(subrem, open_sub_file, OpenSubFile)
|
||||||
|
ADD_SCENE(subrem, edit_preview, EditPreview)
|
||||||
|
ADD_SCENE(subrem, enter_new_name, EnterNewName)
|
133
applications/external/subghz_remote_new/scenes/subrem_scene_edit_label.c
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
|
||||||
|
#include <lib/toolbox/path.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubRemSceneEditLabelStateTextInput,
|
||||||
|
SubRemSceneEditLabelStateWidget,
|
||||||
|
} SubRemSceneEditLabelState;
|
||||||
|
|
||||||
|
void subrem_scene_edit_label_text_input_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, SubRemCustomEventSceneEditLabelInputDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_label_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
if((result == GuiButtonTypeCenter) && (type == InputTypeShort)) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, SubRemCustomEventSceneEditLabelWidgetAcces);
|
||||||
|
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, SubRemCustomEventSceneEditLabelWidgetBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_label_on_enter(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
|
||||||
|
if(furi_string_empty(sub_preset->label)) {
|
||||||
|
if(furi_string_empty(sub_preset->file_path)) {
|
||||||
|
path_extract_filename(sub_preset->file_path, temp_str, true);
|
||||||
|
strcpy(app->file_name_tmp, furi_string_get_cstr(temp_str));
|
||||||
|
} else {
|
||||||
|
strcpy(app->file_name_tmp, "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strcpy(app->file_name_tmp, furi_string_get_cstr(sub_preset->label));
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput* text_input = app->text_input;
|
||||||
|
text_input_set_header_text(text_input, "Label name");
|
||||||
|
text_input_set_result_callback(
|
||||||
|
text_input,
|
||||||
|
subrem_scene_edit_label_text_input_callback,
|
||||||
|
app,
|
||||||
|
app->file_name_tmp,
|
||||||
|
25,
|
||||||
|
false);
|
||||||
|
|
||||||
|
text_input_set_minimum_length(app->text_input, 0);
|
||||||
|
|
||||||
|
widget_add_string_element(
|
||||||
|
app->widget, 63, 12, AlignCenter, AlignCenter, FontPrimary, "Empty Label Name");
|
||||||
|
widget_add_string_element(
|
||||||
|
app->widget, 63, 32, AlignCenter, AlignCenter, FontSecondary, "Continue?");
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
app->widget, GuiButtonTypeCenter, "Ok", subrem_scene_edit_label_widget_callback, app);
|
||||||
|
widget_add_button_element(
|
||||||
|
app->widget, GuiButtonTypeLeft, "Back", subrem_scene_edit_label_widget_callback, app);
|
||||||
|
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||||
|
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_edit_label_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
FuriString* label = app->map_preset->subs_preset[app->chusen_sub]->label;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
if(scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditLabel) ==
|
||||||
|
SubRemSceneEditLabelStateWidget) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||||
|
return true;
|
||||||
|
} else if(
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditLabel) ==
|
||||||
|
SubRemSceneEditLabelStateTextInput) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
return true;
|
||||||
|
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubRemCustomEventSceneEditLabelInputDone) {
|
||||||
|
if(strcmp(app->file_name_tmp, "") == 0) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateWidget);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
furi_string_set(label, app->file_name_tmp);
|
||||||
|
app->map_not_saved = true;
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubRemCustomEventSceneEditLabelWidgetAcces) {
|
||||||
|
furi_string_set(label, app->file_name_tmp);
|
||||||
|
app->map_not_saved = true;
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubRemCustomEventSceneEditLabelWidgetBack) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_label_on_exit(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
123
applications/external/subghz_remote_new/scenes/subrem_scene_edit_menu.c
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
|
||||||
|
void subrem_scene_edit_menu_callback(SubRemCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_menu_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||||
|
app->map_not_saved = false;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventViewEditMenuBack);
|
||||||
|
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDEditMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t subrem_scene_edit_menu_state_to_index(SubRemEditMenuState event_id) {
|
||||||
|
uint8_t ret = 0;
|
||||||
|
|
||||||
|
if(event_id == SubRemEditMenuStateUP) {
|
||||||
|
ret = SubRemSubKeyNameUp;
|
||||||
|
} else if(event_id == SubRemEditMenuStateDOWN) {
|
||||||
|
ret = SubRemSubKeyNameDown;
|
||||||
|
} else if(event_id == SubRemEditMenuStateLEFT) {
|
||||||
|
ret = SubRemSubKeyNameLeft;
|
||||||
|
} else if(event_id == SubRemEditMenuStateRIGHT) {
|
||||||
|
ret = SubRemSubKeyNameRight;
|
||||||
|
} else if(event_id == SubRemEditMenuStateOK) {
|
||||||
|
ret = SubRemSubKeyNameOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subrem_scene_edit_menu_update_data(SubGhzRemoteApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
uint8_t index = subrem_scene_edit_menu_state_to_index(
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditMenu));
|
||||||
|
|
||||||
|
subrem_view_edit_menu_add_data_to_show(
|
||||||
|
app->subrem_edit_menu,
|
||||||
|
index,
|
||||||
|
app->map_preset->subs_preset[index]->label,
|
||||||
|
app->map_preset->subs_preset[index]->file_path,
|
||||||
|
app->map_preset->subs_preset[index]->load_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_menu_on_enter(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
subrem_view_edit_menu_set_callback(
|
||||||
|
app->subrem_edit_menu, subrem_scene_edit_menu_callback, app);
|
||||||
|
|
||||||
|
subrem_scene_edit_menu_update_data(app);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDEditMenu);
|
||||||
|
|
||||||
|
Widget* widget = app->widget;
|
||||||
|
|
||||||
|
widget_add_string_element(
|
||||||
|
widget, 63, 12, AlignCenter, AlignBottom, FontPrimary, "Changes are not saved");
|
||||||
|
widget_add_string_element(
|
||||||
|
widget, 63, 32, AlignCenter, AlignBottom, FontPrimary, "do you want to exit?");
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
widget, GuiButtonTypeRight, "Yes", subrem_scene_edit_menu_widget_callback, app);
|
||||||
|
widget_add_button_element(
|
||||||
|
widget, GuiButtonTypeLeft, "No", subrem_scene_edit_menu_widget_callback, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_edit_menu_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
// Catch widget backEvent
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubRemCustomEventViewEditMenuBack) {
|
||||||
|
if(app->map_not_saved) {
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
|
||||||
|
} else 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if(
|
||||||
|
event.event == SubRemCustomEventViewEditMenuUP ||
|
||||||
|
event.event == SubRemCustomEventViewEditMenuDOWN) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager,
|
||||||
|
SubRemSceneEditMenu,
|
||||||
|
subrem_view_edit_menu_get_index(app->subrem_edit_menu));
|
||||||
|
subrem_scene_edit_menu_update_data(app);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubRemCustomEventViewEditMenuEdit) {
|
||||||
|
app->chusen_sub = subrem_view_edit_menu_get_index(app->subrem_edit_menu);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneEditSubMenu, EditSubmenuIndexEditLabel);
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEditSubMenu);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubRemCustomEventViewEditMenuSave) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEditPreview);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_menu_on_exit(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
74
applications/external/subghz_remote_new/scenes/subrem_scene_edit_preview.c
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
#include "../views/remote.h"
|
||||||
|
|
||||||
|
#define TAG "SubRemScenRemote"
|
||||||
|
|
||||||
|
void subghz_scene_edit_preview_save_popup_callback(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, SubRemCustomEventSceneEditPreviewSaved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_preview_callback(SubRemCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_preview_on_enter(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
Popup* popup = app->popup;
|
||||||
|
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||||
|
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
|
||||||
|
popup_set_timeout(popup, 1500);
|
||||||
|
popup_set_context(popup, app);
|
||||||
|
popup_set_callback(popup, subghz_scene_edit_preview_save_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
|
||||||
|
subrem_view_remote_update_data_labels(app->subrem_remote_view, app->map_preset->subs_preset);
|
||||||
|
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateOFF, 0);
|
||||||
|
|
||||||
|
subrem_view_remote_set_callback(
|
||||||
|
app->subrem_remote_view, subrem_scene_edit_preview_callback, app);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDRemote);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_edit_preview_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeBack ||
|
||||||
|
(event.type == SceneManagerEventTypeCustom &&
|
||||||
|
(event.event == SubRemCustomEventViewRemoteStartLEFT ||
|
||||||
|
event.event == SubRemCustomEventViewRemoteForcedStop))) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
return true;
|
||||||
|
} else if(
|
||||||
|
event.type == SceneManagerEventTypeCustom &&
|
||||||
|
(event.event == SubRemCustomEventViewRemoteStartRIGHT ||
|
||||||
|
event.event == SubRemCustomEventViewRemoteStartOK)) {
|
||||||
|
if(subrem_save_map_to_file(app)) {
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDPopup);
|
||||||
|
app->map_not_saved = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO error screen
|
||||||
|
return true;
|
||||||
|
} else if(
|
||||||
|
event.type == SceneManagerEventTypeCustom &&
|
||||||
|
event.event == SubRemCustomEventSceneEditPreviewSaved) {
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SubRemSceneEditMenu);
|
||||||
|
}
|
||||||
|
// } else if(event.type == SceneManagerEventTypeTick) {
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_preview_on_exit(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
|
||||||
|
popup_reset(app->popup);
|
||||||
|
}
|
54
applications/external/subghz_remote_new/scenes/subrem_scene_edit_submenu.c
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
#include "../helpers/subrem_custom_event.h"
|
||||||
|
|
||||||
|
void subrem_scene_edit_submenu_text_input_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventSceneEditsubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_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_edit_submenu_on_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
submenu_add_item(
|
||||||
|
submenu, "Edit Label", EditSubmenuIndexEditLabel, subrem_scene_edit_submenu_callback, app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu, "Edit File", EditSubmenuIndexEditFile, subrem_scene_edit_submenu_callback, app);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_edit_submenu_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == EditSubmenuIndexEditLabel) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEditLabel);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == EditSubmenuIndexEditFile) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenSubFile);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_edit_submenu_on_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
71
applications/external/subghz_remote_new/scenes/subrem_scene_enter_new_name.c
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
#include "../helpers/subrem_custom_event.h"
|
||||||
|
|
||||||
|
#include <gui/modules/validators.h>
|
||||||
|
|
||||||
|
void subrem_scene_enter_new_name_text_input_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventSceneNewName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_enter_new_name_on_enter(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
TextInput* text_input = app->text_input;
|
||||||
|
bool dev_name_empty = false;
|
||||||
|
|
||||||
|
strncpy(app->file_name_tmp, "subrem_", SUBREM_MAX_LEN_NAME);
|
||||||
|
text_input_set_header_text(text_input, "Map file Name");
|
||||||
|
text_input_set_result_callback(
|
||||||
|
text_input,
|
||||||
|
subrem_scene_enter_new_name_text_input_callback,
|
||||||
|
app,
|
||||||
|
app->file_name_tmp,
|
||||||
|
25,
|
||||||
|
dev_name_empty);
|
||||||
|
|
||||||
|
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||||
|
furi_string_get_cstr(app->file_path), SUBREM_APP_EXTENSION, "");
|
||||||
|
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_enter_new_name_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubRemCustomEventSceneNewName) {
|
||||||
|
if(strcmp(app->file_name_tmp, "") != 0) {
|
||||||
|
furi_string_set(app->file_path, SUBREM_APP_FOLDER);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
app->file_path, "/%s%s", app->file_name_tmp, SUBREM_APP_EXTENSION);
|
||||||
|
|
||||||
|
subrem_map_preset_reset(app->map_preset);
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
|
||||||
|
} else { //error
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_enter_new_name_on_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
|
||||||
|
// Clear validator & view
|
||||||
|
void* validator_context = text_input_get_validator_callback_context(app->text_input);
|
||||||
|
text_input_set_validator(app->text_input, NULL, NULL);
|
||||||
|
validator_is_file_free(validator_context);
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
}
|
29
applications/external/subghz_remote_new/scenes/subrem_scene_open_map_file.c
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#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);
|
||||||
|
uint32_t start_scene_state =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart);
|
||||||
|
|
||||||
|
// TODO if optimization
|
||||||
|
|
||||||
|
if(load_state == SubRemLoadMapStateBack) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
} else if(start_scene_state == SubmenuIndexSubRemEditMapFile) {
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, SubRemSceneEditMenu, SubRemSubKeyNameUp);
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
116
applications/external/subghz_remote_new/scenes/subrem_scene_open_sub_file.c
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
|
||||||
|
void subrem_scene_open_sub_file_error_popup_callback(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, SubRemCustomEventSceneEditOpenSubErrorPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubRemLoadSubState subrem_scene_open_sub_file_dialog(SubGhzRemoteApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
SubRemSubFilePreset* sub = app->map_preset->subs_preset[app->chusen_sub];
|
||||||
|
|
||||||
|
FuriString* temp_file_path = furi_string_alloc_set(sub->file_path);
|
||||||
|
|
||||||
|
SubRemLoadSubState ret = SubRemLoadSubStateNotSet;
|
||||||
|
|
||||||
|
DialogsFileBrowserOptions browser_options;
|
||||||
|
|
||||||
|
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
|
||||||
|
browser_options.base_path = SUBGHZ_APP_FOLDER;
|
||||||
|
|
||||||
|
// Input events and views are managed by file_select
|
||||||
|
if(!dialog_file_browser_show(
|
||||||
|
app->dialogs,
|
||||||
|
temp_file_path,
|
||||||
|
furi_string_empty(temp_file_path) ? NULL : temp_file_path,
|
||||||
|
&browser_options)) {
|
||||||
|
} else {
|
||||||
|
// Check sub file
|
||||||
|
SubRemSubFilePreset* sub_candidate = subrem_sub_file_preset_alloc();
|
||||||
|
furi_string_set(sub_candidate->label, sub->label);
|
||||||
|
furi_string_set(sub_candidate->file_path, temp_file_path);
|
||||||
|
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
FlipperFormat* fff_file = flipper_format_file_alloc(storage);
|
||||||
|
|
||||||
|
if(flipper_format_file_open_existing(
|
||||||
|
fff_file, furi_string_get_cstr(sub_candidate->file_path))) {
|
||||||
|
ret = subrem_sub_preset_load(sub_candidate, app->txrx, fff_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
flipper_format_file_close(fff_file);
|
||||||
|
flipper_format_free(fff_file);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
|
if(ret == SubRemLoadSubStateOK) {
|
||||||
|
subrem_sub_file_preset_free(app->map_preset->subs_preset[app->chusen_sub]);
|
||||||
|
app->map_preset->subs_preset[app->chusen_sub] = sub_candidate;
|
||||||
|
app->map_not_saved = true;
|
||||||
|
} else {
|
||||||
|
subrem_sub_file_preset_free(sub_candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(temp_file_path);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_open_sub_file_on_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
SubRemLoadSubState load_state = subrem_scene_open_sub_file_dialog(app);
|
||||||
|
|
||||||
|
Popup* popup = app->popup;
|
||||||
|
// popup_set_icon();
|
||||||
|
popup_set_header(popup, "ERROR", 63, 16, AlignCenter, AlignBottom);
|
||||||
|
popup_set_timeout(popup, 1500);
|
||||||
|
popup_set_context(popup, app);
|
||||||
|
popup_set_callback(popup, subrem_scene_open_sub_file_error_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
|
||||||
|
if(load_state == SubRemLoadSubStateOK) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
} else if(load_state == SubRemLoadSubStateNotSet) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
} else {
|
||||||
|
switch(load_state) {
|
||||||
|
case SubRemLoadSubStateErrorFreq:
|
||||||
|
|
||||||
|
popup_set_text(popup, "Bad frequency", 63, 30, AlignCenter, AlignBottom);
|
||||||
|
break;
|
||||||
|
case SubRemLoadSubStateErrorMod:
|
||||||
|
|
||||||
|
popup_set_text(popup, "Bad modulation", 63, 30, AlignCenter, AlignBottom);
|
||||||
|
break;
|
||||||
|
case SubRemLoadSubStateErrorProtocol:
|
||||||
|
|
||||||
|
popup_set_text(popup, "Unsupported protocol", 63, 30, AlignCenter, AlignBottom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDPopup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_scene_open_sub_file_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom &&
|
||||||
|
event.event == SubRemCustomEventSceneEditOpenSubErrorPopup) {
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_scene_open_sub_file_on_exit(void* context) {
|
||||||
|
SubGhzRemoteApp* app = context;
|
||||||
|
|
||||||
|
popup_reset(app->popup);
|
||||||
|
}
|
|
@ -1,40 +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) {
|
|
||||||
} 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);
|
|
||||||
}
|
|
|
@ -13,14 +13,18 @@ void subrem_scene_start_on_enter(void* context) {
|
||||||
|
|
||||||
SubGhzRemoteApp* app = context;
|
SubGhzRemoteApp* app = context;
|
||||||
Submenu* submenu = app->submenu;
|
Submenu* submenu = app->submenu;
|
||||||
#if FURI_DEBUG
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Remote_Debug",
|
"Edit Map File",
|
||||||
SubmenuIndexSubRemRemoteView,
|
SubmenuIndexSubRemEditMapFile,
|
||||||
|
subrem_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"New Map File",
|
||||||
|
SubmenuIndexSubRemNewMapFile,
|
||||||
subrem_scene_start_submenu_callback,
|
subrem_scene_start_submenu_callback,
|
||||||
app);
|
app);
|
||||||
#endif
|
|
||||||
// submenu_add_item(
|
// submenu_add_item(
|
||||||
// submenu,
|
// submenu,
|
||||||
// "About",
|
// "About",
|
||||||
|
@ -28,12 +32,10 @@ void subrem_scene_start_on_enter(void* context) {
|
||||||
// subrem_scene_start_submenu_callback,
|
// subrem_scene_start_submenu_callback,
|
||||||
// app);
|
// app);
|
||||||
|
|
||||||
// TODO: set scene state in subrem alloc
|
submenu_set_selected_item(
|
||||||
// submenu_set_selected_item(
|
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
|
||||||
// submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
|
|
||||||
submenu_set_selected_item(submenu, 0);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewSubmenu);
|
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
|
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
@ -41,8 +43,19 @@ bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|
||||||
SubGhzRemoteApp* app = context;
|
SubGhzRemoteApp* app = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
UNUSED(app);
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubmenuIndexSubRemEditMapFile) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexSubRemNewMapFile) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemNewMapFile);
|
||||||
|
scene_manager_next_scene(app->scene_manager, SubRemSceneEnterNewName);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
||||||
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
||||||
// consumed = true;
|
// consumed = true;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "subghz_remote_app_i.h"
|
#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) {
|
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzRemoteApp* app = context;
|
SubGhzRemoteApp* app = context;
|
||||||
|
@ -23,6 +21,14 @@ static void subghz_remote_app_tick_event_callback(void* context) {
|
||||||
SubGhzRemoteApp* subghz_remote_app_alloc() {
|
SubGhzRemoteApp* subghz_remote_app_alloc() {
|
||||||
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
|
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
|
||||||
|
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER);
|
||||||
|
|
||||||
|
if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) {
|
||||||
|
//FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
|
||||||
|
}
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
// Enable power for External CC1101 if it is connected
|
// Enable power for External CC1101 if it is connected
|
||||||
furi_hal_subghz_enable_ext_power();
|
furi_hal_subghz_enable_ext_power();
|
||||||
// Auto switch to internal radio if external radio is not available
|
// Auto switch to internal radio if external radio is not available
|
||||||
|
@ -62,11 +68,25 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
|
||||||
// SubMenu
|
// SubMenu
|
||||||
app->submenu = submenu_alloc();
|
app->submenu = submenu_alloc();
|
||||||
view_dispatcher_add_view(
|
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);
|
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|
||||||
|
// TextInput
|
||||||
|
app->text_input = text_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, SubRemViewIDTextInput, text_input_get_view(app->text_input));
|
||||||
|
|
||||||
|
// Widget
|
||||||
|
app->widget = widget_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, SubRemViewIDWidget, widget_get_view(app->widget));
|
||||||
|
|
||||||
|
// Popup
|
||||||
|
app->popup = popup_alloc();
|
||||||
|
view_dispatcher_add_view(app->view_dispatcher, SubRemViewIDPopup, popup_get_view(app->popup));
|
||||||
|
|
||||||
// Remote view
|
// Remote view
|
||||||
app->subrem_remote_view = subrem_view_remote_alloc();
|
app->subrem_remote_view = subrem_view_remote_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
|
@ -74,34 +94,30 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
|
||||||
SubRemViewIDRemote,
|
SubRemViewIDRemote,
|
||||||
subrem_view_remote_get_view(app->subrem_remote_view));
|
subrem_view_remote_get_view(app->subrem_remote_view));
|
||||||
|
|
||||||
|
// Edit Menu view
|
||||||
|
app->subrem_edit_menu = subrem_view_edit_menu_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher,
|
||||||
|
SubRemViewIDEditMenu,
|
||||||
|
subrem_view_edit_menu_get_view(app->subrem_edit_menu));
|
||||||
|
|
||||||
|
app->map_preset = malloc(sizeof(SubRemMapPreset));
|
||||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
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();
|
app->txrx = subghz_txrx_alloc();
|
||||||
subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
|
|
||||||
|
|
||||||
app->environment = subghz_environment_alloc();
|
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
|
||||||
|
|
||||||
subghz_environment_load_keystore(app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
|
app->map_not_saved = false;
|
||||||
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;
|
|
||||||
|
|
||||||
#ifdef SUBREM_LIGHT
|
#ifdef SUBREM_LIGHT
|
||||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||||
#else
|
#else
|
||||||
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
|
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
|
@ -118,26 +134,41 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
|
||||||
furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
|
furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
|
||||||
|
|
||||||
// Submenu
|
// Submenu
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewSubmenu);
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||||
submenu_free(app->submenu);
|
submenu_free(app->submenu);
|
||||||
|
|
||||||
//Dialog
|
// Dialog
|
||||||
furi_record_close(RECORD_DIALOGS);
|
furi_record_close(RECORD_DIALOGS);
|
||||||
|
|
||||||
|
// TextInput
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||||
|
text_input_free(app->text_input);
|
||||||
|
|
||||||
|
// Widget
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDWidget);
|
||||||
|
widget_free(app->widget);
|
||||||
|
|
||||||
|
// Popup
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDPopup);
|
||||||
|
popup_free(app->popup);
|
||||||
|
|
||||||
// Remote view
|
// Remote view
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
|
||||||
subrem_view_remote_free(app->subrem_remote_view);
|
subrem_view_remote_free(app->subrem_remote_view);
|
||||||
|
|
||||||
|
// Edit view
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDEditMenu);
|
||||||
|
subrem_view_edit_menu_free(app->subrem_edit_menu);
|
||||||
|
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
|
||||||
subghz_receiver_free(app->receiver);
|
subghz_txrx_free(app->txrx);
|
||||||
subghz_environment_free(app->environment);
|
|
||||||
subghz_setting_free(app->setting);
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
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
|
// Notifications
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
#include <flipper_format/flipper_format_i.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/keeloq.h>
|
||||||
// #include <lib/subghz/protocols/star_line.h>
|
// #include <lib/subghz/protocols/star_line.h>
|
||||||
|
|
||||||
#include <lib/subghz/blocks/custom_btn.h>
|
// #include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
// #include <lib/subghz/blocks/custom_btn.h>
|
||||||
|
|
||||||
#define TAG "SubGhzRemote"
|
#define TAG "SubGhzRemote"
|
||||||
|
|
||||||
|
@ -19,280 +20,46 @@ static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
|
||||||
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
|
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
|
||||||
subrem_set_preset_data(SubGhzSetting* setting, FreqPreset* freq_preset, const char* preset) {
|
furi_assert(map_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);
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
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) {
|
static SubRemLoadMapState subrem_map_preset_check(
|
||||||
furi_assert(app);
|
SubRemMapPreset* map_preset,
|
||||||
bool ret = false;
|
SubGhzTxRx* txrx,
|
||||||
SubRemSubFilePreset* sub_preset;
|
FlipperFormat* fff_data_file) {
|
||||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
furi_assert(map_preset);
|
||||||
sub_preset = app->subs_preset[i];
|
furi_assert(txrx);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
bool all_loaded = true;
|
||||||
SubRemLoadMapState ret = SubRemLoadMapStateErrorBrokenFile;
|
SubRemLoadMapState ret = SubRemLoadMapStateErrorBrokenFile;
|
||||||
SubRemLoadSubState sub_preset_loaded;
|
|
||||||
|
SubRemLoadSubState sub_loadig_state;
|
||||||
SubRemSubFilePreset* sub_preset;
|
SubRemSubFilePreset* sub_preset;
|
||||||
uint32_t repeat = 200;
|
|
||||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
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)) {
|
if(furi_string_empty(sub_preset->file_path)) {
|
||||||
// FURI_LOG_I(TAG, "Empty file path");
|
// FURI_LOG_I(TAG, "Empty file path");
|
||||||
continue;
|
} else if(!flipper_format_file_open_existing(
|
||||||
}
|
|
||||||
|
|
||||||
sub_preset_loaded = SubRemLoadSubStateErrorNoFile;
|
|
||||||
|
|
||||||
repeat = 200;
|
|
||||||
do {
|
|
||||||
if(!flipper_format_file_open_existing(
|
|
||||||
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
|
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));
|
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 {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Type or version mismatch");
|
sub_loadig_state = subrem_sub_preset_load(sub_preset, txrx, fff_data_file);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load frequency
|
if(sub_loadig_state != SubRemLoadSubStateOK) {
|
||||||
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");
|
|
||||||
all_loaded = false;
|
all_loaded = false;
|
||||||
|
} else {
|
||||||
|
ret = SubRemLoadMapStateNotAllOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ret != SubRemLoadMapStateErrorBrokenFile && all_loaded) {
|
if(ret != SubRemLoadMapStateErrorBrokenFile && all_loaded) {
|
||||||
|
@ -301,11 +68,52 @@ static SubRemLoadMapState
|
||||||
|
|
||||||
flipper_format_file_close(fff_data_file);
|
flipper_format_file_close(fff_data_file);
|
||||||
}
|
}
|
||||||
furi_string_free(temp_str);
|
|
||||||
|
|
||||||
return ret;
|
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) {
|
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
furi_assert(file_path);
|
furi_assert(file_path);
|
||||||
|
@ -318,19 +126,19 @@ SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_p
|
||||||
#if FURI_DEBUG
|
#if FURI_DEBUG
|
||||||
FURI_LOG_I(TAG, "Open Map File..");
|
FURI_LOG_I(TAG, "Open Map File..");
|
||||||
#endif
|
#endif
|
||||||
subrem_map_preset_reset(app);
|
subrem_map_preset_reset(app->map_preset);
|
||||||
|
|
||||||
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
|
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
|
||||||
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
|
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
|
||||||
ret = SubRemLoadMapStateErrorOpenError;
|
ret = SubRemLoadMapStateErrorOpenError;
|
||||||
} else {
|
} 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");
|
FURI_LOG_E(TAG, "Could no Sub file path in MAP file");
|
||||||
// ret = // error for popup
|
// ret = // error for popup
|
||||||
} else if(!flipper_format_file_close(fff_data_file)) {
|
} else if(!flipper_format_file_close(fff_data_file)) {
|
||||||
ret = SubRemLoadMapStateErrorOpenError;
|
ret = SubRemLoadMapStateErrorOpenError;
|
||||||
} else {
|
} else {
|
||||||
ret = subrem_map_preset_check(app, fff_data_file);
|
ret = subrem_map_preset_check(app->map_preset, app->txrx, fff_data_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +157,53 @@ SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_p
|
||||||
return ret;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
|
|
||||||
|
@ -356,7 +211,7 @@ SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
||||||
SubRemLoadMapState ret = SubRemLoadMapStateBack;
|
SubRemLoadMapState ret = SubRemLoadMapStateBack;
|
||||||
|
|
||||||
DialogsFileBrowserOptions browser_options;
|
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;
|
browser_options.base_path = SUBREM_APP_FOLDER;
|
||||||
|
|
||||||
// Input events and views are managed by file_select
|
// Input events and views are managed by file_select
|
||||||
|
@ -369,3 +224,47 @@ SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool subrem_save_map_to_file(SubGhzRemoteApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
const char* file_name = furi_string_get_cstr(app->file_path);
|
||||||
|
bool saved = false;
|
||||||
|
FlipperFormat* fff_data = flipper_format_string_alloc();
|
||||||
|
|
||||||
|
SubRemSubFilePreset* sub_preset;
|
||||||
|
|
||||||
|
flipper_format_write_header_cstr(
|
||||||
|
fff_data, SUBREM_APP_APP_FILE_TYPE, SUBREM_APP_APP_FILE_VERSION);
|
||||||
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
|
sub_preset = app->map_preset->subs_preset[i];
|
||||||
|
if(!furi_string_empty(sub_preset->file_path)) {
|
||||||
|
flipper_format_write_string(fff_data, map_file_labels[i][0], sub_preset->file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
|
sub_preset = app->map_preset->subs_preset[i];
|
||||||
|
if(!furi_string_empty(sub_preset->file_path)) {
|
||||||
|
flipper_format_write_string(fff_data, map_file_labels[i][1], sub_preset->label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
Stream* flipper_format_stream = flipper_format_get_raw_stream(fff_data);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(!storage_simply_remove(storage, file_name)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//ToDo check Write
|
||||||
|
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||||
|
stream_save_to_file(flipper_format_stream, storage, file_name, FSOM_CREATE_ALWAYS);
|
||||||
|
|
||||||
|
saved = true;
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
flipper_format_free(fff_data);
|
||||||
|
|
||||||
|
return saved;
|
||||||
|
}
|
|
@ -1,29 +1,28 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "helpers/subrem_types.h"
|
#include "helpers/subrem_types.h"
|
||||||
|
#include "helpers/subrem_presets.h"
|
||||||
|
#include "scenes/subrem_scene.h"
|
||||||
|
|
||||||
|
#include "helpers/txrx/subghz_txrx.h"
|
||||||
#include <subrem_remote_fap_icons.h>
|
#include <subrem_remote_fap_icons.h>
|
||||||
|
|
||||||
// #include <assets_icons.h>
|
|
||||||
|
|
||||||
#include "views/remote.h"
|
#include "views/remote.h"
|
||||||
|
#include "views/edit_menu.h"
|
||||||
#include "scenes/subrem_scene.h"
|
|
||||||
|
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
#include <gui/modules/widget.h>
|
#include <gui/modules/widget.h>
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <gui/modules/text_input.h>
|
#include <gui/modules/text_input.h>
|
||||||
#include <dialogs/dialogs.h>
|
|
||||||
#include <storage/storage.h>
|
|
||||||
#include <gui/modules/popup.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 <flipper_format/flipper_format_i.h>
|
||||||
|
|
||||||
#include <lib/subghz/protocols/raw.h>
|
|
||||||
|
|
||||||
#include <lib/subghz/subghz_setting.h>
|
#include <lib/subghz/subghz_setting.h>
|
||||||
#include <lib/subghz/receiver.h>
|
#include <lib/subghz/receiver.h>
|
||||||
#include <lib/subghz/transmitter.h>
|
#include <lib/subghz/transmitter.h>
|
||||||
|
@ -31,48 +30,38 @@
|
||||||
#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote")
|
#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote")
|
||||||
#define SUBREM_MAX_LEN_NAME 64
|
#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 {
|
typedef struct {
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
NotificationApp* notifications;
|
NotificationApp* notifications;
|
||||||
DialogsApp* dialogs;
|
DialogsApp* dialogs;
|
||||||
|
Widget* widget;
|
||||||
|
Popup* popup;
|
||||||
|
TextInput* text_input;
|
||||||
Submenu* submenu;
|
Submenu* submenu;
|
||||||
|
|
||||||
FuriString* file_path;
|
FuriString* file_path;
|
||||||
char file_name_tmp[SUBREM_MAX_LEN_NAME];
|
char file_name_tmp[SUBREM_MAX_LEN_NAME];
|
||||||
|
|
||||||
SubRemViewRemote* subrem_remote_view;
|
SubRemViewRemote* subrem_remote_view;
|
||||||
|
SubRemViewEditMenu* subrem_edit_menu;
|
||||||
|
|
||||||
SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount];
|
SubRemMapPreset* map_preset;
|
||||||
|
|
||||||
SubGhzSetting* setting;
|
SubGhzTxRx* txrx;
|
||||||
SubGhzEnvironment* environment;
|
|
||||||
SubGhzReceiver* receiver;
|
|
||||||
SubGhzTransmitter* transmitter;
|
|
||||||
|
|
||||||
bool tx_running;
|
bool map_not_saved;
|
||||||
|
|
||||||
uint8_t chusen_sub;
|
uint8_t chusen_sub;
|
||||||
} SubGhzRemoteApp;
|
} SubGhzRemoteApp;
|
||||||
|
|
||||||
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app);
|
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app);
|
||||||
|
|
||||||
|
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path);
|
||||||
|
|
||||||
|
void subrem_map_preset_reset(SubRemMapPreset* map_preset);
|
||||||
|
|
||||||
|
bool subrem_save_map_to_file(SubGhzRemoteApp* app);
|
||||||
|
|
||||||
|
void subrem_save_active_sub(void* context);
|
290
applications/external/subghz_remote_new/views/edit_menu.c
vendored
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
#include "edit_menu.h"
|
||||||
|
#include "../subghz_remote_app_i.h"
|
||||||
|
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#define subrem_view_edit_menu_MAX_LABEL_LENGTH 12
|
||||||
|
|
||||||
|
#define FRAME_HEIGHT 12
|
||||||
|
|
||||||
|
struct SubRemViewEditMenu {
|
||||||
|
View* view;
|
||||||
|
SubRemViewEditMenuCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriString* label;
|
||||||
|
FuriString* file_path;
|
||||||
|
SubRemLoadSubState sub_state;
|
||||||
|
|
||||||
|
uint8_t chusen;
|
||||||
|
} SubRemViewEditMenuModel;
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_set_callback(
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu,
|
||||||
|
SubRemViewEditMenuCallback callback,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(subrem_view_edit_menu);
|
||||||
|
|
||||||
|
subrem_view_edit_menu->callback = callback;
|
||||||
|
subrem_view_edit_menu->context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_add_data_to_show(
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_remote,
|
||||||
|
uint8_t index,
|
||||||
|
FuriString* label,
|
||||||
|
FuriString* path,
|
||||||
|
SubRemLoadSubState state) {
|
||||||
|
furi_assert(subrem_view_edit_remote);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
subrem_view_edit_remote->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{
|
||||||
|
model->chusen = index;
|
||||||
|
if(!furi_string_empty(label)) {
|
||||||
|
furi_string_set(model->label, label);
|
||||||
|
} else {
|
||||||
|
furi_string_set(model->label, "Empty label");
|
||||||
|
}
|
||||||
|
furi_string_set(model->file_path, path);
|
||||||
|
model->sub_state = state;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subrem_view_edit_menu_get_index(SubRemViewEditMenu* subrem_view_edit_remote) {
|
||||||
|
furi_assert(subrem_view_edit_remote);
|
||||||
|
uint8_t index;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
subrem_view_edit_remote->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{ index = model->chusen; },
|
||||||
|
true);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_draw(Canvas* canvas, SubRemViewEditMenuModel* model) {
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
canvas_clear(canvas);
|
||||||
|
|
||||||
|
// Draw bottom btn
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_button_left(canvas, "Back");
|
||||||
|
elements_button_center(canvas, "Edit");
|
||||||
|
elements_button_right(canvas, "Save");
|
||||||
|
|
||||||
|
// Draw top frame
|
||||||
|
canvas_draw_line(canvas, 1, 0, 125, 0);
|
||||||
|
canvas_draw_box(canvas, 0, 1, 127, FRAME_HEIGHT - 2);
|
||||||
|
canvas_draw_line(canvas, 1, FRAME_HEIGHT - 1, 125, FRAME_HEIGHT - 1);
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
|
||||||
|
// Draw btn name
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
switch(model->chusen) {
|
||||||
|
case SubRemSubKeyNameUp:
|
||||||
|
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "UP");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SubRemSubKeyNameDown:
|
||||||
|
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "DOWN");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SubRemSubKeyNameLeft:
|
||||||
|
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "LEFT");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SubRemSubKeyNameRight:
|
||||||
|
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "RIGHT");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SubRemSubKeyNameOk:
|
||||||
|
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "OK");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw Label
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_text_box(
|
||||||
|
canvas,
|
||||||
|
38,
|
||||||
|
2,
|
||||||
|
127 - 38,
|
||||||
|
FRAME_HEIGHT,
|
||||||
|
AlignCenter,
|
||||||
|
AlignBottom,
|
||||||
|
furi_string_empty(model->label) ? "Empty label" : furi_string_get_cstr(model->label),
|
||||||
|
true);
|
||||||
|
|
||||||
|
// Draw arrow
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
if(model->chusen != 0) {
|
||||||
|
canvas_draw_icon(canvas, 119, 13, &I_Pin_arrow_up_7x9);
|
||||||
|
}
|
||||||
|
if(model->chusen != 4) {
|
||||||
|
canvas_draw_icon_ex(canvas, 119, 42, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw file_path
|
||||||
|
if(model->sub_state == SubRemLoadSubStateOK) {
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_text_box(
|
||||||
|
canvas,
|
||||||
|
1,
|
||||||
|
FRAME_HEIGHT + 1,
|
||||||
|
118,
|
||||||
|
(63 - FRAME_HEIGHT * 2),
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
furi_string_get_cstr(model->file_path),
|
||||||
|
false);
|
||||||
|
} else if(furi_string_empty(model->file_path)) {
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 1, FRAME_HEIGHT * 2 - 2, "Button not set");
|
||||||
|
} else {
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 1, FRAME_HEIGHT * 2 - 2, "ERR:");
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
switch(model->sub_state) {
|
||||||
|
case SubRemLoadSubStateErrorNoFile:
|
||||||
|
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "File not found");
|
||||||
|
break;
|
||||||
|
case SubRemLoadSubStateErrorFreq:
|
||||||
|
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Bad frequency");
|
||||||
|
break;
|
||||||
|
case SubRemLoadSubStateErrorMod:
|
||||||
|
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Bad modulation");
|
||||||
|
break;
|
||||||
|
case SubRemLoadSubStateErrorProtocol:
|
||||||
|
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Unsupported protocol");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elements_text_box(
|
||||||
|
canvas,
|
||||||
|
1,
|
||||||
|
FRAME_HEIGHT * 2,
|
||||||
|
118,
|
||||||
|
30,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
furi_string_get_cstr(model->file_path),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subrem_view_edit_menu_input(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu = context;
|
||||||
|
|
||||||
|
if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
|
||||||
|
event->type == InputTypeShort) {
|
||||||
|
subrem_view_edit_menu->callback(
|
||||||
|
SubRemCustomEventViewEditMenuBack, subrem_view_edit_menu->context);
|
||||||
|
return true;
|
||||||
|
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
subrem_view_edit_menu->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{
|
||||||
|
if(model->chusen > 0) {
|
||||||
|
model->chusen -= 1;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
subrem_view_edit_menu->callback(
|
||||||
|
SubRemCustomEventViewEditMenuUP, subrem_view_edit_menu->context);
|
||||||
|
return true;
|
||||||
|
} else if(event->key == InputKeyDown && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
subrem_view_edit_menu->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{
|
||||||
|
if(model->chusen < 4) {
|
||||||
|
model->chusen += 1;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
subrem_view_edit_menu->callback(
|
||||||
|
SubRemCustomEventViewEditMenuDOWN, subrem_view_edit_menu->context);
|
||||||
|
return true;
|
||||||
|
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||||
|
subrem_view_edit_menu->callback(
|
||||||
|
SubRemCustomEventViewEditMenuEdit, subrem_view_edit_menu->context);
|
||||||
|
return true;
|
||||||
|
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
|
||||||
|
subrem_view_edit_menu->callback(
|
||||||
|
SubRemCustomEventViewEditMenuSave, subrem_view_edit_menu->context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu_alloc() {
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu = malloc(sizeof(SubRemViewEditMenu));
|
||||||
|
|
||||||
|
// View allocation and configuration
|
||||||
|
subrem_view_edit_menu->view = view_alloc();
|
||||||
|
view_allocate_model(
|
||||||
|
subrem_view_edit_menu->view, ViewModelTypeLocking, sizeof(SubRemViewEditMenuModel));
|
||||||
|
view_set_context(subrem_view_edit_menu->view, subrem_view_edit_menu);
|
||||||
|
view_set_draw_callback(
|
||||||
|
subrem_view_edit_menu->view, (ViewDrawCallback)subrem_view_edit_menu_draw);
|
||||||
|
view_set_input_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_input);
|
||||||
|
view_set_enter_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_enter);
|
||||||
|
view_set_exit_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_exit);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
subrem_view_edit_menu->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{
|
||||||
|
model->label = furi_string_alloc(); // furi_string_alloc_set_str("LABEL");
|
||||||
|
model->file_path = furi_string_alloc(); // furi_string_alloc_set_str("FILE_PATH");
|
||||||
|
|
||||||
|
model->chusen = 0;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
return subrem_view_edit_menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_free(SubRemViewEditMenu* subghz_edit_menu) {
|
||||||
|
furi_assert(subghz_edit_menu);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
subghz_edit_menu->view,
|
||||||
|
SubRemViewEditMenuModel * model,
|
||||||
|
{
|
||||||
|
furi_string_free(model->label);
|
||||||
|
furi_string_free(model->file_path);
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
view_free(subghz_edit_menu->view);
|
||||||
|
free(subghz_edit_menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* subrem_view_edit_menu_get_view(SubRemViewEditMenu* subrem_view_edit_menu) {
|
||||||
|
furi_assert(subrem_view_edit_menu);
|
||||||
|
return subrem_view_edit_menu->view;
|
||||||
|
}
|
29
applications/external/subghz_remote_new/views/edit_menu.h
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/subrem_custom_event.h"
|
||||||
|
#include "../helpers/subrem_presets.h"
|
||||||
|
|
||||||
|
typedef struct SubRemViewEditMenu SubRemViewEditMenu;
|
||||||
|
|
||||||
|
typedef void (*SubRemViewEditMenuCallback)(SubRemCustomEvent event, void* context);
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_set_callback(
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu,
|
||||||
|
SubRemViewEditMenuCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_menu_alloc();
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_free(SubRemViewEditMenu* subrem_view_edit_menu);
|
||||||
|
|
||||||
|
View* subrem_view_edit_menu_get_view(SubRemViewEditMenu* subrem_view_edit_menu);
|
||||||
|
|
||||||
|
void subrem_view_edit_menu_add_data_to_show(
|
||||||
|
SubRemViewEditMenu* subrem_view_edit_remote,
|
||||||
|
uint8_t index,
|
||||||
|
FuriString* label,
|
||||||
|
FuriString* path,
|
||||||
|
SubRemLoadSubState state);
|
||||||
|
|
||||||
|
uint8_t subrem_view_edit_menu_get_index(SubRemViewEditMenu* subrem_view_edit_remote);
|
|
@ -4,7 +4,11 @@
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
#include <gui/elements.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 {
|
struct SubRemViewRemote {
|
||||||
View* view;
|
View* view;
|
||||||
|
@ -12,19 +16,8 @@ struct SubRemViewRemote {
|
||||||
void* context;
|
void* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: model
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// FuriString* up_label;
|
char* labels[SubRemSubKeyNameMaxCount];
|
||||||
// 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;
|
|
||||||
|
|
||||||
SubRemViewRemoteState state;
|
SubRemViewRemoteState state;
|
||||||
|
|
||||||
|
@ -41,49 +34,76 @@ void subrem_view_remote_set_callback(
|
||||||
subrem_view_remote->context = context;
|
subrem_view_remote->context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subrem_view_remote_add_data_to_show(
|
void subrem_view_remote_update_data_labels(
|
||||||
SubRemViewRemote* subrem_view_remote,
|
SubRemViewRemote* subrem_view_remote,
|
||||||
const char* up_label,
|
SubRemSubFilePreset** subs_presets) {
|
||||||
const char* down_label,
|
|
||||||
const char* left_label,
|
|
||||||
const char* right_label,
|
|
||||||
const char* ok_label) {
|
|
||||||
furi_assert(subrem_view_remote);
|
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(
|
with_view_model(
|
||||||
subrem_view_remote->view,
|
subrem_view_remote->view,
|
||||||
SubRemViewRemoteModel * model,
|
SubRemViewRemoteModel * model,
|
||||||
{
|
{
|
||||||
strncpy(model->up_label, up_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
strncpy(model->down_label, down_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
strncpy(
|
||||||
strncpy(model->left_label, left_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
model->labels[i],
|
||||||
strncpy(model->right_label, right_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
furi_string_get_cstr(labels[i]),
|
||||||
strncpy(model->ok_label, ok_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
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);
|
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
|
||||||
|
|
||||||
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn) {
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
furi_assert(subrem_view_remote);
|
furi_string_free(labels[i]);
|
||||||
with_view_model(
|
}
|
||||||
subrem_view_remote->view,
|
|
||||||
SubRemViewRemoteModel * model,
|
|
||||||
{ model->pressed_btn = presed_btn; },
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void subrem_view_remote_set_state(
|
void subrem_view_remote_set_state(
|
||||||
SubRemViewRemote* subrem_view_remote,
|
SubRemViewRemote* subrem_view_remote,
|
||||||
SubRemViewRemoteState state) {
|
SubRemViewRemoteState state,
|
||||||
|
uint8_t presed_btn) {
|
||||||
furi_assert(subrem_view_remote);
|
furi_assert(subrem_view_remote);
|
||||||
with_view_model(
|
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) {
|
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||||
|
@ -103,24 +123,32 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||||
|
|
||||||
//Labels
|
//Labels
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 10, 10, model->up_label);
|
uint8_t y = 0;
|
||||||
canvas_draw_str(canvas, 10, 20, model->down_label);
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
canvas_draw_str(canvas, 10, 30, model->left_label);
|
elements_text_box(
|
||||||
canvas_draw_str(canvas, 10, 40, model->right_label);
|
canvas,
|
||||||
canvas_draw_str(canvas, 10, 50, model->ok_label);
|
SUBREM_VIEW_REMOTE_LEFT_OFFSET,
|
||||||
|
y + 2,
|
||||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
126 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET,
|
||||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
12,
|
||||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
AlignLeft,
|
||||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
AlignBottom,
|
||||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
model->labels[i],
|
||||||
|
false);
|
||||||
|
y += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.");
|
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
|
||||||
|
}
|
||||||
|
|
||||||
//Status text and indicator
|
//Status text and indicator
|
||||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
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");
|
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle");
|
||||||
} else {
|
} else {
|
||||||
switch(model->state) {
|
switch(model->state) {
|
||||||
|
@ -155,10 +183,6 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||||
break;
|
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) {
|
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;
|
SubRemViewRemote* subrem_view_remote = context;
|
||||||
|
|
||||||
if(event->key == InputKeyBack && event->type == InputTypeLong) {
|
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);
|
subrem_view_remote->callback(SubRemCustomEventViewRemoteBack, subrem_view_remote->context);
|
||||||
return true;
|
return true;
|
||||||
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
@ -248,23 +261,10 @@ SubRemViewRemote* subrem_view_remote_alloc() {
|
||||||
{
|
{
|
||||||
model->state = SubRemViewRemoteStateIdle;
|
model->state = SubRemViewRemoteStateIdle;
|
||||||
|
|
||||||
model->up_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
model->down_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
model->labels[i] = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||||
model->left_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
strcpy(model->labels[i], "");
|
||||||
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");
|
|
||||||
|
|
||||||
model->pressed_btn = 0;
|
model->pressed_btn = 0;
|
||||||
},
|
},
|
||||||
|
@ -279,17 +279,9 @@ void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
|
||||||
subghz_remote->view,
|
subghz_remote->view,
|
||||||
SubRemViewRemoteModel * model,
|
SubRemViewRemoteModel * model,
|
||||||
{
|
{
|
||||||
free(model->up_label);
|
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||||
free(model->down_label);
|
free(model->labels[i]);
|
||||||
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);
|
true);
|
||||||
view_free(subghz_remote->view);
|
view_free(subghz_remote->view);
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
#include "../helpers/subrem_custom_event.h"
|
#include "../helpers/subrem_custom_event.h"
|
||||||
|
#include "../helpers/subrem_presets.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SubRemViewRemoteStateIdle,
|
SubRemViewRemoteStateIdle,
|
||||||
SubRemViewRemoteStateLoading,
|
SubRemViewRemoteStateLoading,
|
||||||
SubRemViewRemoteStateSending,
|
SubRemViewRemoteStateSending,
|
||||||
|
SubRemViewRemoteStateOFF,
|
||||||
} SubRemViewRemoteState;
|
} SubRemViewRemoteState;
|
||||||
|
|
||||||
typedef struct SubRemViewRemote SubRemViewRemote;
|
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);
|
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,
|
SubRemViewRemote* subrem_view_remote,
|
||||||
const char* up_label,
|
SubRemSubFilePreset** subs_presets);
|
||||||
const char* down_label,
|
|
||||||
const char* left_label,
|
|
||||||
const char* right_label,
|
|
||||||
const char* ok_label);
|
|
||||||
|
|
||||||
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn);
|
|
||||||
void subrem_view_remote_set_state(
|
void subrem_view_remote_set_state(
|
||||||
SubRemViewRemote* subrem_view_remote,
|
SubRemViewRemote* subrem_view_remote,
|
||||||
SubRemViewRemoteState state);
|
SubRemViewRemoteState state,
|
||||||
|
uint8_t presed_btn);
|