mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-20 20:38:24 -07:00
Update multi fuzzer and subghz remote
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 @gid9798 @xMasterX @G4N4P4T1
|
||||
|
||||
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.
|
||||
@@ -11,6 +11,11 @@ App(
|
||||
"notification",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
fap_author="gid9798 xMasterX",
|
||||
fap_weburl="https://github.com/DarkFlippers/Multi_Fuzzer",
|
||||
fap_version="1.1",
|
||||
targets=["f7"],
|
||||
fap_description="Fuzzer for ibutton readers",
|
||||
fap_icon="ibutt_10px.png",
|
||||
fap_category="iButton",
|
||||
fap_private_libs=[
|
||||
@@ -36,6 +41,11 @@ App(
|
||||
"notification",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
fap_author="gid9798 xMasterX",
|
||||
fap_weburl="https://github.com/DarkFlippers/Multi_Fuzzer",
|
||||
fap_version="1.1",
|
||||
targets=["f7"],
|
||||
fap_description="Fuzzer for lfrfid readers",
|
||||
fap_icon="icons/rfid_10px.png",
|
||||
fap_category="RFID",
|
||||
fap_private_libs=[
|
||||
|
||||
+9
@@ -49,6 +49,11 @@ PacsFuzzerApp* fuzzer_app_alloc() {
|
||||
app->popup = popup_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup));
|
||||
|
||||
// TextInput
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FuzzerViewIDTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
// Main view
|
||||
app->main_view = fuzzer_view_main_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@@ -103,6 +108,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) {
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
// TextInput
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDTextInput);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define FUZZ_TIME_DELAY_MAX (80)
|
||||
#define KEY_NAME_SIZE 22
|
||||
|
||||
typedef struct {
|
||||
const char* custom_dict_extension;
|
||||
@@ -42,11 +44,13 @@ typedef struct {
|
||||
|
||||
Popup* popup;
|
||||
DialogsApp* dialogs;
|
||||
TextInput* text_input;
|
||||
FuzzerViewMain* main_view;
|
||||
FuzzerViewAttack* attack_view;
|
||||
FuzzerViewFieldEditor* field_editor_view;
|
||||
|
||||
FuriString* file_path;
|
||||
char key_name[KEY_NAME_SIZE + 1];
|
||||
|
||||
FuzzerState fuzzer_state;
|
||||
FuzzerConsts* fuzzer_const;
|
||||
|
||||
@@ -7,11 +7,21 @@ typedef enum {
|
||||
FuzzerCustomEventViewMainOk,
|
||||
FuzzerCustomEventViewMainPopupErr,
|
||||
|
||||
FuzzerCustomEventViewAttackBack,
|
||||
FuzzerCustomEventViewAttackOk,
|
||||
// FuzzerCustomEventViewAttackTick, // now not use
|
||||
FuzzerCustomEventViewAttackEnd,
|
||||
|
||||
FuzzerCustomEventViewAttackExit,
|
||||
FuzzerCustomEventViewAttackRunAttack,
|
||||
FuzzerCustomEventViewAttackPause,
|
||||
FuzzerCustomEventViewAttackIdle, // Setup
|
||||
FuzzerCustomEventViewAttackEmulateCurrent,
|
||||
FuzzerCustomEventViewAttackSave,
|
||||
FuzzerCustomEventViewAttackNextUid,
|
||||
FuzzerCustomEventViewAttackPrevUid,
|
||||
|
||||
FuzzerCustomEventViewFieldEditorBack,
|
||||
FuzzerCustomEventViewFieldEditorOk,
|
||||
|
||||
FuzzerCustomEventTextEditResult,
|
||||
|
||||
FuzzerCustomEventPopupClosed,
|
||||
} FuzzerCustomEvent;
|
||||
@@ -10,7 +10,9 @@ typedef struct {
|
||||
typedef enum {
|
||||
FuzzerAttackStateOff = 0,
|
||||
FuzzerAttackStateIdle,
|
||||
FuzzerAttackStateRunning,
|
||||
FuzzerAttackStateAttacking,
|
||||
FuzzerAttackStateEmulating,
|
||||
FuzzerAttackStatePause,
|
||||
FuzzerAttackStateEnd,
|
||||
|
||||
} FuzzerAttackState;
|
||||
@@ -23,6 +25,7 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
FuzzerViewIDPopup,
|
||||
FuzzerViewIDTextInput,
|
||||
|
||||
FuzzerViewIDMain,
|
||||
FuzzerViewIDAttack,
|
||||
|
||||
+207
-184
@@ -1,4 +1,5 @@
|
||||
#include "fake_worker.h"
|
||||
#include "helpers/hardware_worker.h"
|
||||
#include "protocol_i.h"
|
||||
|
||||
#include <timer.h>
|
||||
@@ -8,47 +9,27 @@
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
|
||||
#define TAG "Fuzzer worker"
|
||||
#define TOTAL_PROTOCOL_COUNT fuzzer_proto_get_count_of_protocols()
|
||||
#define PROTOCOL_KEY_FOLDER EXT_PATH(PROTOCOL_KEY_FOLDER_NAME)
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
|
||||
#include <lib/lfrfid/lfrfid_dict_file.h>
|
||||
#include <lib/lfrfid/lfrfid_worker.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <lib/ibutton/ibutton_worker.h>
|
||||
#include <lib/ibutton/ibutton_key.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <toolbox/stream/stream.h>
|
||||
typedef uint8_t FuzzerWorkerPayload[MAX_PAYLOAD_SIZE];
|
||||
|
||||
struct FuzzerWorker {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
LFRFIDWorker* proto_worker;
|
||||
ProtocolId protocol_id;
|
||||
ProtocolDict* protocols_items;
|
||||
#else
|
||||
iButtonWorker* proto_worker;
|
||||
iButtonProtocolId protocol_id; // TODO
|
||||
iButtonProtocols* protocols_items;
|
||||
iButtonKey* key;
|
||||
#endif
|
||||
HardwareWorker* hw_worker;
|
||||
|
||||
const FuzzerProtocol* protocol;
|
||||
HwProtocolID* suported_proto;
|
||||
|
||||
FuzzerWorkerPayload payload;
|
||||
|
||||
FuzzerWorkerAttackType attack_type;
|
||||
uint16_t timer_idle_time_ms;
|
||||
uint16_t timer_emu_time_ms;
|
||||
|
||||
uint8_t payload[MAX_PAYLOAD_SIZE];
|
||||
Stream* uids_stream;
|
||||
uint16_t index;
|
||||
uint8_t chusen_byte;
|
||||
Stream* uids_stream;
|
||||
|
||||
bool treead_running;
|
||||
bool in_emu_phase;
|
||||
FuriTimer* timer;
|
||||
uint16_t timer_idle_time_ms;
|
||||
uint16_t timer_emu_time_ms;
|
||||
|
||||
FuzzerWorkerUidChagedCallback tick_callback;
|
||||
void* tick_context;
|
||||
@@ -57,6 +38,85 @@ struct FuzzerWorker {
|
||||
void* end_context;
|
||||
};
|
||||
|
||||
static bool fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
|
||||
if(!(protocol_index < TOTAL_PROTOCOL_COUNT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->protocol = &fuzzer_proto_items[protocol_index];
|
||||
return hardware_worker_set_protocol_id_by_name(
|
||||
instance->hw_worker, fuzzer_proto_items[protocol_index].name);
|
||||
}
|
||||
|
||||
static FuzzerProtocolsID
|
||||
fuzzer_worker_is_protocol_valid(FuzzerWorker* instance, HwProtocolID protocol_id) {
|
||||
for(FuzzerProtocolsID i = 0; i < TOTAL_PROTOCOL_COUNT; i++) {
|
||||
if(protocol_id == instance->suported_proto[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return TOTAL_PROTOCOL_COUNT;
|
||||
}
|
||||
|
||||
FuzzerWorkerLoadKeyState fuzzer_worker_load_key_from_file(
|
||||
FuzzerWorker* instance,
|
||||
FuzzerProtocolsID* protocol_index,
|
||||
const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
FuzzerWorkerLoadKeyState res = FuzzerWorkerLoadKeyStateUnsuportedProto;
|
||||
if(!hardware_worker_load_key_from_file(instance->hw_worker, filename)) {
|
||||
FURI_LOG_E(TAG, "Load key file: cant load file");
|
||||
res = FuzzerWorkerLoadKeyStateBadFile;
|
||||
} else {
|
||||
FuzzerProtocolsID loaded_id = fuzzer_worker_is_protocol_valid(
|
||||
instance, hardware_worker_get_protocol_id(instance->hw_worker));
|
||||
|
||||
if(!fuzzer_worker_set_protocol(instance, loaded_id)) {
|
||||
FURI_LOG_E(TAG, "Load key file: Unsuported protocol");
|
||||
res = FuzzerWorkerLoadKeyStateUnsuportedProto;
|
||||
} else {
|
||||
if(*protocol_index != loaded_id) {
|
||||
res = FuzzerWorkerLoadKeyStateDifferentProto;
|
||||
} else {
|
||||
res = FuzzerWorkerLoadKeyStateOk;
|
||||
}
|
||||
*protocol_index = loaded_id;
|
||||
|
||||
hardware_worker_get_protocol_data(
|
||||
instance->hw_worker, &instance->payload[0], MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool fuzer_worker_make_key_folder() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
const bool res = storage_simply_mkdir(storage, PROTOCOL_KEY_FOLDER);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool fuzzer_worker_save_key(FuzzerWorker* instance, const char* path) {
|
||||
furi_assert(instance);
|
||||
bool res = false;
|
||||
|
||||
if(!fuzer_worker_make_key_folder()) {
|
||||
FURI_LOG_E(TAG, "Cannot create key folder");
|
||||
} else if(!hardware_worker_save_key(instance->hw_worker, path)) {
|
||||
FURI_LOG_E(TAG, "Cannot save key file");
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Save key Success");
|
||||
res = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol);
|
||||
@@ -67,7 +127,11 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
|
||||
switch(instance->attack_type) {
|
||||
case FuzzerWorkerAttackTypeDefaultDict:
|
||||
if(next) {
|
||||
instance->index++;
|
||||
if(instance->index < (protocol->dict.len - 1)) {
|
||||
instance->index++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(instance->index < protocol->dict.len) {
|
||||
memcpy(
|
||||
@@ -113,6 +177,7 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_string_free(data_str);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -128,18 +193,51 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
protocol_dict_set_data(
|
||||
instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE);
|
||||
#else
|
||||
ibutton_key_set_protocol_id(instance->key, instance->protocol_id);
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
|
||||
|
||||
// TODO check data.size logic
|
||||
data.size = MAX_PAYLOAD_SIZE;
|
||||
memcpy(data.ptr, instance->payload, MAX_PAYLOAD_SIZE); // data.size);
|
||||
#endif
|
||||
if(res) {
|
||||
hardware_worker_set_protocol_data(
|
||||
instance->hw_worker, &instance->payload[0], protocol->data_size);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool fuzzer_worker_load_previous_key(FuzzerWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol);
|
||||
bool res = false;
|
||||
|
||||
const FuzzerProtocol* protocol = instance->protocol;
|
||||
|
||||
switch(instance->attack_type) {
|
||||
case FuzzerWorkerAttackTypeDefaultDict:
|
||||
if(instance->index > 0) {
|
||||
instance->index--;
|
||||
memcpy(
|
||||
instance->payload,
|
||||
&protocol->dict.val[instance->index * protocol->data_size],
|
||||
protocol->data_size);
|
||||
res = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case FuzzerWorkerAttackTypeLoadFile:
|
||||
if(instance->payload[instance->index] != 0x00) {
|
||||
instance->payload[instance->index]--;
|
||||
res = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(res) {
|
||||
hardware_worker_set_protocol_data(
|
||||
instance->hw_worker, &instance->payload[0], protocol->data_size);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -149,13 +247,7 @@ static void fuzzer_worker_on_tick_callback(void* context) {
|
||||
FuzzerWorker* instance = context;
|
||||
|
||||
if(instance->in_emu_phase) {
|
||||
if(instance->treead_running) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_stop(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_stop(instance->proto_worker);
|
||||
#endif
|
||||
}
|
||||
hardware_worker_stop(instance->hw_worker);
|
||||
instance->in_emu_phase = false;
|
||||
furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms));
|
||||
} else {
|
||||
@@ -165,13 +257,7 @@ static void fuzzer_worker_on_tick_callback(void* context) {
|
||||
instance->end_callback(instance->end_context);
|
||||
}
|
||||
} else {
|
||||
if(instance->treead_running) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
|
||||
#else
|
||||
ibutton_worker_emulate_start(instance->proto_worker, instance->key);
|
||||
#endif
|
||||
}
|
||||
hardware_worker_emulate_start(instance->hw_worker);
|
||||
instance->in_emu_phase = true;
|
||||
furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
|
||||
if(instance->tick_callback) {
|
||||
@@ -190,24 +276,29 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output
|
||||
memcpy(output_key->data, instance->payload, instance->protocol->data_size);
|
||||
}
|
||||
|
||||
static void fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
|
||||
instance->protocol = &fuzzer_proto_items[protocol_index];
|
||||
bool fuzzer_worker_next_key(FuzzerWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol);
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
instance->protocol_id =
|
||||
protocol_dict_get_protocol_by_name(instance->protocols_items, instance->protocol->name);
|
||||
#else
|
||||
// TODO iButtonProtocolIdInvalid check
|
||||
instance->protocol_id =
|
||||
ibutton_protocols_get_id_by_name(instance->protocols_items, instance->protocol->name);
|
||||
#endif
|
||||
return fuzzer_worker_load_key(instance, true);
|
||||
}
|
||||
|
||||
bool fuzzer_worker_previous_key(FuzzerWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol);
|
||||
|
||||
return fuzzer_worker_load_previous_key(instance);
|
||||
}
|
||||
|
||||
bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool res = false;
|
||||
fuzzer_worker_set_protocol(instance, protocol_index);
|
||||
|
||||
if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
return res;
|
||||
}
|
||||
|
||||
instance->attack_type = FuzzerWorkerAttackTypeDefaultDict;
|
||||
instance->index = 0;
|
||||
@@ -229,10 +320,15 @@ bool fuzzer_worker_init_attack_file_dict(
|
||||
furi_assert(file_path);
|
||||
|
||||
bool res = false;
|
||||
fuzzer_worker_set_protocol(instance, protocol_index);
|
||||
|
||||
if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
return res;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->uids_stream = buffered_file_stream_alloc(storage);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(!buffered_file_stream_open(
|
||||
instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
@@ -246,7 +342,7 @@ bool fuzzer_worker_init_attack_file_dict(
|
||||
if(!fuzzer_worker_load_key(instance, false)) {
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
buffered_file_stream_close(instance->uids_stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
stream_free(instance->uids_stream);
|
||||
} else {
|
||||
res = true;
|
||||
}
|
||||
@@ -262,88 +358,49 @@ bool fuzzer_worker_init_attack_bf_byte(
|
||||
furi_assert(instance);
|
||||
|
||||
bool res = false;
|
||||
fuzzer_worker_set_protocol(instance, protocol_index);
|
||||
if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
return res;
|
||||
}
|
||||
|
||||
instance->attack_type = FuzzerWorkerAttackTypeLoadFile;
|
||||
instance->index = chusen;
|
||||
|
||||
memcpy(instance->payload, new_uid->data, instance->protocol->data_size);
|
||||
|
||||
res = true;
|
||||
hardware_worker_set_protocol_data(
|
||||
instance->hw_worker, &instance->payload[0], instance->protocol->data_size);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO make it protocol independent
|
||||
bool fuzzer_worker_load_key_from_file(
|
||||
FuzzerWorker* instance,
|
||||
FuzzerProtocolsID protocol_index,
|
||||
const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool res = false;
|
||||
fuzzer_worker_set_protocol(instance, protocol_index);
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename);
|
||||
if(loaded_proto_id == PROTOCOL_NO) {
|
||||
// Err Cant load file
|
||||
FURI_LOG_W(TAG, "Cant load file");
|
||||
} else if(instance->protocol_id != loaded_proto_id) { // Err wrong protocol
|
||||
FURI_LOG_W(TAG, "Wrong protocol");
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Selected: %s Loaded: %s",
|
||||
instance->protocol->name,
|
||||
protocol_dict_get_name(instance->protocols_items, loaded_proto_id));
|
||||
} else {
|
||||
protocol_dict_get_data(
|
||||
instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE);
|
||||
res = true;
|
||||
}
|
||||
#else
|
||||
if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) {
|
||||
// Err Cant load file
|
||||
FURI_LOG_W(TAG, "Cant load file");
|
||||
} else {
|
||||
if(instance->protocol_id != ibutton_key_get_protocol_id(instance->key)) {
|
||||
// Err wrong protocol
|
||||
FURI_LOG_W(TAG, "Wrong protocol");
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Selected: %s Loaded: %s",
|
||||
instance->protocol->name,
|
||||
ibutton_protocols_get_name(
|
||||
instance->protocols_items, ibutton_key_get_protocol_id(instance->key)));
|
||||
} else {
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
|
||||
memcpy(instance->payload, data.ptr, data.size);
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
return true;
|
||||
}
|
||||
|
||||
FuzzerWorker* fuzzer_worker_alloc() {
|
||||
FuzzerWorker* instance = malloc(sizeof(FuzzerWorker));
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
instance->hw_worker = hardware_worker_alloc();
|
||||
hardware_worker_start_thread(instance->hw_worker);
|
||||
|
||||
instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items);
|
||||
#else
|
||||
instance->protocols_items = ibutton_protocols_alloc();
|
||||
instance->key =
|
||||
ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items));
|
||||
instance->suported_proto = malloc(sizeof(HwProtocolID) * TOTAL_PROTOCOL_COUNT);
|
||||
|
||||
for(uint8_t i = 0; i < TOTAL_PROTOCOL_COUNT; i++) {
|
||||
if(!hardware_worker_set_protocol_id_by_name(
|
||||
instance->hw_worker, fuzzer_proto_items[i].name)) {
|
||||
// Check protocol support
|
||||
FURI_LOG_E(TAG, "Not supported protocol name: %s", fuzzer_proto_items[i].name);
|
||||
furi_crash("Not supported protocol name");
|
||||
} else {
|
||||
instance->suported_proto[i] = hardware_worker_get_protocol_id(instance->hw_worker);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%u: %15s Protocol_id: %lu",
|
||||
i + 1,
|
||||
fuzzer_proto_items[i].name,
|
||||
instance->suported_proto[i]);
|
||||
}
|
||||
}
|
||||
|
||||
instance->proto_worker = ibutton_worker_alloc(instance->protocols_items);
|
||||
#endif
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
instance->index = 0;
|
||||
instance->treead_running = false;
|
||||
instance->in_emu_phase = false;
|
||||
|
||||
memset(instance->payload, 0x00, sizeof(instance->payload));
|
||||
@@ -364,16 +421,10 @@ void fuzzer_worker_free(FuzzerWorker* instance) {
|
||||
|
||||
furi_timer_free(instance->timer);
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_free(instance->proto_worker);
|
||||
free(instance->suported_proto);
|
||||
|
||||
protocol_dict_free(instance->protocols_items);
|
||||
#else
|
||||
ibutton_worker_free(instance->proto_worker);
|
||||
|
||||
ibutton_key_free(instance->key);
|
||||
ibutton_protocols_free(instance->protocols_items);
|
||||
#endif
|
||||
hardware_worker_stop_thread(instance->hw_worker);
|
||||
hardware_worker_free(instance->hw_worker);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
@@ -399,25 +450,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
|
||||
instance->timer_emu_time_ms,
|
||||
instance->timer_idle_time_ms);
|
||||
|
||||
if(!instance->treead_running) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_start_thread(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_start_thread(instance->proto_worker);
|
||||
#endif
|
||||
FURI_LOG_D(TAG, "Worker Starting");
|
||||
instance->treead_running = true;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Worker UnPaused");
|
||||
}
|
||||
hardware_worker_emulate_start(instance->hw_worker);
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
// lfrfid_worker_start_thread(instance->proto_worker);
|
||||
lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
|
||||
#else
|
||||
// ibutton_worker_start_thread(instance->proto_worker);
|
||||
ibutton_worker_emulate_start(instance->proto_worker, instance->key);
|
||||
#endif
|
||||
instance->in_emu_phase = true;
|
||||
furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
|
||||
return true;
|
||||
@@ -425,19 +459,18 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
|
||||
return false;
|
||||
}
|
||||
|
||||
void fuzzer_worker_start_emulate(FuzzerWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
hardware_worker_emulate_start(instance->hw_worker);
|
||||
}
|
||||
|
||||
void fuzzer_worker_pause(FuzzerWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_timer_stop(instance->timer);
|
||||
|
||||
if(instance->treead_running) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_stop(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_stop(instance->proto_worker);
|
||||
#endif
|
||||
FURI_LOG_D(TAG, "Worker Paused");
|
||||
}
|
||||
hardware_worker_stop(instance->hw_worker);
|
||||
}
|
||||
|
||||
void fuzzer_worker_stop(FuzzerWorker* instance) {
|
||||
@@ -445,25 +478,15 @@ void fuzzer_worker_stop(FuzzerWorker* instance) {
|
||||
|
||||
furi_timer_stop(instance->timer);
|
||||
|
||||
if(instance->treead_running) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_stop(instance->proto_worker);
|
||||
lfrfid_worker_stop_thread(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_stop(instance->proto_worker);
|
||||
ibutton_worker_stop_thread(instance->proto_worker);
|
||||
#endif
|
||||
FURI_LOG_D(TAG, "Worker Stopping");
|
||||
instance->treead_running = false;
|
||||
}
|
||||
hardware_worker_stop(instance->hw_worker);
|
||||
|
||||
if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) {
|
||||
buffered_file_stream_close(instance->uids_stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
stream_free(instance->uids_stream);
|
||||
instance->attack_type = FuzzerWorkerAttackTypeMax;
|
||||
}
|
||||
|
||||
// TODO anything else
|
||||
// TODO anything else
|
||||
}
|
||||
|
||||
void fuzzer_worker_set_uid_chaged_callback(
|
||||
|
||||
+17
-2
@@ -12,6 +12,14 @@ typedef enum {
|
||||
FuzzerWorkerAttackTypeMax,
|
||||
} FuzzerWorkerAttackType;
|
||||
|
||||
typedef enum {
|
||||
FuzzerWorkerLoadKeyStateBadFile = -2,
|
||||
FuzzerWorkerLoadKeyStateUnsuportedProto,
|
||||
FuzzerWorkerLoadKeyStateOk = 0,
|
||||
FuzzerWorkerLoadKeyStateDifferentProto,
|
||||
|
||||
} FuzzerWorkerLoadKeyState;
|
||||
|
||||
typedef void (*FuzzerWorkerUidChagedCallback)(void* context);
|
||||
typedef void (*FuzzerWorkerEndCallback)(void* context);
|
||||
|
||||
@@ -48,6 +56,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
|
||||
*/
|
||||
void fuzzer_worker_stop(FuzzerWorker* instance);
|
||||
|
||||
void fuzzer_worker_start_emulate(FuzzerWorker* instance);
|
||||
|
||||
/**
|
||||
* Suspend emulation
|
||||
*
|
||||
@@ -100,6 +110,9 @@ bool fuzzer_worker_init_attack_bf_byte(
|
||||
*/
|
||||
void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key);
|
||||
|
||||
bool fuzzer_worker_next_key(FuzzerWorker* instance);
|
||||
bool fuzzer_worker_previous_key(FuzzerWorker* instance);
|
||||
|
||||
/**
|
||||
* Load UID from Flipper Format Key file
|
||||
*
|
||||
@@ -108,11 +121,13 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output
|
||||
* @param filename file path to the key file
|
||||
* @return bool True if loading is successful
|
||||
*/
|
||||
bool fuzzer_worker_load_key_from_file(
|
||||
FuzzerWorkerLoadKeyState fuzzer_worker_load_key_from_file(
|
||||
FuzzerWorker* instance,
|
||||
FuzzerProtocolsID protocol_index,
|
||||
FuzzerProtocolsID* protocol_index,
|
||||
const char* filename);
|
||||
|
||||
bool fuzzer_worker_save_key(FuzzerWorker* instance, const char* path);
|
||||
|
||||
/**
|
||||
* Set callback for uid changed
|
||||
*
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
#include "hardware_worker.h"
|
||||
#include "furi.h"
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
|
||||
#include <lib/lfrfid/lfrfid_dict_file.h>
|
||||
#include <lib/lfrfid/lfrfid_worker.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <lib/ibutton/ibutton_worker.h>
|
||||
#include <lib/ibutton/ibutton_key.h>
|
||||
|
||||
#endif
|
||||
|
||||
#define TAG "Fuzzer HW worker"
|
||||
|
||||
struct HardwareWorker {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
LFRFIDWorker* proto_worker;
|
||||
ProtocolId protocol_id;
|
||||
ProtocolDict* protocols_items;
|
||||
#else
|
||||
iButtonWorker* proto_worker;
|
||||
iButtonProtocolId protocol_id;
|
||||
iButtonProtocols* protocols_items;
|
||||
iButtonKey* key;
|
||||
#endif
|
||||
};
|
||||
|
||||
HardwareWorker* hardware_worker_alloc() {
|
||||
HardwareWorker* instance = malloc(sizeof(HardwareWorker));
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
|
||||
instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items);
|
||||
#else
|
||||
instance->protocols_items = ibutton_protocols_alloc();
|
||||
instance->key =
|
||||
ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items));
|
||||
|
||||
instance->proto_worker = ibutton_worker_alloc(instance->protocols_items);
|
||||
#endif
|
||||
return instance;
|
||||
}
|
||||
|
||||
void hardware_worker_free(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_free(instance->proto_worker);
|
||||
|
||||
protocol_dict_free(instance->protocols_items);
|
||||
#else
|
||||
ibutton_worker_free(instance->proto_worker);
|
||||
|
||||
ibutton_key_free(instance->key);
|
||||
ibutton_protocols_free(instance->protocols_items);
|
||||
#endif
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void hardware_worker_start_thread(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_start_thread(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_start_thread(instance->proto_worker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hardware_worker_stop_thread(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_stop(instance->proto_worker);
|
||||
lfrfid_worker_stop_thread(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_stop(instance->proto_worker);
|
||||
ibutton_worker_stop_thread(instance->proto_worker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hardware_worker_emulate_start(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
|
||||
#else
|
||||
ibutton_worker_emulate_start(instance->proto_worker, instance->key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hardware_worker_stop(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
lfrfid_worker_stop(instance->proto_worker);
|
||||
#else
|
||||
ibutton_worker_stop(instance->proto_worker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hardware_worker_set_protocol_data(
|
||||
HardwareWorker* instance,
|
||||
uint8_t* payload,
|
||||
uint8_t payload_size) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
protocol_dict_set_data(
|
||||
instance->protocols_items, instance->protocol_id, payload, payload_size);
|
||||
#else
|
||||
ibutton_key_set_protocol_id(instance->key, instance->protocol_id);
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
|
||||
|
||||
furi_check(payload_size >= data.size);
|
||||
memcpy(data.ptr, payload, data.size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hardware_worker_get_protocol_data(
|
||||
HardwareWorker* instance,
|
||||
uint8_t* payload,
|
||||
uint8_t payload_size) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
protocol_dict_get_data(
|
||||
instance->protocols_items, instance->protocol_id, payload, payload_size);
|
||||
#else
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
|
||||
furi_check(payload_size >= data.size);
|
||||
memcpy(payload, data.ptr, data.size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool hardware_worker_protocol_is_valid(HardwareWorker* instance) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
if(instance->protocol_id != PROTOCOL_NO) {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if(instance->protocol_id != iButtonProtocolIdInvalid) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hardware_worker_set_protocol_id_by_name(HardwareWorker* instance, const char* protocol_name) {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
instance->protocol_id =
|
||||
protocol_dict_get_protocol_by_name(instance->protocols_items, protocol_name);
|
||||
return (instance->protocol_id != PROTOCOL_NO);
|
||||
#else
|
||||
instance->protocol_id =
|
||||
ibutton_protocols_get_id_by_name(instance->protocols_items, protocol_name);
|
||||
return (instance->protocol_id != iButtonProtocolIdInvalid);
|
||||
#endif
|
||||
}
|
||||
|
||||
HwProtocolID hardware_worker_get_protocol_id(HardwareWorker* instance) {
|
||||
if(hardware_worker_protocol_is_valid(instance)) {
|
||||
return instance->protocol_id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool hardware_worker_load_key_from_file(HardwareWorker* instance, const char* filename) {
|
||||
bool res = false;
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename);
|
||||
if(loaded_proto_id == PROTOCOL_NO) {
|
||||
// Err Cant load file
|
||||
FURI_LOG_W(TAG, "Cant load file");
|
||||
} else {
|
||||
instance->protocol_id = loaded_proto_id;
|
||||
res = true;
|
||||
}
|
||||
#else
|
||||
|
||||
if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) {
|
||||
// Err Cant load file
|
||||
FURI_LOG_W(TAG, "Cant load file");
|
||||
} else {
|
||||
instance->protocol_id = ibutton_key_get_protocol_id(instance->key);
|
||||
res = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool hardware_worker_save_key(HardwareWorker* instance, const char* path) {
|
||||
furi_assert(instance);
|
||||
bool res;
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
res = lfrfid_dict_file_save(instance->protocols_items, instance->protocol_id, path);
|
||||
#else
|
||||
|
||||
res = ibutton_protocols_save(instance->protocols_items, instance->key, path);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
|
||||
#include <lib/lfrfid/lfrfid_dict_file.h>
|
||||
typedef ProtocolId HwProtocolID;
|
||||
|
||||
#else
|
||||
|
||||
#include <lib/ibutton/protocols/protocol_common.h>
|
||||
typedef iButtonProtocolId HwProtocolID;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct HardwareWorker HardwareWorker;
|
||||
|
||||
HardwareWorker* hardware_worker_alloc();
|
||||
|
||||
void hardware_worker_free(HardwareWorker* instance);
|
||||
|
||||
void hardware_worker_start_thread(HardwareWorker* instance);
|
||||
|
||||
void hardware_worker_stop_thread(HardwareWorker* instance);
|
||||
|
||||
void hardware_worker_emulate_start(HardwareWorker* instance);
|
||||
|
||||
void hardware_worker_stop(HardwareWorker* instance);
|
||||
|
||||
void hardware_worker_set_protocol_data(
|
||||
HardwareWorker* instance,
|
||||
uint8_t* payload,
|
||||
uint8_t payload_size);
|
||||
|
||||
void hardware_worker_get_protocol_data(
|
||||
HardwareWorker* instance,
|
||||
uint8_t* payload,
|
||||
uint8_t payload_size);
|
||||
|
||||
bool hardware_worker_set_protocol_id_by_name(HardwareWorker* instance, const char* protocol_name);
|
||||
|
||||
HwProtocolID hardware_worker_get_protocol_id(HardwareWorker* instance);
|
||||
|
||||
bool hardware_worker_load_key_from_file(HardwareWorker* instance, const char* filename);
|
||||
|
||||
bool hardware_worker_save_key(HardwareWorker* instance, const char* path);
|
||||
+70
-70
@@ -152,79 +152,79 @@ const uint8_t uid_list_h10301[][H10301_DATA_SIZE] = {
|
||||
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
const FuzzerProtocol fuzzer_proto_items[] = {
|
||||
[EM4100] =
|
||||
{
|
||||
.name = "EM4100",
|
||||
.data_size = EM4100_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_em4100,
|
||||
.len = COUNT_OF(uid_list_em4100),
|
||||
},
|
||||
},
|
||||
[HIDProx] =
|
||||
{
|
||||
.name = "HIDProx",
|
||||
.data_size = HIDProx_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_hid,
|
||||
.len = COUNT_OF(uid_list_hid),
|
||||
},
|
||||
},
|
||||
[PAC] =
|
||||
{
|
||||
.name = "PAC/Stanley",
|
||||
.data_size = PAC_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_pac,
|
||||
.len = COUNT_OF(uid_list_pac),
|
||||
},
|
||||
},
|
||||
[H10301] =
|
||||
{
|
||||
.name = "H10301",
|
||||
.data_size = H10301_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_h10301,
|
||||
.len = COUNT_OF(uid_list_h10301),
|
||||
},
|
||||
},
|
||||
// EM4100
|
||||
{
|
||||
.name = "EM4100",
|
||||
.data_size = EM4100_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_em4100,
|
||||
.len = COUNT_OF(uid_list_em4100),
|
||||
},
|
||||
},
|
||||
// HIDProx
|
||||
{
|
||||
.name = "HIDProx",
|
||||
.data_size = HIDProx_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_hid,
|
||||
.len = COUNT_OF(uid_list_hid),
|
||||
},
|
||||
},
|
||||
// PAC
|
||||
{
|
||||
.name = "PAC/Stanley",
|
||||
.data_size = PAC_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_pac,
|
||||
.len = COUNT_OF(uid_list_pac),
|
||||
},
|
||||
},
|
||||
// H10301
|
||||
{
|
||||
.name = "H10301",
|
||||
.data_size = H10301_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_h10301,
|
||||
.len = COUNT_OF(uid_list_h10301),
|
||||
},
|
||||
},
|
||||
};
|
||||
#else
|
||||
const FuzzerProtocol fuzzer_proto_items[] = {
|
||||
[DS1990] =
|
||||
{
|
||||
.name = "DS1990",
|
||||
.data_size = DS1990_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_ds1990,
|
||||
.len = COUNT_OF(uid_list_ds1990),
|
||||
},
|
||||
},
|
||||
[Metakom] =
|
||||
{
|
||||
.name = "Metakom",
|
||||
.data_size = Metakom_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_metakom,
|
||||
.len = COUNT_OF(uid_list_metakom),
|
||||
},
|
||||
},
|
||||
[Cyfral] =
|
||||
{
|
||||
.name = "Cyfral",
|
||||
.data_size = Cyfral_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_cyfral,
|
||||
.len = COUNT_OF(uid_list_cyfral),
|
||||
},
|
||||
},
|
||||
// DS1990
|
||||
{
|
||||
.name = "DS1990",
|
||||
.data_size = DS1990_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_ds1990,
|
||||
.len = COUNT_OF(uid_list_ds1990),
|
||||
},
|
||||
},
|
||||
// Metakom
|
||||
{
|
||||
.name = "Metakom",
|
||||
.data_size = Metakom_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_metakom,
|
||||
.len = COUNT_OF(uid_list_metakom),
|
||||
},
|
||||
},
|
||||
// Cyfral
|
||||
{
|
||||
.name = "Cyfral",
|
||||
.data_size = Cyfral_DATA_SIZE,
|
||||
.dict =
|
||||
{
|
||||
.val = (const uint8_t*)&uid_list_cyfral,
|
||||
.len = COUNT_OF(uid_list_cyfral),
|
||||
},
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
+10
-13
@@ -6,18 +6,7 @@
|
||||
|
||||
typedef struct FuzzerPayload FuzzerPayload;
|
||||
|
||||
typedef enum {
|
||||
#if defined(RFID_125_PROTOCOL)
|
||||
EM4100,
|
||||
HIDProx,
|
||||
PAC,
|
||||
H10301,
|
||||
#else
|
||||
DS1990,
|
||||
Metakom,
|
||||
Cyfral,
|
||||
#endif
|
||||
} FuzzerProtocolsID;
|
||||
typedef uint8_t FuzzerProtocolsID;
|
||||
|
||||
typedef enum {
|
||||
FuzzerAttackIdDefaultValues = 0,
|
||||
@@ -51,8 +40,16 @@ void fuzzer_payload_free(FuzzerPayload*);
|
||||
*/
|
||||
uint8_t fuzzer_proto_get_max_data_size();
|
||||
|
||||
// TODO add description
|
||||
/**
|
||||
* Get recomended/default emulation time
|
||||
* @return Default emulation time
|
||||
*/
|
||||
uint8_t fuzzer_proto_get_def_emu_time();
|
||||
|
||||
/**
|
||||
* Get recomended/default idle time
|
||||
* @return Default idle time
|
||||
*/
|
||||
uint8_t fuzzer_proto_get_def_idle_time();
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,11 +7,19 @@
|
||||
#define PROTOCOL_DEF_IDLE_TIME (4)
|
||||
#define PROTOCOL_DEF_EMU_TIME (5)
|
||||
#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
|
||||
|
||||
#define PROTOCOL_KEY_FOLDER_NAME "lfrfid"
|
||||
#define PROTOCOL_KEY_EXTENSION ".rfid"
|
||||
|
||||
#else
|
||||
|
||||
#define MAX_PAYLOAD_SIZE (8)
|
||||
#define PROTOCOL_DEF_IDLE_TIME (2)
|
||||
#define PROTOCOL_DEF_EMU_TIME (2)
|
||||
#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
|
||||
|
||||
#define PROTOCOL_KEY_FOLDER_NAME "ibutton"
|
||||
#define PROTOCOL_KEY_EXTENSION ".ibtn"
|
||||
#endif
|
||||
|
||||
typedef struct ProtoDict ProtoDict;
|
||||
|
||||
+54
-46
@@ -1,7 +1,7 @@
|
||||
#include "../fuzzer_i.h"
|
||||
#include "../helpers/fuzzer_custom_event.h"
|
||||
|
||||
const NotificationSequence sequence_one_green_50_on_blink_blue = {
|
||||
const NotificationSequence sequence_one_red_50_on_blink_blue = {
|
||||
&message_red_255,
|
||||
&message_delay_50,
|
||||
&message_red_0,
|
||||
@@ -28,35 +28,38 @@ static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState
|
||||
switch(state) {
|
||||
case FuzzerAttackStateIdle:
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
fuzzer_view_attack_pause(app->attack_view);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateRunning:
|
||||
case FuzzerAttackStateAttacking:
|
||||
notification_message(app->notifications, &sequence_blink_start_blue);
|
||||
break;
|
||||
case FuzzerAttackStateEmulating:
|
||||
notification_message(app->notifications, &sequence_blink_start_blue);
|
||||
fuzzer_view_attack_start(app->attack_view);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateEnd:
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
notification_message(app->notifications, &sequence_single_vibro);
|
||||
fuzzer_view_attack_end(app->attack_view);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateOff:
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
fuzzer_view_attack_stop(app->attack_view);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStatePause:
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
break;
|
||||
}
|
||||
|
||||
fuzzer_view_update_state(app->attack_view, state);
|
||||
}
|
||||
|
||||
void fuzzer_scene_attack_worker_tick_callback(void* context) {
|
||||
furi_assert(context);
|
||||
PacsFuzzerApp* app = context;
|
||||
|
||||
notification_message(app->notifications, &sequence_one_green_50_on_blink_blue);
|
||||
notification_message(app->notifications, &sequence_one_red_50_on_blink_blue);
|
||||
fuzzer_scene_attack_update_uid(app);
|
||||
|
||||
// view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick);
|
||||
}
|
||||
|
||||
void fuzzer_scene_attack_worker_end_callback(void* context) {
|
||||
@@ -100,48 +103,53 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == FuzzerCustomEventViewAttackBack) {
|
||||
if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
|
||||
FuzzerAttackStateRunning) {
|
||||
// Pause if attack running
|
||||
fuzzer_worker_pause(app->worker);
|
||||
if(event.event == FuzzerCustomEventViewAttackExit) {
|
||||
// Exit
|
||||
fuzzer_worker_stop(app->worker);
|
||||
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff);
|
||||
if(!scene_manager_previous_scene(app->scene_manager)) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
}
|
||||
} else if(event.event == FuzzerCustomEventViewAttackRunAttack) {
|
||||
if(fuzzer_worker_start(
|
||||
app->worker,
|
||||
fuzzer_view_attack_get_time_delay(app->attack_view),
|
||||
fuzzer_view_attack_get_emu_time(app->attack_view))) {
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateAttacking);
|
||||
} else {
|
||||
// Exit
|
||||
fuzzer_worker_stop(app->worker);
|
||||
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff);
|
||||
if(!scene_manager_previous_scene(app->scene_manager)) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
}
|
||||
// Error?
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == FuzzerCustomEventViewAttackOk) {
|
||||
if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
|
||||
FuzzerAttackStateIdle) {
|
||||
// Start or Continue Attack
|
||||
if(fuzzer_worker_start(
|
||||
app->worker,
|
||||
fuzzer_view_attack_get_time_delay(app->attack_view),
|
||||
fuzzer_view_attack_get_emu_time(app->attack_view))) {
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning);
|
||||
} else {
|
||||
// Error?
|
||||
}
|
||||
} else if(
|
||||
scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
|
||||
FuzzerAttackStateRunning) {
|
||||
// Pause if attack running
|
||||
fuzzer_worker_pause(app->worker);
|
||||
} else if(event.event == FuzzerCustomEventViewAttackEmulateCurrent) {
|
||||
fuzzer_worker_start_emulate(app->worker);
|
||||
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateEmulating);
|
||||
} else if(event.event == FuzzerCustomEventViewAttackPause) {
|
||||
fuzzer_worker_pause(app->worker);
|
||||
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStatePause);
|
||||
} else if(event.event == FuzzerCustomEventViewAttackIdle) {
|
||||
fuzzer_worker_pause(app->worker);
|
||||
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
|
||||
} else if(event.event == FuzzerCustomEventViewAttackNextUid) {
|
||||
if(fuzzer_worker_next_key(app->worker)) {
|
||||
fuzzer_scene_attack_update_uid(app);
|
||||
} else {
|
||||
notification_message(app->notifications, &sequence_blink_red_100);
|
||||
}
|
||||
consumed = true;
|
||||
// } else if(event.event == FuzzerCustomEventViewAttackTick) {
|
||||
// consumed = true;
|
||||
} else if(event.event == FuzzerCustomEventViewAttackEnd) {
|
||||
} else if(event.event == FuzzerCustomEventViewAttackPrevUid) {
|
||||
if(fuzzer_worker_previous_key(app->worker)) {
|
||||
fuzzer_scene_attack_update_uid(app);
|
||||
} else {
|
||||
notification_message(app->notifications, &sequence_blink_red_100);
|
||||
}
|
||||
} else if(event.event == FuzzerCustomEventViewAttackSave) {
|
||||
scene_manager_next_scene(app->scene_manager, FuzzerSceneSaveName);
|
||||
}
|
||||
// Callback from worker
|
||||
else if(event.event == FuzzerCustomEventViewAttackEnd) {
|
||||
fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
ADD_SCENE(fuzzer, main, Main)
|
||||
ADD_SCENE(fuzzer, attack, Attack)
|
||||
ADD_SCENE(fuzzer, field_editor, FieldEditor)
|
||||
ADD_SCENE(fuzzer, field_editor, FieldEditor)
|
||||
ADD_SCENE(fuzzer, save_name, SaveName)
|
||||
ADD_SCENE(fuzzer, save_success, SaveSuccess)
|
||||
@@ -22,6 +22,7 @@ void fuzzer_scene_field_editor_on_enter(void* context) {
|
||||
break;
|
||||
|
||||
case FuzzerFieldEditorStateEditingOff:
|
||||
memset(app->payload->data, 0x00, app->payload->data_size);
|
||||
fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false);
|
||||
break;
|
||||
|
||||
|
||||
+21
-10
@@ -2,6 +2,7 @@
|
||||
#include "../helpers/fuzzer_custom_event.h"
|
||||
|
||||
#include "../lib/worker/protocol.h"
|
||||
#define TAG "Fuzzer main menu"
|
||||
|
||||
void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -35,7 +36,7 @@ static bool fuzzer_scene_main_load_custom_dict(void* context) {
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool fuzzer_scene_main_load_key(void* context) {
|
||||
static bool fuzzer_scene_main_load_key_dialog(void* context) {
|
||||
furi_assert(context);
|
||||
PacsFuzzerApp* app = context;
|
||||
|
||||
@@ -134,22 +135,32 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
|
||||
case FuzzerAttackIdLoadFile:
|
||||
if(!fuzzer_scene_main_load_key(app)) {
|
||||
if(!fuzzer_scene_main_load_key_dialog(app)) {
|
||||
break;
|
||||
} else {
|
||||
if(fuzzer_worker_load_key_from_file(
|
||||
app->worker,
|
||||
app->fuzzer_state.proto_index,
|
||||
furi_string_get_cstr(app->file_path))) {
|
||||
switch(fuzzer_worker_load_key_from_file(
|
||||
app->worker,
|
||||
&app->fuzzer_state.proto_index,
|
||||
furi_string_get_cstr(app->file_path))) {
|
||||
case FuzzerWorkerLoadKeyStateOk:
|
||||
case FuzzerWorkerLoadKeyStateDifferentProto:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
FuzzerSceneFieldEditor,
|
||||
FuzzerFieldEditorStateEditingOn);
|
||||
scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor);
|
||||
FURI_LOG_I("Scene", "Load ok");
|
||||
} else {
|
||||
fuzzer_scene_main_show_error(app, "Unsupported protocol\nor broken file");
|
||||
FURI_LOG_W("Scene", "Load err");
|
||||
FURI_LOG_I(TAG, "Load ok");
|
||||
break;
|
||||
|
||||
case FuzzerWorkerLoadKeyStateBadFile:
|
||||
fuzzer_scene_main_show_error(app, "Cant load\nor broken file");
|
||||
FURI_LOG_E(TAG, "Cant load or broken file");
|
||||
break;
|
||||
|
||||
case FuzzerWorkerLoadKeyStateUnsuportedProto:
|
||||
fuzzer_scene_main_show_error(app, "Unsupported protocol");
|
||||
FURI_LOG_E(TAG, "Unsupported protocol");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "../fuzzer_i.h"
|
||||
|
||||
#include <toolbox/random_name.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
static void fuzzer_scene_save_name_text_input_callback(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventTextEditResult);
|
||||
}
|
||||
|
||||
void fuzzer_scene_save_name_on_enter(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
TextInput* text_input = app->text_input;
|
||||
|
||||
set_random_name(app->key_name, KEY_NAME_SIZE);
|
||||
|
||||
text_input_set_header_text(text_input, "Name the key");
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
fuzzer_scene_save_name_text_input_callback,
|
||||
app,
|
||||
app->key_name,
|
||||
KEY_NAME_SIZE,
|
||||
false);
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
app->fuzzer_const->path_key_folder, app->fuzzer_const->key_extension, app->key_name);
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDTextInput);
|
||||
}
|
||||
|
||||
bool fuzzer_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
PacsFuzzerApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == FuzzerCustomEventTextEditResult) {
|
||||
consumed = true;
|
||||
furi_string_printf(
|
||||
app->file_path,
|
||||
"%s/%s%s",
|
||||
app->fuzzer_const->path_key_folder,
|
||||
app->key_name,
|
||||
app->fuzzer_const->key_extension);
|
||||
|
||||
if(fuzzer_worker_save_key(app->worker, furi_string_get_cstr(app->file_path))) {
|
||||
scene_manager_next_scene(app->scene_manager, FuzzerSceneSaveSuccess);
|
||||
} else {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void fuzzer_scene_save_name_on_exit(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
TextInput* text_input = app->text_input;
|
||||
|
||||
void* validator_context = text_input_get_validator_callback_context(text_input);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
validator_is_file_free((ValidatorIsFile*)validator_context);
|
||||
|
||||
text_input_reset(text_input);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "../fuzzer_i.h"
|
||||
|
||||
static void fuzzer_scene_save_popup_timeout_callback(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventPopupClosed);
|
||||
}
|
||||
|
||||
void fuzzer_scene_save_success_on_enter(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
Popup* popup = app->popup;
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
popup_set_context(popup, app);
|
||||
popup_set_callback(popup, fuzzer_scene_save_popup_timeout_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDPopup);
|
||||
}
|
||||
|
||||
bool fuzzer_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
||||
PacsFuzzerApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if((event.type == SceneManagerEventTypeBack) ||
|
||||
((event.type == SceneManagerEventTypeCustom) &&
|
||||
(event.event == FuzzerCustomEventPopupClosed))) {
|
||||
bool result = scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, FuzzerSceneAttack);
|
||||
if(!result) {
|
||||
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, FuzzerSceneMain);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void fuzzer_scene_save_success_on_exit(void* context) {
|
||||
PacsFuzzerApp* app = context;
|
||||
|
||||
popup_reset(app->popup);
|
||||
}
|
||||
+257
-146
@@ -6,7 +6,12 @@
|
||||
|
||||
#define ATTACK_SCENE_MAX_UID_LENGTH 25
|
||||
#define UID_MAX_DISPLAYED_LEN (8U)
|
||||
#define LIFT_RIGHT_OFFSET (3)
|
||||
#define LEFT_RIGHT_OFFSET (3U)
|
||||
|
||||
#define LINE_1_Y (12U)
|
||||
#define LINE_2_Y (24U)
|
||||
#define LINE_3_Y (36U)
|
||||
#define LINE_4_Y (48U)
|
||||
|
||||
struct FuzzerViewAttack {
|
||||
View* view;
|
||||
@@ -60,44 +65,11 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid
|
||||
true);
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_start(FuzzerViewAttack* view) {
|
||||
void fuzzer_view_update_state(FuzzerViewAttack* view, FuzzerAttackState state) {
|
||||
furi_assert(view);
|
||||
|
||||
with_view_model(
|
||||
view->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{ model->attack_state = FuzzerAttackStateRunning; },
|
||||
true);
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_stop(FuzzerViewAttack* view) {
|
||||
furi_assert(view);
|
||||
|
||||
with_view_model(
|
||||
view->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{ model->attack_state = FuzzerAttackStateOff; },
|
||||
true);
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_pause(FuzzerViewAttack* view) {
|
||||
furi_assert(view);
|
||||
|
||||
with_view_model(
|
||||
view->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{ model->attack_state = FuzzerAttackStateIdle; },
|
||||
true);
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_end(FuzzerViewAttack* view) {
|
||||
furi_assert(view);
|
||||
|
||||
with_view_model(
|
||||
view->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{ model->attack_state = FuzzerAttackStateEnd; },
|
||||
true);
|
||||
view->view, FuzzerViewAttackModel * model, { model->attack_state = state; }, true);
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_set_callback(
|
||||
@@ -110,35 +82,31 @@ void fuzzer_view_attack_set_callback(
|
||||
view_attack->context = context;
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
char temp_str[50];
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name);
|
||||
|
||||
static void
|
||||
fuzzer_view_attack_draw_time_delays_line(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
char temp_str[25];
|
||||
uint16_t crt;
|
||||
const uint16_t y = LINE_2_Y;
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
if(!model->td_emt_cursor) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(temp_str, sizeof(temp_str), "Time delay:");
|
||||
canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str);
|
||||
canvas_draw_str_aligned(canvas, LEFT_RIGHT_OFFSET, y, AlignLeft, AlignBottom, temp_str);
|
||||
crt = canvas_string_width(canvas, temp_str);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
snprintf(
|
||||
temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, crt + LIFT_RIGHT_OFFSET + 3, 21, AlignLeft, AlignBottom, temp_str);
|
||||
canvas, crt + LEFT_RIGHT_OFFSET + 3, y, AlignLeft, AlignBottom, temp_str);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(
|
||||
temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str);
|
||||
canvas, 128 - LEFT_RIGHT_OFFSET, y, AlignRight, AlignBottom, temp_str);
|
||||
} else {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(
|
||||
@@ -148,138 +116,206 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
model->time_delay / 10,
|
||||
model->time_delay % 10);
|
||||
|
||||
canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str);
|
||||
canvas_draw_str_aligned(canvas, LEFT_RIGHT_OFFSET, y, AlignLeft, AlignBottom, temp_str);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str);
|
||||
canvas, 128 - LEFT_RIGHT_OFFSET, y, AlignRight, AlignBottom, temp_str);
|
||||
crt = canvas_string_width(canvas, temp_str);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(temp_str, sizeof(temp_str), "Emulation time:");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str);
|
||||
canvas, 128 - LEFT_RIGHT_OFFSET - crt - 3, y, AlignRight, AlignBottom, temp_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void fuzzer_view_attack_draw_time_delays_str(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
char temp_str[20];
|
||||
uint16_t crt;
|
||||
const uint16_t y = LINE_2_Y;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name);
|
||||
snprintf(
|
||||
temp_str,
|
||||
sizeof(temp_str),
|
||||
"TD: %d.%d Emt: %d.%d",
|
||||
model->time_delay / 10,
|
||||
model->time_delay % 10,
|
||||
model->emu_time / 10,
|
||||
model->emu_time % 10);
|
||||
|
||||
crt = canvas_string_width(canvas, temp_str);
|
||||
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 - LEFT_RIGHT_OFFSET - crt, y, AlignLeft, AlignBottom, temp_str);
|
||||
}
|
||||
|
||||
static void fuzzer_view_attack_draw_idle(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
if(model->td_emt_cursor) {
|
||||
elements_button_center(canvas, "Start");
|
||||
elements_button_left(canvas, "EmT -");
|
||||
elements_button_right(canvas, "+ EmT");
|
||||
} else {
|
||||
elements_button_center(canvas, "Start");
|
||||
elements_button_left(canvas, "TD -");
|
||||
elements_button_right(canvas, "+ TD");
|
||||
}
|
||||
}
|
||||
|
||||
static void fuzzer_view_attack_draw_running(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
UNUSED(model);
|
||||
elements_button_left(canvas, "Stop");
|
||||
elements_button_center(canvas, "Pause");
|
||||
}
|
||||
|
||||
static void fuzzer_view_attack_draw_end(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
UNUSED(model);
|
||||
// elements_button_center(canvas, "Restart"); // Reset
|
||||
elements_button_left(canvas, "Exit");
|
||||
}
|
||||
|
||||
void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Header - Attack name
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, LINE_1_Y, AlignCenter, AlignBottom, model->attack_name);
|
||||
|
||||
// Time delays line or Status line
|
||||
switch(model->attack_state) {
|
||||
case FuzzerAttackStateIdle:
|
||||
fuzzer_view_attack_draw_time_delays_line(canvas, model);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateAttacking:
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, LEFT_RIGHT_OFFSET, LINE_2_Y, "Attacking");
|
||||
fuzzer_view_attack_draw_time_delays_str(canvas, model);
|
||||
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateEmulating:
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, LINE_2_Y, AlignCenter, AlignBottom, "Emulating:");
|
||||
|
||||
break;
|
||||
|
||||
case FuzzerAttackStatePause:
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, LEFT_RIGHT_OFFSET, LINE_2_Y, "Paused");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_icon_ex(canvas, 62, LINE_2_Y - 9, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||
canvas_draw_icon(canvas, 69, LINE_2_Y - 9, &I_Pin_arrow_up_7x9);
|
||||
canvas_draw_str(canvas, 79, LINE_2_Y, "Change UID");
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateEnd:
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, LINE_2_Y, AlignCenter, AlignBottom, "Attack is over");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Protocol name
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 64, LINE_3_Y, AlignCenter, AlignBottom, model->protocol_name);
|
||||
|
||||
// Current UID
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
}
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 38, AlignCenter, AlignTop, furi_string_get_cstr(model->uid_str));
|
||||
canvas, 64, LINE_4_Y, AlignCenter, AlignBottom, furi_string_get_cstr(model->uid_str));
|
||||
|
||||
// Btns
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(model->attack_state == FuzzerAttackStateRunning) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
if(model->attack_state == FuzzerAttackStateAttacking ||
|
||||
model->attack_state == FuzzerAttackStateEmulating) {
|
||||
fuzzer_view_attack_draw_running(canvas, model);
|
||||
} else if(model->attack_state == FuzzerAttackStateIdle) {
|
||||
if(model->td_emt_cursor) {
|
||||
elements_button_center(canvas, "Start");
|
||||
elements_button_left(canvas, "EmT -");
|
||||
elements_button_right(canvas, "+ EmT");
|
||||
} else {
|
||||
elements_button_center(canvas, "Start");
|
||||
elements_button_left(canvas, "TD -");
|
||||
elements_button_right(canvas, "+ TD");
|
||||
}
|
||||
|
||||
fuzzer_view_attack_draw_idle(canvas, model);
|
||||
} else if(model->attack_state == FuzzerAttackStatePause) {
|
||||
elements_button_left(canvas, "Back");
|
||||
elements_button_right(canvas, "Save");
|
||||
elements_button_center(canvas, "Emu");
|
||||
} else if(model->attack_state == FuzzerAttackStateEnd) {
|
||||
// elements_button_center(canvas, "Restart"); // Reset
|
||||
elements_button_left(canvas, "Exit");
|
||||
fuzzer_view_attack_draw_end(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
bool fuzzer_view_attack_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
FuzzerViewAttack* view_attack = context;
|
||||
|
||||
static bool fuzzer_view_attack_input_idle(
|
||||
FuzzerViewAttack* view_attack,
|
||||
InputEvent* event,
|
||||
FuzzerViewAttackModel* model) {
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
|
||||
view_attack->callback(FuzzerCustomEventViewAttackExit, view_attack->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context);
|
||||
view_attack->callback(FuzzerCustomEventViewAttackRunAttack, view_attack->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
with_view_model(
|
||||
view_attack->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{
|
||||
if(model->attack_state == FuzzerAttackStateIdle) {
|
||||
if(!model->td_emt_cursor) {
|
||||
// TimeDelay --
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->time_delay > model->time_delay_min) {
|
||||
model->time_delay--;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if((model->time_delay - 10) >= model->time_delay_min) {
|
||||
model->time_delay -= 10;
|
||||
} else {
|
||||
model->time_delay = model->time_delay_min;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// EmuTime --
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->emu_time > model->emu_time_min) {
|
||||
model->emu_time--;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if((model->emu_time - 10) >= model->emu_time_min) {
|
||||
model->emu_time -= 10;
|
||||
} else {
|
||||
model->emu_time = model->emu_time_min;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(
|
||||
(model->attack_state == FuzzerAttackStateEnd) &&
|
||||
(event->type == InputTypeShort)) {
|
||||
// Exit if Ended
|
||||
view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
|
||||
if(!model->td_emt_cursor) {
|
||||
// TimeDelay --
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->time_delay > model->time_delay_min) {
|
||||
model->time_delay--;
|
||||
}
|
||||
},
|
||||
true);
|
||||
} else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
|
||||
if((model->time_delay - 10) >= model->time_delay_min) {
|
||||
model->time_delay -= 10;
|
||||
} else {
|
||||
model->time_delay = model->time_delay_min;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// EmuTime --
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->emu_time > model->emu_time_min) {
|
||||
model->emu_time--;
|
||||
}
|
||||
} else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
|
||||
if((model->emu_time - 10) >= model->emu_time_min) {
|
||||
model->emu_time -= 10;
|
||||
} else {
|
||||
model->emu_time = model->emu_time_min;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
with_view_model(
|
||||
view_attack->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{
|
||||
if(model->attack_state == FuzzerAttackStateIdle) {
|
||||
if(!model->td_emt_cursor) {
|
||||
// TimeDelay ++
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->time_delay < FUZZ_TIME_DELAY_MAX) {
|
||||
model->time_delay++;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
model->time_delay += 10;
|
||||
if(model->time_delay > FUZZ_TIME_DELAY_MAX) {
|
||||
model->time_delay = FUZZ_TIME_DELAY_MAX;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// EmuTime ++
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->emu_time < FUZZ_TIME_DELAY_MAX) {
|
||||
model->emu_time++;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
model->emu_time += 10;
|
||||
if(model->emu_time > FUZZ_TIME_DELAY_MAX) {
|
||||
model->emu_time = FUZZ_TIME_DELAY_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Nothing
|
||||
if(!model->td_emt_cursor) {
|
||||
// TimeDelay ++
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->time_delay < FUZZ_TIME_DELAY_MAX) {
|
||||
model->time_delay++;
|
||||
}
|
||||
},
|
||||
true);
|
||||
} else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
|
||||
model->time_delay += 10;
|
||||
if(model->time_delay > FUZZ_TIME_DELAY_MAX) {
|
||||
model->time_delay = FUZZ_TIME_DELAY_MAX;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// EmuTime ++
|
||||
if(event->type == InputTypeShort) {
|
||||
if(model->emu_time < FUZZ_TIME_DELAY_MAX) {
|
||||
model->emu_time++;
|
||||
}
|
||||
} else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
|
||||
model->emu_time += 10;
|
||||
if(model->emu_time > FUZZ_TIME_DELAY_MAX) {
|
||||
model->emu_time = FUZZ_TIME_DELAY_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if(
|
||||
(event->key == InputKeyUp || event->key == InputKeyDown) &&
|
||||
@@ -291,6 +327,81 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) {
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuzzer_view_attack_input_end(
|
||||
FuzzerViewAttack* view_attack,
|
||||
InputEvent* event,
|
||||
FuzzerViewAttackModel* model) {
|
||||
UNUSED(model);
|
||||
if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
|
||||
event->type == InputTypeShort) {
|
||||
// Exit if Ended
|
||||
view_attack->callback(FuzzerCustomEventViewAttackExit, view_attack->context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fuzzer_view_attack_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
FuzzerViewAttack* view_attack = context;
|
||||
|
||||
// if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
// view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
|
||||
// return true;
|
||||
// } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
// view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context);
|
||||
// return true;
|
||||
// } else
|
||||
// {
|
||||
with_view_model(
|
||||
view_attack->view,
|
||||
FuzzerViewAttackModel * model,
|
||||
{
|
||||
switch(model->attack_state) {
|
||||
case FuzzerAttackStateIdle:
|
||||
fuzzer_view_attack_input_idle(view_attack, event, model);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateEnd:
|
||||
fuzzer_view_attack_input_end(view_attack, event, model);
|
||||
break;
|
||||
|
||||
case FuzzerAttackStateAttacking:
|
||||
case FuzzerAttackStateEmulating:
|
||||
if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
|
||||
event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackIdle, view_attack->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackPause, view_attack->context);
|
||||
}
|
||||
break;
|
||||
|
||||
case FuzzerAttackStatePause:
|
||||
if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
|
||||
event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackIdle, view_attack->context);
|
||||
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
|
||||
view_attack->callback(FuzzerCustomEventViewAttackSave, view_attack->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
view_attack->callback(
|
||||
FuzzerCustomEventViewAttackEmulateCurrent, view_attack->context);
|
||||
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
|
||||
view_attack->callback(
|
||||
FuzzerCustomEventViewAttackPrevUid, view_attack->context);
|
||||
} else if(event->key == InputKeyDown && event->type == InputTypeShort) {
|
||||
view_attack->callback(
|
||||
FuzzerCustomEventViewAttackNextUid, view_attack->context);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
true);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-7
@@ -29,13 +29,7 @@ void fuzzer_view_attack_reset_data(
|
||||
|
||||
void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid);
|
||||
|
||||
void fuzzer_view_attack_start(FuzzerViewAttack* view);
|
||||
|
||||
void fuzzer_view_attack_stop(FuzzerViewAttack* view);
|
||||
|
||||
void fuzzer_view_attack_pause(FuzzerViewAttack* view);
|
||||
|
||||
void fuzzer_view_attack_end(FuzzerViewAttack* view);
|
||||
void fuzzer_view_update_state(FuzzerViewAttack* view, FuzzerAttackState state);
|
||||
|
||||
uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view);
|
||||
|
||||
|
||||
@@ -6,5 +6,6 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
order=11,
|
||||
fap_icon="subrem_10px.png",
|
||||
fap_description="SubGhz Remote, uses up to 5 .sub files",
|
||||
fap_category="Sub-GHz",
|
||||
)
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
SubRemEditMenuStateUP = 0,
|
||||
SubRemEditMenuStateDOWN,
|
||||
SubRemEditMenuStateLEFT,
|
||||
SubRemEditMenuStateRIGHT,
|
||||
SubRemEditMenuStateOK,
|
||||
} SubRemEditMenuState;
|
||||
|
||||
typedef enum {
|
||||
// StartSubmenuIndex
|
||||
SubmenuIndexSubRemOpenMapFile = 0,
|
||||
SubmenuIndexSubRemEditMapFile,
|
||||
SubmenuIndexSubRemNewMapFile,
|
||||
#if FURI_DEBUG
|
||||
SubmenuIndexSubRemRemoteView,
|
||||
#endif
|
||||
// SubmenuIndexSubRemAbout,
|
||||
|
||||
// EditSubmenuIndex
|
||||
EditSubmenuIndexEditLabel,
|
||||
EditSubmenuIndexEditFile,
|
||||
|
||||
// SubRemCustomEvent
|
||||
SubRemCustomEventViewRemoteStartUP = 100,
|
||||
SubRemCustomEventViewRemoteStartDOWN,
|
||||
@@ -17,4 +31,21 @@ typedef enum {
|
||||
SubRemCustomEventViewRemoteBack,
|
||||
SubRemCustomEventViewRemoteStop,
|
||||
SubRemCustomEventViewRemoteForcedStop,
|
||||
|
||||
SubRemCustomEventViewEditMenuBack,
|
||||
SubRemCustomEventViewEditMenuUP,
|
||||
SubRemCustomEventViewEditMenuDOWN,
|
||||
SubRemCustomEventViewEditMenuEdit,
|
||||
SubRemCustomEventViewEditMenuSave,
|
||||
|
||||
SubRemCustomEventSceneEditsubmenu,
|
||||
SubRemCustomEventSceneEditLabelInputDone,
|
||||
SubRemCustomEventSceneEditLabelWidgetAcces,
|
||||
SubRemCustomEventSceneEditLabelWidgetBack,
|
||||
|
||||
SubRemCustomEventSceneEditOpenSubErrorPopup,
|
||||
|
||||
SubRemCustomEventSceneEditPreviewSaved,
|
||||
|
||||
SubRemCustomEventSceneNewName,
|
||||
} SubRemCustomEvent;
|
||||
@@ -18,7 +18,11 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
SubRemViewIDSubmenu,
|
||||
SubRemViewIDWidget,
|
||||
SubRemViewIDPopup,
|
||||
SubRemViewIDTextInput,
|
||||
SubRemViewIDRemote,
|
||||
SubRemViewIDEditMenu,
|
||||
} SubRemViewID;
|
||||
|
||||
typedef enum {
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,5 +1,9 @@
|
||||
#ifndef SUBREM_LIGHT
|
||||
ADD_SCENE(subrem, start, Start)
|
||||
#endif
|
||||
ADD_SCENE(subrem, open_map_file, OpenMapFile)
|
||||
ADD_SCENE(subrem, remote, Remote)
|
||||
ADD_SCENE(subrem, 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)
|
||||
@@ -5,30 +5,16 @@ void subrem_scene_open_map_file_on_enter(void* context) {
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
SubRemLoadMapState load_state = subrem_load_from_file(app);
|
||||
uint32_t start_scene_state =
|
||||
scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart);
|
||||
|
||||
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
|
||||
if(load_state == SubRemLoadMapStateBack) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
} else if(start_scene_state == SubmenuIndexSubRemEditMapFile) {
|
||||
scene_manager_set_scene_state(app->scene_manager, SubRemSceneEditMenu, SubRemSubKeyNameUp);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
|
||||
} else if(start_scene_state == SubmenuIndexSubRemOpenMapFile) {
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
|
||||
} else {
|
||||
if(load_state != SubRemLoadMapStateBack) {
|
||||
#ifdef SUBREM_LIGHT
|
||||
dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file");
|
||||
#else
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter);
|
||||
dialog_message_set_text(
|
||||
message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(message, "Back", NULL, NULL);
|
||||
dialog_message_show(app->dialogs, message);
|
||||
|
||||
dialog_message_free(message);
|
||||
#endif
|
||||
}
|
||||
// TODO: Map Preset Reset
|
||||
if(!scene_manager_previous_scene(app->scene_manager)) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ void subrem_scene_remote_on_enter(void* context) {
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
subrem_view_remote_update_data_labels(app->subrem_remote_view, app->map_preset->subs_preset);
|
||||
subrem_view_remote_set_radio(
|
||||
app->subrem_remote_view,
|
||||
subghz_txrx_radio_device_get(app->txrx) != SubGhzRadioDeviceTypeInternal);
|
||||
|
||||
subrem_view_remote_set_callback(app->subrem_remote_view, subrem_scene_remote_callback, app);
|
||||
|
||||
|
||||
@@ -27,16 +27,28 @@ void subrem_scene_start_on_enter(void* context) {
|
||||
subrem_scene_start_submenu_callback,
|
||||
app);
|
||||
#endif
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Edit Map File",
|
||||
SubmenuIndexSubRemEditMapFile,
|
||||
subrem_scene_start_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"New Map File",
|
||||
SubmenuIndexSubRemNewMapFile,
|
||||
subrem_scene_start_submenu_callback,
|
||||
app);
|
||||
// submenu_add_item(
|
||||
// submenu,
|
||||
// "About",
|
||||
// SubmenuIndexSubGhzRemoteAbout,
|
||||
// subrem_scene_start_submenu_callback,
|
||||
// app);
|
||||
#ifndef SUBREM_LIGHT
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
|
||||
#endif
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||
}
|
||||
|
||||
@@ -48,23 +60,33 @@ bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexSubRemOpenMapFile) {
|
||||
#ifndef SUBREM_LIGHT
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
|
||||
#endif
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
consumed = true;
|
||||
}
|
||||
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
||||
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
||||
// consumed = true;
|
||||
// }
|
||||
#if FURI_DEBUG
|
||||
else if(event.event == SubmenuIndexSubRemRemoteView) {
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
|
||||
consumed = true;
|
||||
}
|
||||
#endif
|
||||
else if(event.event == SubmenuIndexSubRemEditMapFile) {
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexSubRemNewMapFile) {
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemNewMapFile);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneEnterNewName);
|
||||
consumed = true;
|
||||
}
|
||||
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
||||
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
||||
// consumed = true;
|
||||
// }
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
+74
-35
@@ -1,5 +1,4 @@
|
||||
#include "subghz_remote_app_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
@@ -19,26 +18,23 @@ static void subghz_remote_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
SubGhzRemoteApp* subghz_remote_app_alloc(char* p) {
|
||||
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
|
||||
static void subghz_remote_make_app_folder(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// Migrate old users data
|
||||
storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER);
|
||||
storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER);
|
||||
|
||||
if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) {
|
||||
//FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
|
||||
// FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
|
||||
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
// // Enable power for External CC1101 if it is connected
|
||||
// furi_hal_subghz_enable_ext_power();
|
||||
// // Auto switch to internal radio if external radio is not available
|
||||
// furi_delay_ms(15);
|
||||
// if(!furi_hal_subghz_check_radio()) {
|
||||
// furi_hal_subghz_select_radio_type(SubGhzRadioInternal);
|
||||
// furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
|
||||
// }
|
||||
SubGhzRemoteApp* subghz_remote_app_alloc() {
|
||||
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
@@ -75,6 +71,20 @@ SubGhzRemoteApp* subghz_remote_app_alloc(char* p) {
|
||||
// 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(
|
||||
@@ -82,6 +92,13 @@ SubGhzRemoteApp* subghz_remote_app_alloc(char* p) {
|
||||
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();
|
||||
@@ -91,19 +108,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc(char* p) {
|
||||
|
||||
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
|
||||
|
||||
if(p && strlen(p)) {
|
||||
furi_string_set(app->file_path, p);
|
||||
subrem_map_file_load(app, furi_string_get_cstr(app->file_path));
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
|
||||
} else {
|
||||
#ifdef SUBREM_LIGHT
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
#else
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
|
||||
#endif
|
||||
}
|
||||
app->map_not_saved = false;
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -113,11 +118,6 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
// // Disable power for External CC1101 if it was enabled and module is connected
|
||||
// furi_hal_subghz_disable_ext_power();
|
||||
// // Reinit SPI handles for internal radio / nfc
|
||||
// furi_hal_subghz_init_radio_type(SubGhzRadioInternal);
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
@@ -125,10 +125,26 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
|
||||
// Dialog
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
// TextInput
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
// Remote view
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
|
||||
subrem_view_remote_free(app->subrem_remote_view);
|
||||
|
||||
// Edit view
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDEditMenu);
|
||||
subrem_view_edit_menu_free(app->subrem_edit_menu);
|
||||
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
@@ -152,10 +168,33 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) {
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t subghz_remote_app(char* p) {
|
||||
UNUSED(p);
|
||||
dolphin_deed(DolphinDeedPluginStart);
|
||||
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(p);
|
||||
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;
|
||||
|
||||
if((arg != NULL) && (strlen(arg) != 0)) {
|
||||
furi_string_set(subghz_remote_app->file_path, (const char*)arg);
|
||||
SubRemLoadMapState load_state = subrem_map_file_load(
|
||||
subghz_remote_app, furi_string_get_cstr(subghz_remote_app->file_path));
|
||||
|
||||
if(load_state == SubRemLoadMapStateOK || load_state == SubRemLoadMapStateNotAllOK) {
|
||||
map_loaded = true;
|
||||
} else {
|
||||
// TODO Replace
|
||||
dialog_message_show_storage_error(subghz_remote_app->dialogs, "Cannot load\nmap file");
|
||||
}
|
||||
}
|
||||
|
||||
if(map_loaded) {
|
||||
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneRemote);
|
||||
} else {
|
||||
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
|
||||
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneStart);
|
||||
scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile);
|
||||
}
|
||||
|
||||
view_dispatcher_run(subghz_remote_app->view_dispatcher);
|
||||
|
||||
|
||||
+47
-7
@@ -7,10 +7,8 @@
|
||||
// #include <lib/subghz/protocols/keeloq.h>
|
||||
// #include <lib/subghz/protocols/star_line.h>
|
||||
|
||||
#ifdef APP_SUBGHZREMOTE
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
#endif
|
||||
|
||||
#define TAG "SubGhzRemote"
|
||||
|
||||
@@ -22,7 +20,7 @@ static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
|
||||
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
|
||||
};
|
||||
|
||||
static void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
|
||||
void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
|
||||
furi_assert(map_preset);
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
@@ -228,9 +226,7 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset)
|
||||
NULL,
|
||||
0);
|
||||
|
||||
#ifdef APP_SUBGHZREMOTE
|
||||
subghz_custom_btns_reset();
|
||||
#endif
|
||||
|
||||
if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) {
|
||||
ret = true;
|
||||
@@ -246,12 +242,12 @@ bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
|
||||
|
||||
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
|
||||
subghz_txrx_stop(app->txrx);
|
||||
#ifdef APP_SUBGHZREMOTE
|
||||
|
||||
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
|
||||
subghz_txrx_reset_dynamic_and_custom_btns(app->txrx);
|
||||
}
|
||||
subghz_custom_btns_reset();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,3 +274,47 @@ SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subrem_save_map_to_file(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
const char* file_name = furi_string_get_cstr(app->file_path);
|
||||
bool saved = false;
|
||||
FlipperFormat* fff_data = flipper_format_string_alloc();
|
||||
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
|
||||
flipper_format_write_header_cstr(
|
||||
fff_data, SUBREM_APP_APP_FILE_TYPE, SUBREM_APP_APP_FILE_VERSION);
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->map_preset->subs_preset[i];
|
||||
if(!furi_string_empty(sub_preset->file_path)) {
|
||||
flipper_format_write_string(fff_data, map_file_labels[i][0], sub_preset->file_path);
|
||||
}
|
||||
}
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->map_preset->subs_preset[i];
|
||||
if(!furi_string_empty(sub_preset->file_path)) {
|
||||
flipper_format_write_string(fff_data, map_file_labels[i][1], sub_preset->label);
|
||||
}
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* flipper_format_stream = flipper_format_get_raw_stream(fff_data);
|
||||
|
||||
do {
|
||||
if(!storage_simply_remove(storage, file_name)) {
|
||||
break;
|
||||
}
|
||||
//ToDo check Write
|
||||
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||
stream_save_to_file(flipper_format_stream, storage, file_name, FSOM_CREATE_ALWAYS);
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
flipper_format_free(fff_data);
|
||||
|
||||
return saved;
|
||||
}
|
||||
+14
-5
@@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define SUBREM_LIGHT 1
|
||||
#define APP_SUBGHZREMOTE 1
|
||||
|
||||
#include "helpers/subrem_types.h"
|
||||
#include "helpers/subrem_presets.h"
|
||||
#include "scenes/subrem_scene.h"
|
||||
@@ -12,6 +9,7 @@
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "views/remote.h"
|
||||
#include "views/edit_menu.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
@@ -35,16 +33,23 @@ typedef struct {
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
Popup* popup;
|
||||
TextInput* text_input;
|
||||
Submenu* submenu;
|
||||
|
||||
FuriString* file_path;
|
||||
char file_name_tmp[SUBREM_MAX_LEN_NAME];
|
||||
|
||||
SubRemViewRemote* subrem_remote_view;
|
||||
SubRemViewEditMenu* subrem_edit_menu;
|
||||
|
||||
SubRemMapPreset* map_preset;
|
||||
|
||||
SubGhzTxRx* txrx;
|
||||
|
||||
bool map_not_saved;
|
||||
|
||||
uint8_t chusen_sub;
|
||||
} SubGhzRemoteApp;
|
||||
|
||||
@@ -54,6 +59,10 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset);
|
||||
|
||||
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
|
||||
|
||||
void subrem_save_active_sub(void* context);
|
||||
|
||||
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);
|
||||
|
||||
@@ -22,6 +22,7 @@ typedef struct {
|
||||
SubRemViewRemoteState state;
|
||||
|
||||
uint8_t pressed_btn;
|
||||
bool is_external;
|
||||
} SubRemViewRemoteModel;
|
||||
|
||||
void subrem_view_remote_set_callback(
|
||||
@@ -106,6 +107,15 @@ void subrem_view_remote_set_state(
|
||||
true);
|
||||
}
|
||||
|
||||
void subrem_view_remote_set_radio(SubRemViewRemote* subrem_view_remote, bool external) {
|
||||
furi_assert(subrem_view_remote);
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{ model->is_external = external; },
|
||||
true);
|
||||
}
|
||||
|
||||
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
@@ -143,6 +153,8 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||
elements_button_right(canvas, "Save");
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 126, 62, AlignRight, AlignBottom, ((model->is_external) ? "Ext" : "Int"));
|
||||
}
|
||||
|
||||
//Status text and indicator
|
||||
@@ -267,6 +279,7 @@ SubRemViewRemote* subrem_view_remote_alloc() {
|
||||
}
|
||||
|
||||
model->pressed_btn = 0;
|
||||
model->is_external = false;
|
||||
},
|
||||
true);
|
||||
return subrem_view_remote;
|
||||
|
||||
+3
-1
@@ -33,4 +33,6 @@ void subrem_view_remote_update_data_labels(
|
||||
void subrem_view_remote_set_state(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteState state,
|
||||
uint8_t presed_btn);
|
||||
uint8_t presed_btn);
|
||||
|
||||
void subrem_view_remote_set_radio(SubRemViewRemote* subrem_view_remote, bool external);
|
||||
@@ -1,15 +0,0 @@
|
||||
App(
|
||||
appid="subrem_configurator",
|
||||
name="Remote Maker",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="subghz_remote_config_app",
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=50,
|
||||
fap_description="File Editor for the SubGhz Remote app",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon="subrem_10px.png",
|
||||
)
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
SubRemEditMenuStateUP = 0,
|
||||
SubRemEditMenuStateDOWN,
|
||||
SubRemEditMenuStateLEFT,
|
||||
SubRemEditMenuStateRIGHT,
|
||||
SubRemEditMenuStateOK,
|
||||
} SubRemEditMenuState;
|
||||
|
||||
typedef enum {
|
||||
// StartSubmenuIndex
|
||||
SubmenuIndexSubRemEditMapFile = 0,
|
||||
SubmenuIndexSubRemNewMapFile,
|
||||
#if FURI_DEBUG
|
||||
SubmenuIndexSubRemRemoteView,
|
||||
#endif
|
||||
// SubmenuIndexSubRemAbout,
|
||||
|
||||
// EditSubmenuIndex
|
||||
EditSubmenuIndexEditLabel,
|
||||
EditSubmenuIndexEditFile,
|
||||
|
||||
// SubRemCustomEvent
|
||||
SubRemCustomEventViewRemoteStartUP = 100,
|
||||
SubRemCustomEventViewRemoteStartDOWN,
|
||||
SubRemCustomEventViewRemoteStartLEFT,
|
||||
SubRemCustomEventViewRemoteStartRIGHT,
|
||||
SubRemCustomEventViewRemoteStartOK,
|
||||
SubRemCustomEventViewRemoteBack,
|
||||
SubRemCustomEventViewRemoteStop,
|
||||
SubRemCustomEventViewRemoteForcedStop,
|
||||
|
||||
SubRemCustomEventViewEditMenuBack,
|
||||
SubRemCustomEventViewEditMenuUP,
|
||||
SubRemCustomEventViewEditMenuDOWN,
|
||||
SubRemCustomEventViewEditMenuEdit,
|
||||
SubRemCustomEventViewEditMenuSave,
|
||||
|
||||
SubRemCustomEventSceneEditsubmenu,
|
||||
SubRemCustomEventSceneEditLabelInputDone,
|
||||
SubRemCustomEventSceneEditLabelWidgetAcces,
|
||||
SubRemCustomEventSceneEditLabelWidgetBack,
|
||||
|
||||
SubRemCustomEventSceneEditOpenSubErrorPopup,
|
||||
|
||||
SubRemCustomEventSceneEditPreviewSaved,
|
||||
|
||||
SubRemCustomEventSceneNewName,
|
||||
} SubRemCustomEvent;
|
||||
@@ -1,181 +0,0 @@
|
||||
#include "subrem_presets.h"
|
||||
|
||||
#define TAG "SubRemPresets"
|
||||
|
||||
SubRemSubFilePreset* subrem_sub_file_preset_alloc() {
|
||||
SubRemSubFilePreset* sub_preset = malloc(sizeof(SubRemSubFilePreset));
|
||||
|
||||
sub_preset->fff_data = flipper_format_string_alloc();
|
||||
sub_preset->file_path = furi_string_alloc();
|
||||
sub_preset->protocaol_name = furi_string_alloc();
|
||||
sub_preset->label = furi_string_alloc();
|
||||
|
||||
sub_preset->freq_preset.name = furi_string_alloc();
|
||||
|
||||
sub_preset->type = SubGhzProtocolTypeUnknown;
|
||||
sub_preset->load_state = SubRemLoadSubStateNotSet;
|
||||
|
||||
return sub_preset;
|
||||
}
|
||||
|
||||
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset) {
|
||||
furi_assert(sub_preset);
|
||||
|
||||
furi_string_free(sub_preset->label);
|
||||
furi_string_free(sub_preset->protocaol_name);
|
||||
furi_string_free(sub_preset->file_path);
|
||||
flipper_format_free(sub_preset->fff_data);
|
||||
|
||||
furi_string_free(sub_preset->freq_preset.name);
|
||||
|
||||
free(sub_preset);
|
||||
}
|
||||
|
||||
void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) {
|
||||
furi_assert(sub_preset);
|
||||
|
||||
furi_string_set_str(sub_preset->label, "");
|
||||
furi_string_reset(sub_preset->protocaol_name);
|
||||
furi_string_reset(sub_preset->file_path);
|
||||
|
||||
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
|
||||
stream_clean(fff_data_stream);
|
||||
|
||||
sub_preset->type = SubGhzProtocolTypeUnknown;
|
||||
sub_preset->load_state = SubRemLoadSubStateNotSet;
|
||||
}
|
||||
|
||||
SubRemLoadSubState subrem_sub_preset_load(
|
||||
SubRemSubFilePreset* sub_preset,
|
||||
SubGhzTxRx* txrx,
|
||||
FlipperFormat* fff_data_file) {
|
||||
furi_assert(sub_preset);
|
||||
furi_assert(txrx);
|
||||
furi_assert(fff_data_file);
|
||||
|
||||
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
|
||||
|
||||
SubRemLoadSubState ret;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t temp_data32;
|
||||
uint32_t repeat = 200;
|
||||
|
||||
ret = SubRemLoadSubStateError;
|
||||
|
||||
do {
|
||||
stream_clean(fff_data_stream);
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
|
||||
(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
|
||||
temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Type or version mismatch");
|
||||
break;
|
||||
}
|
||||
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(txrx);
|
||||
|
||||
//Load frequency or using default from settings
|
||||
ret = SubRemLoadSubStateErrorFreq;
|
||||
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
|
||||
FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency");
|
||||
sub_preset->freq_preset.frequency = subghz_setting_get_default_frequency(setting);
|
||||
} else if(!subghz_txrx_radio_device_is_frequecy_valid(txrx, temp_data32)) {
|
||||
FURI_LOG_E(TAG, "Frequency not supported on chosen radio module");
|
||||
break;
|
||||
}
|
||||
sub_preset->freq_preset.frequency = temp_data32;
|
||||
|
||||
//Load preset
|
||||
ret = SubRemLoadSubStateErrorMod;
|
||||
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Preset");
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set_str(
|
||||
temp_str, subghz_txrx_get_preset_name(txrx, furi_string_get_cstr(temp_str)));
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "")) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
|
||||
FURI_LOG_E(TAG, "CUSTOM preset is not supported");
|
||||
break;
|
||||
// TODO Custom preset loading logic if need
|
||||
// sub_preset->freq_preset.preset_index =
|
||||
// subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));
|
||||
}
|
||||
|
||||
furi_string_set(sub_preset->freq_preset.name, temp_str);
|
||||
|
||||
// Load protocol
|
||||
ret = SubRemLoadSubStateErrorProtocol;
|
||||
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
FlipperFormat* fff_data = sub_preset->fff_data;
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
|
||||
//if RAW
|
||||
subghz_protocol_raw_gen_fff_data(
|
||||
fff_data,
|
||||
furi_string_get_cstr(sub_preset->file_path),
|
||||
subghz_txrx_radio_device_get_name(txrx));
|
||||
} else {
|
||||
stream_copy_full(
|
||||
flipper_format_get_raw_stream(fff_data_file),
|
||||
flipper_format_get_raw_stream(fff_data));
|
||||
}
|
||||
|
||||
if(subghz_txrx_load_decoder_by_name_protocol(txrx, furi_string_get_cstr(temp_str))) {
|
||||
SubGhzProtocolStatus status =
|
||||
subghz_protocol_decoder_base_deserialize(subghz_txrx_get_decoder(txrx), fff_data);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Protocol not found");
|
||||
break;
|
||||
}
|
||||
|
||||
const SubGhzProtocol* protocol = subghz_txrx_get_decoder(txrx)->protocol;
|
||||
|
||||
if(protocol->flag & SubGhzProtocolFlag_Send) {
|
||||
if((protocol->type == SubGhzProtocolTypeStatic) ||
|
||||
(protocol->type == SubGhzProtocolTypeDynamic) ||
|
||||
(protocol->type == SubGhzProtocolTypeBinRAW) ||
|
||||
(protocol->type == SubGhzProtocolTypeRAW)) {
|
||||
sub_preset->type = protocol->type;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unsuported Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set(sub_preset->protocaol_name, temp_str);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Protocol does not support transmission");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable Repeat");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = SubRemLoadSubStateOK;
|
||||
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
|
||||
#endif
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
sub_preset->load_state = ret;
|
||||
return ret;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -1,670 +0,0 @@
|
||||
#include "subghz_txrx_i.h"
|
||||
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhz"
|
||||
|
||||
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
|
||||
UNUSED(instance);
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {
|
||||
UNUSED(instance);
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
SubGhzTxRx* subghz_txrx_alloc() {
|
||||
SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));
|
||||
instance->setting = subghz_setting_alloc();
|
||||
subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user"));
|
||||
|
||||
instance->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
instance->preset->name = furi_string_alloc();
|
||||
subghz_txrx_set_preset(
|
||||
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
|
||||
|
||||
instance->txrx_state = SubGhzTxRxStateSleep;
|
||||
|
||||
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
|
||||
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
|
||||
subghz_txrx_set_debug_pin_state(instance, false);
|
||||
|
||||
instance->worker = subghz_worker_alloc();
|
||||
instance->fff_data = flipper_format_string_alloc();
|
||||
|
||||
instance->environment = subghz_environment_alloc();
|
||||
instance->is_database_loaded =
|
||||
subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME);
|
||||
subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
|
||||
subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||
instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
|
||||
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||
instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
|
||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||
instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
|
||||
subghz_environment_set_protocol_registry(
|
||||
instance->environment, (void*)&subghz_protocol_registry);
|
||||
instance->receiver = subghz_receiver_alloc_init(instance->environment);
|
||||
|
||||
subghz_worker_set_overrun_callback(
|
||||
instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
subghz_worker_set_pair_callback(
|
||||
instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
subghz_worker_set_context(instance->worker, instance->receiver);
|
||||
|
||||
//set default device Internal
|
||||
subghz_devices_init();
|
||||
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
|
||||
instance->radio_device_type =
|
||||
subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeInternal);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_txrx_free(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
|
||||
subghz_txrx_radio_device_power_off(instance);
|
||||
subghz_devices_end(instance->radio_device);
|
||||
}
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
subghz_worker_free(instance->worker);
|
||||
subghz_receiver_free(instance->receiver);
|
||||
subghz_environment_free(instance->environment);
|
||||
flipper_format_free(instance->fff_data);
|
||||
furi_string_free(instance->preset->name);
|
||||
subghz_setting_free(instance->setting);
|
||||
|
||||
free(instance->preset);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->is_database_loaded;
|
||||
}
|
||||
|
||||
void subghz_txrx_set_preset(
|
||||
SubGhzTxRx* instance,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
furi_assert(instance);
|
||||
furi_string_set(instance->preset->name, preset_name);
|
||||
SubGhzRadioPreset* preset = instance->preset;
|
||||
preset->frequency = frequency;
|
||||
preset->data = preset_data;
|
||||
preset->data_size = preset_data_size;
|
||||
}
|
||||
|
||||
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {
|
||||
UNUSED(instance);
|
||||
const char* preset_name = "";
|
||||
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
|
||||
preset_name = "AM270";
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
|
||||
preset_name = "AM650";
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
|
||||
preset_name = "FM238";
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
|
||||
preset_name = "FM476";
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
|
||||
preset_name = "CUSTOM";
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown preset");
|
||||
}
|
||||
return preset_name;
|
||||
}
|
||||
|
||||
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return *instance->preset;
|
||||
}
|
||||
|
||||
void subghz_txrx_get_frequency_and_modulation(
|
||||
SubGhzTxRx* instance,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation,
|
||||
bool long_name) {
|
||||
furi_assert(instance);
|
||||
SubGhzRadioPreset* preset = instance->preset;
|
||||
if(frequency != NULL) {
|
||||
furi_string_printf(
|
||||
frequency,
|
||||
"%03ld.%02ld",
|
||||
preset->frequency / 1000000 % 1000,
|
||||
preset->frequency / 10000 % 100);
|
||||
}
|
||||
if(modulation != NULL) {
|
||||
if(long_name) {
|
||||
furi_string_printf(modulation, "%s", furi_string_get_cstr(preset->name));
|
||||
} else {
|
||||
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {
|
||||
furi_assert(instance);
|
||||
subghz_devices_reset(instance->radio_device);
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data);
|
||||
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||
}
|
||||
|
||||
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
furi_assert(
|
||||
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
|
||||
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
|
||||
uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency);
|
||||
subghz_devices_flush_rx(instance->radio_device);
|
||||
subghz_txrx_speaker_on(instance);
|
||||
|
||||
subghz_devices_start_async_rx(
|
||||
instance->radio_device, subghz_worker_rx_callback, instance->worker);
|
||||
subghz_worker_start(instance->worker);
|
||||
instance->txrx_state = SubGhzTxRxStateRx;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void subghz_txrx_idle(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||
}
|
||||
|
||||
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state == SubGhzTxRxStateRx);
|
||||
|
||||
if(subghz_worker_is_running(instance->worker)) {
|
||||
subghz_worker_stop(instance->worker);
|
||||
subghz_devices_stop_async_rx(instance->radio_device);
|
||||
}
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||
}
|
||||
|
||||
void subghz_txrx_sleep(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
subghz_devices_sleep(instance->radio_device);
|
||||
instance->txrx_state = SubGhzTxRxStateSleep;
|
||||
}
|
||||
|
||||
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_devices_set_frequency(instance->radio_device, frequency);
|
||||
|
||||
bool ret = subghz_devices_set_tx(instance->radio_device);
|
||||
if(ret) {
|
||||
subghz_txrx_speaker_on(instance);
|
||||
instance->txrx_state = SubGhzTxRxStateTx;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
|
||||
furi_assert(instance);
|
||||
furi_assert(flipper_format);
|
||||
|
||||
subghz_txrx_stop(instance);
|
||||
|
||||
SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t repeat = 200;
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable Repeat");
|
||||
break;
|
||||
}
|
||||
ret = SubGhzTxRxStartTxStateOk;
|
||||
|
||||
SubGhzRadioPreset* preset = instance->preset;
|
||||
instance->transmitter =
|
||||
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
|
||||
|
||||
if(instance->transmitter) {
|
||||
if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==
|
||||
SubGhzProtocolStatusOk) {
|
||||
if(strcmp(furi_string_get_cstr(preset->name), "") != 0) {
|
||||
subghz_txrx_begin(
|
||||
instance,
|
||||
subghz_setting_get_preset_data_by_name(
|
||||
instance->setting, furi_string_get_cstr(preset->name)));
|
||||
if(preset->frequency) {
|
||||
if(!subghz_txrx_tx(instance, preset->frequency)) {
|
||||
FURI_LOG_E(TAG, "Only Rx");
|
||||
ret = SubGhzTxRxStartTxStateErrorOnlyRx;
|
||||
}
|
||||
} else {
|
||||
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||
}
|
||||
|
||||
} else {
|
||||
FURI_LOG_E(
|
||||
TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name));
|
||||
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||
}
|
||||
|
||||
if(ret == SubGhzTxRxStartTxStateOk) {
|
||||
//Start TX
|
||||
subghz_devices_start_async_tx(
|
||||
instance->radio_device, subghz_transmitter_yield, instance->transmitter);
|
||||
}
|
||||
} else {
|
||||
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||
}
|
||||
} else {
|
||||
ret = SubGhzTxRxStartTxStateErrorParserOthers;
|
||||
}
|
||||
if(ret != SubGhzTxRxStartTxStateOk) {
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
if(instance->txrx_state != SubGhzTxRxStateIDLE) {
|
||||
subghz_txrx_idle(instance);
|
||||
}
|
||||
}
|
||||
|
||||
} while(false);
|
||||
furi_string_free(temp_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
subghz_txrx_stop(instance);
|
||||
subghz_txrx_begin(
|
||||
instance,
|
||||
subghz_setting_get_preset_data_by_name(
|
||||
subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));
|
||||
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||
}
|
||||
|
||||
void subghz_txrx_set_need_save_callback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzTxRxNeedSaveCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
instance->need_save_callback = callback;
|
||||
instance->need_save_context = context;
|
||||
}
|
||||
|
||||
static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state == SubGhzTxRxStateTx);
|
||||
//Stop TX
|
||||
subghz_devices_stop_async_tx(instance->radio_device);
|
||||
subghz_transmitter_stop(instance->transmitter);
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
|
||||
//if protocol dynamic then we save the last upload
|
||||
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
|
||||
if(instance->need_save_callback) {
|
||||
instance->need_save_callback(instance->need_save_context);
|
||||
}
|
||||
}
|
||||
subghz_txrx_idle(instance);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
//Todo: Show message
|
||||
}
|
||||
|
||||
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->fff_data;
|
||||
}
|
||||
|
||||
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->setting;
|
||||
}
|
||||
|
||||
void subghz_txrx_stop(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
switch(instance->txrx_state) {
|
||||
case SubGhzTxRxStateTx:
|
||||
subghz_txrx_tx_stop(instance);
|
||||
subghz_txrx_speaker_unmute(instance);
|
||||
break;
|
||||
case SubGhzTxRxStateRx:
|
||||
subghz_txrx_rx_end(instance);
|
||||
subghz_txrx_speaker_mute(instance);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
switch(instance->hopper_state) {
|
||||
case SubGhzHopperStateOFF:
|
||||
case SubGhzHopperStatePause:
|
||||
return;
|
||||
case SubGhzHopperStateRSSITimeOut:
|
||||
if(instance->hopper_timeout != 0) {
|
||||
instance->hopper_timeout--;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = subghz_devices_get_rssi(instance->radio_device);
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
instance->hopper_timeout = 10;
|
||||
instance->hopper_state = SubGhzHopperStateRSSITimeOut;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
instance->hopper_state = SubGhzHopperStateRunning;
|
||||
}
|
||||
// Select next frequency
|
||||
if(instance->hopper_idx_frequency <
|
||||
subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {
|
||||
instance->hopper_idx_frequency++;
|
||||
} else {
|
||||
instance->hopper_idx_frequency = 0;
|
||||
}
|
||||
|
||||
if(instance->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_txrx_rx_end(instance);
|
||||
};
|
||||
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
|
||||
subghz_receiver_reset(instance->receiver);
|
||||
instance->preset->frequency =
|
||||
subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);
|
||||
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||
}
|
||||
}
|
||||
|
||||
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->hopper_state;
|
||||
}
|
||||
|
||||
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {
|
||||
furi_assert(instance);
|
||||
instance->hopper_state = state;
|
||||
}
|
||||
|
||||
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->hopper_state == SubGhzHopperStatePause) {
|
||||
instance->hopper_state = SubGhzHopperStateRunning;
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->hopper_state == SubGhzHopperStateRunning) {
|
||||
instance->hopper_state = SubGhzHopperStatePause;
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_speaker_on(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
|
||||
}
|
||||
|
||||
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_acquire(30)) {
|
||||
if(!instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);
|
||||
}
|
||||
} else {
|
||||
instance->speaker_state = SubGhzSpeakerStateDisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
|
||||
}
|
||||
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
if(!instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
|
||||
}
|
||||
furi_hal_speaker_release();
|
||||
if(instance->speaker_state == SubGhzSpeakerStateShutdown)
|
||||
instance->speaker_state = SubGhzSpeakerStateDisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_speaker_mute(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
|
||||
}
|
||||
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
if(!instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
|
||||
}
|
||||
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
if(!instance->debug_pin_state) {
|
||||
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {
|
||||
furi_assert(instance);
|
||||
instance->speaker_state = state;
|
||||
}
|
||||
|
||||
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->speaker_state;
|
||||
}
|
||||
|
||||
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {
|
||||
furi_assert(instance);
|
||||
furi_assert(name_protocol);
|
||||
bool res = false;
|
||||
instance->decoder_result =
|
||||
subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);
|
||||
if(instance->decoder_result) {
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->decoder_result;
|
||||
}
|
||||
|
||||
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return (
|
||||
(instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==
|
||||
SubGhzProtocolFlag_Save);
|
||||
}
|
||||
|
||||
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {
|
||||
furi_assert(instance);
|
||||
const SubGhzProtocol* protocol = instance->decoder_result->protocol;
|
||||
if(check_type) {
|
||||
return (
|
||||
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
|
||||
protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic);
|
||||
}
|
||||
return (
|
||||
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
|
||||
protocol->encoder->deserialize);
|
||||
}
|
||||
|
||||
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {
|
||||
furi_assert(instance);
|
||||
subghz_receiver_set_filter(instance->receiver, filter);
|
||||
}
|
||||
|
||||
void subghz_txrx_set_rx_calback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzReceiverCallback callback,
|
||||
void* context) {
|
||||
subghz_receiver_set_rx_callback(instance->receiver, callback, context);
|
||||
}
|
||||
|
||||
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback,
|
||||
void* context) {
|
||||
subghz_protocol_raw_file_encoder_worker_set_callback_end(
|
||||
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),
|
||||
callback,
|
||||
context);
|
||||
}
|
||||
|
||||
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
subghz_txrx_radio_device_power_on(instance);
|
||||
}
|
||||
|
||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
||||
if(device) {
|
||||
is_connect = subghz_devices_is_connect(device);
|
||||
}
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
subghz_txrx_radio_device_power_off(instance);
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
SubGhzRadioDeviceType
|
||||
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
subghz_txrx_radio_device_power_on(instance);
|
||||
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(instance->radio_device);
|
||||
instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101;
|
||||
} else {
|
||||
subghz_txrx_radio_device_power_off(instance);
|
||||
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
|
||||
subghz_devices_end(instance->radio_device);
|
||||
}
|
||||
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
|
||||
}
|
||||
|
||||
return instance->radio_device_type;
|
||||
}
|
||||
|
||||
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->radio_device_type;
|
||||
}
|
||||
|
||||
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return subghz_devices_get_rssi(instance->radio_device);
|
||||
}
|
||||
|
||||
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return subghz_devices_get_name(instance->radio_device);
|
||||
}
|
||||
|
||||
bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
return subghz_devices_is_frequency_valid(instance->radio_device, frequency);
|
||||
}
|
||||
|
||||
bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_devices_set_frequency(instance->radio_device, frequency);
|
||||
|
||||
bool ret = subghz_devices_set_tx(instance->radio_device);
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state) {
|
||||
furi_assert(instance);
|
||||
instance->debug_pin_state = state;
|
||||
}
|
||||
|
||||
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->debug_pin_state;
|
||||
}
|
||||
|
||||
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
subghz_environment_reset_keeloq(instance->environment);
|
||||
|
||||
subghz_custom_btns_reset();
|
||||
}
|
||||
|
||||
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->receiver;
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/subghz_worker.h>
|
||||
#include <lib/subghz/subghz_setting.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/protocols/raw.h>
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
typedef struct SubGhzTxRx SubGhzTxRx;
|
||||
|
||||
typedef void (*SubGhzTxRxNeedSaveCallback)(void* context);
|
||||
|
||||
typedef enum {
|
||||
SubGhzTxRxStartTxStateOk,
|
||||
SubGhzTxRxStartTxStateErrorOnlyRx,
|
||||
SubGhzTxRxStartTxStateErrorParserOthers,
|
||||
} SubGhzTxRxStartTxState;
|
||||
|
||||
// Type from subghz_types.h need for txrx working
|
||||
/** SubGhzTxRx state */
|
||||
typedef enum {
|
||||
SubGhzTxRxStateIDLE,
|
||||
SubGhzTxRxStateRx,
|
||||
SubGhzTxRxStateTx,
|
||||
SubGhzTxRxStateSleep,
|
||||
} SubGhzTxRxState;
|
||||
|
||||
/** SubGhzHopperState state */
|
||||
typedef enum {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunning,
|
||||
SubGhzHopperStatePause,
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
|
||||
/** SubGhzSpeakerState state */
|
||||
typedef enum {
|
||||
SubGhzSpeakerStateDisable,
|
||||
SubGhzSpeakerStateShutdown,
|
||||
SubGhzSpeakerStateEnable,
|
||||
} SubGhzSpeakerState;
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeAuto,
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzTxRx
|
||||
*
|
||||
* @return SubGhzTxRx* pointer to SubGhzTxRx
|
||||
*/
|
||||
SubGhzTxRx* subghz_txrx_alloc();
|
||||
|
||||
/**
|
||||
* Free SubGhzTxRx
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_free(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Check if the database is loaded
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return bool True if the database is loaded
|
||||
*/
|
||||
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set preset
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param preset_name Name of preset
|
||||
* @param frequency Frequency in Hz
|
||||
* @param preset_data Data of preset
|
||||
* @param preset_data_size Size of preset data
|
||||
*/
|
||||
void subghz_txrx_set_preset(
|
||||
SubGhzTxRx* instance,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
|
||||
/**
|
||||
* Get name of preset
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param preset String of preset
|
||||
* @return const char* Name of preset
|
||||
*/
|
||||
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);
|
||||
|
||||
/**
|
||||
* Get of preset
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzRadioPreset Preset
|
||||
*/
|
||||
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Get string frequency and modulation
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param frequency Pointer to a string frequency
|
||||
* @param modulation Pointer to a string modulation
|
||||
*/
|
||||
void subghz_txrx_get_frequency_and_modulation(
|
||||
SubGhzTxRx* instance,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation,
|
||||
bool long_name);
|
||||
|
||||
/**
|
||||
* Start TX CC1101
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param flipper_format Pointer to a FlipperFormat
|
||||
* @return SubGhzTxRxStartTxState
|
||||
*/
|
||||
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Start RX CC1101
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_rx_start(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Stop TX/RX CC1101
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_stop(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set sleep mode CC1101
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_sleep(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Update frequency CC1101 in automatic mode (hopper)
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_hopper_update(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Get state hopper
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzHopperState
|
||||
*/
|
||||
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set state hopper
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param state State hopper
|
||||
*/
|
||||
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);
|
||||
|
||||
/**
|
||||
* Unpause hopper
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set pause hopper
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Speaker on
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_speaker_on(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Speaker off
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_speaker_off(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Speaker mute
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_speaker_mute(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Speaker unmute
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
*/
|
||||
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set state speaker
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param state State speaker
|
||||
*/
|
||||
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);
|
||||
|
||||
/**
|
||||
* Get state speaker
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzSpeakerState
|
||||
*/
|
||||
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* load decoder by name protocol
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param name_protocol Name protocol
|
||||
* @return bool True if the decoder is loaded
|
||||
*/
|
||||
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);
|
||||
|
||||
/**
|
||||
* Get decoder
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase
|
||||
*/
|
||||
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Set callback for save data
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param callback Callback for save data
|
||||
* @param context Context for callback
|
||||
*/
|
||||
void subghz_txrx_set_need_save_callback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzTxRxNeedSaveCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Get pointer to a load data key
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return FlipperFormat*
|
||||
*/
|
||||
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Get pointer to a SugGhzSetting
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzSetting*
|
||||
*/
|
||||
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Is it possible to save this protocol
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return bool True if it is possible to save this protocol
|
||||
*/
|
||||
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);
|
||||
|
||||
/**
|
||||
* Is it possible to send this protocol
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return bool True if it is possible to send this protocol
|
||||
*/
|
||||
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);
|
||||
|
||||
/**
|
||||
* Set filter, what types of decoder to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param filter Filter
|
||||
*/
|
||||
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);
|
||||
|
||||
/**
|
||||
* Set callback for receive data
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param callback Callback for receive data
|
||||
* @param context Context for callback
|
||||
*/
|
||||
void subghz_txrx_set_rx_calback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzReceiverCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Set callback for Raw decoder, end of data transfer
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param callback Callback for Raw decoder, end of data transfer
|
||||
* @param context Context for callback
|
||||
*/
|
||||
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback,
|
||||
void* context);
|
||||
|
||||
/* Checking if an external radio device is connected
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param name Name of external radio device
|
||||
* @return bool True if is connected to the external radio device
|
||||
*/
|
||||
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name);
|
||||
|
||||
/* Set the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param radio_device_type Radio device type
|
||||
* @return SubGhzRadioDeviceType Type of installed radio device
|
||||
*/
|
||||
SubGhzRadioDeviceType
|
||||
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
/* Get the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return SubGhzRadioDeviceType Type of installed radio device
|
||||
*/
|
||||
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance);
|
||||
|
||||
/* Get RSSI the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return float RSSI
|
||||
*/
|
||||
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance);
|
||||
|
||||
/* Get name the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return const char* Name of installed radio device
|
||||
*/
|
||||
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance);
|
||||
|
||||
/* Get get intelligence whether frequency the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return bool True if the frequency is valid
|
||||
*/
|
||||
bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency);
|
||||
|
||||
bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency);
|
||||
|
||||
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state);
|
||||
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance);
|
||||
|
||||
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance);
|
||||
|
||||
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw
|
||||
-31
@@ -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;
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
ADD_SCENE(subrem, start, Start)
|
||||
ADD_SCENE(subrem, open_map_file, OpenMapFile)
|
||||
ADD_SCENE(subrem, edit_menu, EditMenu)
|
||||
ADD_SCENE(subrem, edit_submenu, EditSubMenu)
|
||||
ADD_SCENE(subrem, edit_label, EditLabel)
|
||||
ADD_SCENE(subrem, open_sub_file, OpenSubFile)
|
||||
ADD_SCENE(subrem, edit_preview, EditPreview)
|
||||
ADD_SCENE(subrem, enter_new_name, EnterNewName)
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
#include "../subghz_remote_app_i.h"
|
||||
|
||||
void subrem_scene_open_map_file_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
SubRemLoadMapState load_state = subrem_load_from_file(app);
|
||||
uint32_t start_scene_state =
|
||||
scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart);
|
||||
|
||||
// TODO if optimization
|
||||
|
||||
if(load_state == SubRemLoadMapStateBack) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
} else if(start_scene_state == SubmenuIndexSubRemEditMapFile) {
|
||||
scene_manager_set_scene_state(app->scene_manager, SubRemSceneEditMenu, SubRemSubKeyNameUp);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
|
||||
}
|
||||
}
|
||||
|
||||
bool subrem_scene_open_map_file_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void subrem_scene_open_map_file_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#include "../subghz_remote_app_i.h"
|
||||
#include "../helpers/subrem_custom_event.h"
|
||||
|
||||
void subrem_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void subrem_scene_start_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzRemoteApp* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Edit Map File",
|
||||
SubmenuIndexSubRemEditMapFile,
|
||||
subrem_scene_start_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"New Map File",
|
||||
SubmenuIndexSubRemNewMapFile,
|
||||
subrem_scene_start_submenu_callback,
|
||||
app);
|
||||
// submenu_add_item(
|
||||
// submenu,
|
||||
// "About",
|
||||
// SubmenuIndexSubGhzRemoteAbout,
|
||||
// subrem_scene_start_submenu_callback,
|
||||
// app);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||
}
|
||||
|
||||
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzRemoteApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexSubRemEditMapFile) {
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexSubRemNewMapFile) {
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemNewMapFile);
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneEnterNewName);
|
||||
consumed = true;
|
||||
}
|
||||
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
||||
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
||||
// consumed = true;
|
||||
// }
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void subrem_scene_start_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzRemoteApp* app = context;
|
||||
submenu_reset(app->submenu);
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
#include "subghz_remote_app_i.h"
|
||||
|
||||
static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool subghz_remote_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void subghz_remote_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
SubGhzRemoteApp* subghz_remote_app_alloc() {
|
||||
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER);
|
||||
storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER);
|
||||
|
||||
if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) {
|
||||
//FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// furi_hal_power_suppress_charge_enter();
|
||||
|
||||
app->file_path = furi_string_alloc();
|
||||
furi_string_set(app->file_path, SUBREM_APP_FOLDER);
|
||||
|
||||
// GUI
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View Dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&subrem_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, subghz_remote_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, subghz_remote_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, subghz_remote_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Open Notification record
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// SubMenu
|
||||
app->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, SubRemViewIDSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Dialog
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
// TextInput
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, SubRemViewIDTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, SubRemViewIDWidget, widget_get_view(app->widget));
|
||||
|
||||
// Popup
|
||||
app->popup = popup_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, SubRemViewIDPopup, popup_get_view(app->popup));
|
||||
|
||||
// Remote view
|
||||
app->subrem_remote_view = subrem_view_remote_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
SubRemViewIDRemote,
|
||||
subrem_view_remote_get_view(app->subrem_remote_view));
|
||||
|
||||
// Edit Menu view
|
||||
app->subrem_edit_menu = subrem_view_edit_menu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
SubRemViewIDEditMenu,
|
||||
subrem_view_edit_menu_get_view(app->subrem_edit_menu));
|
||||
|
||||
app->map_preset = malloc(sizeof(SubRemMapPreset));
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
app->map_preset->subs_preset[i] = subrem_sub_file_preset_alloc();
|
||||
}
|
||||
|
||||
app->txrx = subghz_txrx_alloc();
|
||||
|
||||
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
|
||||
|
||||
app->map_not_saved = false;
|
||||
|
||||
#ifdef SUBREM_LIGHT
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
#else
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneStart);
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
|
||||
#endif
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void subghz_remote_app_free(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// furi_hal_power_suppress_charge_exit();
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
// Dialog
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
// TextInput
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDTextInput);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
// Remote view
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
|
||||
subrem_view_remote_free(app->subrem_remote_view);
|
||||
|
||||
// Edit view
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDEditMenu);
|
||||
subrem_view_edit_menu_free(app->subrem_edit_menu);
|
||||
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
subghz_txrx_free(app->txrx);
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
subrem_sub_file_preset_free(app->map_preset->subs_preset[i]);
|
||||
}
|
||||
free(app->map_preset);
|
||||
|
||||
// Notifications
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
// Path strings
|
||||
furi_string_free(app->file_path);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t subghz_remote_config_app(void* p) {
|
||||
UNUSED(p);
|
||||
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
|
||||
|
||||
furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER);
|
||||
|
||||
view_dispatcher_run(subghz_remote_app->view_dispatcher);
|
||||
|
||||
subghz_remote_app_free(subghz_remote_app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
#include "subghz_remote_app_i.h"
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include "helpers/txrx/subghz_txrx.h"
|
||||
|
||||
// #include <lib/subghz/protocols/keeloq.h>
|
||||
// #include <lib/subghz/protocols/star_line.h>
|
||||
|
||||
// #include <lib/subghz/protocols/protocol_items.h>
|
||||
// #include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhzRemote"
|
||||
|
||||
static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = {
|
||||
[SubRemSubKeyNameUp] = {"UP", "ULABEL"},
|
||||
[SubRemSubKeyNameDown] = {"DOWN", "DLABEL"},
|
||||
[SubRemSubKeyNameLeft] = {"LEFT", "LLABEL"},
|
||||
[SubRemSubKeyNameRight] = {"RIGHT", "RLABEL"},
|
||||
[SubRemSubKeyNameOk] = {"OK", "OKLABEL"},
|
||||
};
|
||||
|
||||
void subrem_map_preset_reset(SubRemMapPreset* map_preset) {
|
||||
furi_assert(map_preset);
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
subrem_sub_file_preset_reset(map_preset->subs_preset[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static SubRemLoadMapState subrem_map_preset_check(
|
||||
SubRemMapPreset* map_preset,
|
||||
SubGhzTxRx* txrx,
|
||||
FlipperFormat* fff_data_file) {
|
||||
furi_assert(map_preset);
|
||||
furi_assert(txrx);
|
||||
|
||||
bool all_loaded = true;
|
||||
SubRemLoadMapState ret = SubRemLoadMapStateErrorBrokenFile;
|
||||
|
||||
SubRemLoadSubState sub_loadig_state;
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = map_preset->subs_preset[i];
|
||||
|
||||
sub_loadig_state = SubRemLoadSubStateErrorNoFile;
|
||||
|
||||
if(furi_string_empty(sub_preset->file_path)) {
|
||||
// FURI_LOG_I(TAG, "Empty file path");
|
||||
} else if(!flipper_format_file_open_existing(
|
||||
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
|
||||
sub_preset->load_state = SubRemLoadSubStateErrorNoFile;
|
||||
FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path));
|
||||
} else {
|
||||
sub_loadig_state = subrem_sub_preset_load(sub_preset, txrx, fff_data_file);
|
||||
}
|
||||
|
||||
if(sub_loadig_state != SubRemLoadSubStateOK) {
|
||||
all_loaded = false;
|
||||
} else {
|
||||
ret = SubRemLoadMapStateNotAllOK;
|
||||
}
|
||||
|
||||
if(ret != SubRemLoadMapStateErrorBrokenFile && all_loaded) {
|
||||
ret = SubRemLoadMapStateOK;
|
||||
}
|
||||
|
||||
flipper_format_file_close(fff_data_file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool subrem_map_preset_load(SubRemMapPreset* map_preset, FlipperFormat* fff_data_file) {
|
||||
furi_assert(map_preset);
|
||||
bool ret = false;
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = map_preset->subs_preset[i];
|
||||
if(!flipper_format_read_string(
|
||||
fff_data_file, map_file_labels[i][0], sub_preset->file_path)) {
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_W(TAG, "No file patch for %s", map_file_labels[i][0]);
|
||||
#endif
|
||||
sub_preset->type = SubGhzProtocolTypeUnknown;
|
||||
} else if(!path_contains_only_ascii(furi_string_get_cstr(sub_preset->file_path))) {
|
||||
FURI_LOG_E(TAG, "Incorrect characters in [%s] file path", map_file_labels[i][0]);
|
||||
sub_preset->type = SubGhzProtocolTypeUnknown;
|
||||
} else if(!flipper_format_rewind(fff_data_file)) {
|
||||
// Rewind error
|
||||
} else if(!flipper_format_read_string(
|
||||
fff_data_file, map_file_labels[i][1], sub_preset->label)) {
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]);
|
||||
#endif
|
||||
ret = true;
|
||||
} else {
|
||||
ret = true;
|
||||
}
|
||||
if(ret) {
|
||||
// Preload seccesful
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"%-5s: %s %s",
|
||||
map_file_labels[i][0],
|
||||
furi_string_get_cstr(sub_preset->label),
|
||||
furi_string_get_cstr(sub_preset->file_path));
|
||||
sub_preset->load_state = SubRemLoadSubStatePreloaded;
|
||||
}
|
||||
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path) {
|
||||
furi_assert(app);
|
||||
furi_assert(file_path);
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Load Map File Start");
|
||||
#endif
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
SubRemLoadMapState ret = SubRemLoadMapStateErrorOpenError;
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Open Map File..");
|
||||
#endif
|
||||
subrem_map_preset_reset(app->map_preset);
|
||||
|
||||
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
|
||||
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
|
||||
ret = SubRemLoadMapStateErrorOpenError;
|
||||
} else {
|
||||
if(!subrem_map_preset_load(app->map_preset, fff_data_file)) {
|
||||
FURI_LOG_E(TAG, "Could no Sub file path in MAP file");
|
||||
// ret = // error for popup
|
||||
} else if(!flipper_format_file_close(fff_data_file)) {
|
||||
ret = SubRemLoadMapStateErrorOpenError;
|
||||
} else {
|
||||
ret = subrem_map_preset_check(app->map_preset, app->txrx, fff_data_file);
|
||||
}
|
||||
}
|
||||
|
||||
if(ret == SubRemLoadMapStateOK) {
|
||||
FURI_LOG_I(TAG, "Load Map File Seccesful");
|
||||
} else if(ret == SubRemLoadMapStateNotAllOK) {
|
||||
FURI_LOG_I(TAG, "Load Map File Seccesful [Not all files]");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Broken Map File");
|
||||
}
|
||||
|
||||
flipper_format_file_close(fff_data_file);
|
||||
flipper_format_free(fff_data_file);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subrem_save_protocol_to_file(FlipperFormat* flipper_format, const char* sub_file_name) {
|
||||
furi_assert(flipper_format);
|
||||
furi_assert(sub_file_name);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
|
||||
|
||||
bool saved = false;
|
||||
uint32_t repeat = 200;
|
||||
FuriString* file_dir = furi_string_alloc();
|
||||
|
||||
path_extract_dirname(sub_file_name, file_dir);
|
||||
do {
|
||||
// removing additional fields
|
||||
flipper_format_delete_key(flipper_format, "Repeat");
|
||||
// flipper_format_delete_key(flipper_format, "Manufacture");
|
||||
|
||||
if(!storage_simply_remove(storage, sub_file_name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
//ToDo check Write
|
||||
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||
stream_save_to_file(flipper_format_stream, storage, sub_file_name, FSOM_CREATE_ALWAYS);
|
||||
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable Repeat");
|
||||
break;
|
||||
}
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
|
||||
furi_string_free(file_dir);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return saved;
|
||||
}
|
||||
|
||||
void subrem_save_active_sub(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
|
||||
subrem_save_protocol_to_file(
|
||||
sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path));
|
||||
}
|
||||
|
||||
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
FuriString* file_path = furi_string_alloc();
|
||||
SubRemLoadMapState ret = SubRemLoadMapStateBack;
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_subrem_10px);
|
||||
browser_options.base_path = SUBREM_APP_FOLDER;
|
||||
|
||||
// Input events and views are managed by file_select
|
||||
if(!dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options)) {
|
||||
} else {
|
||||
ret = subrem_map_file_load(app, furi_string_get_cstr(app->file_path));
|
||||
}
|
||||
|
||||
furi_string_free(file_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subrem_save_map_to_file(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
const char* file_name = furi_string_get_cstr(app->file_path);
|
||||
bool saved = false;
|
||||
FlipperFormat* fff_data = flipper_format_string_alloc();
|
||||
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
|
||||
flipper_format_write_header_cstr(
|
||||
fff_data, SUBREM_APP_APP_FILE_TYPE, SUBREM_APP_APP_FILE_VERSION);
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->map_preset->subs_preset[i];
|
||||
if(!furi_string_empty(sub_preset->file_path)) {
|
||||
flipper_format_write_string(fff_data, map_file_labels[i][0], sub_preset->file_path);
|
||||
}
|
||||
}
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->map_preset->subs_preset[i];
|
||||
if(!furi_string_empty(sub_preset->file_path)) {
|
||||
flipper_format_write_string(fff_data, map_file_labels[i][1], sub_preset->label);
|
||||
}
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* flipper_format_stream = flipper_format_get_raw_stream(fff_data);
|
||||
|
||||
do {
|
||||
if(!storage_simply_remove(storage, file_name)) {
|
||||
break;
|
||||
}
|
||||
//ToDo check Write
|
||||
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||
stream_save_to_file(flipper_format_stream, storage, file_name, FSOM_CREATE_ALWAYS);
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
flipper_format_free(fff_data);
|
||||
|
||||
return saved;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/subrem_types.h"
|
||||
#include "helpers/subrem_presets.h"
|
||||
#include "scenes/subrem_scene.h"
|
||||
|
||||
#include "helpers/txrx/subghz_txrx.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "views/remote.h"
|
||||
#include "views/edit_menu.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#define SUBREM_APP_FOLDER EXT_PATH("subghz/remote")
|
||||
#define SUBREM_MAX_LEN_NAME 64
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
Popup* popup;
|
||||
TextInput* text_input;
|
||||
Submenu* submenu;
|
||||
|
||||
FuriString* file_path;
|
||||
char file_name_tmp[SUBREM_MAX_LEN_NAME];
|
||||
|
||||
SubRemViewRemote* subrem_remote_view;
|
||||
SubRemViewEditMenu* subrem_edit_menu;
|
||||
|
||||
SubRemMapPreset* map_preset;
|
||||
|
||||
SubGhzTxRx* txrx;
|
||||
|
||||
bool map_not_saved;
|
||||
|
||||
uint8_t chusen_sub;
|
||||
} SubGhzRemoteApp;
|
||||
|
||||
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app);
|
||||
|
||||
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path);
|
||||
|
||||
void subrem_map_preset_reset(SubRemMapPreset* map_preset);
|
||||
|
||||
bool subrem_save_map_to_file(SubGhzRemoteApp* app);
|
||||
|
||||
void subrem_save_active_sub(void* context);
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,294 +0,0 @@
|
||||
#include "remote.h"
|
||||
#include "../subghz_remote_app_i.h"
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 30
|
||||
#define SUBREM_VIEW_REMOTE_LEFT_OFFSET 10
|
||||
#define SUBREM_VIEW_REMOTE_RIGHT_OFFSET 22
|
||||
|
||||
struct SubRemViewRemote {
|
||||
View* view;
|
||||
SubRemViewRemoteCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char* labels[SubRemSubKeyNameMaxCount];
|
||||
|
||||
SubRemViewRemoteState state;
|
||||
|
||||
uint8_t pressed_btn;
|
||||
} SubRemViewRemoteModel;
|
||||
|
||||
void subrem_view_remote_set_callback(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteCallback callback,
|
||||
void* context) {
|
||||
furi_assert(subrem_view_remote);
|
||||
|
||||
subrem_view_remote->callback = callback;
|
||||
subrem_view_remote->context = context;
|
||||
}
|
||||
|
||||
void subrem_view_remote_update_data_labels(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemSubFilePreset** subs_presets) {
|
||||
furi_assert(subrem_view_remote);
|
||||
furi_assert(subs_presets);
|
||||
|
||||
FuriString* labels[SubRemSubKeyNameMaxCount];
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = subs_presets[i];
|
||||
switch(sub_preset->load_state) {
|
||||
case SubRemLoadSubStateOK:
|
||||
if(!furi_string_empty(sub_preset->label)) {
|
||||
labels[i] = furi_string_alloc_set(sub_preset->label);
|
||||
} else if(!furi_string_empty(sub_preset->file_path)) {
|
||||
labels[i] = furi_string_alloc();
|
||||
path_extract_filename(sub_preset->file_path, labels[i], true);
|
||||
} else {
|
||||
labels[i] = furi_string_alloc_set("Empty Label");
|
||||
}
|
||||
break;
|
||||
|
||||
case SubRemLoadSubStateErrorNoFile:
|
||||
labels[i] = furi_string_alloc_set("[X] Can't open file");
|
||||
break;
|
||||
|
||||
case SubRemLoadSubStateErrorFreq:
|
||||
case SubRemLoadSubStateErrorMod:
|
||||
case SubRemLoadSubStateErrorProtocol:
|
||||
labels[i] = furi_string_alloc_set("[X] Error in .sub file");
|
||||
break;
|
||||
|
||||
default:
|
||||
labels[i] = furi_string_alloc_set("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
strncpy(
|
||||
model->labels[i],
|
||||
furi_string_get_cstr(labels[i]),
|
||||
SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
furi_string_free(labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void subrem_view_remote_set_state(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteState state,
|
||||
uint8_t presed_btn) {
|
||||
furi_assert(subrem_view_remote);
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
model->state = state;
|
||||
model->pressed_btn = presed_btn;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
//Icons for Labels
|
||||
//canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64);
|
||||
canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
|
||||
canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
|
||||
canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
|
||||
canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7);
|
||||
canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9);
|
||||
canvas_draw_icon(canvas, 0, 53, &I_back_10px);
|
||||
|
||||
//Labels
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
uint8_t y = 0;
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
elements_text_box(
|
||||
canvas,
|
||||
SUBREM_VIEW_REMOTE_LEFT_OFFSET,
|
||||
y + 2,
|
||||
126 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET,
|
||||
12,
|
||||
AlignLeft,
|
||||
AlignBottom,
|
||||
model->labels[i],
|
||||
false);
|
||||
y += 10;
|
||||
}
|
||||
|
||||
if(model->state == SubRemViewRemoteStateOFF) {
|
||||
elements_button_left(canvas, "Back");
|
||||
elements_button_right(canvas, "Save");
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
|
||||
}
|
||||
|
||||
//Status text and indicator
|
||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||
|
||||
if(model->state == SubRemViewRemoteStateIdle || model->state == SubRemViewRemoteStateOFF) {
|
||||
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle");
|
||||
} else {
|
||||
switch(model->state) {
|
||||
case SubRemViewRemoteStateSending:
|
||||
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Send");
|
||||
break;
|
||||
case SubRemViewRemoteStateLoading:
|
||||
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Load");
|
||||
break;
|
||||
default:
|
||||
#if FURI_DEBUG
|
||||
canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Wrong_state");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
switch(model->pressed_btn) {
|
||||
case SubRemSubKeyNameUp:
|
||||
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9);
|
||||
break;
|
||||
case SubRemSubKeyNameDown:
|
||||
canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||
break;
|
||||
case SubRemSubKeyNameLeft:
|
||||
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270);
|
||||
break;
|
||||
case SubRemSubKeyNameRight:
|
||||
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90);
|
||||
break;
|
||||
case SubRemSubKeyNameOk:
|
||||
canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool subrem_view_remote_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
SubRemViewRemote* subrem_view_remote = context;
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeLong) {
|
||||
subrem_view_remote->callback(SubRemCustomEventViewRemoteBack, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{ model->pressed_btn = 0; },
|
||||
true);
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteForcedStop, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
return true;
|
||||
}
|
||||
// BACK button processing end
|
||||
|
||||
if(event->key == InputKeyUp && event->type == InputTypePress) {
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteStartUP, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyDown && event->type == InputTypePress) {
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteStartDOWN, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypePress) {
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteStartLEFT, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyRight && event->type == InputTypePress) {
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteStartRIGHT, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypePress) {
|
||||
subrem_view_remote->callback(
|
||||
SubRemCustomEventViewRemoteStartOK, subrem_view_remote->context);
|
||||
return true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
subrem_view_remote->callback(SubRemCustomEventViewRemoteStop, subrem_view_remote->context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void subrem_view_remote_enter(void* context) {
|
||||
furi_assert(context);
|
||||
}
|
||||
|
||||
void subrem_view_remote_exit(void* context) {
|
||||
furi_assert(context);
|
||||
}
|
||||
|
||||
SubRemViewRemote* subrem_view_remote_alloc() {
|
||||
SubRemViewRemote* subrem_view_remote = malloc(sizeof(SubRemViewRemote));
|
||||
|
||||
// View allocation and configuration
|
||||
subrem_view_remote->view = view_alloc();
|
||||
view_allocate_model(
|
||||
subrem_view_remote->view, ViewModelTypeLocking, sizeof(SubRemViewRemoteModel));
|
||||
view_set_context(subrem_view_remote->view, subrem_view_remote);
|
||||
view_set_draw_callback(subrem_view_remote->view, (ViewDrawCallback)subrem_view_remote_draw);
|
||||
view_set_input_callback(subrem_view_remote->view, subrem_view_remote_input);
|
||||
view_set_enter_callback(subrem_view_remote->view, subrem_view_remote_enter);
|
||||
view_set_exit_callback(subrem_view_remote->view, subrem_view_remote_exit);
|
||||
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
model->state = SubRemViewRemoteStateIdle;
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
model->labels[i] = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
strcpy(model->labels[i], "");
|
||||
}
|
||||
|
||||
model->pressed_btn = 0;
|
||||
},
|
||||
true);
|
||||
return subrem_view_remote;
|
||||
}
|
||||
|
||||
void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
|
||||
furi_assert(subghz_remote);
|
||||
|
||||
with_view_model(
|
||||
subghz_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
free(model->labels[i]);
|
||||
}
|
||||
},
|
||||
true);
|
||||
view_free(subghz_remote->view);
|
||||
free(subghz_remote);
|
||||
}
|
||||
|
||||
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote) {
|
||||
furi_assert(subrem_view_remote);
|
||||
return subrem_view_remote->view;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/subrem_custom_event.h"
|
||||
#include "../helpers/subrem_presets.h"
|
||||
|
||||
typedef enum {
|
||||
SubRemViewRemoteStateIdle,
|
||||
SubRemViewRemoteStateLoading,
|
||||
SubRemViewRemoteStateSending,
|
||||
SubRemViewRemoteStateOFF,
|
||||
} SubRemViewRemoteState;
|
||||
|
||||
typedef struct SubRemViewRemote SubRemViewRemote;
|
||||
|
||||
typedef void (*SubRemViewRemoteCallback)(SubRemCustomEvent event, void* context);
|
||||
|
||||
void subrem_view_remote_set_callback(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteCallback callback,
|
||||
void* context);
|
||||
|
||||
SubRemViewRemote* subrem_view_remote_alloc();
|
||||
|
||||
void subrem_view_remote_free(SubRemViewRemote* subrem_view_remote);
|
||||
|
||||
View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote);
|
||||
|
||||
void subrem_view_remote_update_data_labels(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemSubFilePreset** subs_presets);
|
||||
|
||||
void subrem_view_remote_set_state(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteState state,
|
||||
uint8_t presed_btn);
|
||||
Reference in New Issue
Block a user