RECEIVER CONFIG PLUGIN: Made new Plugin and added Models Database.

This code adds new plugin "protopirate_config_plugin" and does all the
Receiver Configuration. This plugin should reduce the RAM usage even more,
and the Models DB (And its customised Variable Item List) now take up
no space in the main app.
This commit is contained in:
Leeroy committed 2026-09-17 00:36:59 +10:00
1 parent 39feb30b80
commit 06de58415a
17 files changed
+1927 -386

No files matched your search

+18
View File
@@ -17,6 +17,8 @@ _ENABLE_EMULATE = ProtoPirateDefineEnabled("ENABLE_EMULATE_FEATURE")
_MAIN_APP_SOURCES = [
"*.c*",
"!helpers/protopirate_models.c",
"!helpers/variable_item_list.c",
"!protocols/plugins",
"!scenes/plugins",
"!protocols",
@@ -419,3 +421,19 @@ App(
fap_icon_assets="images",
fal_embedded=True,
)
App(
appid="protopirate_config_plugin",
apptype=FlipperAppType.PLUGIN,
entry_point="protopirate_config_plugin_ep",
requires=["proto_pirate"],
sources=[
"helpers/protopirate_protocol_plugin_host.c",
"helpers/protopirate_models.c",
"helpers/protopirate_txrx.c",
"helpers/variable_item_list.c",
"protocols/protocol_items.c",
"scenes/plugins/protopirate_config_plugin.c",
],
fal_embedded=True,
)
+283
View File
@@ -0,0 +1,283 @@
#include "protopirate_models.h"
#define TAG "ProtoPirateModels"
#define PROTOPIRATE_MODELS_FILE APP_ASSETS_PATH("models.txt")
//Models File Format.
#define MODELS_FILE_HEADER "ProtoPirate Car Models Database"
#define MODELS_FILE_VERSION 2
#define MODELS_FIELD_MODEL_COUNT "ModelCount"
#define MODELS_FIELD_MODEL_NAME "ModelName"
#define MODELS_FIELD_FREQUENCY "Frequency"
#define MODELS_FIELD_PRESET_NAME "PresetName"
#define MODELS_FIELD_CUSTOM_PRESET_DATA "CustomPresetData"
bool car_model_get_by_index(
ProtoPirateCarModel* car_model,
uint16_t index,
uint16_t car_models_count,
SubGhzSetting* app_settings) {
//Temp variables
uint32_t version = 0;
uint32_t frequency;
uint8_t* preset_data = NULL;
uint32_t preset_data_size = 0;
bool error = true;
bool custom_preset = false;
//Objects that need allocating.
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_file_alloc(storage);
FuriString* preset_name = furi_string_alloc();
FuriString* model_name = furi_string_alloc();
FuriString* header = furi_string_alloc();
//Index 0 is None.
switch(index) {
case 0:
//Leave as same as error (none)
break;
default: {
//Open Models File, check the Header, check Version
if(!flipper_format_file_open_existing(ff, PROTOPIRATE_MODELS_FILE)) {
FURI_LOG_I(TAG, "models.txt file not found");
break;
} else if(!flipper_format_read_header(ff, header, &version)) {
FURI_LOG_W(TAG, "Failed to read models.txt header");
break;
} else if(furi_string_cmp_str(header, MODELS_FILE_HEADER) != 0) {
FURI_LOG_W(TAG, "Invalid models.txt file header");
break;
} else if(version != MODELS_FILE_VERSION) {
FURI_LOG_W(TAG, "Unsupported models.txt version %lu", (unsigned long)version);
break;
}
//Fields we add the number to for searching the file.
char model_name_index[16]; //Frequency65535 + NULL
char frequency_index[16]; //Frequency65535 + NULL
char preset_name_index[18]; //PresetName65535 + NULL
char preset_data_index[23]; //CustomPresetData65535 + NULL
//Build the values we need to grab.
snprintf(model_name_index, sizeof(model_name_index), MODELS_FIELD_MODEL_NAME "%u", index);
snprintf(frequency_index, sizeof(frequency_index), MODELS_FIELD_FREQUENCY "%u", index);
snprintf(
preset_name_index, sizeof(preset_name_index), MODELS_FIELD_PRESET_NAME "%u", index);
snprintf(
preset_data_index,
sizeof(preset_data_index),
MODELS_FIELD_CUSTOM_PRESET_DATA "%u",
index);
//Read the Model Name.
if(!flipper_format_read_string(ff, model_name_index, model_name)) {
FURI_LOG_E("ProtoPirate", "Failed to read %s", model_name_index);
break;
}
// Read frequency
if(!flipper_format_read_uint32(ff, frequency_index, &frequency, 1)) {
FURI_LOG_W(TAG, "Failed to read frequency.");
frequency = 0;
break;
}
//Read the Modulation Preset.
if(!flipper_format_read_string(ff, preset_name_index, preset_name)) {
//Set the Custom preset name.
furi_string_set_str(preset_name, "Custom");
custom_preset = true;
} else {
//Get the built in preset if it exists
const char* preset_name_long = furi_string_get_cstr(preset_name);
const char* preset_name_short = preset_name_long;
//Which Preset have we selected?
if(!strcmp(preset_name_long, "FuriHalSubGhzPresetOok270Async")) {
preset_name_short = "AM270";
} else if(!strcmp(preset_name_long, "FuriHalSubGhzPresetOok650Async")) {
preset_name_short = "AM650";
} else if(!strcmp(preset_name_long, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name_short = "FM238";
} else if(!strcmp(preset_name_long, "FuriHalSubGhzPreset2FSKDev12KAsync")) {
preset_name_short = "FM12K";
} else if(!strcmp(preset_name_long, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name_short = "FM476";
} else if(!strcmp(preset_name_long, "FuriHalSubGhzPresetCustom")) {
preset_name_short = "Custom";
custom_preset = true;
}
//Set the preset_name
furi_string_set_str(preset_name, preset_name_short);
if(!custom_preset) {
size_t preset_index = subghz_setting_get_preset_count(app_settings);
for(size_t i = 0; i < subghz_setting_get_preset_count(app_settings); i++) {
if(!strcmp(
subghz_setting_get_preset_name(app_settings, i), preset_name_short)) {
preset_index = i;
break;
}
}
if(preset_index >= subghz_setting_get_preset_count(app_settings)) {
preset_name_short = "AM650";
for(size_t i = 0; i < subghz_setting_get_preset_count(app_settings); i++) {
if(!strcmp(
subghz_setting_get_preset_name(app_settings, i),
preset_name_short)) {
preset_index = i;
break;
}
}
if(preset_index >= subghz_setting_get_preset_count(app_settings)) {
FURI_LOG_E(TAG, "Failed to get preset index!");
break;
}
}
//Get the preset data into a buffer to copy it.
uint8_t* temp_preset_data = NULL;
uint32_t temp_preset_data_size = 0;
temp_preset_data = subghz_setting_get_preset_data(app_settings, preset_index);
temp_preset_data_size =
subghz_setting_get_preset_data_size(app_settings, preset_index);
//Error fallback
if(temp_preset_data == NULL) {
FURI_LOG_E(TAG, "Failed to get preset data!");
break;
} else {
//Copy the preset data, so we can initialize it.
preset_data = malloc(temp_preset_data_size);
memcpy(preset_data, temp_preset_data, temp_preset_data_size);
preset_data_size = temp_preset_data_size;
//Yay, we made it...
error = false;
}
}
}
/* Custom_preset_data (only if present) */
if(custom_preset &&
flipper_format_get_value_count(ff, preset_data_index, &preset_data_size) &&
preset_data_size > 0) {
if(preset_data_size >= 1024) {
FURI_LOG_E(
"ProtoPirate",
"%s too large: %lu",
preset_data_index,
(unsigned long)preset_data_size);
break;
}
preset_data = malloc(preset_data_size);
if(!preset_data) {
FURI_LOG_E(
"ProtoPirate",
"Malloc failed: %s (%lu bytes)",
preset_data_index,
(unsigned long)preset_data_size);
break;
}
flipper_format_rewind(ff);
if(!flipper_format_read_hex(ff, preset_data_index, preset_data, preset_data_size)) {
break;
}
//Yay, we made it...
error = false;
}
}
}
if(!error) {
//Set the Car Model name and index.
furi_string_set((car_model)->name, model_name);
car_model->index = index;
//Alloc a preset if none, or recycle.
if(!car_model->preset) {
car_model->preset = malloc(sizeof(SubGhzRadioPreset));
car_model->preset->name = furi_string_alloc();
} else {
//Free old data..
if(car_model->preset->data) {
free(car_model->preset->data);
car_model->preset->data = NULL;
}
}
furi_string_set(car_model->preset->name, preset_name);
car_model->preset->data = preset_data;
car_model->preset->data_size = preset_data_size;
car_model->preset->frequency = frequency;
} else {
car_model->index = 0;
furi_string_set_str(
car_model->name,
(car_models_count) ? "< Select a Car Model >" : "No Models in Database");
if(car_model->preset) {
if(car_model->preset->data) {
free(car_model->preset->data);
car_model->preset->data = NULL;
}
car_model->preset->data_size = 0;
if(car_model->preset->name) {
furi_string_free(car_model->preset->name);
car_model->preset->name = NULL;
}
free(car_model->preset);
car_model->preset = NULL;
}
// If we allocated preset_data but never assigned it, free it
if(custom_preset && preset_data_size) {
free(preset_data);
}
}
//Close up and return.
flipper_format_free(ff);
furi_string_free(header);
furi_string_free(model_name);
furi_string_free(preset_name);
furi_record_close(RECORD_STORAGE);
return (!error);
}
uint16_t car_model_get_count(void) {
uint32_t version = 0;
uint32_t model_count = 0;
//Set up storage, init temp variables
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_file_alloc(storage);
FuriString* header = furi_string_alloc();
//Open file, read header, check version, and grab the model count.
if(!flipper_format_file_open_existing(ff, PROTOPIRATE_MODELS_FILE)) {
FURI_LOG_W(TAG, "Failed to open " PROTOPIRATE_MODELS_FILE);
} else if(!flipper_format_read_header(ff, header, &version)) {
FURI_LOG_W(TAG, "Failed to read models file header.");
} else if(version != MODELS_FILE_VERSION || furi_string_cmp_str(header, MODELS_FILE_HEADER) != 0) {
FURI_LOG_W(TAG, "Models file version is invalid.");
} else if(!flipper_format_read_uint32(ff, MODELS_FIELD_MODEL_COUNT, &model_count, 1)) {
FURI_LOG_W(TAG, "Failed to read " MODELS_FIELD_MODEL_COUNT ".");
}
//Free temp variables, close storage.
furi_string_free(header);
flipper_format_free(ff);
furi_record_close(RECORD_STORAGE);
//Finished, return the count or 0 if error.
return (uint16_t)model_count;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <furi.h>
#include <lib/subghz/types.h>
#include "core/record.h"
#include <lib/subghz/subghz_setting.h>
typedef struct ProtoPirateCarModel {
uint16_t index;
FuriString* name;
SubGhzRadioPreset* preset;
int16_t last_preset_index;
} ProtoPirateCarModel;
bool car_model_get_by_index(
ProtoPirateCarModel* car_model,
uint16_t index,
uint16_t model_count,
SubGhzSetting* app_settings);
uint16_t car_model_get_count(void);
+9 -9
View File
@@ -110,14 +110,14 @@ static void psa_bf_plugin_unload(ProtoPirateApp* app) {
furi_check(app);
app->psa_bf_plugin = NULL;
if(app->psa_bf_plugin_manager) {
plugin_manager_free(app->psa_bf_plugin_manager);
app->psa_bf_plugin_manager = NULL;
if(app->plugin_manager) {
plugin_manager_free(app->plugin_manager);
app->plugin_manager = NULL;
}
if(app->psa_bf_plugin_resolver) {
composite_api_resolver_free(app->psa_bf_plugin_resolver);
app->psa_bf_plugin_resolver = NULL;
if(app->plugin_resolver) {
composite_api_resolver_free(app->plugin_resolver);
app->plugin_resolver = NULL;
}
}
@@ -126,7 +126,7 @@ bool protopirate_psa_bf_plugin_ensure_loaded(ProtoPirateApp* app) {
if(app->psa_bf_plugin) return true;
if(app->psa_bf_plugin_manager || app->psa_bf_plugin_resolver) {
if(app->plugin_manager || app->plugin_resolver) {
psa_bf_plugin_unload(app);
}
@@ -163,8 +163,8 @@ bool protopirate_psa_bf_plugin_ensure_loaded(ProtoPirateApp* app) {
return false;
}
app->psa_bf_plugin_resolver = resolver;
app->psa_bf_plugin_manager = manager;
app->plugin_resolver = resolver;
app->plugin_manager = manager;
app->psa_bf_plugin = plugin;
plugin->set_host_api(&protopirate_psa_bf_host_api);
return true;
+13 -1
View File
@@ -22,6 +22,7 @@ void protopirate_settings_set_defaults(ProtoPirateSettings* settings) {
settings->emulate_feature_enabled = false;
settings->check_saved = false;
settings->datetime_filenames = false;
settings->car_model_index = 0;
}
void protopirate_settings_load(ProtoPirateSettings* settings) {
@@ -134,6 +135,13 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
}
settings->datetime_filenames = (datetime_filenames_temp == 1);
// Read Selected Car Model
uint32_t car_model_index_temp = 0;
if(!flipper_format_read_uint32(ff, "CarModelIndex", &car_model_index_temp, 1)) {
car_model_index_temp = 0;
}
settings->car_model_index = car_model_index_temp;
FURI_LOG_I(
TAG,
"Settings loaded: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d, check_saved=%d, sound = %d",
@@ -225,6 +233,11 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
uint32_t datetime_filenames_temp = settings->datetime_filenames ? 1 : 0;
if(!flipper_format_write_uint32(ff, "DateTimeFilenames", &datetime_filenames_temp, 1)) {
FURI_LOG_E(TAG, "Failed to write Date Time Filenames");
}
uint32_t car_model_index_temp = settings->car_model_index;
if(!flipper_format_write_uint32(ff, "CarModelIndex", &car_model_index_temp, 1)) {
FURI_LOG_E(TAG, "Failed to write car model");
break;
}
write_ok = true;
@@ -239,7 +252,6 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
settings->emulate_feature_enabled,
settings->check_saved,
settings->sound);
} while(false);
flipper_format_free(ff);
+1
View File
@@ -19,6 +19,7 @@ typedef struct {
bool emulate_feature_enabled;
bool check_saved;
bool datetime_filenames;
uint16_t car_model_index;
} ProtoPirateSettings;
void protopirate_settings_load(ProtoPirateSettings* settings);
+8 -8
View File
@@ -131,14 +131,14 @@ static void protopirate_tool_scene_plugin_unload(ProtoPirateApp* app) {
app->tool_scene_plugin = NULL;
if(app->tool_scene_plugin_manager) {
plugin_manager_free(app->tool_scene_plugin_manager);
app->tool_scene_plugin_manager = NULL;
if(app->plugin_manager) {
plugin_manager_free(app->plugin_manager);
app->plugin_manager = NULL;
}
if(app->tool_scene_plugin_resolver) {
composite_api_resolver_free(app->tool_scene_plugin_resolver);
app->tool_scene_plugin_resolver = NULL;
if(app->plugin_resolver) {
composite_api_resolver_free(app->plugin_resolver);
app->plugin_resolver = NULL;
}
}
@@ -198,8 +198,8 @@ static bool protopirate_tool_scene_plugin_ensure_loaded(
return false;
}
app->tool_scene_plugin_resolver = resolver;
app->tool_scene_plugin_manager = manager;
app->plugin_resolver = resolver;
app->plugin_manager = manager;
app->tool_scene_plugin = plugin;
app->tool_scene_plugin_kind = kind;
plugin->set_host_api(&protopirate_tool_scene_host_api);
-19
View File
@@ -5,24 +5,6 @@
#define TAG "ProtoPirateViews"
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app) {
furi_check(app);
if(app->variable_item_list) {
return true;
}
app->variable_item_list = variable_item_list_alloc();
if(!app->variable_item_list) {
return false;
}
view_dispatcher_add_view(
app->view_dispatcher,
ProtoPirateViewVariableItemList,
variable_item_list_get_view(app->variable_item_list));
return true;
}
bool protopirate_ensure_widget(ProtoPirateApp* app) {
furi_check(app);
if(app->widget) {
@@ -101,7 +83,6 @@ void protopirate_views_free(ProtoPirateApp* app) {
if(app->variable_item_list) {
FURI_LOG_D(TAG, "Removing variable_item_list view");
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
variable_item_list_free(app->variable_item_list);
app->variable_item_list = NULL;
}
+688
View File
@@ -0,0 +1,688 @@
#include "helpers/variable_item_list.h"
#include <gui/elements.h>
#include <gui/canvas.h>
#include <furi.h>
#include <m-array.h>
#include <stdint.h>
/**************************************************************************************************/
/* ICON ASSETS */
/**************************************************************************************************/
struct Icon {
const uint16_t width;
const uint16_t height;
const uint8_t frame_count;
const uint8_t frame_rate;
const uint8_t* const* frames;
};
const uint8_t _I_WarningDolphin_45x42_0[] = {
0x01, 0x00, 0xc6, 0x00, 0x00, 0x1c, 0x22, 0x04, 0x05, 0x7f, 0xfc, 0x1e, 0x20, 0x05, 0x1e, 0x04,
0x02, 0x30, 0x05, 0x29, 0x84, 0x02, 0xc1, 0x20, 0x02, 0x8c, 0x22, 0x01, 0x80, 0x02, 0x94, 0x10,
0x32, 0x30, 0x10, 0x10, 0x87, 0xca, 0x84, 0x03, 0x10, 0x42, 0x81, 0x48, 0x28, 0x38, 0x08, 0x04,
0x3e, 0x01, 0x84, 0x83, 0xe0, 0x30, 0x11, 0x08, 0x05, 0xa2, 0x11, 0x40, 0xa0, 0x4b, 0xc6, 0xc5,
0x40, 0xd0, 0x56, 0xe0, 0x10, 0x60, 0x29, 0x54, 0xf0, 0x10, 0x18, 0xf0, 0x14, 0x6b, 0xf6, 0x0c,
0x04, 0x3e, 0x40, 0x05, 0x12, 0x80, 0xc1, 0xe4, 0x01, 0xd2, 0xf8, 0x40, 0xe4, 0x18, 0x09, 0xf4,
0x03, 0xf1, 0x01, 0x90, 0x40, 0x28, 0x30, 0x0f, 0xe4, 0x00, 0x16, 0x24, 0x11, 0xbf, 0x01, 0x44,
0xee, 0x53, 0xf0, 0x29, 0xf0, 0x3e, 0x02, 0x91, 0x3b, 0x8c, 0xc3, 0x81, 0x13, 0x90, 0x48, 0x20,
0x3f, 0xf9, 0xfc, 0x42, 0x60, 0x05, 0x10, 0x98, 0x81, 0x56, 0x11, 0x38, 0x02, 0x9c, 0x1a, 0x31,
0x1e, 0x02, 0x8f, 0x02, 0x03, 0x1c, 0x90, 0xc0, 0x7c, 0x02, 0xf1, 0xce, 0x02, 0x07, 0x01, 0x1f,
0x80, 0x63, 0xa8, 0x08, 0x71, 0x3c, 0x8e, 0x39, 0x24, 0x40, 0x51, 0xc7, 0x81, 0x53, 0x0f, 0x3c,
0x02, 0x9d, 0x1e, 0x38, 0x29, 0x10, 0x29, 0x17, 0xc8, 0x0a, 0x32, 0x3a, 0x00, 0x14, 0x4b, 0xa2,
0x05, 0x58, 0x98, 0x15, 0x22, 0x20, 0x54, 0x84, 0x81, 0x50,
};
const uint8_t* const _I_WarningDolphin_45x42[] = {_I_WarningDolphin_45x42_0};
const Icon I_WarningDolphin_45x42 = {
.width = 45,
.height = 42,
.frame_count = 1,
.frame_rate = 0,
.frames = _I_WarningDolphin_45x42};
const uint8_t _I_Lock_7x8_0[] = {
0x00,
0x1c,
0x22,
0x22,
0x7f,
0x7f,
0x77,
0x7f,
0x3e,
};
const uint8_t* const _I_Lock_7x8[] = {_I_Lock_7x8_0};
const Icon I_Lock_7x8 =
{.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Lock_7x8};
/**************************************************************************************************/
struct VariableItem {
FuriString* label;
uint8_t current_value_index;
FuriString* current_value_text;
uint8_t values_count;
VariableItemChangeCallback change_callback;
void* context;
bool locked;
FuriString* locked_message;
};
ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST); //-V658
struct VariableItemList {
View* view;
VariableItemListEnterCallback callback;
void* context;
FuriTimer* scroll_timer;
FuriTimer* locked_timer;
};
typedef struct {
VariableItemArray_t items;
uint8_t position;
uint8_t window_position;
FuriString* header;
size_t scroll_counter;
bool locked_message_visible;
} VariableItemListModel;
static void variable_item_list_process_up(VariableItemList* variable_item_list);
static void variable_item_list_process_down(VariableItemList* variable_item_list);
static void variable_item_list_process_left(VariableItemList* variable_item_list);
static void variable_item_list_process_right(VariableItemList* variable_item_list);
static void variable_item_list_process_ok(VariableItemList* variable_item_list);
static size_t variable_item_list_items_on_screen(VariableItemListModel* model) {
size_t res = 4;
return (furi_string_empty(model->header)) ? res : res - 1;
}
static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
VariableItemListModel* model = _model;
const uint8_t item_height = 16;
uint8_t item_width = canvas_width(canvas) - 5;
canvas_clear(canvas);
if(!furi_string_empty(model->header)) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header));
}
uint8_t position = 0;
VariableItemArray_it_t it;
canvas_set_font(canvas, FontSecondary);
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
uint8_t item_position = position - model->window_position;
uint8_t items_on_screen = variable_item_list_items_on_screen(model);
uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height;
if(item_position < items_on_screen) {
const VariableItem* item = VariableItemArray_cref(it);
uint8_t item_y = y_offset + (item_position * item_height);
uint8_t item_text_y = item_y + item_height - 4;
size_t scroll_counter = 0;
if(position == model->position) {
canvas_set_color(canvas, ColorBlack);
elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2);
canvas_set_color(canvas, ColorWhite);
scroll_counter = model->scroll_counter;
if(scroll_counter < 1) { // Show text beginning a little longer
scroll_counter = 0;
} else {
scroll_counter -= 1;
}
} else {
canvas_set_color(canvas, ColorBlack);
}
uint8_t value_pos_x = 73;
uint8_t label_width = 66;
if(item->locked) {
// Span label up to lock icon
value_pos_x = 110;
label_width = 100;
} else if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) {
// Only label text, no value text, show longer label
label_width = 109;
} else if(furi_string_size(item->current_value_text) < 4U) {
// Smaller value section for short values
value_pos_x = 80;
label_width = 71;
}
elements_scrollable_text_line(
canvas,
6,
item_text_y,
label_width,
item->label,
scroll_counter,
(position != model->position));
if(item->locked) {
canvas_draw_icon(canvas, value_pos_x, item_text_y - 8, &I_Lock_7x8);
} else {
if(item->current_value_index > 0) {
canvas_draw_str(canvas, value_pos_x, item_text_y, "<");
}
elements_scrollable_text_line(
canvas,
value_pos_x + 6,
item_text_y,
109 - (value_pos_x),
item->current_value_text,
scroll_counter,
false);
if(item->current_value_index < (item->values_count - 1)) {
canvas_draw_str(canvas, 115, item_text_y, ">");
}
}
}
position++;
}
elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));
if(model->locked_message_visible) {
//if(momentum_settings.popup_overlay) {
// canvas_draw_overlay(canvas);
//}
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 8, 10, 110, 48);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
elements_multiline_text_aligned(
canvas,
84,
32,
AlignCenter,
AlignCenter,
furi_string_get_cstr(
VariableItemArray_get(model->items, model->position)->locked_message));
}
}
void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) {
furi_check(variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
uint8_t position = index;
const size_t items_count = VariableItemArray_size(model->items);
uint8_t items_on_screen = variable_item_list_items_on_screen(model);
if(position >= items_count) {
position = 0;
}
model->position = position;
model->window_position = position;
if(model->window_position > 0) {
model->window_position -= 1;
}
if(items_count <= items_on_screen) {
model->window_position = 0;
} else {
const size_t pos = items_count - items_on_screen;
if(model->window_position > pos) {
model->window_position = pos;
}
}
},
true);
}
/* uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) {
furi_check(variable_item_list);
VariableItemListModel* model = view_get_model(variable_item_list->view);
uint8_t idx = model->position;
view_commit_model(variable_item_list->view, false);
return idx;
}*/
/* void variable_item_list_set_header(VariableItemList* variable_item_list, const char* header) {
furi_check(variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
if(header == NULL) {
furi_string_reset(model->header);
} else {
furi_string_set_str(model->header, header);
}
},
true);
}*/
static bool variable_item_list_input_callback(InputEvent* event, void* context) {
VariableItemList* variable_item_list = context;
furi_assert(variable_item_list);
bool consumed = false;
bool locked_message_visible = false;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ locked_message_visible = model->locked_message_visible; },
false);
if((event->type != InputTypePress && event->type != InputTypeRelease) &&
locked_message_visible) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->locked_message_visible = false; },
true);
consumed = true;
} else if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyUp:
consumed = true;
variable_item_list_process_up(variable_item_list);
break;
case InputKeyDown:
consumed = true;
variable_item_list_process_down(variable_item_list);
break;
case InputKeyLeft:
consumed = true;
variable_item_list_process_left(variable_item_list);
break;
case InputKeyRight:
consumed = true;
variable_item_list_process_right(variable_item_list);
break;
case InputKeyOk:
variable_item_list_process_ok(variable_item_list);
break;
default:
break;
}
} else if(event->type == InputTypeRepeat) {
switch(event->key) {
case InputKeyUp:
consumed = true;
variable_item_list_process_up(variable_item_list);
break;
case InputKeyDown:
consumed = true;
variable_item_list_process_down(variable_item_list);
break;
case InputKeyLeft:
consumed = true;
variable_item_list_process_left(variable_item_list);
break;
case InputKeyRight:
consumed = true;
variable_item_list_process_right(variable_item_list);
break;
default:
break;
}
}
return consumed;
}
void variable_item_list_process_up(VariableItemList* variable_item_list) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
uint8_t items_on_screen = variable_item_list_items_on_screen(model);
if(model->position > 0) {
model->position--;
if((model->position == model->window_position) && (model->window_position > 0)) {
model->window_position--;
}
} else {
model->position = VariableItemArray_size(model->items) - 1;
if(model->position > (items_on_screen - 1)) {
model->window_position = model->position - (items_on_screen - 1);
}
}
model->scroll_counter = 0;
},
true);
}
void variable_item_list_process_down(VariableItemList* variable_item_list) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
uint8_t items_on_screen = variable_item_list_items_on_screen(model);
if(model->position < (VariableItemArray_size(model->items) - 1)) {
model->position++;
if((model->position - model->window_position) > (items_on_screen - 2) &&
model->window_position <
(VariableItemArray_size(model->items) - items_on_screen)) {
model->window_position++;
}
} else {
model->position = 0;
model->window_position = 0;
}
model->scroll_counter = 0;
},
true);
}
VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) {
VariableItem* item = NULL;
VariableItemArray_it_t it;
uint8_t position = 0;
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
if(position == model->position) {
break;
}
position++;
}
item = VariableItemArray_ref(it);
furi_assert(item);
return item;
}
void variable_item_list_process_left(VariableItemList* variable_item_list) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(!item->values_count) {
//No List to change. Send VariableItemListEventCycleLeft to the handler as current value index
//This way no breaking API changes are made.
item->current_value_index = VariableItemListEventCycleLeft;
if(item->change_callback) {
item->change_callback(item);
}
} else if(item->current_value_index > 0) {
item->current_value_index--;
model->scroll_counter = 0;
if(item->change_callback) {
item->change_callback(item);
}
}
},
true);
}
void variable_item_list_process_right(VariableItemList* variable_item_list) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(!item->values_count) {
//No List to change. Send VariableItemListEventCycleLeft to the handler as current value index
//This way no breaking API changes are made.
item->current_value_index = VariableItemListEventCycleRight;
if(item->change_callback) {
item->change_callback(item);
}
} else if(item->current_value_index < (item->values_count - 1)) {
item->current_value_index++;
model->scroll_counter = 0;
if(item->change_callback) {
item->change_callback(item);
}
}
},
true);
}
void variable_item_list_process_ok(VariableItemList* variable_item_list) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(variable_item_list->callback) {
variable_item_list->callback(variable_item_list->context, model->position);
}
},
true);
}
static void variable_item_list_scroll_timer_callback(void* context) {
furi_assert(context);
VariableItemList* variable_item_list = context;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->scroll_counter++; },
true);
}
void variable_item_list_locked_timer_callback(void* context) {
furi_assert(context);
VariableItemList* variable_item_list = context;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->locked_message_visible = false; },
true);
}
VariableItemList* variable_item_list_alloc(void) {
VariableItemList* variable_item_list = malloc(sizeof(VariableItemList));
variable_item_list->view = view_alloc();
view_set_context(variable_item_list->view, variable_item_list);
view_allocate_model(
variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListModel));
view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);
view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);
variable_item_list->locked_timer = furi_timer_alloc(
variable_item_list_locked_timer_callback, FuriTimerTypeOnce, variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
VariableItemArray_init(model->items);
model->position = 0;
model->window_position = 0;
model->header = furi_string_alloc();
model->scroll_counter = 0;
},
true);
variable_item_list->scroll_timer = furi_timer_alloc(
variable_item_list_scroll_timer_callback, FuriTimerTypePeriodic, variable_item_list);
furi_timer_start(variable_item_list->scroll_timer, 333);
return variable_item_list;
}
void variable_item_list_free(VariableItemList* variable_item_list) {
furi_check(variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
furi_string_free(model->header);
VariableItemArray_it_t it;
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
furi_string_free(VariableItemArray_ref(it)->label);
furi_string_free(VariableItemArray_ref(it)->current_value_text);
furi_string_free(VariableItemArray_ref(it)->locked_message);
}
VariableItemArray_clear(model->items);
},
false);
furi_timer_stop(variable_item_list->scroll_timer);
furi_timer_free(variable_item_list->scroll_timer);
furi_timer_stop(variable_item_list->locked_timer);
furi_timer_free(variable_item_list->locked_timer);
view_free(variable_item_list->view);
free(variable_item_list);
variable_item_list = NULL;
}
void variable_item_list_reset(VariableItemList* variable_item_list) {
furi_check(variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
VariableItemArray_it_t it;
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
furi_string_free(VariableItemArray_ref(it)->label);
furi_string_free(VariableItemArray_ref(it)->current_value_text);
furi_string_free(VariableItemArray_ref(it)->locked_message);
}
VariableItemArray_reset(model->items);
furi_string_reset(model->header);
},
false);
}
View* variable_item_list_get_view(VariableItemList* variable_item_list) {
furi_check(variable_item_list);
return variable_item_list->view;
}
VariableItem* variable_item_list_add(
VariableItemList* variable_item_list,
const char* label,
uint8_t values_count,
VariableItemChangeCallback change_callback,
void* context) {
VariableItem* item = NULL;
furi_check(label);
furi_check(variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
item = VariableItemArray_push_new(model->items);
item->label = furi_string_alloc_set(label);
item->values_count = values_count;
item->change_callback = change_callback;
item->context = context;
item->current_value_index = 0;
item->current_value_text = furi_string_alloc();
item->locked = false;
item->locked_message = furi_string_alloc();
},
true);
return item;
}
VariableItem* variable_item_list_get(VariableItemList* variable_item_list, uint8_t position) {
furi_check(variable_item_list);
VariableItem* item = NULL;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
if(position < VariableItemArray_size(model->items)) {
item = VariableItemArray_get(model->items, position);
}
},
true);
return item;
}
void variable_item_list_set_enter_callback(
VariableItemList* variable_item_list,
VariableItemListEnterCallback callback,
void* context) {
furi_check(callback);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{
UNUSED(model);
variable_item_list->callback = callback;
variable_item_list->context = context;
},
false);
}
void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
furi_check(item);
item->current_value_index = current_value_index;
}
/*void variable_item_set_values_count(VariableItem* item, uint8_t values_count) {
furi_check(item);
item->values_count = values_count;
}*/
void variable_item_set_item_label(VariableItem* item, const char* label) {
furi_check(item);
furi_check(label);
furi_string_set(item->label, label);
}
void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {
furi_check(item);
furi_string_set(item->current_value_text, current_value_text);
}
void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) {
furi_check(item);
item->locked = locked;
if(locked_message) {
furi_string_set(item->locked_message, locked_message);
} else if(locked && furi_string_empty(item->locked_message)) {
furi_string_set(item->locked_message, "Locked!");
}
}
uint8_t variable_item_get_current_value_index(VariableItem* item) {
furi_check(item);
return item->current_value_index;
}
void* variable_item_get_context(VariableItem* item) {
furi_check(item);
return item->context;
}
+159
View File
@@ -0,0 +1,159 @@
/**
* @file variable_item_list.h
* GUI: VariableItemList view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
//For sending Left and Right events up to app
//When there are ZERO values (no cycling used by list itself)
//This allows easy use of lists that cycle the whole line,
//And use more than 255 values.
typedef enum {
VariableItemListEventCycleReset,
VariableItemListEventCycleLeft,
VariableItemListEventCycleRight,
} VariableItemListEvents;
typedef struct VariableItemList VariableItemList;
typedef struct VariableItem VariableItem;
typedef void (*VariableItemChangeCallback)(VariableItem* item);
typedef void (*VariableItemListEnterCallback)(void* context, uint32_t index);
/** Allocate and initialize VariableItemList
*
* @return VariableItemList*
*/
VariableItemList* variable_item_list_alloc(void);
/** Deinitialize and free VariableItemList
*
* @param variable_item_list VariableItemList instance
*/
void variable_item_list_free(VariableItemList* variable_item_list);
/** Clear all elements from list
*
* @param variable_item_list VariableItemList instance
*/
void variable_item_list_reset(VariableItemList* variable_item_list);
/** Get VariableItemList View instance
*
* @param variable_item_list VariableItemList instance
*
* @return View instance
*/
View* variable_item_list_get_view(VariableItemList* variable_item_list);
/** Add item to VariableItemList
*
* @param variable_item_list VariableItemList instance
* @param label item name
* @param values_count item values count
* @param change_callback called on value change in gui
* @param context item context
*
* @return VariableItem* item instance
*/
VariableItem* variable_item_list_add(
VariableItemList* variable_item_list,
const char* label,
uint8_t values_count,
VariableItemChangeCallback change_callback,
void* context);
/** Get pre-existing item instance in VariableItemList
*
* @param variable_item_list VariableItemList instance
* @param position index of the item instance to get
*
* @return VariableItem* item instance
*/
VariableItem* variable_item_list_get(VariableItemList* variable_item_list, uint8_t position);
/** Set enter callback
*
* @param variable_item_list VariableItemList instance
* @param callback VariableItemListEnterCallback instance
* @param context pointer to context
*/
void variable_item_list_set_enter_callback(
VariableItemList* variable_item_list,
VariableItemListEnterCallback callback,
void* context);
void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index);
//uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list);
/** Set optional header for variable item list
* Must be called before adding items OR after adding items and before set_selected_item()
*
* @param variable_item_list VariableItemList instance
* @param header header to set
*/
// void variable_item_list_set_header(VariableItemList* variable_item_list, const char* header);
/** Set item current selected index
*
* @param item VariableItem* instance
* @param current_value_index The current value index
*/
void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index);
/** Set number of values for item
*
* @param item VariableItem* instance
* @param values_count The new values count
*/
//void variable_item_set_values_count(VariableItem* item, uint8_t values_count);
/** Set new label for item
*
* @param item VariableItem* instance
* @param label The new label text
*/
void variable_item_set_item_label(VariableItem* item, const char* label);
/** Set item current selected text
*
* @param item VariableItem* instance
* @param current_value_text The current value text
*/
void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text);
/** Set item locked state and text
* Locked message is saved, you can set_locked(item, false, "Message") and then set_locked(item, true, NULL), it will use previous locked message
*
* @param item VariableItem* instance
* @param locked Is item locked boolean
* @param locked_message The locked message text
*/
void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message);
/** Get item current selected index
*
* @param item VariableItem* instance
*
* @return uint8_t current selected index
*/
uint8_t variable_item_get_current_value_index(VariableItem* item);
/** Get item context
*
* @param item VariableItem* instance
*
* @return void* item context
*/
void* variable_item_get_context(VariableItem* item);
#ifdef __cplusplus
}
#endif
+28
View File
@@ -0,0 +1,28 @@
Filetype: ProtoPirate Car Models Database
Version: 2
ModelCount: 6
ModelName1: 433.92 Mhz AM650
Frequency1: 433920000
PresetName1: FuriHalSubGhzPresetOok650Async
ModelName2: 433.92 Mhz FM476
Frequency2: 433920000
PresetName2: FuriHalSubGhzPreset2FSKDev476Async
ModelName3: 315 Mhz AM650
Frequency3: 315000000
PresetName3: FuriHalSubGhzPresetOok650Async
ModelName4: 315 Mhz FM476
Frequency4: 315000000
PresetName4: FuriHalSubGhzPreset2FSKDev476Async
ModelName5: Ford Falcon BA-FGX
Frequency5: 433920000
PresetName5: FuriHalSubGhzPresetOok650Async
ModelName6: Ford Territory SX-SYII
Frequency6: 433920000
PresetName6: FuriHalSubGhzPresetOok650Async
+133 -8
View File
@@ -12,6 +12,73 @@
#define TAG "ProtoPirateApp"
// -----------------------------------------------------------------------------
// Plugin load / unload
// -----------------------------------------------------------------------------
void config_plugin_unload(ProtoPirateApp* app) {
furi_check(app);
app->config_plugin = NULL;
if(app->plugin_manager) {
plugin_manager_free(app->plugin_manager);
app->plugin_manager = NULL;
}
if(app->plugin_resolver) {
composite_api_resolver_free(app->plugin_resolver);
app->plugin_resolver = NULL;
}
}
bool config_plugin_load(ProtoPirateApp* app) {
furi_check(app);
if(app->emulate_plugin) return true;
if(app->plugin_manager || app->plugin_resolver) {
config_plugin_unload(app);
}
CompositeApiResolver* resolver = composite_api_resolver_alloc();
if(!resolver) {
FURI_LOG_E(TAG, "Failed to allocate config plugin resolver");
return false;
}
composite_api_resolver_add(resolver, firmware_api_interface);
PluginManager* manager = plugin_manager_alloc(
PROTOPIRATE_CONFIG_PLUGIN_APP_ID,
PROTOPIRATE_CONFIG_PLUGIN_API_VERSION,
composite_api_resolver_get(resolver));
if(!manager) {
FURI_LOG_E(TAG, "Failed to allocate config plugin manager");
composite_api_resolver_free(resolver);
return false;
}
PluginManagerError error = plugin_manager_load_single(manager, CONFIG_PLUGIN_PATH);
if(error != PluginManagerErrorNone) {
FURI_LOG_E(TAG, "Failed to load config plugin %s: %d", CONFIG_PLUGIN_PATH, (int)error);
plugin_manager_free(manager);
composite_api_resolver_free(resolver);
return false;
}
const ProtoPirateConfigPlugin* plugin = plugin_manager_get_ep(manager, 0U);
if(!plugin || !plugin->on_enter || !plugin->on_exit) {
FURI_LOG_E(TAG, "Config plugin entry point is invalid");
plugin_manager_free(manager);
composite_api_resolver_free(resolver);
return false;
}
app->plugin_resolver = resolver;
app->plugin_manager = manager;
app->config_plugin = plugin;
return true;
}
static bool protopirate_app_custom_event_callback(void* context, uint32_t event) {
furi_check(context);
ProtoPirateApp* app = context;
@@ -154,7 +221,33 @@ ProtoPirateApp* protopirate_app_alloc() {
settings.auto_save,
settings.hopping_enabled);
protopirate_preset_init(app, preset_name, frequency, preset_data, preset_data_size);
config_plugin_load(app);
app->car_models_count = app->config_plugin->car_model_get_count();
app->selected_model = malloc(sizeof(ProtoPirateCarModel));
app->selected_model->name = furi_string_alloc();
app->selected_model->preset = NULL; // important initialization
app->selected_model->index = 0; // optional but clean
app->variable_item_list = NULL;
//Grab selected car model.
if(settings.car_model_index) {
app->config_plugin->car_model_get_by_index(
app->selected_model, settings.car_model_index, app->car_models_count, app->setting);
app->selected_model->last_preset_index = settings.preset_index;
protopirate_preset_init(
app,
furi_string_get_cstr(app->selected_model->preset->name),
app->selected_model->preset->frequency,
app->selected_model->preset->data,
app->selected_model->preset->data_size);
} else {
app->config_plugin->car_model_get_by_index(
app->selected_model, 0, app->car_models_count, app->setting);
protopirate_preset_init(app, preset_name, frequency, preset_data, preset_data_size);
}
config_plugin_unload(app);
// Apply hopping state from settings
app->txrx->hopper_state = settings.hopping_enabled ? ProtoPirateHopperStateRunning :
@@ -189,16 +282,48 @@ void protopirate_app_free(ProtoPirateApp* app) {
settings.emulate_feature_enabled = false;
#endif
// Find current preset index
settings.preset_index = 0;
const char* current_preset = furi_string_get_cstr(app->txrx->preset->name);
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
if(strcmp(subghz_setting_get_preset_name(app->setting, i), current_preset) == 0) {
settings.preset_index = i;
break;
//Get the selected Model, and get the preset to save.
if(app->selected_model && app->selected_model->index) {
//Get Preset Index before model was selected.
settings.car_model_index = app->selected_model->index;
settings.preset_index = app->selected_model->last_preset_index;
} else {
// Find current preset index
settings.car_model_index = 0; //Clear the selected model.
settings.preset_index = 0;
const char* current_preset = furi_string_get_cstr(app->txrx->preset->name);
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
if(strcmp(subghz_setting_get_preset_name(app->setting, i), current_preset) == 0) {
settings.preset_index = i;
break;
}
}
}
//Free the Model Name
furi_string_free(app->selected_model->name);
app->selected_model->index = 0;
//Free the preset information.
if(app->selected_model->preset) {
//Free the preset data
if((app->selected_model)->preset->data) {
free(app->selected_model->preset->data);
app->selected_model->preset->data = NULL;
}
//Free the Preset name
furi_string_free(app->selected_model->preset->name);
//Free the preset.
free(app->selected_model->preset);
app->selected_model->preset = NULL;
}
//Free the Model.
free(app->selected_model);
app->selected_model = NULL;
FURI_LOG_I(
TAG,
"Saving settings: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d",
+17 -10
View File
@@ -3,7 +3,6 @@
#include <stddef.h>
#include "helpers/protopirate_types.h"
#include "helpers/protopirate_settings.h"
#include "scenes/protopirate_scene.h"
#include "views/protopirate_receiver.h"
#include "protopirate_history.h"
@@ -13,7 +12,7 @@
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_input.h>
#include <notification/notification_messages.h>
@@ -33,16 +32,22 @@
#ifdef ENABLE_EMULATE_FEATURE
#include "scenes/plugins/protopirate_emulate_plugin.h"
#endif
#include "scenes/plugins/protopirate_config_plugin.h"
#include "scenes/plugins/protopirate_psa_bf_plugin.h"
#include "scenes/plugins/protopirate_tool_scene_plugin.h"
#include "helpers/protopirate_views.h"
#include "helpers/protopirate_radio.h"
#include "helpers/protopirate_protocol_plugin_host.h"
#include "helpers/protopirate_txrx.h"
#include "helpers/protopirate_models.h"
#include <loader/firmware_api/firmware_api.h>
#include "helpers/protopirate_settings.h"
#define CONFIG_PLUGIN_PATH APP_ASSETS_PATH("plugins/protopirate_config_plugin.fal")
#define PROTOPIRATE_KEYSTORE_DIR_NAME APP_ASSETS_PATH("encrypted")
typedef struct ProtoPirateApp ProtoPirateApp;
typedef struct VariableItemList VariableItemList;
typedef struct ProtoPirateTxRx {
SubGhzWorker* worker;
@@ -94,29 +99,28 @@ struct ProtoPirateApp {
bool save_from_saved_info;
bool emulate_disabled_for_loaded;
bool emulate_feature_enabled;
CompositeApiResolver* plugin_resolver;
PluginManager* plugin_manager;
#ifdef ENABLE_EMULATE_FEATURE
#define EMULATE_NAV_NONE 0U
#define EMULATE_NAV_POP 1U
#define EMULATE_NAV_STOP_APP 2U
CompositeApiResolver* emulate_plugin_resolver;
PluginManager* emulate_plugin_manager;
const ProtoPirateEmulatePlugin* emulate_plugin;
uint8_t emulate_nav_pending;
#endif
CompositeApiResolver* psa_bf_plugin_resolver;
PluginManager* psa_bf_plugin_manager;
const ProtoPirateConfigPlugin* config_plugin;
const ProtoPiratePsaBfPlugin* psa_bf_plugin;
CompositeApiResolver* tool_scene_plugin_resolver;
PluginManager* tool_scene_plugin_manager;
const ProtoPirateToolScenePlugin* tool_scene_plugin;
ProtoPirateToolScenePluginKind tool_scene_plugin_kind;
#define TOOL_SCENE_NAV_NONE 0U
#define TOOL_SCENE_NAV_POP 1U
#define TOOL_SCENE_NAV_NEXT 2U
#define TOOL_SCENE_NAV_SEARCH_PREVIOUS 3U
uint8_t tool_scene_nav_pending;
uint32_t tool_scene_nav_target;
ProtoPirateCarModel* selected_model;
uint32_t car_models_count;
};
#ifdef ENABLE_EMULATE_FEATURE
@@ -133,6 +137,9 @@ bool protopirate_tool_scene_on_event(void* app, SceneManagerEvent event);
void protopirate_tool_scene_on_exit(void* app);
void protopirate_tool_scene_plugin_release(ProtoPirateApp* app);
bool config_plugin_load(ProtoPirateApp* app);
void config_plugin_unload(ProtoPirateApp* app);
void protopirate_app_free(ProtoPirateApp* app);
static const NotificationSequence sequence_tx = {
+499
View File
@@ -0,0 +1,499 @@
#include "protopirate_config_plugin.h"
#include "../../protopirate_app_i.h"
#include "../../helpers/protopirate_models.h"
#define ON_OFF_COUNT 2
const char* const on_off_text[ON_OFF_COUNT] = {
"OFF",
"ON",
};
const uint32_t hopping_value[ON_OFF_COUNT] = {
ProtoPirateHopperStateOFF,
ProtoPirateHopperStateRunning,
};
const char* const sequence_time_text[ON_OFF_COUNT] = {
"Sequential",
"Time",
};
#ifdef ENABLE_EMULATE_FEATURE
#define TX_POWER_COUNT 9
const char* const tx_power_text[TX_POWER_COUNT] = {
"Preset",
"10dBm +",
"7dBm",
"5dBm",
"0dBm",
"-10dBm",
"-15dBm",
"-20dBm",
"-30dBm",
};
#endif
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app) {
furi_check(app);
if(app->variable_item_list) {
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
}
app->variable_item_list = variable_item_list_alloc();
if(!app->variable_item_list) {
return false;
}
view_dispatcher_add_view(
app->view_dispatcher,
ProtoPirateViewVariableItemList,
variable_item_list_get_view(app->variable_item_list));
return true;
}
static void protopirate_scene_receiver_config_set_frequency(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
if(app->txrx->hopper_state == ProtoPirateHopperStateOFF) {
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_frequency(app->setting, index) / 1000000,
(subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf);
app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index);
} else {
variable_item_set_current_value_index(
item, subghz_setting_get_frequency_default_index(app->setting));
}
}
static void protopirate_scene_receiver_config_set_preset(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(app->setting, index));
protopirate_preset_init(
app,
subghz_setting_get_preset_name(app->setting, index),
app->txrx->preset->frequency,
subghz_setting_get_preset_data(app->setting, index),
subghz_setting_get_preset_data_size(app->setting, index));
if(!protopirate_refresh_protocol_registry(app, false)) {
notification_message(app->notifications, &sequence_error);
}
}
static void protopirate_scene_receiver_config_set_hopping_running(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, on_off_text[index]);
if(hopping_value[index] == ProtoPirateHopperStateOFF) {
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_default_frequency(app->setting) / 1000000,
(subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000);
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
text_buf);
app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting);
variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
subghz_setting_get_frequency_default_index(app->setting));
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
" -----");
variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
subghz_setting_get_frequency_default_index(app->setting));
}
app->txrx->hopper_state = hopping_value[index];
}
static void protopirate_scene_receiver_config_set_model(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t direction = variable_item_get_current_value_index(item);
//Get current selection
uint16_t model_index;
model_index =
(app->selected_model && app->selected_model->index) ? app->selected_model->index : 0;
uint16_t old_model_index = model_index;
//Cycle the index.
switch(direction) {
case VariableItemListEventCycleReset: {
model_index = 0;
break;
}
case VariableItemListEventCycleLeft: {
if(model_index > 0)
model_index--;
else
model_index = app->car_models_count;
break;
}
case VariableItemListEventCycleRight: {
if(model_index < app->car_models_count)
model_index++;
else
model_index = 0;
}
}
//Get a Car Model object, and dont forget to shut down on app free!
car_model_get_by_index(
(ProtoPirateCarModel*)app->selected_model,
model_index,
app->car_models_count,
app->setting);
//Add he car model the list, with the correct selection and text.
variable_item_set_item_label(item, furi_string_get_cstr(app->selected_model->name));
variable_item_set_current_value_text(item, "");
variable_item_set_current_value_index(item, 0);
//Restore Original Preset.
VariableItem* freq_menu =
variable_item_list_get(app->variable_item_list, ProtoPirateSettingIndexFrequency);
VariableItem* hop_menu =
variable_item_list_get(app->variable_item_list, ProtoPirateSettingIndexHopping);
VariableItem* preset_menu =
variable_item_list_get(app->variable_item_list, ProtoPirateSettingIndexModulation);
//set the Preset, Frequency and Hopper off or restore.
if(model_index) {
variable_item_set_current_value_text(freq_menu, "Locked");
variable_item_set_current_value_text(hop_menu, "Locked");
app->txrx->hopper_state = ProtoPirateHopperStateOFF;
app->txrx->preset->frequency = app->selected_model->preset->frequency;
//Save Original Preset.
if(old_model_index) {
app->selected_model->last_preset_index = subghz_setting_get_inx_preset_by_name(
app->setting, furi_string_get_cstr(app->txrx->preset->name));
}
protopirate_preset_init(
app,
furi_string_get_cstr(app->selected_model->preset->name),
app->selected_model->preset->frequency,
app->selected_model->preset->data,
app->selected_model->preset->data_size);
} else {
//Restore Original Preset.
protopirate_scene_receiver_config_set_frequency(freq_menu);
protopirate_scene_receiver_config_set_hopping_running(hop_menu);
protopirate_preset_init(
app,
subghz_setting_get_preset_name(app->setting, app->selected_model->last_preset_index),
app->txrx->preset->frequency,
subghz_setting_get_preset_data(app->setting, app->selected_model->last_preset_index),
subghz_setting_get_preset_data_size(
app->setting, app->selected_model->last_preset_index));
variable_item_set_current_value_text(
preset_menu,
subghz_setting_get_preset_name(app->setting, app->selected_model->last_preset_index));
}
//Refresh the protocol registry now that we have a new Modulation Type.
if(!protopirate_refresh_protocol_registry(app, false)) {
notification_message(app->notifications, &sequence_error);
}
//Lock or Unlock the menus.
bool lock = (app->selected_model->index != 0);
variable_item_set_locked(freq_menu, lock, "Turn off\nCar Model\nto do that!");
variable_item_set_locked(hop_menu, lock, "Turn off\nCar Model\nto do that!");
variable_item_set_locked(preset_menu, lock, "Turn off\nCar Model\nto do that!");
}
static uint8_t
protopirate_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_check(context);
ProtoPirateApp* app = context;
uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
if(value == subghz_setting_get_frequency(app->setting, i)) {
index = i;
break;
} else {
index = subghz_setting_get_frequency_default_index(app->setting);
}
}
return index;
}
static uint8_t
protopirate_scene_receiver_config_next_preset(const char* preset_name, void* context) {
furi_check(context);
ProtoPirateApp* app = context;
uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) {
index = i;
break;
}
}
return index;
}
static uint8_t protopirate_scene_receiver_config_hopper_value_index(
const uint32_t value,
const uint32_t values[],
uint8_t values_count,
void* context) {
furi_check(context);
UNUSED(values_count);
ProtoPirateApp* app = context;
if(value == values[0]) {
return 0;
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
" -----");
return 1;
}
}
static void protopirate_scene_receiver_config_set_auto_save(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->auto_save = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
static void protopirate_scene_receiver_config_set_datetime_filenames(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->datetime_filenames = (index == 1);
variable_item_set_current_value_text(item, sequence_time_text[index]);
}
static void protopirate_scene_receiver_config_set_check_saved(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->check_saved = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
static void protopirate_scene_receiver_config_set_sound(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->sound = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
#ifdef ENABLE_EMULATE_FEATURE
static void protopirate_scene_receiver_config_set_tx_power(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->tx_power = index;
variable_item_set_current_value_text(item, tx_power_text[index]);
}
#endif
static void
protopirate_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_check(context);
ProtoPirateApp* app = context;
switch(index) {
case ProtoPirateSettingIndexCarModel: {
//Reset the Models Menu
VariableItem* model_menu =
variable_item_list_get(app->variable_item_list, ProtoPirateSettingIndexCarModel);
variable_item_set_current_value_index(model_menu, 0);
protopirate_scene_receiver_config_set_model(model_menu);
break;
}
case ProtoPirateSettingIndexLock: {
view_dispatcher_send_custom_event(
app->view_dispatcher, ProtoPirateCustomEventSceneSettingLock);
break;
}
}
}
static void plugin_on_enter(void* context) {
ProtoPirateApp* app = context;
VariableItem* item;
uint8_t value_index;
if(!protopirate_ensure_variable_item_list(app)) {
notification_message(app->notifications, &sequence_error);
scene_manager_previous_scene(app->scene_manager);
return;
}
//Add he car model the list, with the correct selection and text.
item = variable_item_list_add(
app->variable_item_list,
furi_string_get_cstr(app->selected_model->name),
0, //Plus NONE
protopirate_scene_receiver_config_set_model,
app);
item = variable_item_list_add(
app->variable_item_list,
"Frequency:",
subghz_setting_get_frequency_count(app->setting),
protopirate_scene_receiver_config_set_frequency,
app);
value_index =
protopirate_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app);
scene_manager_set_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig, (uint32_t)item);
variable_item_set_current_value_index(item, value_index);
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_frequency(app->setting, value_index) / 1000000,
(subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf);
variable_item_set_locked(
item,
app->selected_model && (app->selected_model->index),
"Turn off\nCar Model\nto do that!");
item = variable_item_list_add(
app->variable_item_list,
"Hopping:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_hopping_running,
app);
value_index = protopirate_scene_receiver_config_hopper_value_index(
app->txrx->hopper_state, hopping_value, ON_OFF_COUNT, app);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, on_off_text[value_index]);
variable_item_set_locked(
item,
app->selected_model && (app->selected_model->index),
"Turn off\nCar Model\nto do that!");
item = variable_item_list_add(
app->variable_item_list,
"Modulation:",
subghz_setting_get_preset_count(app->setting),
protopirate_scene_receiver_config_set_preset,
app);
value_index = protopirate_scene_receiver_config_next_preset(
furi_string_get_cstr(app->txrx->preset->name), app);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(app->setting, value_index));
variable_item_set_locked(
item,
app->selected_model && (app->selected_model->index),
"Turn off\nCar Model\nto do that!");
#ifdef ENABLE_EMULATE_FEATURE
// TX power option
item = variable_item_list_add(
app->variable_item_list,
"TX Power:",
TX_POWER_COUNT,
protopirate_scene_receiver_config_set_tx_power,
app);
variable_item_set_current_value_index(item, app->tx_power);
variable_item_set_current_value_text(item, tx_power_text[app->tx_power]);
#endif
// Auto-save option
item = variable_item_list_add(
app->variable_item_list,
"Auto-Save:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_auto_save,
app);
variable_item_set_current_value_index(item, app->auto_save ? 1 : 0);
variable_item_set_current_value_text(item, on_off_text[app->auto_save ? 1 : 0]);
// Date/time filenames option
item = variable_item_list_add(
app->variable_item_list,
"Filenames:",
2,
protopirate_scene_receiver_config_set_datetime_filenames,
app);
variable_item_set_current_value_index(item, app->datetime_filenames ? 1 : 0);
variable_item_set_current_value_text(
item, sequence_time_text[app->datetime_filenames ? 1 : 0]);
item = variable_item_list_add(
app->variable_item_list,
"Check Saved:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_check_saved,
app);
variable_item_set_current_value_index(item, app->check_saved ? 1 : 0);
variable_item_set_current_value_text(item, on_off_text[app->check_saved ? 1 : 0]);
// Sound option
item = variable_item_list_add(
app->variable_item_list,
"Sound:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_sound,
app);
variable_item_set_current_value_index(item, app->sound);
variable_item_set_current_value_text(item, on_off_text[app->sound ? 1 : 0]);
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
variable_item_list_set_enter_callback(
app->variable_item_list, protopirate_scene_receiver_config_var_list_enter_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
}
static void plugin_on_exit(void* context) {
ProtoPirateApp* app = context;
//Reset the variable item list.
variable_item_list_set_selected_item(app->variable_item_list, 0);
variable_item_list_reset(app->variable_item_list);
variable_item_list_free(app->variable_item_list);
//app->variable_item_list = NULL;
}
static const ProtoPirateConfigPlugin protopirate_config_plugin = {
.plugin_name = "ProtoPirate Config",
.car_model_get_by_index = car_model_get_by_index,
.car_model_get_count = car_model_get_count,
.on_enter = plugin_on_enter,
.on_exit = plugin_on_exit,
};
static const FlipperAppPluginDescriptor protopirate_config_plugin_descriptor = {
.appid = PROTOPIRATE_CONFIG_PLUGIN_APP_ID,
.ep_api_version = PROTOPIRATE_CONFIG_PLUGIN_API_VERSION,
.entry_point = &protopirate_config_plugin,
};
const FlipperAppPluginDescriptor* protopirate_config_plugin_ep(void) {
return &protopirate_config_plugin_descriptor;
}
@@ -0,0 +1,35 @@
#pragma once
#include "helpers/protopirate_models.h"
#include <lib/flipper_application/flipper_application.h>
#include "helpers/variable_item_list.h"
#define PROTOPIRATE_CONFIG_PLUGIN_APP_ID "protopirate_config_plugin"
#define PROTOPIRATE_CONFIG_PLUGIN_API_VERSION 2U
enum ProtoPirateSettingIndex {
ProtoPirateSettingIndexCarModel,
ProtoPirateSettingIndexFrequency,
ProtoPirateSettingIndexHopping,
ProtoPirateSettingIndexModulation,
#ifdef ENABLE_EMULATE_FEATURE
ProtoPirateSettingIndexTXPower,
#endif
ProtoPirateSettingIndexAutoSave,
ProtoPirateSettingIndexDateTimeFilenames,
ProtoPirateSettingIndexSound,
ProtoPirateSettingIndexCheckSaved,
ProtoPirateSettingIndexLock,
};
typedef struct ProtoPirateConfigPlugin {
const char* plugin_name;
bool (*car_model_get_by_index)(
ProtoPirateCarModel* car_model,
uint16_t index,
uint16_t model_count,
SubGhzSetting* app_settings);
uint16_t (*car_model_get_count)(void);
void (*on_enter)(void* app);
void (*on_exit)(void* app);
} ProtoPirateConfigPlugin;
+9 -9
View File
@@ -94,14 +94,14 @@ static void emulate_plugin_unload(ProtoPirateApp* app) {
app->emulate_plugin = NULL;
if(app->emulate_plugin_manager) {
plugin_manager_free(app->emulate_plugin_manager);
app->emulate_plugin_manager = NULL;
if(app->plugin_manager) {
plugin_manager_free(app->plugin_manager);
app->plugin_manager = NULL;
}
if(app->emulate_plugin_resolver) {
composite_api_resolver_free(app->emulate_plugin_resolver);
app->emulate_plugin_resolver = NULL;
if(app->plugin_resolver) {
composite_api_resolver_free(app->plugin_resolver);
app->plugin_resolver = NULL;
}
}
@@ -110,7 +110,7 @@ static bool emulate_plugin_load(ProtoPirateApp* app) {
if(app->emulate_plugin) return true;
if(app->emulate_plugin_manager || app->emulate_plugin_resolver) {
if(app->plugin_manager || app->plugin_resolver) {
emulate_plugin_unload(app);
}
@@ -148,8 +148,8 @@ static bool emulate_plugin_load(ProtoPirateApp* app) {
return false;
}
app->emulate_plugin_resolver = resolver;
app->emulate_plugin_manager = manager;
app->plugin_resolver = resolver;
app->plugin_manager = manager;
app->emulate_plugin = plugin;
plugin->set_host_api(&protopirate_emulate_host_api);
+7 -322
View File
@@ -1,334 +1,16 @@
// scenes/protopirate_scene_receiver_config.c
#include "../protopirate_app_i.h"
enum ProtoPirateSettingIndex {
ProtoPirateSettingIndexFrequency,
ProtoPirateSettingIndexHopping,
ProtoPirateSettingIndexModulation,
#ifdef ENABLE_EMULATE_FEATURE
ProtoPirateSettingIndexTXPower,
#endif
ProtoPirateSettingIndexAutoSave,
ProtoPirateSettingIndexDateTimeFilenames,
ProtoPirateSettingIndexSound,
ProtoPirateSettingIndexCheckSaved,
ProtoPirateSettingIndexLock,
};
#define ON_OFF_COUNT 2
const char* const on_off_text[ON_OFF_COUNT] = {
"OFF",
"ON",
};
const uint32_t hopping_value[ON_OFF_COUNT] = {
ProtoPirateHopperStateOFF,
ProtoPirateHopperStateRunning,
};
const char* const sequence_time_text[ON_OFF_COUNT] = {
"Sequential",
"Time",
};
#ifdef ENABLE_EMULATE_FEATURE
#define TX_POWER_COUNT 9
const char* const tx_power_text[TX_POWER_COUNT] = {
"Preset",
"10dBm +",
"7dBm",
"5dBm",
"0dBm",
"-10dBm",
"-15dBm",
"-20dBm",
"-30dBm",
};
#endif
uint8_t protopirate_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_check(context);
ProtoPirateApp* app = context;
uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
if(value == subghz_setting_get_frequency(app->setting, i)) {
index = i;
break;
} else {
index = subghz_setting_get_frequency_default_index(app->setting);
}
}
return index;
}
uint8_t protopirate_scene_receiver_config_next_preset(const char* preset_name, void* context) {
furi_check(context);
ProtoPirateApp* app = context;
uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) {
index = i;
break;
}
}
return index;
}
uint8_t protopirate_scene_receiver_config_hopper_value_index(
const uint32_t value,
const uint32_t values[],
uint8_t values_count,
void* context) {
furi_check(context);
UNUSED(values_count);
ProtoPirateApp* app = context;
if(value == values[0]) {
return 0;
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
" -----");
return 1;
}
}
static void protopirate_scene_receiver_config_set_frequency(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
if(app->txrx->hopper_state == ProtoPirateHopperStateOFF) {
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_frequency(app->setting, index) / 1000000,
(subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf);
app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index);
} else {
variable_item_set_current_value_index(
item, subghz_setting_get_frequency_default_index(app->setting));
}
}
static void protopirate_scene_receiver_config_set_preset(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(app->setting, index));
protopirate_preset_init(
app,
subghz_setting_get_preset_name(app->setting, index),
app->txrx->preset->frequency,
subghz_setting_get_preset_data(app->setting, index),
subghz_setting_get_preset_data_size(app->setting, index));
if(!protopirate_refresh_protocol_registry(app, false)) {
notification_message(app->notifications, &sequence_error);
}
}
static void protopirate_scene_receiver_config_set_hopping_running(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, on_off_text[index]);
if(hopping_value[index] == ProtoPirateHopperStateOFF) {
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_default_frequency(app->setting) / 1000000,
(subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000);
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
text_buf);
app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting);
variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
subghz_setting_get_frequency_default_index(app->setting));
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
" -----");
variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig),
subghz_setting_get_frequency_default_index(app->setting));
}
app->txrx->hopper_state = hopping_value[index];
}
static void protopirate_scene_receiver_config_set_auto_save(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->auto_save = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
static void protopirate_scene_receiver_config_set_datetime_filenames(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->datetime_filenames = (index == 1);
variable_item_set_current_value_text(item, sequence_time_text[index]);
}
static void protopirate_scene_receiver_config_set_check_saved(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->check_saved = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
static void protopirate_scene_receiver_config_set_sound(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->sound = (index == 1);
variable_item_set_current_value_text(item, on_off_text[index]);
}
#ifdef ENABLE_EMULATE_FEATURE
static void protopirate_scene_receiver_config_set_tx_power(VariableItem* item) {
ProtoPirateApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->tx_power = index;
variable_item_set_current_value_text(item, tx_power_text[index]);
}
#endif
static void
protopirate_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_check(context);
ProtoPirateApp* app = context;
if(index == ProtoPirateSettingIndexLock) {
view_dispatcher_send_custom_event(
app->view_dispatcher, ProtoPirateCustomEventSceneSettingLock);
}
}
void protopirate_scene_receiver_config_on_enter(void* context) {
ProtoPirateApp* app = context;
VariableItem* item;
uint8_t value_index;
if(!protopirate_ensure_variable_item_list(app)) {
if(!config_plugin_load(app)) {
notification_message(app->notifications, &sequence_error);
scene_manager_previous_scene(app->scene_manager);
return;
}
item = variable_item_list_add(
app->variable_item_list,
"Frequency:",
subghz_setting_get_frequency_count(app->setting),
protopirate_scene_receiver_config_set_frequency,
app);
value_index =
protopirate_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app);
scene_manager_set_scene_state(
app->scene_manager, ProtoPirateSceneReceiverConfig, (uint32_t)item);
variable_item_set_current_value_index(item, value_index);
char text_buf[10] = {0};
snprintf(
text_buf,
sizeof(text_buf),
"%lu.%02lu",
subghz_setting_get_frequency(app->setting, value_index) / 1000000,
(subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf);
item = variable_item_list_add(
app->variable_item_list,
"Hopping:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_hopping_running,
app);
value_index = protopirate_scene_receiver_config_hopper_value_index(
app->txrx->hopper_state, hopping_value, ON_OFF_COUNT, app);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, on_off_text[value_index]);
item = variable_item_list_add(
app->variable_item_list,
"Modulation:",
subghz_setting_get_preset_count(app->setting),
protopirate_scene_receiver_config_set_preset,
app);
value_index = protopirate_scene_receiver_config_next_preset(
furi_string_get_cstr(app->txrx->preset->name), app);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(app->setting, value_index));
#ifdef ENABLE_EMULATE_FEATURE
// TX power option
item = variable_item_list_add(
app->variable_item_list,
"TX Power:",
TX_POWER_COUNT,
protopirate_scene_receiver_config_set_tx_power,
app);
variable_item_set_current_value_index(item, app->tx_power);
variable_item_set_current_value_text(item, tx_power_text[app->tx_power]);
#endif
// Auto-save option
item = variable_item_list_add(
app->variable_item_list,
"Auto-Save:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_auto_save,
app);
variable_item_set_current_value_index(item, app->auto_save ? 1 : 0);
variable_item_set_current_value_text(item, on_off_text[app->auto_save ? 1 : 0]);
// Date/time filenames option
item = variable_item_list_add(
app->variable_item_list,
"Filenames:",
2,
protopirate_scene_receiver_config_set_datetime_filenames,
app);
variable_item_set_current_value_index(item, app->datetime_filenames ? 1 : 0);
variable_item_set_current_value_text(
item, sequence_time_text[app->datetime_filenames ? 1 : 0]);
item = variable_item_list_add(
app->variable_item_list,
"Check Saved:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_check_saved,
app);
variable_item_set_current_value_index(item, app->check_saved ? 1 : 0);
variable_item_set_current_value_text(item, on_off_text[app->check_saved ? 1 : 0]);
// Sound option
item = variable_item_list_add(
app->variable_item_list,
"Sound:",
ON_OFF_COUNT,
protopirate_scene_receiver_config_set_sound,
app);
variable_item_set_current_value_index(item, app->sound);
variable_item_set_current_value_text(item, on_off_text[(!app->sound) ? 0 : 1]);
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
variable_item_list_set_enter_callback(
app->variable_item_list, protopirate_scene_receiver_config_var_list_enter_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
app->config_plugin->on_enter(app);
}
bool protopirate_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
@@ -347,6 +29,9 @@ bool protopirate_scene_receiver_config_on_event(void* context, SceneManagerEvent
void protopirate_scene_receiver_config_on_exit(void* context) {
ProtoPirateApp* app = context;
variable_item_list_set_selected_item(app->variable_item_list, 0);
variable_item_list_reset(app->variable_item_list);
if(app->config_plugin && app->config_plugin->on_exit) {
app->config_plugin->on_exit(app);
}
config_plugin_unload(app);
}