fix module p1

This commit is contained in:
MX 2023-09-04 23:11:40 +03:00
parent ac4177c248
commit 99eb10d6e7
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
50 changed files with 0 additions and 3816 deletions

View file

@ -1,4 +0,0 @@
dist/*
.vscode
.clang-format
.editorconfig

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 DarkFlippers @gid9798 @xMasterX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,43 +0,0 @@
# App(
# appid="subghz_remote_ofw",
# name="Sub-GHz Remote",
# apptype=FlipperAppType.EXTERNAL,
# entry_point="subghz_remote_app",
# requires=[
# "gui",
# "dialogs",
# ],
# stack_size=2 * 1024,
# targets=["f7"],
# fap_icon="icon.png",
# fap_author="gid9798 xMasterX",
# fap_description="SubGhz Remote, uses up to 5 .sub files",
# fap_category="Sub-GHz",
# fap_icon_assets="icons",
# fap_icon_assets_symbol="subghz_remote",
# fap_version="1.2",
# fap_weburl="https://github.com/DarkFlippers/SubGHz_Remote",
# )
App(
appid="subghz_remote",
name="Sub-GHz Remote",
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="subghz_remote_app",
icon="A_SubGHzRemote_14",
order=11,
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
targets=["f7"],
fap_icon="icon.png",
fap_author="gid9798 xMasterX",
fap_description="SubGhz Remote, uses up to 5 .sub files",
fap_category="Sub-GHz",
fap_icon_assets="icons",
fap_icon_assets_symbol="subghz_remote",
fap_version="1.2",
fap_weburl="https://github.com/DarkFlippers/SubGHz_Remote",
)

View file

@ -1,25 +0,0 @@
## v1.2
- **Official FirmWare Support**
- Add warning screen on CustomFW
- The .sub file format may differ from the official one and may be broken
## v1.1
- **Was combined with a configuration plugin**
- Editing/Creating map file
- Support for starting arguments
## v1.0
**Initial implementation:**
- Transmission
- GUI
- All .sub files for which transfer is available are supported
- Signal types:
- Static
- Dynamic
- RAW
- BinRAW
*Custom modulations are not supported yet*
**Map File Format** - FlipperFormat .txt file

View file

@ -1,24 +0,0 @@
With this application, you can combine up to 5 .sub files into one remote, and use flipper as a remote with multiple buttons.
## What is "Map" Files?
"Map" is short for mapping
A Map Files is a .txt files that the application uses to store information about remotes
# How to use
## First screen
After launching the application, you will see the MAP file selection screen (file browser).
- Select map file or press "back" to go Main menu
## Main menu
- Open map file - switching to remote
- Select map file
- On remote screen, use the navigation buttons(D-pad) to send a signal
- Edit Map File - map file editor
- Select map file
- Up/Down - slot nafigation
- Ok - edit menu
- Left - preview/save
- New Map File - Creating a new map file
- Enter a name
- The rest is similar to map file editor
# About map file
Map file - FlipperFormat .txt file.
Stores custom names, and paths to used .sub files.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,58 +0,0 @@
#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,
SubRemCustomEventViewRemoteStartLEFT,
SubRemCustomEventViewRemoteStartRIGHT,
SubRemCustomEventViewRemoteStartOK,
SubRemCustomEventViewRemoteBack,
SubRemCustomEventViewRemoteStop,
SubRemCustomEventViewRemoteForcedStop,
SubRemCustomEventViewEditMenuBack,
SubRemCustomEventViewEditMenuUP,
SubRemCustomEventViewEditMenuDOWN,
SubRemCustomEventViewEditMenuEdit,
SubRemCustomEventViewEditMenuSave,
SubRemCustomEventSceneEditsubmenu,
SubRemCustomEventSceneEditLabelInputDone,
SubRemCustomEventSceneEditLabelWidgetAcces,
SubRemCustomEventSceneEditLabelWidgetBack,
SubRemCustomEventSceneEditOpenSubErrorPopup,
SubRemCustomEventSceneEditPreviewSaved,
SubRemCustomEventSceneNewName,
#ifdef FW_ORIGIN_Official
SubRemCustomEventSceneFwWarningExit,
SubRemCustomEventSceneFwWarningNext,
SubRemCustomEventSceneFwWarningContinue,
#endif
} SubRemCustomEvent;

View file

@ -1,183 +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_frequency_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) ||
#ifndef FW_ORIGIN_Official
(protocol->type == SubGhzProtocolTypeBinRAW) ||
#endif
(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,674 +0,0 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/subghz_protocol_registry.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#ifndef FW_ORIGIN_Official
#include <lib/subghz/blocks/custom_btn.h>
#endif
#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, SubGhzRadioDeviceTypeExternalCC1101);
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_frequency_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_allowed(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;
}
#ifndef FW_ORIGIN_Official
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_environment_reset_keeloq(instance->environment);
subghz_custom_btns_reset();
}
#endif
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_frequency_valid(SubGhzTxRx* instance, uint32_t frequency);
bool subghz_txrx_radio_device_is_tx_allowed(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);
#ifndef FW_ORIGIN_Official
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance);
#endif
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: 4.9 KiB

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,12 +0,0 @@
ADD_SCENE(subrem, start, Start)
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)
#ifdef FW_ORIGIN_Official
ADD_SCENE(subrem, fw_warning, FwWarning)
#endif

View file

@ -1,133 +0,0 @@
#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);
#ifndef FW_ORIGIN_Official
text_input_set_minimum_length(app->text_input, 0);
#endif
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);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,129 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
#ifdef FW_ORIGIN_Official
typedef enum {
SceneFwWarningStateAttention,
SceneFwWarningStateAccept,
} SceneFwWarningState;
static void subrem_scene_fw_warning_widget_render(SubGhzRemoteApp* app, SceneFwWarningState state);
static void
subrem_scene_fw_warning_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
if(type == InputTypeShort) {
SubRemCustomEvent event = SubRemCustomEventSceneFwWarningExit;
switch(scene_manager_get_scene_state(app->scene_manager, SubRemSceneFwWarning)) {
case SceneFwWarningStateAttention:
if(result == GuiButtonTypeRight) {
event = SubRemCustomEventSceneFwWarningNext;
}
break;
case SceneFwWarningStateAccept:
if(result == GuiButtonTypeRight) {
event = SubRemCustomEventSceneFwWarningContinue;
}
break;
}
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
}
static void
subrem_scene_fw_warning_widget_render(SubGhzRemoteApp* app, SceneFwWarningState state) {
furi_assert(app);
Widget* widget = app->widget;
widget_reset(widget);
switch(state) {
case SceneFwWarningStateAttention:
widget_add_button_element(
widget, GuiButtonTypeLeft, "Exit", subrem_scene_fw_warning_widget_callback, app);
widget_add_button_element(
widget, GuiButtonTypeRight, "Continue", subrem_scene_fw_warning_widget_callback, app);
widget_add_string_element(
widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, "Not official FW");
widget_add_string_multiline_element(
widget,
64,
32,
AlignCenter,
AlignCenter,
FontSecondary,
"You are using custom firmware\nPlease download a compatible\nversion of the application");
break;
case SceneFwWarningStateAccept:
widget_add_button_element(
widget, GuiButtonTypeLeft, "Exit", subrem_scene_fw_warning_widget_callback, app);
widget_add_button_element(
widget, GuiButtonTypeRight, "Accept", subrem_scene_fw_warning_widget_callback, app);
widget_add_string_element(
widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, "Not official FW");
widget_add_string_multiline_element(
widget,
64,
32,
AlignCenter,
AlignCenter,
FontSecondary,
"Yes, I understand that\nthe application can\nbreak my subghz key file");
break;
}
}
void subrem_scene_fw_warning_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneFwWarning, SceneFwWarningStateAttention);
subrem_scene_fw_warning_widget_render(app, SceneFwWarningStateAttention);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
}
bool subrem_scene_fw_warning_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventSceneFwWarningExit) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
consumed = true;
} else if(event.event == SubRemCustomEventSceneFwWarningNext) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneFwWarning, SceneFwWarningStateAccept);
subrem_scene_fw_warning_widget_render(app, SceneFwWarningStateAccept);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
consumed = true;
} else if(event.event == SubRemCustomEventSceneFwWarningContinue) {
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void subrem_scene_fw_warning_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
widget_reset(app->widget);
}
#endif

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);
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);
}
}
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,119 +0,0 @@
#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();
if(furi_string_empty(sub->file_path)) {
furi_string_set(temp_file_path, SUBGHZ_RAW_FOLDER);
} else {
furi_string_set(temp_file_path, sub->file_path);
}
SubRemLoadSubState ret = SubRemLoadSubStateNotSet;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px);
browser_options.base_path = SUBGHZ_RAW_FOLDER;
// Input events and views are managed by file_select
if(!dialog_file_browser_show(app->dialogs, temp_file_path, 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);
}

View file

@ -1,118 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../views/remote.h"
#include <lib/subghz/protocols/raw.h>
#define TAG "SubRemScenRemote"
void subrem_scene_remote_callback(SubRemCustomEvent event, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subrem_scene_remote_raw_callback_end_tx(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventViewRemoteForcedStop);
}
static uint8_t subrem_scene_remote_event_to_index(SubRemCustomEvent event_id) {
uint8_t ret = 0;
if(event_id == SubRemCustomEventViewRemoteStartUP) {
ret = SubRemSubKeyNameUp;
} else if(event_id == SubRemCustomEventViewRemoteStartDOWN) {
ret = SubRemSubKeyNameDown;
} else if(event_id == SubRemCustomEventViewRemoteStartLEFT) {
ret = SubRemSubKeyNameLeft;
} else if(event_id == SubRemCustomEventViewRemoteStartRIGHT) {
ret = SubRemSubKeyNameRight;
} else if(event_id == SubRemCustomEventViewRemoteStartOK) {
ret = SubRemSubKeyNameOk;
}
return ret;
}
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_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDRemote);
}
bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventViewRemoteBack) {
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
return true;
} else if(
event.event == SubRemCustomEventViewRemoteStartUP ||
event.event == SubRemCustomEventViewRemoteStartDOWN ||
event.event == SubRemCustomEventViewRemoteStartLEFT ||
event.event == SubRemCustomEventViewRemoteStartRIGHT ||
event.event == SubRemCustomEventViewRemoteStartOK) {
// Start sending sub
subrem_tx_stop_sub(app, true);
uint8_t chusen_sub = subrem_scene_remote_event_to_index(event.event);
app->chusen_sub = chusen_sub;
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateLoading, chusen_sub);
if(subrem_tx_start_sub(app, app->map_preset->subs_preset[chusen_sub])) {
if(app->map_preset->subs_preset[chusen_sub]->type == SubGhzProtocolTypeRAW) {
subghz_txrx_set_raw_file_encoder_worker_callback_end(
app->txrx, subrem_scene_remote_raw_callback_end_tx, app);
}
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateSending, chusen_sub);
notification_message(app->notifications, &sequence_blink_start_magenta);
} else {
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_red_100);
}
return true;
} else if(event.event == SubRemCustomEventViewRemoteForcedStop) {
subrem_tx_stop_sub(app, true);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
return true;
} else if(event.event == SubRemCustomEventViewRemoteStop) {
if(subrem_tx_stop_sub(app, false)) {
subrem_view_remote_set_state(
app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
}
return true;
}
}
// } else if(event.type == SceneManagerEventTypeTick) {
// }
return false;
}
void subrem_scene_remote_on_exit(void* context) {
SubGhzRemoteApp* app = context;
subrem_tx_stop_sub(app, true);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
notification_message(app->notifications, &sequence_blink_stop);
}

View file

@ -1,100 +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,
"Open Map File",
SubmenuIndexSubRemOpenMapFile,
subrem_scene_start_submenu_callback,
app);
#if FURI_DEBUG
submenu_add_item(
submenu,
"Remote_Debug",
SubmenuIndexSubRemRemoteView,
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);
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 == SubmenuIndexSubRemOpenMapFile) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
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;
}
void subrem_scene_start_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -1,218 +0,0 @@
#include "subghz_remote_app_i.h"
#include <lib/toolbox/version.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);
}
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);
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
}
furi_record_close(RECORD_STORAGE);
}
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
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;
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_app(void* arg) {
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
subghz_remote_make_app_folder(subghz_remote_app);
bool map_loaded = false;
#ifdef FW_ORIGIN_Official
const bool fw_ofw = strcmp(version_get_firmware_origin(version_get()), "Official") == 0;
#endif
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);
#ifdef FW_ORIGIN_Official
if(fw_ofw) {
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile);
}
}
if(!fw_ofw) {
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneFwWarning);
}
#else
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile);
}
#endif
view_dispatcher_run(subghz_remote_app->view_dispatcher);
subghz_remote_app_free(subghz_remote_app);
return 0;
}

View file

@ -1,317 +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"
#ifndef FW_ORIGIN_Official
#include <lib/subghz/blocks/custom_btn.h>
#endif
#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));
}
bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset) {
furi_assert(app);
furi_assert(sub_preset);
bool ret = false;
subrem_tx_stop_sub(app, true);
if(sub_preset->type == SubGhzProtocolTypeUnknown) {
ret = false;
} else {
FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label));
subghz_txrx_load_decoder_by_name_protocol(
app->txrx, furi_string_get_cstr(sub_preset->protocaol_name));
subghz_txrx_set_preset(
app->txrx,
furi_string_get_cstr(sub_preset->freq_preset.name),
sub_preset->freq_preset.frequency,
NULL,
0);
#ifndef FW_ORIGIN_Official
subghz_custom_btns_reset();
#endif
if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) {
ret = true;
}
}
return ret;
}
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
furi_assert(app);
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
subghz_txrx_stop(app->txrx);
#ifndef FW_ORIGIN_Official
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
subghz_txrx_reset_dynamic_and_custom_btns(app->txrx);
}
subghz_custom_btns_reset();
#endif
return true;
}
return false;
}
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,68 +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 "subghz_remote_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);
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

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

View file

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

View file

@ -1,307 +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;
bool is_external;
} 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_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);
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.");
canvas_draw_str_aligned(
canvas, 126, 62, AlignRight, AlignBottom, ((model->is_external) ? "Ext" : "Int"));
}
//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;
model->is_external = false;
},
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,38 +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);
void subrem_view_remote_set_radio(SubRemViewRemote* subrem_view_remote, bool external);