Skip to content

Commit

Permalink
SubGHz Remote: separate repository
Browse files Browse the repository at this point in the history
  • Loading branch information
gid9798 committed Aug 29, 2023
1 parent 444f977 commit bca3518
Show file tree
Hide file tree
Showing 49 changed files with 3,793 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist/*
.vscode
.clang-format
.editorconfig
43 changes: 43 additions & 0 deletions application.fam
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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",
# )
25 changes: 25 additions & 0 deletions catalog/docs/Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## 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
23 changes: 23 additions & 0 deletions catalog/docs/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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.<br>
Stores custom names, and paths to used .sub files.
Binary file added catalog/screenshots/Editor_main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added catalog/screenshots/Editor_submenu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added catalog/screenshots/Remote_idle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added catalog/screenshots/Remote_send.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions helpers/subrem_custom_event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#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;
183 changes: 183 additions & 0 deletions helpers/subrem_presets.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#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;
}
39 changes: 39 additions & 0 deletions helpers/subrem_presets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "subrem_types.h"
#include "txrx/subghz_txrx.h"

#include <flipper_format/flipper_format_i.h>
#include <lib/subghz/types.h>

typedef struct {
FuriString* name;
uint32_t frequency;
// size_t preset_index; // Need for custom preset
} FreqPreset;

// Sub File preset
typedef struct {
FlipperFormat* fff_data;
FreqPreset freq_preset;
FuriString* file_path;
FuriString* protocaol_name;
FuriString* label;
SubGhzProtocolType type;
SubRemLoadSubState load_state;
} SubRemSubFilePreset;

typedef struct {
SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount];
} SubRemMapPreset;

SubRemSubFilePreset* subrem_sub_file_preset_alloc();

void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset);

void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset);

SubRemLoadSubState subrem_sub_preset_load(
SubRemSubFilePreset* sub_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file);
Loading

0 comments on commit bca3518

Please sign in to comment.