Merge pull request #545 from DarkFlippers/subrem_app

SubRem app Improvement
This commit is contained in:
MMX 2023-07-14 17:11:09 +03:00 committed by GitHub
commit b663726576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 253 additions and 2504 deletions

View File

@ -1,16 +0,0 @@
App(
appid="subrem_configurator",
name="Remote Maker",
apptype=FlipperAppType.EXTERNAL,
entry_point="subghz_remote_config_app",
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
order=50,
fap_description="File Editor for the SubGhz Remote app",
fap_category="Sub-Ghz",
fap_icon_assets="icons",
fap_icon="icons/subrem_10px.png",
)

View File

@ -1,50 +0,0 @@
#pragma once
typedef enum {
SubRemEditMenuStateUP = 0,
SubRemEditMenuStateDOWN,
SubRemEditMenuStateLEFT,
SubRemEditMenuStateRIGHT,
SubRemEditMenuStateOK,
} SubRemEditMenuState;
typedef enum {
// StartSubmenuIndex
SubmenuIndexSubRemEditMapFile = 0,
SubmenuIndexSubRemNewMapFile,
#if FURI_DEBUG
SubmenuIndexSubRemRemoteView,
#endif
// SubmenuIndexSubRemAbout,
// EditSubmenuIndex
EditSubmenuIndexEditLabel,
EditSubmenuIndexEditFile,
// SubRemCustomEvent
SubRemCustomEventViewRemoteStartUP = 100,
SubRemCustomEventViewRemoteStartDOWN,
SubRemCustomEventViewRemoteStartLEFT,
SubRemCustomEventViewRemoteStartRIGHT,
SubRemCustomEventViewRemoteStartOK,
SubRemCustomEventViewRemoteBack,
SubRemCustomEventViewRemoteStop,
SubRemCustomEventViewRemoteForcedStop,
SubRemCustomEventViewEditMenuBack,
SubRemCustomEventViewEditMenuUP,
SubRemCustomEventViewEditMenuDOWN,
SubRemCustomEventViewEditMenuEdit,
SubRemCustomEventViewEditMenuSave,
SubRemCustomEventSceneEditsubmenu,
SubRemCustomEventSceneEditLabelInputDone,
SubRemCustomEventSceneEditLabelWidgetAcces,
SubRemCustomEventSceneEditLabelWidgetBack,
SubRemCustomEventSceneEditOpenSubErrorPopup,
SubRemCustomEventSceneEditPreviewSaved,
SubRemCustomEventSceneNewName,
} SubRemCustomEvent;

View File

@ -1,181 +0,0 @@
#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(!subghz_txrx_radio_device_is_frequecy_valid(txrx, temp_data32)) {
FURI_LOG_E(TAG, "Frequency not supported on chosen radio module");
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),
subghz_txrx_radio_device_get_name(txrx));
} else {
stream_copy_full(
flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(fff_data));
}
if(subghz_txrx_load_decoder_by_name_protocol(txrx, furi_string_get_cstr(temp_str))) {
SubGhzProtocolStatus status =
subghz_protocol_decoder_base_deserialize(subghz_txrx_get_decoder(txrx), fff_data);
if(status != SubGhzProtocolStatusOk) {
break;
}
} else {
FURI_LOG_E(TAG, "Protocol not found");
break;
}
const SubGhzProtocol* protocol = subghz_txrx_get_decoder(txrx)->protocol;
if(protocol->flag & SubGhzProtocolFlag_Send) {
if((protocol->type == SubGhzProtocolTypeStatic) ||
(protocol->type == SubGhzProtocolTypeDynamic) ||
(protocol->type == SubGhzProtocolTypeBinRAW) ||
(protocol->type == SubGhzProtocolTypeRAW)) {
sub_preset->type = protocol->type;
} else {
FURI_LOG_E(TAG, "Unsuported Protocol");
break;
}
furi_string_set(sub_preset->protocaol_name, temp_str);
} else {
FURI_LOG_E(TAG, "Protocol does not support transmission");
break;
}
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubRemLoadSubStateOK;
#if FURI_DEBUG
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
#endif
} while(false);
furi_string_free(temp_str);
sub_preset->load_state = ret;
return ret;
}

View File

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

View File

@ -1,48 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#define SUBREM_APP_APP_FILE_VERSION 1
#define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file"
#define SUBREM_APP_EXTENSION ".txt"
typedef enum {
SubRemSubKeyNameUp = (0U),
SubRemSubKeyNameDown,
SubRemSubKeyNameLeft,
SubRemSubKeyNameRight,
SubRemSubKeyNameOk,
SubRemSubKeyNameMaxCount,
} SubRemSubKeyName;
typedef enum {
SubRemViewIDSubmenu,
SubRemViewIDWidget,
SubRemViewIDPopup,
SubRemViewIDTextInput,
SubRemViewIDRemote,
SubRemViewIDEditMenu,
} SubRemViewID;
typedef enum {
SubRemLoadSubStateNotSet = 0,
SubRemLoadSubStatePreloaded,
SubRemLoadSubStateError,
SubRemLoadSubStateErrorIncorectPath,
SubRemLoadSubStateErrorNoFile,
SubRemLoadSubStateErrorFreq,
SubRemLoadSubStateErrorMod,
SubRemLoadSubStateErrorProtocol,
SubRemLoadSubStateOK,
} SubRemLoadSubState;
typedef enum {
SubRemLoadMapStateBack = 0,
SubRemLoadMapStateError,
SubRemLoadMapStateErrorOpenError,
SubRemLoadMapStateErrorStorage,
SubRemLoadMapStateErrorBrokenFile,
SubRemLoadMapStateNotAllOK,
SubRemLoadMapStateOK,
} SubRemLoadMapState;

View File

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

View File

@ -1,670 +0,0 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#include <lib/subghz/blocks/custom_btn.h>
#define TAG "SubGhz"
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
UNUSED(instance);
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
//CC1101 power-up time
furi_delay_ms(10);
}
}
static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {
UNUSED(instance);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
}
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, SUBGHZ_KEYSTORE_DIR_NAME);
subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
subghz_environment_set_came_atomo_rainbow_table_file_name(
instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
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);
//set default device Internal
subghz_devices_init();
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
instance->radio_device_type =
subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeInternal);
return instance;
}
void subghz_txrx_free(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
subghz_txrx_radio_device_power_off(instance);
subghz_devices_end(instance->radio_device);
}
subghz_devices_deinit();
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);
subghz_devices_reset(instance->radio_device);
subghz_devices_idle(instance->radio_device);
subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency);
subghz_devices_flush_rx(instance->radio_device);
subghz_txrx_speaker_on(instance);
subghz_devices_start_async_rx(
instance->radio_device, 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);
subghz_devices_idle(instance->radio_device);
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);
subghz_devices_stop_async_rx(instance->radio_device);
}
subghz_devices_idle(instance->radio_device);
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_txrx_sleep(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_devices_sleep(instance->radio_device);
instance->txrx_state = SubGhzTxRxStateSleep;
}
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
subghz_devices_set_frequency(instance->radio_device, frequency);
bool ret = subghz_devices_set_tx(instance->radio_device);
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
subghz_devices_start_async_tx(
instance->radio_device, 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
subghz_devices_stop_async_tx(instance->radio_device);
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
}
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 = subghz_devices_get_rssi(instance->radio_device);
// 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) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);
}
} else {
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, 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) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
}
}
}
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &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);
}
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) {
furi_assert(instance);
bool is_connect = false;
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
if(!is_otg_enabled) {
subghz_txrx_radio_device_power_on(instance);
}
const SubGhzDevice* device = subghz_devices_get_by_name(name);
if(device) {
is_connect = subghz_devices_is_connect(device);
}
if(!is_otg_enabled) {
subghz_txrx_radio_device_power_off(instance);
}
return is_connect;
}
SubGhzRadioDeviceType
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) {
furi_assert(instance);
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
subghz_txrx_radio_device_power_on(instance);
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
subghz_devices_begin(instance->radio_device);
instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101;
} else {
subghz_txrx_radio_device_power_off(instance);
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
subghz_devices_end(instance->radio_device);
}
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
}
return instance->radio_device_type;
}
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->radio_device_type;
}
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) {
furi_assert(instance);
return subghz_devices_get_rssi(instance->radio_device);
}
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) {
furi_assert(instance);
return subghz_devices_get_name(instance->radio_device);
}
bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
return subghz_devices_is_frequency_valid(instance->radio_device, frequency);
}
bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
subghz_devices_set_frequency(instance->radio_device, frequency);
bool ret = subghz_devices_set_tx(instance->radio_device);
subghz_devices_idle(instance->radio_device);
return ret;
}
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;
}
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_environment_reset_keeloq(instance->environment);
subghz_custom_btns_reset();
}
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->receiver;
}

View File

@ -1,375 +0,0 @@
#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>
#include <lib/subghz/devices/devices.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;
/** SubGhzRadioDeviceType */
typedef enum {
SubGhzRadioDeviceTypeAuto,
SubGhzRadioDeviceTypeInternal,
SubGhzRadioDeviceTypeExternalCC1101,
} SubGhzRadioDeviceType;
/**
* 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);
/* Checking if an external radio device is connected
*
* @param instance Pointer to a SubGhzTxRx
* @param name Name of external radio device
* @return bool True if is connected to the external radio device
*/
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name);
/* Set the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @param radio_device_type Radio device type
* @return SubGhzRadioDeviceType Type of installed radio device
*/
SubGhzRadioDeviceType
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type);
/* Get the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzRadioDeviceType Type of installed radio device
*/
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance);
/* Get RSSI the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return float RSSI
*/
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance);
/* Get name the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return const char* Name of installed radio device
*/
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance);
/* Get get intelligence whether frequency the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if the frequency is valid
*/
bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency);
bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency);
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state);
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance);
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance);
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw

View File

@ -1,31 +0,0 @@
#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;
const SubGhzDevice* radio_device;
SubGhzRadioDeviceType radio_device_type;
SubGhzTxRxNeedSaveCallback need_save_callback;
void* need_save_context;
bool debug_pin_state;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

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

View File

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

View File

@ -1,8 +0,0 @@
ADD_SCENE(subrem, start, Start)
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)

View File

@ -1,29 +0,0 @@
#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);
}

View File

@ -1,73 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
void subrem_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void subrem_scene_start_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Edit Map File",
SubmenuIndexSubRemEditMapFile,
subrem_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"New Map File",
SubmenuIndexSubRemNewMapFile,
subrem_scene_start_submenu_callback,
app);
// submenu_add_item(
// submenu,
// "About",
// SubmenuIndexSubGhzRemoteAbout,
// subrem_scene_start_submenu_callback,
// app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
}
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == 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) {
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
// consumed = true;
// }
}
return consumed;
}
void subrem_scene_start_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -1,183 +0,0 @@
#include "subghz_remote_app_i.h"
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool subghz_remote_app_back_event_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void subghz_remote_app_tick_event_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
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);
// furi_hal_power_suppress_charge_enter();
app->file_path = furi_string_alloc();
furi_string_set(app->file_path, SUBREM_APP_FOLDER);
// GUI
app->gui = furi_record_open(RECORD_GUI);
// View Dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&subrem_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, subghz_remote_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, subghz_remote_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, subghz_remote_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewIDSubmenu, submenu_get_view(app->submenu));
// Dialog
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
app->subrem_remote_view = subrem_view_remote_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewIDRemote,
subrem_view_remote_get_view(app->subrem_remote_view));
// 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++) {
app->map_preset->subs_preset[i] = subrem_sub_file_preset_alloc();
}
app->txrx = subghz_txrx_alloc();
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
app->map_not_saved = false;
#ifdef SUBREM_LIGHT
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
#else
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
#endif
return app;
}
void subghz_remote_app_free(SubGhzRemoteApp* app) {
furi_assert(app);
// furi_hal_power_suppress_charge_exit();
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
submenu_free(app->submenu);
// Dialog
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
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
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);
view_dispatcher_free(app->view_dispatcher);
subghz_txrx_free(app->txrx);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
subrem_sub_file_preset_free(app->map_preset->subs_preset[i]);
}
free(app->map_preset);
// Notifications
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Close records
furi_record_close(RECORD_GUI);
// Path strings
furi_string_free(app->file_path);
free(app);
}
int32_t subghz_remote_config_app(void* p) {
UNUSED(p);
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
view_dispatcher_run(subghz_remote_app->view_dispatcher);
subghz_remote_app_free(subghz_remote_app);
return 0;
}

View File

@ -1,270 +0,0 @@
#include "subghz_remote_app_i.h"
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format_i.h>
#include "helpers/txrx/subghz_txrx.h"
// #include <lib/subghz/protocols/keeloq.h>
// #include <lib/subghz/protocols/star_line.h>
// #include <lib/subghz/protocols/protocol_items.h>
// #include <lib/subghz/blocks/custom_btn.h>
#define TAG "SubGhzRemote"
static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
[SubRemSubKeyNameUp] = {"UP", "ULABEL"},
[SubRemSubKeyNameDown] = {"DOWN", "DLABEL"},
[SubRemSubKeyNameLeft] = {"LEFT", "LLABEL"},
[SubRemSubKeyNameRight] = {"RIGHT", "RLABEL"},
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
};
void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
furi_assert(map_preset);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
subrem_sub_file_preset_reset(map_preset->subs_preset[i]);
}
}
static SubRemLoadMapState subrem_map_preset_check(
SubRemMapPreset* map_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file) {
furi_assert(map_preset);
furi_assert(txrx);
bool all_loaded = true;
SubRemLoadMapState ret = SubRemLoadMapStateErrorBrokenFile;
SubRemLoadSubState sub_loadig_state;
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = map_preset->subs_preset[i];
sub_loadig_state = SubRemLoadSubStateErrorNoFile;
if(furi_string_empty(sub_preset->file_path)) {
// FURI_LOG_I(TAG, "Empty file path");
} else if(!flipper_format_file_open_existing(
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
sub_preset->load_state = SubRemLoadSubStateErrorNoFile;
FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path));
} else {
sub_loadig_state = subrem_sub_preset_load(sub_preset, txrx, fff_data_file);
}
if(sub_loadig_state != SubRemLoadSubStateOK) {
all_loaded = false;
} else {
ret = SubRemLoadMapStateNotAllOK;
}
if(ret != SubRemLoadMapStateErrorBrokenFile && all_loaded) {
ret = SubRemLoadMapStateOK;
}
flipper_format_file_close(fff_data_file);
}
return ret;
}
static bool subrem_map_preset_load(SubRemMapPreset* map_preset, FlipperFormat* fff_data_file) {
furi_assert(map_preset);
bool ret = false;
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = map_preset->subs_preset[i];
if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][0], sub_preset->file_path)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No file patch for %s", map_file_labels[i][0]);
#endif
sub_preset->type = SubGhzProtocolTypeUnknown;
} else if(!path_contains_only_ascii(furi_string_get_cstr(sub_preset->file_path))) {
FURI_LOG_E(TAG, "Incorrect characters in [%s] file path", map_file_labels[i][0]);
sub_preset->type = SubGhzProtocolTypeUnknown;
} else if(!flipper_format_rewind(fff_data_file)) {
// Rewind error
} else if(!flipper_format_read_string(
fff_data_file, map_file_labels[i][1], sub_preset->label)) {
#if FURI_DEBUG
FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]);
#endif
ret = true;
} else {
ret = true;
}
if(ret) {
// Preload seccesful
FURI_LOG_I(
TAG,
"%-5s: %s %s",
map_file_labels[i][0],
furi_string_get_cstr(sub_preset->label),
furi_string_get_cstr(sub_preset->file_path));
sub_preset->load_state = SubRemLoadSubStatePreloaded;
}
flipper_format_rewind(fff_data_file);
}
return ret;
}
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path) {
furi_assert(app);
furi_assert(file_path);
#if FURI_DEBUG
FURI_LOG_I(TAG, "Load Map File Start");
#endif
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
SubRemLoadMapState ret = SubRemLoadMapStateErrorOpenError;
#if FURI_DEBUG
FURI_LOG_I(TAG, "Open Map File..");
#endif
subrem_map_preset_reset(app->map_preset);
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
ret = SubRemLoadMapStateErrorOpenError;
} else {
if(!subrem_map_preset_load(app->map_preset, fff_data_file)) {
FURI_LOG_E(TAG, "Could no Sub file path in MAP file");
// ret = // error for popup
} else if(!flipper_format_file_close(fff_data_file)) {
ret = SubRemLoadMapStateErrorOpenError;
} else {
ret = subrem_map_preset_check(app->map_preset, app->txrx, fff_data_file);
}
}
if(ret == SubRemLoadMapStateOK) {
FURI_LOG_I(TAG, "Load Map File Seccesful");
} else if(ret == SubRemLoadMapStateNotAllOK) {
FURI_LOG_I(TAG, "Load Map File Seccesful [Not all files]");
} else {
FURI_LOG_E(TAG, "Broken Map File");
}
flipper_format_file_close(fff_data_file);
flipper_format_free(fff_data_file);
furi_record_close(RECORD_STORAGE);
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) {
furi_assert(app);
FuriString* file_path = furi_string_alloc();
SubRemLoadMapState ret = SubRemLoadMapStateBack;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_subrem_10px);
browser_options.base_path = SUBREM_APP_FOLDER;
// Input events and views are managed by file_select
if(!dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options)) {
} else {
ret = subrem_map_file_load(app, furi_string_get_cstr(app->file_path));
}
furi_string_free(file_path);
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;
}

View File

@ -1,63 +0,0 @@
#pragma once
#include "helpers/subrem_types.h"
#include "helpers/subrem_presets.h"
#include "scenes/subrem_scene.h"
#include "helpers/txrx/subghz_txrx.h"
#include <subrem_configurator_icons.h>
#include "views/remote.h"
#include "views/edit_menu.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_input.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>
#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote")
#define SUBREM_MAX_LEN_NAME 64
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
Popup* popup;
TextInput* text_input;
Submenu* submenu;
FuriString* file_path;
char file_name_tmp[SUBREM_MAX_LEN_NAME];
SubRemViewRemote* subrem_remote_view;
SubRemViewEditMenu* subrem_edit_menu;
SubRemMapPreset* map_preset;
SubGhzTxRx* txrx;
bool map_not_saved;
uint8_t chusen_sub;
} SubGhzRemoteApp;
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);

View File

@ -1,294 +0,0 @@
#include "remote.h"
#include "../subghz_remote_app_i.h"
#include <input/input.h>
#include <gui/elements.h>
#include <lib/toolbox/path.h>
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 30
#define SUBREM_VIEW_REMOTE_LEFT_OFFSET 10
#define SUBREM_VIEW_REMOTE_RIGHT_OFFSET 22
struct SubRemViewRemote {
View* view;
SubRemViewRemoteCallback callback;
void* context;
};
typedef struct {
char* labels[SubRemSubKeyNameMaxCount];
SubRemViewRemoteState state;
uint8_t pressed_btn;
} SubRemViewRemoteModel;
void subrem_view_remote_set_callback(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteCallback callback,
void* context) {
furi_assert(subrem_view_remote);
subrem_view_remote->callback = callback;
subrem_view_remote->context = context;
}
void subrem_view_remote_update_data_labels(
SubRemViewRemote* subrem_view_remote,
SubRemSubFilePreset** subs_presets) {
furi_assert(subrem_view_remote);
furi_assert(subs_presets);
FuriString* labels[SubRemSubKeyNameMaxCount];
SubRemSubFilePreset* sub_preset;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = subs_presets[i];
switch(sub_preset->load_state) {
case SubRemLoadSubStateOK:
if(!furi_string_empty(sub_preset->label)) {
labels[i] = furi_string_alloc_set(sub_preset->label);
} else if(!furi_string_empty(sub_preset->file_path)) {
labels[i] = furi_string_alloc();
path_extract_filename(sub_preset->file_path, labels[i], true);
} else {
labels[i] = furi_string_alloc_set("Empty Label");
}
break;
case SubRemLoadSubStateErrorNoFile:
labels[i] = furi_string_alloc_set("[X] Can't open file");
break;
case SubRemLoadSubStateErrorFreq:
case SubRemLoadSubStateErrorMod:
case SubRemLoadSubStateErrorProtocol:
labels[i] = furi_string_alloc_set("[X] Error in .sub file");
break;
default:
labels[i] = furi_string_alloc_set("");
break;
}
}
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
strncpy(
model->labels[i],
furi_string_get_cstr(labels[i]),
SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
}
},
true);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
furi_string_free(labels[i]);
}
}
void subrem_view_remote_set_state(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteState state,
uint8_t presed_btn) {
furi_assert(subrem_view_remote);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
model->state = state;
model->pressed_btn = presed_btn;
},
true);
}
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_clear(canvas);
//Icons for Labels
//canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64);
canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7);
canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 0, 53, &I_back_10px);
//Labels
canvas_set_font(canvas, FontSecondary);
uint8_t y = 0;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
elements_text_box(
canvas,
SUBREM_VIEW_REMOTE_LEFT_OFFSET,
y + 2,
126 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET,
12,
AlignLeft,
AlignBottom,
model->labels[i],
false);
y += 10;
}
if(model->state == SubRemViewRemoteStateOFF) {
elements_button_left(canvas, "Back");
elements_button_right(canvas, "Save");
} else {
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
}
//Status text and indicator
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
if(model->state == SubRemViewRemoteStateIdle || model->state == SubRemViewRemoteStateOFF) {
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle");
} else {
switch(model->state) {
case SubRemViewRemoteStateSending:
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Send");
break;
case SubRemViewRemoteStateLoading:
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Load");
break;
default:
#if FURI_DEBUG
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Wrong_state");
#endif
break;
}
switch(model->pressed_btn) {
case SubRemSubKeyNameUp:
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9);
break;
case SubRemSubKeyNameDown:
canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180);
break;
case SubRemSubKeyNameLeft:
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270);
break;
case SubRemSubKeyNameRight:
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90);
break;
case SubRemSubKeyNameOk:
canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7);
break;
}
}
}
bool subrem_view_remote_input(InputEvent* event, void* context) {
furi_assert(context);
SubRemViewRemote* subrem_view_remote = context;
if(event->key == InputKeyBack && event->type == InputTypeLong) {
subrem_view_remote->callback(SubRemCustomEventViewRemoteBack, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{ model->pressed_btn = 0; },
true);
subrem_view_remote->callback(
SubRemCustomEventViewRemoteForcedStop, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyBack) {
return true;
}
// BACK button processing end
if(event->key == InputKeyUp && event->type == InputTypePress) {
subrem_view_remote->callback(
SubRemCustomEventViewRemoteStartUP, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyDown && event->type == InputTypePress) {
subrem_view_remote->callback(
SubRemCustomEventViewRemoteStartDOWN, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyLeft && event->type == InputTypePress) {
subrem_view_remote->callback(
SubRemCustomEventViewRemoteStartLEFT, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyRight && event->type == InputTypePress) {
subrem_view_remote->callback(
SubRemCustomEventViewRemoteStartRIGHT, subrem_view_remote->context);
return true;
} else if(event->key == InputKeyOk && event->type == InputTypePress) {
subrem_view_remote->callback(
SubRemCustomEventViewRemoteStartOK, subrem_view_remote->context);
return true;
} else if(event->type == InputTypeRelease) {
subrem_view_remote->callback(SubRemCustomEventViewRemoteStop, subrem_view_remote->context);
return true;
}
return true;
}
void subrem_view_remote_enter(void* context) {
furi_assert(context);
}
void subrem_view_remote_exit(void* context) {
furi_assert(context);
}
SubRemViewRemote* subrem_view_remote_alloc() {
SubRemViewRemote* subrem_view_remote = malloc(sizeof(SubRemViewRemote));
// View allocation and configuration
subrem_view_remote->view = view_alloc();
view_allocate_model(
subrem_view_remote->view, ViewModelTypeLocking, sizeof(SubRemViewRemoteModel));
view_set_context(subrem_view_remote->view, subrem_view_remote);
view_set_draw_callback(subrem_view_remote->view, (ViewDrawCallback)subrem_view_remote_draw);
view_set_input_callback(subrem_view_remote->view, subrem_view_remote_input);
view_set_enter_callback(subrem_view_remote->view, subrem_view_remote_enter);
view_set_exit_callback(subrem_view_remote->view, subrem_view_remote_exit);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{
model->state = SubRemViewRemoteStateIdle;
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
model->labels[i] = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
strcpy(model->labels[i], "");
}
model->pressed_btn = 0;
},
true);
return subrem_view_remote;
}
void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
furi_assert(subghz_remote);
with_view_model(
subghz_remote->view,
SubRemViewRemoteModel * model,
{
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
free(model->labels[i]);
}
},
true);
view_free(subghz_remote->view);
free(subghz_remote);
}
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote) {
furi_assert(subrem_view_remote);
return subrem_view_remote->view;
}

View File

@ -1,36 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subrem_custom_event.h"
#include "../helpers/subrem_presets.h"
typedef enum {
SubRemViewRemoteStateIdle,
SubRemViewRemoteStateLoading,
SubRemViewRemoteStateSending,
SubRemViewRemoteStateOFF,
} SubRemViewRemoteState;
typedef struct SubRemViewRemote SubRemViewRemote;
typedef void (*SubRemViewRemoteCallback)(SubRemCustomEvent event, void* context);
void subrem_view_remote_set_callback(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteCallback callback,
void* context);
SubRemViewRemote* subrem_view_remote_alloc();
void subrem_view_remote_free(SubRemViewRemote* subrem_view_remote);
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote);
void subrem_view_remote_update_data_labels(
SubRemViewRemote* subrem_view_remote,
SubRemSubFilePreset** subs_presets);
void subrem_view_remote_set_state(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteState state,
uint8_t presed_btn);

View File

@ -12,6 +12,7 @@ static const char* tab_default_paths[] = {
[ArchiveTabIButton] = ANY_PATH("ibutton"),
[ArchiveTabNFC] = ANY_PATH("nfc"),
[ArchiveTabSubGhz] = ANY_PATH("subghz"),
[ArchiveTabSubGhzRemote] = EXT_PATH("subghz_remote"),
[ArchiveTabLFRFID] = ANY_PATH("lfrfid"),
[ArchiveTabInfrared] = ANY_PATH("infrared"),
[ArchiveTabBadUsb] = ANY_PATH("badusb"),
@ -25,6 +26,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeIButton] = ".ibtn",
[ArchiveFileTypeNFC] = ".nfc",
[ArchiveFileTypeSubGhz] = ".sub",
[ArchiveFileTypeSubGhzRemote] = ".txt",
[ArchiveFileTypeLFRFID] = ".rfid",
[ArchiveFileTypeInfrared] = ".ir",
[ArchiveFileTypeBadUsb] = ".txt",
@ -40,6 +42,7 @@ static const ArchiveFileTypeEnum known_type[] = {
[ArchiveTabIButton] = ArchiveFileTypeIButton,
[ArchiveTabNFC] = ArchiveFileTypeNFC,
[ArchiveTabSubGhz] = ArchiveFileTypeSubGhz,
[ArchiveTabSubGhzRemote] = ArchiveFileTypeSubGhzRemote,
[ArchiveTabLFRFID] = ArchiveFileTypeLFRFID,
[ArchiveTabInfrared] = ArchiveFileTypeInfrared,
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,

View File

@ -22,6 +22,12 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
file->type = i;
return; // *.txt file is a BadUSB script only if it is in BadUSB folder
}
} else if(i == ArchiveFileTypeSubGhzRemote) {
if(furi_string_search(
file->path, archive_get_default_path(ArchiveTabSubGhzRemote)) == 0) {
file->type = i;
return; // *.txt file is a SubRem map file only if it is in SubRem folder
}
} else {
file->type = i;
return;

View File

@ -12,6 +12,7 @@ typedef enum {
ArchiveFileTypeIButton,
ArchiveFileTypeNFC,
ArchiveFileTypeSubGhz,
ArchiveFileTypeSubGhzRemote,
ArchiveFileTypeLFRFID,
ArchiveFileTypeInfrared,
ArchiveFileTypeBadUsb,

View File

@ -20,6 +20,8 @@ static const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) {
return "NFC";
case ArchiveFileTypeSubGhz:
return "Sub-GHz";
case ArchiveFileTypeSubGhzRemote:
return "Sub-GHz Remote";
case ArchiveFileTypeLFRFID:
return "125 kHz RFID";
case ArchiveFileTypeInfrared:

View File

@ -14,6 +14,7 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabIButton] = "iButton",
[ArchiveTabNFC] = "NFC",
[ArchiveTabSubGhz] = "Sub-GHz",
[ArchiveTabSubGhzRemote] = "SubRem",
[ArchiveTabLFRFID] = "RFID LF",
[ArchiveTabInfrared] = "Infrared",
[ArchiveTabBadUsb] = "Bad USB",
@ -27,6 +28,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeIButton] = &I_ibutt_10px,
[ArchiveFileTypeNFC] = &I_Nfc_10px,
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
[ArchiveFileTypeSubGhzRemote] = &I_subrem_10px,
[ArchiveFileTypeLFRFID] = &I_125_10px,
[ArchiveFileTypeInfrared] = &I_ir_10px,
[ArchiveFileTypeBadUsb] = &I_badusb_10px,

View File

@ -24,6 +24,7 @@
typedef enum {
ArchiveTabFavorites,
ArchiveTabSubGhz,
ArchiveTabSubGhzRemote,
ArchiveTabLFRFID,
ArchiveTabNFC,
ArchiveTabInfrared,

View File

@ -4,9 +4,14 @@ App(
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="subghz_remote_app",
icon="A_SubGHzRemote_14",
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
order=11,
fap_libs=["assets",],
fap_icon="icon.png",
fap_description="SubGhz Remote, uses up to 5 .sub files",
fap_category="Sub-Ghz",
)

View File

@ -1,13 +1,27 @@
#pragma once
typedef enum {
SubRemEditMenuStateUP = 0,
SubRemEditMenuStateDOWN,
SubRemEditMenuStateLEFT,
SubRemEditMenuStateRIGHT,
SubRemEditMenuStateOK,
} SubRemEditMenuState;
typedef enum {
// StartSubmenuIndex
SubmenuIndexSubRemOpenMapFile = 0,
SubmenuIndexSubRemEditMapFile,
SubmenuIndexSubRemNewMapFile,
#if FURI_DEBUG
SubmenuIndexSubRemRemoteView,
#endif
// SubmenuIndexSubRemAbout,
// EditSubmenuIndex
EditSubmenuIndexEditLabel,
EditSubmenuIndexEditFile,
// SubRemCustomEvent
SubRemCustomEventViewRemoteStartUP = 100,
SubRemCustomEventViewRemoteStartDOWN,
@ -17,4 +31,21 @@ typedef enum {
SubRemCustomEventViewRemoteBack,
SubRemCustomEventViewRemoteStop,
SubRemCustomEventViewRemoteForcedStop,
SubRemCustomEventViewEditMenuBack,
SubRemCustomEventViewEditMenuUP,
SubRemCustomEventViewEditMenuDOWN,
SubRemCustomEventViewEditMenuEdit,
SubRemCustomEventViewEditMenuSave,
SubRemCustomEventSceneEditsubmenu,
SubRemCustomEventSceneEditLabelInputDone,
SubRemCustomEventSceneEditLabelWidgetAcces,
SubRemCustomEventSceneEditLabelWidgetBack,
SubRemCustomEventSceneEditOpenSubErrorPopup,
SubRemCustomEventSceneEditPreviewSaved,
SubRemCustomEventSceneNewName,
} SubRemCustomEvent;

View File

@ -18,7 +18,11 @@ typedef enum {
typedef enum {
SubRemViewIDSubmenu,
SubRemViewIDWidget,
SubRemViewIDPopup,
SubRemViewIDTextInput,
SubRemViewIDRemote,
SubRemViewIDEditMenu,
} SubRemViewID;
typedef enum {

View File

@ -1,5 +1,9 @@
#ifndef SUBREM_LIGHT
ADD_SCENE(subrem, start, Start)
#endif
ADD_SCENE(subrem, open_map_file, OpenMapFile)
ADD_SCENE(subrem, remote, Remote)
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)

View File

@ -5,30 +5,16 @@ void subrem_scene_open_map_file_on_enter(void* 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);
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
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);
} else if(start_scene_state == SubmenuIndexSubRemOpenMapFile) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
} else {
if(load_state != SubRemLoadMapStateBack) {
#ifdef SUBREM_LIGHT
dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file");
#else
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter);
dialog_message_set_text(
message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, "Back", NULL, NULL);
dialog_message_show(app->dialogs, message);
dialog_message_free(message);
#endif
}
// TODO: Map Preset Reset
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
}
}

View File

@ -39,6 +39,9 @@ void subrem_scene_remote_on_enter(void* context) {
SubGhzRemoteApp* app = context;
subrem_view_remote_update_data_labels(app->subrem_remote_view, app->map_preset->subs_preset);
subrem_view_remote_set_radio(
app->subrem_remote_view,
subghz_txrx_radio_device_get(app->txrx) != SubGhzRadioDeviceTypeInternal);
subrem_view_remote_set_callback(app->subrem_remote_view, subrem_scene_remote_callback, app);

View File

@ -27,16 +27,28 @@ void subrem_scene_start_on_enter(void* context) {
subrem_scene_start_submenu_callback,
app);
#endif
submenu_add_item(
submenu,
"Edit Map File",
SubmenuIndexSubRemEditMapFile,
subrem_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"New Map File",
SubmenuIndexSubRemNewMapFile,
subrem_scene_start_submenu_callback,
app);
// submenu_add_item(
// submenu,
// "About",
// SubmenuIndexSubGhzRemoteAbout,
// subrem_scene_start_submenu_callback,
// app);
#ifndef SUBREM_LIGHT
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
#endif
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
}
@ -48,23 +60,33 @@ bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSubRemOpenMapFile) {
#ifndef SUBREM_LIGHT
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
#endif
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
consumed = true;
}
// } else if(event.event == SubmenuIndexSubRemAbout) {
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
// consumed = true;
// }
#if FURI_DEBUG
else if(event.event == SubmenuIndexSubRemRemoteView) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
consumed = true;
}
#endif
else 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) {
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
// consumed = true;
// }
}
return consumed;

View File

@ -18,25 +18,23 @@ static void subghz_remote_app_tick_event_callback(void* context) {
scene_manager_handle_tick_event(app->scene_manager);
}
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
static void subghz_remote_make_app_folder(SubGhzRemoteApp* app) {
furi_assert(app);
Storage* storage = furi_record_open(RECORD_STORAGE);
// Migrate old users data
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_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
}
furi_record_close(RECORD_STORAGE);
}
// // Enable power for External CC1101 if it is connected
// furi_hal_subghz_enable_ext_power();
// // Auto switch to internal radio if external radio is not available
// furi_delay_ms(15);
// if(!furi_hal_subghz_check_radio()) {
// furi_hal_subghz_select_radio_type(SubGhzRadioInternal);
// furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
// }
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
furi_hal_power_suppress_charge_enter();
@ -73,6 +71,20 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
// Dialog
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
app->subrem_remote_view = subrem_view_remote_alloc();
view_dispatcher_add_view(
@ -80,6 +92,13 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
SubRemViewIDRemote,
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++) {
app->map_preset->subs_preset[i] = subrem_sub_file_preset_alloc();
@ -89,13 +108,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() {
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
#ifdef SUBREM_LIGHT
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
#else
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
#endif
app->map_not_saved = false;
return app;
}
@ -105,11 +118,6 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
furi_hal_power_suppress_charge_exit();
// // Disable power for External CC1101 if it was enabled and module is connected
// furi_hal_subghz_disable_ext_power();
// // Reinit SPI handles for internal radio / nfc
// furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
submenu_free(app->submenu);
@ -117,10 +125,26 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
// Dialog
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
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
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);
view_dispatcher_free(app->view_dispatcher);
@ -144,11 +168,33 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
free(app);
}
int32_t subghz_remote_app(void* p) {
UNUSED(p);
int32_t subghz_remote_app(void* arg) {
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
subghz_remote_make_app_folder(subghz_remote_app);
bool map_loaded = false;
if((arg != NULL) && (strlen(arg) != 0)) {
furi_string_set(subghz_remote_app->file_path, (const char*)arg);
SubRemLoadMapState load_state = subrem_map_file_load(
subghz_remote_app, furi_string_get_cstr(subghz_remote_app->file_path));
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
map_loaded = true;
} else {
// TODO Replace
dialog_message_show_storage_error(subghz_remote_app->dialogs, "Cannot load\nmap file");
}
}
if(map_loaded) {
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneRemote);
} else {
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneStart);
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile);
}
view_dispatcher_run(subghz_remote_app->view_dispatcher);

View File

@ -7,10 +7,8 @@
// #include <lib/subghz/protocols/keeloq.h>
// #include <lib/subghz/protocols/star_line.h>
#ifdef APP_SUBGHZREMOTE
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/blocks/custom_btn.h>
#endif
#define TAG "SubGhzRemote"
@ -22,7 +20,7 @@ static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
};
static void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
furi_assert(map_preset);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
@ -228,9 +226,7 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset)
NULL,
0);
#ifdef APP_SUBGHZREMOTE
subghz_custom_btns_reset();
#endif
if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) {
ret = true;
@ -246,12 +242,12 @@ bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
subghz_txrx_stop(app->txrx);
#ifdef APP_SUBGHZREMOTE
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
subghz_txrx_reset_dynamic_and_custom_btns(app->txrx);
}
subghz_custom_btns_reset();
#endif
return true;
}
@ -278,3 +274,47 @@ SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
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;
}

View File

@ -1,21 +1,15 @@
#pragma once
#define SUBREM_LIGHT 1
#define APP_SUBGHZREMOTE 1
#include "helpers/subrem_types.h"
#include "helpers/subrem_presets.h"
#include "scenes/subrem_scene.h"
#include "helpers/txrx/subghz_txrx.h"
#ifdef APP_SUBGHZREMOTE
#include <assets_icons.h>
#else
#include <subrem_remote_fap_icons.h>
#endif
#include "views/remote.h"
#include "views/edit_menu.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
@ -39,16 +33,23 @@ typedef struct {
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
Popup* popup;
TextInput* text_input;
Submenu* submenu;
FuriString* file_path;
char file_name_tmp[SUBREM_MAX_LEN_NAME];
SubRemViewRemote* subrem_remote_view;
SubRemViewEditMenu* subrem_edit_menu;
SubRemMapPreset* map_preset;
SubGhzTxRx* txrx;
bool map_not_saved;
uint8_t chusen_sub;
} SubGhzRemoteApp;
@ -58,4 +59,10 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset);
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
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);

View File

@ -22,6 +22,7 @@ typedef struct {
SubRemViewRemoteState state;
uint8_t pressed_btn;
bool is_external;
} SubRemViewRemoteModel;
void subrem_view_remote_set_callback(
@ -106,6 +107,15 @@ void subrem_view_remote_set_state(
true);
}
void subrem_view_remote_set_radio(SubRemViewRemote* subrem_view_remote, bool external) {
furi_assert(subrem_view_remote);
with_view_model(
subrem_view_remote->view,
SubRemViewRemoteModel * model,
{ model->is_external = external; },
true);
}
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
@ -143,6 +153,8 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
elements_button_right(canvas, "Save");
} else {
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
canvas_draw_str_aligned(
canvas, 126, 62, AlignRight, AlignBottom, ((model->is_external) ? "Ext" : "Int"));
}
//Status text and indicator
@ -267,6 +279,7 @@ SubRemViewRemote* subrem_view_remote_alloc() {
}
model->pressed_btn = 0;
model->is_external = false;
},
true);
return subrem_view_remote;

View File

@ -34,3 +34,5 @@ void subrem_view_remote_set_state(
SubRemViewRemote* subrem_view_remote,
SubRemViewRemoteState state,
uint8_t presed_btn);
void subrem_view_remote_set_radio(SubRemViewRemote* subrem_view_remote, bool external);