1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Merge remote-tracking branch 'upstream/dev' into feat/subghz-save-hopping-state

# Conflicts:
#	applications/main/subghz/scenes/subghz_scene_receiver.c
This commit is contained in:
DerSkythe
2023-09-09 22:41:08 +04:00
181 changed files with 1557 additions and 4123 deletions

View File

@@ -47,6 +47,7 @@ steps:
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-default
- mv dist/f7-C/* artifacts-default/
@@ -414,6 +415,7 @@ steps:
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-default
- mv dist/f7-C/* artifacts-default/

4
.gitmodules vendored
View File

@@ -38,3 +38,7 @@
[submodule "lib/stm32wb_copro"]
path = lib/stm32wb_copro
url = https://github.com/flipperdevices/stm32wb_copro.git
[submodule "subghz_remote"]
path = applications/main/subghz_remote
url = https://github.com/DarkFlippers/SubGHz_Remote.git
branch = ufw_main_app

View File

@@ -1,11 +1,41 @@
## New changes
* !!! **Warning! After installing, Desktop settings (Favoutite apps, PIN Code, AutoLock time..) will be resetted to default due to settings changes, Please set your PIN code, Favourite apps again in Settings->Desktop** !!!
* Desktop: **New way to set favourite apps and fully configurable dummy mode** (now you can set up to 4 favourite apps!) (port of OFW PR 2972 by nminaylov) (by @gid9798 | PR #578)
* Desktop: Fix lock timer after rebooting (by @gid9798 | PR #578)
* Infrared: Updated universal assets (by @amec0e | PR #581)
* Core: Added proper error message on out of memory crash (by @Willy-JL)
* SubGHz: Fix FAAC SLH add manually issues and fix sending signals with unknown seed
* SubGHz: Temporarily reverted changes from OFW PR 2984: SubGhz: fix todo (by Skorpionm) - Fixes Enhanced Sub-GHz Chat app and various issues related to receiving signals that was found in 061 release
* **Apple BLE Spam app** (by @Willy-JL | Plus research from ECTO-1A, xMasterX and techryptic) -> (app can be found in builds ` `, `e`, `n`, `r`)
* SubGHz: **FAAC SLH - Programming mode** (by @xMasterX & @Eng1n33r (full research and PoC by @Skorpionm)| PR #585) -> [How to use](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
* SubGHz: FAAC SLH -> Add manually new options
* SubGHz: Fix 0xFFFF counter value being skipped
* SubGHz: Fix path reset on save name scene exit
* SubGHz: Various fixes
* SubGHz Remote: Fix Sub-GHz Remote folder name (by @OperKH | PR #583)
* SubGHz Remote: submodule (by @gid9798 | PR #592)
* Infrared: Updated universal assets (by @amec0e | PR #594)
* Infrared: Remake custom universal remotes to use new design (New icons by @Svaarich)
* UI: Keyboard ok to toggle select all in cursor mode (by @Willy-JL)
* CI/CD: Fixed regular builds having `c` in version name in the device info while not being actual `c` build
* Docs: New FAAC SLH instructions
* Docs: Readme & Changelog fixes (by @gid9798 | PR #586)
* OFW: iButton: Return to the file selection if file is corrupted
* OFW: Account for the "-" in line carry-over
* OFW: github: workflow improvements
* OFW: Storage: force mount
* OFW: Add File Naming setting for more detailed naming -> **Breaking API change, API 35.x -> API 36.x** - **Update your apps!**
* OFW: Disconnect from BLE on protobuf error
* OFW: Add support for Mifare Classic 4k SAK 0x38 ATQA 0x02, 0x04, 0x08
* OFW: Undo some TODO
* OFW: Check the filetype of the update manifest
* OFW: StorageListRequest: size filter
* OFW: SubGhz: heap overflow text error
* OFW: nfc: add rfal wrong state error handling
* OFW: Rfid: fix crash on broken key launch from archive (fix was already done in UL in similar way)
* OFW: AC OFF button
* OFW: New IR universal remote graphics
* OFW: Intelligent probing with warnings for fwflash.py
* OFW: FuriHal: explicitly pull display pins at early init stage, move PUPD config to early stage
* OFW: Fix display last symbol in multiline text
* OFW: Properly reset the NFC device data
* OFW: fbt: various improvements and bug fixes
* OFW: Littlefs updated to v2.7.0
* OFW: loader: restored support for debug apps
* OFW: Removed explicit dependency on scons for external scripting
----

View File

@@ -53,12 +53,12 @@
- Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
- New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON`
- Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk)
- Read mode UI improvements (shows time when signal was received) (by @wosk)
- External CC1101 module support (Hardware SPI used)
- **Hold right in received signal list to delete selected signal**
- **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0 / CAME Atomo** - now you can use arrow buttons to send signal with different button code
- `Add manually` menu extended with new protocols
- BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis)
- FAAC SLH, BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis)
- Debug mode counter increase settings (+1 -> +5, +10, default: +1)
- Debug PIN output settings for protocol development
@@ -120,8 +120,8 @@ Encoders or sending made by @xMasterX:
Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version):
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: @mmx7)]
- Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- FAAC SLH (Spa) -> Update!!! Check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Keeloq: BFT Mitto -> Update! Check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Star Line
- Security+ v1 & v2 (encoders was made in OFW)

View File

@@ -26,7 +26,6 @@ void test_furi_memmgr() {
mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
}
// TODO FL-3492: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
free(ptr);
// allocate and zero-initialize array (calloc)

View File

@@ -67,7 +67,6 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS];
} while(0)
static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size);
static void clean_directory(Storage* fs_api, const char* clean_dir);
static void
test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id);
static void test_rpc_encode_and_feed(MsgList_t msg_list, uint8_t session);
@@ -149,11 +148,41 @@ static void test_rpc_teardown_second_session(void) {
rpc_session[1].session = NULL;
}
static void test_rpc_storage_clean_directory(Storage* fs_api, const char* clean_dir) {
furi_check(fs_api);
furi_check(clean_dir);
storage_simply_remove_recursive(fs_api, clean_dir);
FS_Error error = storage_common_mkdir(fs_api, clean_dir);
furi_check(error == FSE_OK);
}
static void test_rpc_storage_create_file(Storage* fs_api, const char* path, size_t size) {
File* file = storage_file_alloc(fs_api);
bool success = false;
do {
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
if(!storage_file_seek(file, size, true)) break;
success = true;
} while(false);
storage_file_close(file);
storage_file_free(file);
furi_check(success);
}
static void test_rpc_storage_setup(void) {
test_rpc_setup();
Storage* fs_api = furi_record_open(RECORD_STORAGE);
clean_directory(fs_api, TEST_DIR_NAME);
test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file100", 100);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file250", 250);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file500", 200);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file1000", 1000);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file2500", 2500);
test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file5000", 5000);
furi_record_close(RECORD_STORAGE);
}
@@ -161,7 +190,7 @@ static void test_rpc_storage_teardown(void) {
test_rpc_teardown();
Storage* fs_api = furi_record_open(RECORD_STORAGE);
clean_directory(fs_api, TEST_DIR_NAME);
test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME);
furi_record_close(RECORD_STORAGE);
}
@@ -179,36 +208,6 @@ static void test_rpc_session_terminated_callback(void* context) {
xSemaphoreGive(callbacks_context->terminate_semaphore);
}
static void clean_directory(Storage* fs_api, const char* clean_dir) {
furi_check(fs_api);
furi_check(clean_dir);
File* dir = storage_file_alloc(fs_api);
if(storage_dir_open(dir, clean_dir)) {
FileInfo fileinfo;
char* name = malloc(MAX_NAME_LENGTH + 1);
while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
size_t size = strlen(clean_dir) + strlen(name) + 1 + 1;
char* fullname = malloc(size);
snprintf(fullname, size, "%s/%s", clean_dir, name);
if(file_info_is_dir(&fileinfo)) {
clean_directory(fs_api, fullname);
}
FS_Error error = storage_common_remove(fs_api, fullname);
furi_check(error == FSE_OK);
free(fullname);
}
free(name);
} else {
FS_Error error = storage_common_mkdir(fs_api, clean_dir);
(void)error;
furi_check(error == FSE_OK);
}
storage_dir_close(dir);
storage_file_free(dir);
}
static void test_rpc_print_message_list(MsgList_t msg_list) {
#if DEBUG_PRINT
MsgList_reverse(msg_list);
@@ -282,24 +281,40 @@ static void test_rpc_add_ping_to_list(MsgList_t msg_list, bool request, uint32_t
response->which_content = (request == PING_REQUEST) ? PB_Main_system_ping_request_tag :
PB_Main_system_ping_response_tag;
}
static void test_rpc_fill_basic_message(PB_Main* message, uint16_t tag, uint32_t command_id) {
message->command_id = command_id;
message->command_status = PB_CommandStatus_OK;
message->cb_content.funcs.encode = NULL;
message->which_content = tag;
message->has_next = false;
}
static void test_rpc_create_storage_list_request(
PB_Main* message,
const char* path,
bool include_md5,
uint32_t command_id,
uint32_t filter_max_size) {
furi_check(message);
furi_check(path);
test_rpc_fill_basic_message(message, PB_Main_storage_list_request_tag, command_id);
message->content.storage_list_request.path = strdup(path);
message->content.storage_list_request.include_md5 = include_md5;
message->content.storage_list_request.filter_max_size = filter_max_size;
}
static void test_rpc_create_simple_message(
PB_Main* message,
uint16_t tag,
const char* str,
uint32_t command_id,
bool flag) {
uint32_t command_id) {
furi_check(message);
char* str_copy = NULL;
if(str) {
str_copy = strdup(str);
}
message->command_id = command_id;
message->command_status = PB_CommandStatus_OK;
message->cb_content.funcs.encode = NULL;
message->which_content = tag;
message->has_next = false;
test_rpc_fill_basic_message(message, tag, command_id);
switch(tag) {
case PB_Main_storage_info_request_tag:
message->content.storage_info_request.path = str_copy;
@@ -307,10 +322,6 @@ static void test_rpc_create_simple_message(
case PB_Main_storage_stat_request_tag:
message->content.storage_stat_request.path = str_copy;
break;
case PB_Main_storage_list_request_tag:
message->content.storage_list_request.path = str_copy;
message->content.storage_list_request.include_md5 = flag;
break;
case PB_Main_storage_mkdir_request_tag:
message->content.storage_mkdir_request.path = str_copy;
break;
@@ -573,11 +584,29 @@ static void
message->content.storage_list_response.file[2].name = str;
}
static bool test_rpc_system_storage_list_filter(
const FileInfo* fileinfo,
const char* name,
size_t filter_max_size) {
bool result = false;
do {
if(!path_contains_only_ascii(name)) break;
if(filter_max_size) {
if(fileinfo->size > filter_max_size) break;
}
result = true;
} while(false);
return result;
}
static void test_rpc_storage_list_create_expected_list(
MsgList_t msg_list,
const char* path,
uint32_t command_id,
bool append_md5) {
bool append_md5,
size_t filter_max_size) {
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* dir = storage_file_alloc(fs_api);
@@ -615,7 +644,7 @@ static void test_rpc_storage_list_create_expected_list(
i = 0;
}
if(path_contains_only_ascii(name)) {
if(test_rpc_system_storage_list_filter(&fileinfo, name, filter_max_size)) {
list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :
PB_Storage_File_FileType_FILE;
list->file[i].size = fileinfo.size;
@@ -698,17 +727,21 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) {
MsgList_clear(msg_list);
}
static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) {
static void test_rpc_storage_list_run(
const char* path,
uint32_t command_id,
bool md5,
size_t filter_max_size) {
PB_Main request;
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_list_request_tag, path, command_id, md5);
test_rpc_create_storage_list_request(&request, path, md5, command_id, filter_max_size);
if(!strcmp(path, "/")) {
test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);
} else {
test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5);
test_rpc_storage_list_create_expected_list(
expected_msg_list, path, command_id, md5, filter_max_size);
}
test_rpc_encode_and_feed_one(&request, 0);
test_rpc_decode_and_compare(expected_msg_list, 0);
@@ -718,25 +751,32 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id, boo
}
MU_TEST(test_storage_list) {
test_rpc_storage_list_run("/", ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false);
test_rpc_storage_list_run("error_path", ++command_id, false);
test_rpc_storage_list_run("/", ++command_id, false, 0);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false, 0);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false, 0);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false, 0);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false, 0);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false, 0);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false, 0);
test_rpc_storage_list_run("error_path", ++command_id, false, 0);
}
MU_TEST(test_storage_list_md5) {
test_rpc_storage_list_run("/", ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true);
test_rpc_storage_list_run("error_path", ++command_id, true);
test_rpc_storage_list_run("/", ++command_id, true, 0);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true, 0);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true, 0);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true, 0);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true, 0);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true, 0);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true, 0);
test_rpc_storage_list_run("error_path", ++command_id, true, 0);
}
MU_TEST(test_storage_list_size) {
test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 0);
test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1);
test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1000);
test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 2500);
}
static void
@@ -804,8 +844,7 @@ static void test_storage_read_run(const char* path, uint32_t command_id) {
MsgList_init(expected_msg_list);
test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_read_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);
test_rpc_encode_and_feed_one(&request, 0);
test_rpc_decode_and_compare(expected_msg_list, 0);
@@ -859,8 +898,7 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) {
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_info_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id);
PB_Main* response = MsgList_push_new(expected_msg_list);
response->command_id = command_id;
@@ -892,8 +930,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_stat_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
FileInfo fileinfo;
@@ -1005,11 +1042,7 @@ static void test_storage_write_read_run(
test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);
test_rpc_create_simple_message(
MsgList_push_raw(input_msg_list),
PB_Main_storage_read_request_tag,
path,
++*command_id,
false);
MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);
test_rpc_add_read_or_write_to_list(
expected_msg_list,
READ_RESPONSE,
@@ -1082,8 +1115,7 @@ MU_TEST(test_storage_interrupt_continuous_same_system) {
MsgList_push_new(input_msg_list),
PB_Main_storage_mkdir_request_tag,
TEST_DIR "dir1",
command_id + 1,
false);
command_id + 1);
test_rpc_add_read_or_write_to_list(
input_msg_list,
WRITE_REQUEST,
@@ -1163,8 +1195,7 @@ static void test_storage_delete_run(
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_delete_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
request.content.storage_delete_request.recursive = recursive;
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
@@ -1245,8 +1276,7 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_mkdir_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
test_rpc_encode_and_feed_one(&request, 0);
@@ -1297,12 +1327,11 @@ static void test_storage_md5sum_run(
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(
&request, PB_Main_storage_md5sum_request_tag, path, command_id, false);
test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);
if(status == PB_CommandStatus_OK) {
PB_Main* response = MsgList_push_new(expected_msg_list);
test_rpc_create_simple_message(
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false);
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);
response->command_status = status;
} else {
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
@@ -1461,6 +1490,7 @@ MU_TEST_SUITE(test_rpc_storage) {
MU_RUN_TEST(test_storage_stat);
MU_RUN_TEST(test_storage_list);
MU_RUN_TEST(test_storage_list_md5);
MU_RUN_TEST(test_storage_list_size);
MU_RUN_TEST(test_storage_read);
MU_RUN_TEST(test_storage_write_read);
MU_RUN_TEST(test_storage_write);
@@ -1759,8 +1789,7 @@ MU_TEST(test_rpc_multisession_storage) {
MsgList_push_raw(input_0),
PB_Main_storage_read_request_tag,
TEST_DIR "file0.txt",
++command_id,
false);
++command_id);
test_rpc_add_read_or_write_to_list(
expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id);
@@ -1768,8 +1797,7 @@ MU_TEST(test_rpc_multisession_storage) {
MsgList_push_raw(input_1),
PB_Main_storage_read_request_tag,
TEST_DIR "file1.txt",
++command_id,
false);
++command_id);
test_rpc_add_read_or_write_to_list(
expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id);

View File

@@ -10,7 +10,7 @@
#include <lib/subghz/devices/devices.h>
#include <lib/subghz/devices/cc1101_configs.h>
#define TAG "SubGhz TEST"
#define TAG "SubGhzTest"
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
@@ -42,8 +42,6 @@ static void subghz_test_rx_callback(
static void subghz_test_init(void) {
environment_handler = subghz_environment_alloc();
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment_handler, NICE_FLOR_S_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(

View File

@@ -15,7 +15,7 @@
#include <cc1101.h>
#include <stdio.h>
#define TAG "SubGhz_Device_CC1101_Ext"
#define TAG "SubGhzDeviceCc1101Ext"
#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2
#define SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO &gpio_ext_pc3

View File

@@ -2,7 +2,7 @@
#include "cc1101_ext.h"
#include <lib/subghz/devices/cc1101_configs.h>
#define TAG "SubGhzDeviceCC1101Ext"
#define TAG "SubGhzDeviceCc1101Ext"
static bool subghz_device_cc1101_ext_interconnect_is_frequency_valid(uint32_t frequency) {
bool ret = subghz_device_cc1101_ext_is_frequency_valid(frequency);

View File

@@ -4,7 +4,7 @@
#include <toolbox/stream/file_stream.h>
// Define log tag
#define TAG "example_apps_assets"
#define TAG "ExampleAppsAssets"
static void example_apps_data_print_file_content(Storage* storage, const char* path) {
Stream* stream = file_stream_alloc(storage);

View File

@@ -2,7 +2,7 @@
#include <storage/storage.h>
// Define log tag
#define TAG "example_apps_data"
#define TAG "ExampleAppsData"
// Application entry point
int32_t example_apps_data_main(void* p) {

View File

@@ -11,7 +11,7 @@
#include <loader/firmware_api/firmware_api.h>
#include <storage/storage.h>
#define TAG "example_plugins"
#define TAG "ExamplePlugins"
int32_t example_plugins_app(void* p) {
UNUSED(p);

View File

@@ -11,7 +11,7 @@
#include <furi.h>
#define TAG "example_plugins"
#define TAG "ExamplePlugins"
int32_t example_plugins_multi_app(void* p) {
UNUSED(p);

View File

@@ -8,7 +8,7 @@
#include <loader/firmware_api/firmware_api.h>
#define TAG "example_advanced_plugins"
#define TAG "ExampleAdvancedPlugins"
int32_t example_advanced_plugins_app(void* p) {
UNUSED(p);

View File

@@ -9,7 +9,7 @@
#include "ducky_script_i.h"
#include <dolphin/dolphin.h>
#define TAG "BadUSB"
#define TAG "BadUsb"
#define WORKER_TAG TAG "Worker"
#define BADUSB_ASCII_TO_KEY(script, x) \

View File

@@ -171,7 +171,7 @@ static const DuckyCmd ducky_commands[] = {
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
};
#define TAG "BadUSB"
#define TAG "BadUsb"
#define WORKER_TAG TAG "Worker"
int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {

View File

@@ -18,10 +18,12 @@ GPIOItems* gpio_items_alloc() {
}
items->pins = malloc(sizeof(GpioPinRecord) * items->count);
for(size_t i = 0; i < items->count; i++) {
size_t index = 0;
for(size_t i = 0; i < gpio_pins_count; i++) {
if(!gpio_pins[i].debug) {
items->pins[i].pin = gpio_pins[i].pin;
items->pins[i].name = gpio_pins[i].name;
items->pins[index].pin = gpio_pins[i].pin;
items->pins[index].name = gpio_pins[i].name;
index++;
}
}
return items;

View File

@@ -19,7 +19,7 @@ void gpio_scene_usb_uart_on_enter(void* context) {
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(prev_state == 0) {
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
scene_usb_uart->cfg.vcp_ch = 0; // TODO FL-3495: settings load
scene_usb_uart->cfg.vcp_ch = 0;
scene_usb_uart->cfg.uart_ch = 0;
scene_usb_uart->cfg.flow_pins = 0;
scene_usb_uart->cfg.baudrate_mode = 0;

View File

@@ -3,7 +3,7 @@
#include <toolbox/path.h>
#include <dolphin/dolphin.h>
#define TAG "iButtonApp"
#define TAG "IButtonApp"
static const NotificationSequence sequence_blink_set_yellow = {
&message_blink_set_color_yellow,
@@ -195,16 +195,23 @@ bool ibutton_load_key(iButton* ibutton) {
bool ibutton_select_and_load_key(iButton* ibutton) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
bool success = false;
dialog_file_browser_set_basic_options(
&browser_options, IBUTTON_APP_FILENAME_EXTENSION, &I_ibutt_10px);
browser_options.base_path = IBUTTON_APP_FOLDER;
if(furi_string_empty(ibutton->file_path)) {
furi_string_set(ibutton->file_path, browser_options.base_path);
}
return dialog_file_browser_show(
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) &&
ibutton_load_key(ibutton);
do {
if(!dialog_file_browser_show(
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options))
break;
success = ibutton_load_key(ibutton);
} while(!success);
return success;
}
bool ibutton_save_key(iButton* ibutton) {

View File

@@ -29,7 +29,8 @@
#include "scenes/ibutton_scene.h"
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
#define IBUTTON_APP_EXTENSION ".ibtn"
#define IBUTTON_APP_FILENAME_PREFIX "iBtn"
#define IBUTTON_APP_FILENAME_EXTENSION ".ibtn"
#define IBUTTON_KEY_NAME_SIZE 22

View File

@@ -1,6 +1,6 @@
#include "../ibutton_i.h"
#include <toolbox/random_name.h>
#include <toolbox/name_generator.h>
#include <toolbox/path.h>
#include <dolphin/dolphin.h>
@@ -17,7 +17,8 @@ void ibutton_scene_save_name_on_enter(void* context) {
const bool is_new_file = furi_string_empty(ibutton->file_path);
if(is_new_file) {
set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE);
name_generator_make_auto(
ibutton->key_name, IBUTTON_KEY_NAME_SIZE, IBUTTON_APP_FILENAME_PREFIX);
}
text_input_set_header_text(text_input, "Name the key");
@@ -29,8 +30,8 @@ void ibutton_scene_save_name_on_enter(void* context) {
IBUTTON_KEY_NAME_SIZE,
is_new_file);
ValidatorIsFile* validator_is_file =
validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
IBUTTON_APP_FOLDER, IBUTTON_APP_FILENAME_EXTENSION, ibutton->key_name);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);
@@ -48,7 +49,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
"%s/%s%s",
IBUTTON_APP_FOLDER,
ibutton->key_name,
IBUTTON_APP_EXTENSION);
IBUTTON_APP_FILENAME_EXTENSION);
if(ibutton_save_key(ibutton)) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);

View File

@@ -1,7 +1,6 @@
#include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h"
#include <furi_hal_rtc.h>
void infrared_scene_universal_ac_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context);
@@ -21,11 +20,11 @@ void infrared_scene_universal_ac_on_enter(void* context) {
0,
6,
15,
&I_power_19x20,
&I_power_hover_19x20,
&I_off_19x20,
&I_off_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
button_panel_add_icon(button_panel, 4, 37, &I_power_text_24x5);
button_panel_add_icon(button_panel, 10, 37, &I_off_text_12x5);
infrared_brute_force_add_record(brute_force, i++, "Off");
button_panel_add_item(
button_panel,

View File

@@ -192,12 +192,9 @@ int32_t lfrfid_app(void* p) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
dolphin_deed(DolphinDeedRfidEmulate);
} else {
// TODO: exit properly
lfrfid_free(app);
return 0;
view_dispatcher_stop(app->view_dispatcher);
}
}
} else {
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
@@ -218,13 +215,16 @@ bool lfrfid_save_key(LfRfid* app) {
lfrfid_make_app_folder(app);
if(furi_string_end_with(app->file_path, LFRFID_APP_EXTENSION)) {
if(furi_string_end_with(app->file_path, LFRFID_APP_FILENAME_EXTENSION)) {
size_t filename_start = furi_string_search_rchar(app->file_path, '/');
furi_string_left(app->file_path, filename_start);
}
furi_string_cat_printf(
app->file_path, "/%s%s", furi_string_get_cstr(app->file_name), LFRFID_APP_EXTENSION);
app->file_path,
"/%s%s",
furi_string_get_cstr(app->file_name),
LFRFID_APP_FILENAME_EXTENSION);
result = lfrfid_save_key_data(app, app->file_path);
return result;
@@ -234,7 +234,8 @@ bool lfrfid_load_key_from_file_select(LfRfid* app) {
furi_assert(app);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px);
dialog_file_browser_set_basic_options(
&browser_options, LFRFID_APP_FILENAME_EXTENSION, &I_125_10px);
browser_options.base_path = LFRFID_APP_FOLDER;
// Input events and views are managed by file_browser

View File

@@ -40,8 +40,9 @@
#define LFRFID_APP_FOLDER ANY_PATH("lfrfid")
#define LFRFID_SD_FOLDER EXT_PATH("lfrfid")
#define LFRFID_APP_EXTENSION ".rfid"
#define LFRFID_APP_SHADOW_EXTENSION ".shd"
#define LFRFID_APP_FILENAME_PREFIX "RFID"
#define LFRFID_APP_FILENAME_EXTENSION ".rfid"
#define LFRFID_APP_SHADOW_FILENAME_EXTENSION ".shd"
#define LFRFID_APP_RAW_ASK_EXTENSION ".ask.raw"
#define LFRFID_APP_RAW_PSK_EXTENSION ".psk.raw"

View File

@@ -1,6 +1,6 @@
#include <lib/toolbox/random_name.h>
#include "../lfrfid_i.h"
#include <dolphin/dolphin.h>
#include <toolbox/name_generator.h>
void lfrfid_scene_save_name_on_enter(void* context) {
LfRfid* app = context;
@@ -11,7 +11,10 @@ void lfrfid_scene_save_name_on_enter(void* context) {
bool key_name_is_empty = furi_string_empty(app->file_name);
if(key_name_is_empty) {
furi_string_set(app->file_path, LFRFID_APP_FOLDER);
set_random_name(app->text_store, LFRFID_TEXT_STORE_SIZE);
name_generator_make_auto(
app->text_store, LFRFID_TEXT_STORE_SIZE, LFRFID_APP_FILENAME_PREFIX);
furi_string_set(folder_path, LFRFID_APP_FOLDER);
} else {
lfrfid_text_store_set(app, "%s", furi_string_get_cstr(app->file_name));
@@ -31,7 +34,7 @@ void lfrfid_scene_save_name_on_enter(void* context) {
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
furi_string_get_cstr(folder_path),
LFRFID_APP_EXTENSION,
LFRFID_APP_FILENAME_EXTENSION,
furi_string_get_cstr(app->file_name));
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);

View File

@@ -223,7 +223,11 @@ void nfc_blink_stop(Nfc* nfc) {
bool nfc_save_file(Nfc* nfc) {
furi_string_printf(
nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION);
nfc->dev->load_path,
"%s/%s%s",
NFC_APP_FOLDER,
nfc->dev->dev_name,
NFC_APP_FILENAME_EXTENSION);
bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
return file_saved;
}

View File

@@ -1,5 +1,5 @@
#include "../nfc_i.h"
#include <lib/toolbox/random_name.h>
#include <lib/toolbox/name_generator.h>
#include <gui/modules/validators.h>
#include <toolbox/path.h>
#include <dolphin/dolphin.h>
@@ -17,7 +17,7 @@ void nfc_scene_save_name_on_enter(void* context) {
TextInput* text_input = nfc->text_input;
bool dev_name_empty = false;
if(!strcmp(nfc->dev->dev_name, "")) {
set_random_name(nfc->text_store, sizeof(nfc->text_store));
name_generator_make_auto(nfc->text_store, NFC_DEV_NAME_MAX_LEN, NFC_APP_FILENAME_PREFIX);
dev_name_empty = true;
} else {
nfc_text_store_set(nfc, nfc->dev->dev_name);
@@ -34,14 +34,14 @@ void nfc_scene_save_name_on_enter(void* context) {
FuriString* folder_path;
folder_path = furi_string_alloc();
if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) {
if(furi_string_end_with(nfc->dev->load_path, NFC_APP_FILENAME_EXTENSION)) {
path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path);
} else {
furi_string_set(folder_path, NFC_APP_FOLDER);
}
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name);
furi_string_get_cstr(folder_path), NFC_APP_FILENAME_EXTENSION, nfc->dev->dev_name);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);

View File

@@ -6,6 +6,8 @@ typedef enum {
SubGhzCustomEventManagerSetRAW,
//SubmenuIndex
SubmenuIndexFaacSLH_Manual_433,
SubmenuIndexFaacSLH_Manual_868,
SubmenuIndexFaacSLH_433,
SubmenuIndexFaacSLH_868,
SubmenuIndexBFTClone,

View File

@@ -44,8 +44,6 @@ SubGhzTxRx* subghz_txrx_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(
@@ -664,6 +662,8 @@ void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_environment_reset_keeloq(instance->environment);
faac_slh_reset_prog_mode();
subghz_custom_btns_reset();
}

View File

@@ -228,7 +228,9 @@ bool subghz_txrx_gen_faac_slh_protocol(
seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF;
}
bool tmp_allow_zero_seed = true;
flipper_format_write_hex(txrx->fff_data, "Seed", seed_data, sizeof(uint32_t));
flipper_format_write_bool(txrx->fff_data, "AllowZeroSeed", &tmp_allow_zero_seed, 1);
}
subghz_transmitter_free(txrx->transmitter);

View File

@@ -23,4 +23,4 @@ ADD_SCENE(subghz, more_raw, MoreRAW)
ADD_SCENE(subghz, decode_raw, DecodeRAW)
ADD_SCENE(subghz, delete_raw, DeleteRAW)
ADD_SCENE(subghz, need_saving, NeedSaving)
ADD_SCENE(subghz, rpc, Rpc)
ADD_SCENE(subghz, rpc, Rpc)

View File

@@ -32,7 +32,7 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = {
"17(1W)",
};
#define DEBUG_COUNTER_COUNT 6
#define DEBUG_COUNTER_COUNT 13
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
"+1",
"+2",
@@ -40,6 +40,13 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
"+4",
"+5",
"+10",
"0",
"-1",
"-2",
"-3",
"-4",
"-5",
"-10",
};
const uint32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
1,
@@ -48,6 +55,13 @@ const uint32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
4,
5,
10,
0,
-1,
-2,
-3,
-4,
-5,
-10,
};
static void subghz_scene_radio_settings_set_device(VariableItem* item) {

View File

@@ -256,7 +256,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
FuriString* temp_str = furi_string_alloc();
furi_string_printf(
temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION);
temp_str,
"%s/%s%s",
SUBGHZ_RAW_FOLDER,
RAW_FILE_NAME,
SUBGHZ_APP_FILENAME_EXTENSION);
subghz_protocol_raw_gen_fff_data(
subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(temp_str),

View File

@@ -199,7 +199,6 @@ void subghz_scene_receiver_on_enter(void* context) {
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
}
subghz_scene_receiver_update_statusbar(subghz);
subghz_txrx_rx_start(subghz->txrx);
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
@@ -207,6 +206,8 @@ void subghz_scene_receiver_on_enter(void* context) {
furi_check(
subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME));
subghz_scene_receiver_update_statusbar(subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
}

View File

@@ -1,10 +1,10 @@
#include "../subghz_i.h"
#include "subghz/types.h"
#include <lib/toolbox/random_name.h>
#include "../helpers/subghz_custom_event.h"
#include <lib/subghz/protocols/raw.h>
#include <gui/modules/validators.h>
#include <dolphin/dolphin.h>
#include <toolbox/name_generator.h>
#define MAX_TEXT_INPUT_LEN 23
@@ -79,7 +79,8 @@ void subghz_scene_save_name_on_enter(void* context) {
subghz_scene_save_name_get_timefilename(file_name, "S", true);
}
} else {
set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME);
name_generator_make_auto(
file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX);
furi_string_set(file_name, file_name_buf);
}
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
@@ -112,7 +113,7 @@ void subghz_scene_save_name_on_enter(void* context) {
dev_name_empty);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
furi_string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, "");
furi_string_get_cstr(subghz->file_path), SUBGHZ_APP_FILENAME_EXTENSION, "");
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
furi_string_free(file_name);
@@ -131,13 +132,24 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
furi_string_set(subghz->file_path, subghz->file_path_tmp);
}
}
scene_manager_previous_scene(subghz->scene_manager);
if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSetSeed)) {
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSetType);
} else {
scene_manager_previous_scene(subghz->scene_manager);
}
// Set file path to default
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneSaveName) {
if(strcmp(subghz->file_name_tmp, "") != 0) {
furi_string_cat_printf(
subghz->file_path, "/%s%s", subghz->file_name_tmp, SUBGHZ_APP_EXTENSION);
subghz->file_path,
"/%s%s",
subghz->file_name_tmp,
SUBGHZ_APP_FILENAME_EXTENSION);
if(subghz_path_is_file(subghz->file_path_tmp)) {
if(!subghz_rename_file(subghz)) {
return false;

View File

@@ -19,7 +19,7 @@ void subghz_scene_saved_menu_on_enter(void* context) {
SubmenuIndexEmulate,
subghz_scene_saved_menu_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"Rename",

View File

@@ -18,7 +18,7 @@ void subghz_scene_set_cnt_on_enter(void* context) {
switch(state) {
case SubmenuIndexBFTClone:
byte_input_set_header_text(byte_input, "Enter COUNTER in hex");
byte_input_set_header_text(byte_input, "Enter COUNTER in Hex");
byte_input_set_result_callback(
byte_input,
subghz_scene_set_cnt_byte_input_callback,
@@ -27,9 +27,9 @@ void subghz_scene_set_cnt_on_enter(void* context) {
subghz->secure_data->cnt,
2);
break;
case SubmenuIndexFaacSLH_433:
case SubmenuIndexFaacSLH_868:
byte_input_set_header_text(byte_input, "Enter COUNTER in hex, 20bits");
case SubmenuIndexFaacSLH_Manual_433:
case SubmenuIndexFaacSLH_Manual_868:
byte_input_set_header_text(byte_input, "Enter COUNTER in Hex 20 bits");
byte_input_set_result_callback(
byte_input,
subghz_scene_set_cnt_byte_input_callback,

View File

@@ -13,7 +13,7 @@ void subghz_scene_set_fix_on_enter(void* context) {
// Setup view
ByteInput* byte_input = subghz->byte_input;
byte_input_set_header_text(byte_input, "Enter FIX in hex");
byte_input_set_header_text(byte_input, "Enter FIX in Hex");
byte_input_set_result_callback(
byte_input,
subghz_scene_set_fix_byte_input_callback,

View File

@@ -14,7 +14,7 @@ void subghz_scene_set_seed_on_enter(void* context) {
// Setup view
ByteInput* byte_input = subghz->byte_input;
byte_input_set_header_text(byte_input, "Enter SEED in hex");
byte_input_set_header_text(byte_input, "Enter SEED in Hex");
byte_input_set_result_callback(
byte_input,
subghz_scene_set_seed_byte_input_callback,
@@ -45,13 +45,6 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
seed = subghz->secure_data->seed[0] << 24 | subghz->secure_data->seed[1] << 16 |
subghz->secure_data->seed[2] << 8 | subghz->secure_data->seed[3];
if(seed == 0) {
furi_string_set(subghz->error_str, "Seed value\ncan not be 0.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
consumed = true;
break;
}
generated_protocol = subghz_txrx_gen_keeloq_bft_protocol(
subghz->txrx,
"AM650",
@@ -69,8 +62,8 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
}
consumed = true;
break;
case SubmenuIndexFaacSLH_433:
case SubmenuIndexFaacSLH_868:
case SubmenuIndexFaacSLH_Manual_433:
case SubmenuIndexFaacSLH_Manual_868:
fix_part = subghz->secure_data->fix[0] << 24 | subghz->secure_data->fix[1] << 16 |
subghz->secure_data->fix[2] << 8 | subghz->secure_data->fix[3];
@@ -80,13 +73,7 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
seed = subghz->secure_data->seed[0] << 24 | subghz->secure_data->seed[1] << 16 |
subghz->secure_data->seed[2] << 8 | subghz->secure_data->seed[3];
if(seed == 0) {
furi_string_set(subghz->error_str, "Seed value\ncan not be 0.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
consumed = true;
break;
}
if(state == SubmenuIndexFaacSLH_433) {
if(state == SubmenuIndexFaacSLH_Manual_433) {
generated_protocol = subghz_txrx_gen_faac_slh_protocol(
subghz->txrx,
"AM650",
@@ -96,7 +83,7 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
(cnt & 0xFFFFF),
seed,
"FAAC_SLH");
} else if(state == SubmenuIndexFaacSLH_868) {
} else if(state == SubmenuIndexFaacSLH_Manual_868) {
generated_protocol = subghz_txrx_gen_faac_slh_protocol(
subghz->txrx,
"AM650",

View File

@@ -15,14 +15,14 @@ void subghz_scene_set_type_on_enter(void* context) {
submenu_add_item(
subghz->submenu,
"Faac SLH 868MHz",
SubmenuIndexFaacSLH_868,
"FAAC SLH [Man.] 868MHz",
SubmenuIndexFaacSLH_Manual_868,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"Faac SLH 433MHz",
SubmenuIndexFaacSLH_433,
"FAAC SLH [Man.] 433MHz",
SubmenuIndexFaacSLH_Manual_433,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
@@ -31,6 +31,18 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexBFTClone,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"FAAC SLH 868MHz",
SubmenuIndexFaacSLH_868,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"FAAC SLH 433MHz",
SubmenuIndexFaacSLH_433,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"BFT Mitto 433MHz",
@@ -319,10 +331,10 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
uint32_t key = (uint32_t)rand();
switch(event.event) {
case SubmenuIndexFaacSLH_868:
case SubmenuIndexFaacSLH_Manual_868:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix);
break;
case SubmenuIndexFaacSLH_433:
case SubmenuIndexFaacSLH_Manual_433:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix);
break;
case SubmenuIndexBFTClone:
@@ -390,6 +402,38 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
generated_protocol = subghz_txrx_gen_data_protocol(
subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24);
break;
case SubmenuIndexFaacSLH_433:
generated_protocol = subghz_txrx_gen_faac_slh_protocol(
subghz->txrx,
"AM650",
433920000,
((key & 0x00FFFFF0) | 0xA0000006) >> 4,
0x6,
0x00002,
key,
"FAAC_SLH");
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexFaacSLH_868:
generated_protocol = subghz_txrx_gen_faac_slh_protocol(
subghz->txrx,
"AM650",
868350000,
((key & 0x00FFFFF0) | 0xA0000006) >> 4,
0x6,
0x00002,
key,
"FAAC_SLH");
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexBeninca433:
generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx,

View File

@@ -79,11 +79,15 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
subghz_txrx_stop(subghz->txrx);
if(subghz_custom_btn_get() != SUBGHZ_CUSTOM_BTN_OK) {
subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK);
uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
int8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
furi_hal_subghz_set_rolling_counter_mult(0);
// Calling restore!
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
subghz_txrx_stop(subghz->txrx);
// Calling restore 2nd time special for FAAC SLH!
// TODO: Find better way to restore after custom button is used!!!
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
subghz_txrx_stop(subghz->txrx);
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
}
return true;

View File

@@ -28,7 +28,7 @@
// Rx RAW | only internal module
// Chat | both
#define TAG "SubGhz CLI"
#define TAG "SubGhzCli"
static void subghz_cli_radio_device_power_on() {
uint8_t attempts = 5;
@@ -326,8 +326,6 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME);
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
@@ -526,8 +524,6 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
printf(
"SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n");
}
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(

View File

@@ -242,7 +242,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
storage,
furi_string_get_cstr(file_path),
furi_string_get_cstr(file_name),
SUBGHZ_APP_EXTENSION,
SUBGHZ_APP_FILENAME_EXTENSION,
file_name,
max_len);
@@ -251,7 +251,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
"%s/%s%s",
furi_string_get_cstr(file_path),
furi_string_get_cstr(file_name),
SUBGHZ_APP_EXTENSION);
SUBGHZ_APP_FILENAME_EXTENSION);
furi_string_set(subghz->file_path, temp_str);
res = true;
}
@@ -325,7 +325,8 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
FuriString* file_path = furi_string_alloc();
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
dialog_file_browser_set_basic_options(
&browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px);
browser_options.base_path = SUBGHZ_APP_FOLDER;
// Input events and views are managed by file_select
@@ -399,7 +400,7 @@ void subghz_file_name_clear(SubGhz* subghz) {
}
bool subghz_path_is_file(FuriString* path) {
return furi_string_end_with(path, SUBGHZ_APP_EXTENSION);
return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION);
}
void subghz_lock(SubGhz* subghz) {

View File

@@ -47,7 +47,7 @@
typedef struct {
uint8_t fix[4];
uint8_t cnt[3];
uint8_t cnt[4];
uint8_t seed[4];
} SecureData;

View File

@@ -9,7 +9,7 @@
#include <assets_icons.h>
#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
#define TAG "SubGhzReadRAW"
#define TAG "SubGhzReadRaw"
struct SubGhzReadRAW {
View* view;

View File

@@ -1,17 +0,0 @@
App(
appid="subghz_remote",
name="Sub-GHz Remote",
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="subghz_remote_app",
icon="A_SubGHzRemote_14",
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
order=11,
fap_libs=["assets",],
fap_icon="icon.png",
fap_description="SubGhz Remote, uses up to 5 .sub files",
fap_category="Sub-GHz",
)

View File

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

View File

@@ -1,181 +0,0 @@
#include "subrem_presets.h"
#define TAG "SubRemPresets"
SubRemSubFilePreset* subrem_sub_file_preset_alloc() {
SubRemSubFilePreset* sub_preset = malloc(sizeof(SubRemSubFilePreset));
sub_preset->fff_data = flipper_format_string_alloc();
sub_preset->file_path = furi_string_alloc();
sub_preset->protocaol_name = furi_string_alloc();
sub_preset->label = furi_string_alloc();
sub_preset->freq_preset.name = furi_string_alloc();
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
return sub_preset;
}
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_free(sub_preset->label);
furi_string_free(sub_preset->protocaol_name);
furi_string_free(sub_preset->file_path);
flipper_format_free(sub_preset->fff_data);
furi_string_free(sub_preset->freq_preset.name);
free(sub_preset);
}
void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) {
furi_assert(sub_preset);
furi_string_set_str(sub_preset->label, "");
furi_string_reset(sub_preset->protocaol_name);
furi_string_reset(sub_preset->file_path);
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
stream_clean(fff_data_stream);
sub_preset->type = SubGhzProtocolTypeUnknown;
sub_preset->load_state = SubRemLoadSubStateNotSet;
}
SubRemLoadSubState subrem_sub_preset_load(
SubRemSubFilePreset* sub_preset,
SubGhzTxRx* txrx,
FlipperFormat* fff_data_file) {
furi_assert(sub_preset);
furi_assert(txrx);
furi_assert(fff_data_file);
Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data);
SubRemLoadSubState ret;
FuriString* temp_str = furi_string_alloc();
uint32_t temp_data32;
uint32_t repeat = 200;
ret = SubRemLoadSubStateError;
do {
stream_clean(fff_data_stream);
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(TAG, "Missing or incorrect header");
break;
}
if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
} else {
FURI_LOG_E(TAG, "Type or version mismatch");
break;
}
SubGhzSetting* setting = subghz_txrx_get_setting(txrx);
//Load frequency or using default from settings
ret = SubRemLoadSubStateErrorFreq;
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency");
sub_preset->freq_preset.frequency = subghz_setting_get_default_frequency(setting);
} else if(!subghz_txrx_radio_device_is_frequency_valid(txrx, temp_data32)) {
FURI_LOG_E(TAG, "Frequency not supported on chosen radio module");
break;
}
sub_preset->freq_preset.frequency = temp_data32;
//Load preset
ret = SubRemLoadSubStateErrorMod;
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset");
break;
}
furi_string_set_str(
temp_str, subghz_txrx_get_preset_name(txrx, furi_string_get_cstr(temp_str)));
if(!strcmp(furi_string_get_cstr(temp_str), "")) {
break;
}
if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
FURI_LOG_E(TAG, "CUSTOM preset is not supported");
break;
// TODO Custom preset loading logic if need
// sub_preset->freq_preset.preset_index =
// subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));
}
furi_string_set(sub_preset->freq_preset.name, temp_str);
// Load protocol
ret = SubRemLoadSubStateErrorProtocol;
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
FlipperFormat* fff_data = sub_preset->fff_data;
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
//if RAW
subghz_protocol_raw_gen_fff_data(
fff_data,
furi_string_get_cstr(sub_preset->file_path),
subghz_txrx_radio_device_get_name(txrx));
} else {
stream_copy_full(
flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(fff_data));
}
if(subghz_txrx_load_decoder_by_name_protocol(txrx, furi_string_get_cstr(temp_str))) {
SubGhzProtocolStatus status =
subghz_protocol_decoder_base_deserialize(subghz_txrx_get_decoder(txrx), fff_data);
if(status != SubGhzProtocolStatusOk) {
break;
}
} else {
FURI_LOG_E(TAG, "Protocol not found");
break;
}
const SubGhzProtocol* protocol = subghz_txrx_get_decoder(txrx)->protocol;
if(protocol->flag & SubGhzProtocolFlag_Send) {
if((protocol->type == SubGhzProtocolTypeStatic) ||
(protocol->type == SubGhzProtocolTypeDynamic) ||
(protocol->type == SubGhzProtocolTypeBinRAW) ||
(protocol->type == SubGhzProtocolTypeRAW)) {
sub_preset->type = protocol->type;
} else {
FURI_LOG_E(TAG, "Unsuported Protocol");
break;
}
furi_string_set(sub_preset->protocaol_name, temp_str);
} else {
FURI_LOG_E(TAG, "Protocol does not support transmission");
break;
}
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubRemLoadSubStateOK;
#if FURI_DEBUG
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
#endif
} while(false);
furi_string_free(temp_str);
sub_preset->load_state = ret;
return ret;
}

View File

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

View File

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

View File

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

View File

@@ -1,670 +0,0 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#include <lib/subghz/blocks/custom_btn.h>
#define TAG "SubGhz"
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
UNUSED(instance);
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
//CC1101 power-up time
furi_delay_ms(10);
}
}
static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {
UNUSED(instance);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
}
SubGhzTxRx* subghz_txrx_alloc() {
SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));
instance->setting = subghz_setting_alloc();
subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user"));
instance->preset = malloc(sizeof(SubGhzRadioPreset));
instance->preset->name = furi_string_alloc();
subghz_txrx_set_preset(
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
instance->txrx_state = SubGhzTxRxStateSleep;
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
subghz_txrx_set_debug_pin_state(instance, false);
instance->worker = subghz_worker_alloc();
instance->fff_data = flipper_format_string_alloc();
instance->environment = subghz_environment_alloc();
instance->is_database_loaded =
subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME);
subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
subghz_environment_set_came_atomo_rainbow_table_file_name(
instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
subghz_environment_set_protocol_registry(
instance->environment, (void*)&subghz_protocol_registry);
instance->receiver = subghz_receiver_alloc_init(instance->environment);
subghz_worker_set_overrun_callback(
instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(instance->worker, instance->receiver);
//set default device Internal
subghz_devices_init();
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
instance->radio_device_type =
subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101);
return instance;
}
void subghz_txrx_free(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
subghz_txrx_radio_device_power_off(instance);
subghz_devices_end(instance->radio_device);
}
subghz_devices_deinit();
subghz_worker_free(instance->worker);
subghz_receiver_free(instance->receiver);
subghz_environment_free(instance->environment);
flipper_format_free(instance->fff_data);
furi_string_free(instance->preset->name);
subghz_setting_free(instance->setting);
free(instance->preset);
free(instance);
}
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->is_database_loaded;
}
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size) {
furi_assert(instance);
furi_string_set(instance->preset->name, preset_name);
SubGhzRadioPreset* preset = instance->preset;
preset->frequency = frequency;
preset->data = preset_data;
preset->data_size = preset_data_size;
}
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {
UNUSED(instance);
const char* preset_name = "";
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
preset_name = "AM270";
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
preset_name = "AM650";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name = "FM238";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name = "FM476";
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
preset_name = "CUSTOM";
} else {
FURI_LOG_E(TAG, "Unknown preset");
}
return preset_name;
}
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {
furi_assert(instance);
return *instance->preset;
}
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation,
bool long_name) {
furi_assert(instance);
SubGhzRadioPreset* preset = instance->preset;
if(frequency != NULL) {
furi_string_printf(
frequency,
"%03ld.%02ld",
preset->frequency / 1000000 % 1000,
preset->frequency / 10000 % 100);
}
if(modulation != NULL) {
if(long_name) {
furi_string_printf(modulation, "%s", furi_string_get_cstr(preset->name));
} else {
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name));
}
}
}
static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {
furi_assert(instance);
subghz_devices_reset(instance->radio_device);
subghz_devices_idle(instance->radio_device);
subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency);
subghz_devices_flush_rx(instance->radio_device);
subghz_txrx_speaker_on(instance);
subghz_devices_start_async_rx(
instance->radio_device, subghz_worker_rx_callback, instance->worker);
subghz_worker_start(instance->worker);
instance->txrx_state = SubGhzTxRxStateRx;
return value;
}
static void subghz_txrx_idle(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(instance->worker)) {
subghz_worker_stop(instance->worker);
subghz_devices_stop_async_rx(instance->radio_device);
}
subghz_devices_idle(instance->radio_device);
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_txrx_sleep(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_devices_sleep(instance->radio_device);
instance->txrx_state = SubGhzTxRxStateSleep;
}
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
subghz_devices_set_frequency(instance->radio_device, frequency);
bool ret = subghz_devices_set_tx(instance->radio_device);
if(ret) {
subghz_txrx_speaker_on(instance);
instance->txrx_state = SubGhzTxRxStateTx;
}
return ret;
}
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
furi_assert(instance);
furi_assert(flipper_format);
subghz_txrx_stop(instance);
SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;
FuriString* temp_str = furi_string_alloc();
uint32_t repeat = 200;
do {
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubGhzTxRxStartTxStateOk;
SubGhzRadioPreset* preset = instance->preset;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
if(instance->transmitter) {
if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==
SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(preset->name), "") != 0) {
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
instance->setting, furi_string_get_cstr(preset->name)));
if(preset->frequency) {
if(!subghz_txrx_tx(instance, preset->frequency)) {
FURI_LOG_E(TAG, "Only Rx");
ret = SubGhzTxRxStartTxStateErrorOnlyRx;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
FURI_LOG_E(
TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name));
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret == SubGhzTxRxStartTxStateOk) {
//Start TX
subghz_devices_start_async_tx(
instance->radio_device, subghz_transmitter_yield, instance->transmitter);
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret != SubGhzTxRxStartTxStateOk) {
subghz_transmitter_free(instance->transmitter);
if(instance->txrx_state != SubGhzTxRxStateIDLE) {
subghz_txrx_idle(instance);
}
}
} while(false);
furi_string_free(temp_str);
return ret;
}
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_txrx_stop(instance);
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));
subghz_txrx_rx(instance, instance->preset->frequency);
}
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context) {
furi_assert(instance);
instance->need_save_callback = callback;
instance->need_save_context = context;
}
static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateTx);
//Stop TX
subghz_devices_stop_async_tx(instance->radio_device);
subghz_transmitter_stop(instance->transmitter);
subghz_transmitter_free(instance->transmitter);
//if protocol dynamic then we save the last upload
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
if(instance->need_save_callback) {
instance->need_save_callback(instance->need_save_context);
}
}
subghz_txrx_idle(instance);
subghz_txrx_speaker_off(instance);
//Todo: Show message
}
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->fff_data;
}
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->setting;
}
void subghz_txrx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->txrx_state) {
case SubGhzTxRxStateTx:
subghz_txrx_tx_stop(instance);
subghz_txrx_speaker_unmute(instance);
break;
case SubGhzTxRxStateRx:
subghz_txrx_rx_end(instance);
subghz_txrx_speaker_mute(instance);
break;
default:
break;
}
}
void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->hopper_state) {
case SubGhzHopperStateOFF:
case SubGhzHopperStatePause:
return;
case SubGhzHopperStateRSSITimeOut:
if(instance->hopper_timeout != 0) {
instance->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = subghz_devices_get_rssi(instance->radio_device);
// Stay if RSSI is high enough
if(rssi > -90.0f) {
instance->hopper_timeout = 10;
instance->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
instance->hopper_state = SubGhzHopperStateRunning;
}
// Select next frequency
if(instance->hopper_idx_frequency <
subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {
instance->hopper_idx_frequency++;
} else {
instance->hopper_idx_frequency = 0;
}
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
};
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
subghz_receiver_reset(instance->receiver);
instance->preset->frequency =
subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);
subghz_txrx_rx(instance, instance->preset->frequency);
}
}
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->hopper_state;
}
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {
furi_assert(instance);
instance->hopper_state = state;
}
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStatePause) {
instance->hopper_state = SubGhzHopperStateRunning;
}
}
void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStateRunning) {
instance->hopper_state = SubGhzHopperStatePause;
}
}
void subghz_txrx_speaker_on(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);
}
} else {
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
furi_hal_speaker_release();
if(instance->speaker_state == SubGhzSpeakerStateShutdown)
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_mute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);
}
}
}
}
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_ibutton);
}
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
if(!instance->debug_pin_state) {
subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);
}
}
}
}
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {
furi_assert(instance);
instance->speaker_state = state;
}
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->speaker_state;
}
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {
furi_assert(instance);
furi_assert(name_protocol);
bool res = false;
instance->decoder_result =
subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);
if(instance->decoder_result) {
res = true;
}
return res;
}
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->decoder_result;
}
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {
furi_assert(instance);
return (
(instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==
SubGhzProtocolFlag_Save);
}
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {
furi_assert(instance);
const SubGhzProtocol* protocol = instance->decoder_result->protocol;
if(check_type) {
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic);
}
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize);
}
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {
furi_assert(instance);
subghz_receiver_set_filter(instance->receiver, filter);
}
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context) {
subghz_receiver_set_rx_callback(instance->receiver, callback, context);
}
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context) {
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),
callback,
context);
}
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) {
furi_assert(instance);
bool is_connect = false;
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
if(!is_otg_enabled) {
subghz_txrx_radio_device_power_on(instance);
}
const SubGhzDevice* device = subghz_devices_get_by_name(name);
if(device) {
is_connect = subghz_devices_is_connect(device);
}
if(!is_otg_enabled) {
subghz_txrx_radio_device_power_off(instance);
}
return is_connect;
}
SubGhzRadioDeviceType
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) {
furi_assert(instance);
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
subghz_txrx_radio_device_power_on(instance);
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
subghz_devices_begin(instance->radio_device);
instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101;
} else {
subghz_txrx_radio_device_power_off(instance);
if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {
subghz_devices_end(instance->radio_device);
}
instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
instance->radio_device_type = SubGhzRadioDeviceTypeInternal;
}
return instance->radio_device_type;
}
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->radio_device_type;
}
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) {
furi_assert(instance);
return subghz_devices_get_rssi(instance->radio_device);
}
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) {
furi_assert(instance);
return subghz_devices_get_name(instance->radio_device);
}
bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
return subghz_devices_is_frequency_valid(instance->radio_device, frequency);
}
bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
subghz_devices_idle(instance->radio_device);
subghz_devices_set_frequency(instance->radio_device, frequency);
bool ret = subghz_devices_set_tx(instance->radio_device);
subghz_devices_idle(instance->radio_device);
return ret;
}
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state) {
furi_assert(instance);
instance->debug_pin_state = state;
}
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->debug_pin_state;
}
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_environment_reset_keeloq(instance->environment);
subghz_custom_btns_reset();
}
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->receiver;
}

View File

@@ -1,375 +0,0 @@
#pragma once
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/raw.h>
#include <lib/subghz/devices/devices.h>
typedef struct SubGhzTxRx SubGhzTxRx;
typedef void (*SubGhzTxRxNeedSaveCallback)(void* context);
typedef enum {
SubGhzTxRxStartTxStateOk,
SubGhzTxRxStartTxStateErrorOnlyRx,
SubGhzTxRxStartTxStateErrorParserOthers,
} SubGhzTxRxStartTxState;
// Type from subghz_types.h need for txrx working
/** SubGhzTxRx state */
typedef enum {
SubGhzTxRxStateIDLE,
SubGhzTxRxStateRx,
SubGhzTxRxStateTx,
SubGhzTxRxStateSleep,
} SubGhzTxRxState;
/** SubGhzHopperState state */
typedef enum {
SubGhzHopperStateOFF,
SubGhzHopperStateRunning,
SubGhzHopperStatePause,
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
/** SubGhzSpeakerState state */
typedef enum {
SubGhzSpeakerStateDisable,
SubGhzSpeakerStateShutdown,
SubGhzSpeakerStateEnable,
} SubGhzSpeakerState;
/** SubGhzRadioDeviceType */
typedef enum {
SubGhzRadioDeviceTypeAuto,
SubGhzRadioDeviceTypeInternal,
SubGhzRadioDeviceTypeExternalCC1101,
} SubGhzRadioDeviceType;
/**
* Allocate SubGhzTxRx
*
* @return SubGhzTxRx* pointer to SubGhzTxRx
*/
SubGhzTxRx* subghz_txrx_alloc();
/**
* Free SubGhzTxRx
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_free(SubGhzTxRx* instance);
/**
* Check if the database is loaded
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if the database is loaded
*/
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);
/**
* Set preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param preset_data Data of preset
* @param preset_data_size Size of preset data
*/
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size);
/**
* Get name of preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset String of preset
* @return const char* Name of preset
*/
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);
/**
* Get of preset
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzRadioPreset Preset
*/
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);
/**
* Get string frequency and modulation
*
* @param instance Pointer to a SubGhzTxRx
* @param frequency Pointer to a string frequency
* @param modulation Pointer to a string modulation
*/
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation,
bool long_name);
/**
* Start TX CC1101
*
* @param instance Pointer to a SubGhzTxRx
* @param flipper_format Pointer to a FlipperFormat
* @return SubGhzTxRxStartTxState
*/
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
/**
* Start RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_rx_start(SubGhzTxRx* instance);
/**
* Stop TX/RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_stop(SubGhzTxRx* instance);
/**
* Set sleep mode CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_sleep(SubGhzTxRx* instance);
/**
* Update frequency CC1101 in automatic mode (hopper)
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_update(SubGhzTxRx* instance);
/**
* Get state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzHopperState
*/
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);
/**
* Set state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @param state State hopper
*/
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);
/**
* Unpause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
/**
* Set pause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/**
* Speaker on
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_on(SubGhzTxRx* instance);
/**
* Speaker off
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_off(SubGhzTxRx* instance);
/**
* Speaker mute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_mute(SubGhzTxRx* instance);
/**
* Speaker unmute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance);
/**
* Set state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @param state State speaker
*/
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);
/**
* Get state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSpeakerState
*/
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);
/**
* load decoder by name protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_protocol Name protocol
* @return bool True if the decoder is loaded
*/
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);
/**
* Get decoder
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase
*/
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);
/**
* Set callback for save data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for save data
* @param context Context for callback
*/
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context);
/**
* Get pointer to a load data key
*
* @param instance Pointer to a SubGhzTxRx
* @return FlipperFormat*
*/
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);
/**
* Get pointer to a SugGhzSetting
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSetting*
*/
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);
/**
* Is it possible to save this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to save this protocol
*/
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);
/**
* Is it possible to send this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to send this protocol
*/
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);
/**
* Set filter, what types of decoder to use
*
* @param instance Pointer to a SubGhzTxRx
* @param filter Filter
*/
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);
/**
* Set callback for receive data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for receive data
* @param context Context for callback
*/
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context);
/**
* Set callback for Raw decoder, end of data transfer
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for Raw decoder, end of data transfer
* @param context Context for callback
*/
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context);
/* Checking if an external radio device is connected
*
* @param instance Pointer to a SubGhzTxRx
* @param name Name of external radio device
* @return bool True if is connected to the external radio device
*/
bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name);
/* Set the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @param radio_device_type Radio device type
* @return SubGhzRadioDeviceType Type of installed radio device
*/
SubGhzRadioDeviceType
subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type);
/* Get the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzRadioDeviceType Type of installed radio device
*/
SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance);
/* Get RSSI the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return float RSSI
*/
float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance);
/* Get name the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return const char* Name of installed radio device
*/
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance);
/* Get get intelligence whether frequency the selected radio device to use
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if the frequency is valid
*/
bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency);
bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency);
void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state);
bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance);
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance);
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw

View File

@@ -1,31 +0,0 @@
#pragma once
#include "subghz_txrx.h"
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SubGhzRadioPreset* preset;
SubGhzSetting* setting;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
bool is_database_loaded;
SubGhzHopperState hopper_state;
SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state;
const SubGhzDevice* radio_device;
SubGhzRadioDeviceType radio_device_type;
SubGhzTxRxNeedSaveCallback need_save_callback;
void* need_save_context;
bool debug_pin_state;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

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

View File

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

View File

@@ -1,9 +0,0 @@
ADD_SCENE(subrem, start, Start)
ADD_SCENE(subrem, open_map_file, OpenMapFile)
ADD_SCENE(subrem, remote, Remote)
ADD_SCENE(subrem, edit_menu, EditMenu)
ADD_SCENE(subrem, edit_submenu, EditSubMenu)
ADD_SCENE(subrem, edit_label, EditLabel)
ADD_SCENE(subrem, open_sub_file, OpenSubFile)
ADD_SCENE(subrem, edit_preview, EditPreview)
ADD_SCENE(subrem, enter_new_name, EnterNewName)

View File

@@ -1,133 +0,0 @@
#include "../subghz_remote_app_i.h"
#include <lib/toolbox/path.h>
typedef enum {
SubRemSceneEditLabelStateTextInput,
SubRemSceneEditLabelStateWidget,
} SubRemSceneEditLabelState;
void subrem_scene_edit_label_text_input_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(
app->view_dispatcher, SubRemCustomEventSceneEditLabelInputDone);
}
void subrem_scene_edit_label_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
if((result == GuiButtonTypeCenter) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event(
app->view_dispatcher, SubRemCustomEventSceneEditLabelWidgetAcces);
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event(
app->view_dispatcher, SubRemCustomEventSceneEditLabelWidgetBack);
}
}
void subrem_scene_edit_label_on_enter(void* context) {
SubGhzRemoteApp* app = context;
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
FuriString* temp_str = furi_string_alloc();
if(furi_string_empty(sub_preset->label)) {
if(furi_string_empty(sub_preset->file_path)) {
path_extract_filename(sub_preset->file_path, temp_str, true);
strcpy(app->file_name_tmp, furi_string_get_cstr(temp_str));
} else {
strcpy(app->file_name_tmp, "");
}
} else {
strcpy(app->file_name_tmp, furi_string_get_cstr(sub_preset->label));
}
TextInput* text_input = app->text_input;
text_input_set_header_text(text_input, "Label name");
text_input_set_result_callback(
text_input,
subrem_scene_edit_label_text_input_callback,
app,
app->file_name_tmp,
25,
false);
text_input_set_minimum_length(app->text_input, 0);
widget_add_string_element(
app->widget, 63, 12, AlignCenter, AlignCenter, FontPrimary, "Empty Label Name");
widget_add_string_element(
app->widget, 63, 32, AlignCenter, AlignCenter, FontSecondary, "Continue?");
widget_add_button_element(
app->widget, GuiButtonTypeCenter, "Ok", subrem_scene_edit_label_widget_callback, app);
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", subrem_scene_edit_label_widget_callback, app);
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
furi_string_free(temp_str);
}
bool subrem_scene_edit_label_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
FuriString* label = app->map_preset->subs_preset[app->chusen_sub]->label;
if(event.type == SceneManagerEventTypeBack) {
if(scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditLabel) ==
SubRemSceneEditLabelStateWidget) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
return true;
} else if(
scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditLabel) ==
SubRemSceneEditLabelStateTextInput) {
scene_manager_previous_scene(app->scene_manager);
return true;
}
scene_manager_previous_scene(app->scene_manager);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventSceneEditLabelInputDone) {
if(strcmp(app->file_name_tmp, "") == 0) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateWidget);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
} else {
furi_string_set(label, app->file_name_tmp);
app->map_not_saved = true;
scene_manager_previous_scene(app->scene_manager);
}
return true;
} else if(event.event == SubRemCustomEventSceneEditLabelWidgetAcces) {
furi_string_set(label, app->file_name_tmp);
app->map_not_saved = true;
scene_manager_previous_scene(app->scene_manager);
return true;
} else if(event.event == SubRemCustomEventSceneEditLabelWidgetBack) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneEditLabel, SubRemSceneEditLabelStateTextInput);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
return true;
}
}
return false;
}
void subrem_scene_edit_label_on_exit(void* context) {
SubGhzRemoteApp* app = context;
// Clear view
text_input_reset(app->text_input);
widget_reset(app->widget);
}

View File

@@ -1,123 +0,0 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_edit_menu_callback(SubRemCustomEvent event, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subrem_scene_edit_menu_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
app->map_not_saved = false;
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventViewEditMenuBack);
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDEditMenu);
}
}
static uint8_t subrem_scene_edit_menu_state_to_index(SubRemEditMenuState event_id) {
uint8_t ret = 0;
if(event_id == SubRemEditMenuStateUP) {
ret = SubRemSubKeyNameUp;
} else if(event_id == SubRemEditMenuStateDOWN) {
ret = SubRemSubKeyNameDown;
} else if(event_id == SubRemEditMenuStateLEFT) {
ret = SubRemSubKeyNameLeft;
} else if(event_id == SubRemEditMenuStateRIGHT) {
ret = SubRemSubKeyNameRight;
} else if(event_id == SubRemEditMenuStateOK) {
ret = SubRemSubKeyNameOk;
}
return ret;
}
static void subrem_scene_edit_menu_update_data(SubGhzRemoteApp* app) {
furi_assert(app);
uint8_t index = subrem_scene_edit_menu_state_to_index(
scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditMenu));
subrem_view_edit_menu_add_data_to_show(
app->subrem_edit_menu,
index,
app->map_preset->subs_preset[index]->label,
app->map_preset->subs_preset[index]->file_path,
app->map_preset->subs_preset[index]->load_state);
}
void subrem_scene_edit_menu_on_enter(void* context) {
SubGhzRemoteApp* app = context;
subrem_view_edit_menu_set_callback(
app->subrem_edit_menu, subrem_scene_edit_menu_callback, app);
subrem_scene_edit_menu_update_data(app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDEditMenu);
Widget* widget = app->widget;
widget_add_string_element(
widget, 63, 12, AlignCenter, AlignBottom, FontPrimary, "Changes are not saved");
widget_add_string_element(
widget, 63, 32, AlignCenter, AlignBottom, FontPrimary, "do you want to exit?");
widget_add_button_element(
widget, GuiButtonTypeRight, "Yes", subrem_scene_edit_menu_widget_callback, app);
widget_add_button_element(
widget, GuiButtonTypeLeft, "No", subrem_scene_edit_menu_widget_callback, app);
}
bool subrem_scene_edit_menu_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeBack) {
// Catch widget backEvent
return true;
}
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventViewEditMenuBack) {
if(app->map_not_saved) {
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget);
} else if(!scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SubRemSceneStart)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
return true;
} else if(
event.event == SubRemCustomEventViewEditMenuUP ||
event.event == SubRemCustomEventViewEditMenuDOWN) {
scene_manager_set_scene_state(
app->scene_manager,
SubRemSceneEditMenu,
subrem_view_edit_menu_get_index(app->subrem_edit_menu));
subrem_scene_edit_menu_update_data(app);
return true;
} else if(event.event == SubRemCustomEventViewEditMenuEdit) {
app->chusen_sub = subrem_view_edit_menu_get_index(app->subrem_edit_menu);
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneEditSubMenu, EditSubmenuIndexEditLabel);
scene_manager_next_scene(app->scene_manager, SubRemSceneEditSubMenu);
return true;
} else if(event.event == SubRemCustomEventViewEditMenuSave) {
scene_manager_next_scene(app->scene_manager, SubRemSceneEditPreview);
return true;
}
}
return false;
}
void subrem_scene_edit_menu_on_exit(void* context) {
SubGhzRemoteApp* app = context;
widget_reset(app->widget);
}

View File

@@ -1,74 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../views/remote.h"
#define TAG "SubRemScenRemote"
void subghz_scene_edit_preview_save_popup_callback(void* context) {
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(
app->view_dispatcher, SubRemCustomEventSceneEditPreviewSaved);
}
void subrem_scene_edit_preview_callback(SubRemCustomEvent event, void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subrem_scene_edit_preview_on_enter(void* context) {
SubGhzRemoteApp* app = context;
// Setup view
Popup* popup = app->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, subghz_scene_edit_preview_save_popup_callback);
popup_enable_timeout(popup);
subrem_view_remote_update_data_labels(app->subrem_remote_view, app->map_preset->subs_preset);
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateOFF, 0);
subrem_view_remote_set_callback(
app->subrem_remote_view, subrem_scene_edit_preview_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDRemote);
}
bool subrem_scene_edit_preview_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeBack ||
(event.type == SceneManagerEventTypeCustom &&
(event.event == SubRemCustomEventViewRemoteStartLEFT ||
event.event == SubRemCustomEventViewRemoteForcedStop))) {
scene_manager_previous_scene(app->scene_manager);
return true;
} else if(
event.type == SceneManagerEventTypeCustom &&
(event.event == SubRemCustomEventViewRemoteStartRIGHT ||
event.event == SubRemCustomEventViewRemoteStartOK)) {
if(subrem_save_map_to_file(app)) {
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDPopup);
app->map_not_saved = false;
return true;
}
// TODO error screen
return true;
} else if(
event.type == SceneManagerEventTypeCustom &&
event.event == SubRemCustomEventSceneEditPreviewSaved) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SubRemSceneEditMenu);
}
// } else if(event.type == SceneManagerEventTypeTick) {
// }
return false;
}
void subrem_scene_edit_preview_on_exit(void* context) {
SubGhzRemoteApp* app = context;
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle, 0);
popup_reset(app->popup);
}

View File

@@ -1,54 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
void subrem_scene_edit_submenu_text_input_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventSceneEditsubmenu);
}
void subrem_scene_edit_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void subrem_scene_edit_submenu_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "Edit Label", EditSubmenuIndexEditLabel, subrem_scene_edit_submenu_callback, app);
submenu_add_item(
submenu, "Edit File", EditSubmenuIndexEditFile, subrem_scene_edit_submenu_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
}
bool subrem_scene_edit_submenu_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == EditSubmenuIndexEditLabel) {
scene_manager_next_scene(app->scene_manager, SubRemSceneEditLabel);
consumed = true;
} else if(event.event == EditSubmenuIndexEditFile) {
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenSubFile);
consumed = true;
}
}
return consumed;
}
void subrem_scene_edit_submenu_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -1,70 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
#include <gui/modules/validators.h>
void subrem_scene_enter_new_name_text_input_callback(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventSceneNewName);
}
void subrem_scene_enter_new_name_on_enter(void* context) {
SubGhzRemoteApp* app = context;
// Setup view
TextInput* text_input = app->text_input;
//strncpy(app->file_name_tmp, "subrem_", SUBREM_MAX_LEN_NAME);
text_input_set_header_text(text_input, "Map file Name");
text_input_set_result_callback(
text_input,
subrem_scene_enter_new_name_text_input_callback,
app,
app->file_name_tmp,
25,
false);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
furi_string_get_cstr(app->file_path), SUBREM_APP_EXTENSION, "");
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDTextInput);
}
bool subrem_scene_enter_new_name_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubRemCustomEventSceneNewName) {
if(strcmp(app->file_name_tmp, "") != 0) {
furi_string_set(app->file_path, SUBREM_APP_FOLDER);
furi_string_cat_printf(
app->file_path, "/%s%s", app->file_name_tmp, SUBREM_APP_EXTENSION);
subrem_map_preset_reset(app->map_preset);
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
} else { //error
}
consumed = true;
}
}
return consumed;
}
void subrem_scene_enter_new_name_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
// Clear validator & view
void* validator_context = text_input_get_validator_callback_context(app->text_input);
text_input_set_validator(app->text_input, NULL, NULL);
validator_is_file_free(validator_context);
text_input_reset(app->text_input);
}

View File

@@ -1,29 +0,0 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_open_map_file_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
SubRemLoadMapState load_state = subrem_load_from_file(app);
uint32_t start_scene_state =
scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart);
if(load_state == SubRemLoadMapStateBack) {
scene_manager_previous_scene(app->scene_manager);
} else if(start_scene_state == SubmenuIndexSubRemEditMapFile) {
scene_manager_set_scene_state(app->scene_manager, SubRemSceneEditMenu, SubRemSubKeyNameUp);
scene_manager_next_scene(app->scene_manager, SubRemSceneEditMenu);
} else if(start_scene_state == SubmenuIndexSubRemOpenMapFile) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
}
}
bool subrem_scene_open_map_file_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void subrem_scene_open_map_file_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -1,118 +0,0 @@
#include "../subghz_remote_app_i.h"
void subrem_scene_open_sub_file_error_popup_callback(void* context) {
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(
app->view_dispatcher, SubRemCustomEventSceneEditOpenSubErrorPopup);
}
SubRemLoadSubState subrem_scene_open_sub_file_dialog(SubGhzRemoteApp* app) {
furi_assert(app);
SubRemSubFilePreset* sub = app->map_preset->subs_preset[app->chusen_sub];
FuriString* temp_file_path = furi_string_alloc();
if(furi_string_empty(sub->file_path)) {
furi_string_set(temp_file_path, SUBGHZ_RAW_FOLDER);
} else {
furi_string_set(temp_file_path, sub->file_path);
}
SubRemLoadSubState ret = SubRemLoadSubStateNotSet;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
browser_options.base_path = SUBGHZ_RAW_FOLDER;
// Input events and views are managed by file_select
if(!dialog_file_browser_show(app->dialogs, temp_file_path, temp_file_path, &browser_options)) {
} else {
// Check sub file
SubRemSubFilePreset* sub_candidate = subrem_sub_file_preset_alloc();
furi_string_set(sub_candidate->label, sub->label);
furi_string_set(sub_candidate->file_path, temp_file_path);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_file = flipper_format_file_alloc(storage);
if(flipper_format_file_open_existing(
fff_file, furi_string_get_cstr(sub_candidate->file_path))) {
ret = subrem_sub_preset_load(sub_candidate, app->txrx, fff_file);
}
flipper_format_file_close(fff_file);
flipper_format_free(fff_file);
furi_record_close(RECORD_STORAGE);
if(ret == SubRemLoadSubStateOK) {
subrem_sub_file_preset_free(app->map_preset->subs_preset[app->chusen_sub]);
app->map_preset->subs_preset[app->chusen_sub] = sub_candidate;
app->map_not_saved = true;
} else {
subrem_sub_file_preset_free(sub_candidate);
}
}
furi_string_free(temp_file_path);
return ret;
}
void subrem_scene_open_sub_file_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
SubRemLoadSubState load_state = subrem_scene_open_sub_file_dialog(app);
Popup* popup = app->popup;
// popup_set_icon();
popup_set_header(popup, "ERROR", 63, 16, AlignCenter, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, subrem_scene_open_sub_file_error_popup_callback);
popup_enable_timeout(popup);
if(load_state == SubRemLoadSubStateOK) {
scene_manager_previous_scene(app->scene_manager);
} else if(load_state == SubRemLoadSubStateNotSet) {
scene_manager_previous_scene(app->scene_manager);
} else {
switch(load_state) {
case SubRemLoadSubStateErrorFreq:
popup_set_text(popup, "Bad frequency", 63, 30, AlignCenter, AlignBottom);
break;
case SubRemLoadSubStateErrorMod:
popup_set_text(popup, "Bad modulation", 63, 30, AlignCenter, AlignBottom);
break;
case SubRemLoadSubStateErrorProtocol:
popup_set_text(popup, "Unsupported protocol", 63, 30, AlignCenter, AlignBottom);
break;
default:
break;
}
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDPopup);
}
}
bool subrem_scene_open_sub_file_on_event(void* context, SceneManagerEvent event) {
SubGhzRemoteApp* app = context;
if(event.type == SceneManagerEventTypeCustom &&
event.event == SubRemCustomEventSceneEditOpenSubErrorPopup) {
scene_manager_previous_scene(app->scene_manager);
return true;
}
return false;
}
void subrem_scene_open_sub_file_on_exit(void* context) {
SubGhzRemoteApp* app = context;
popup_reset(app->popup);
}

View File

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

View File

@@ -1,100 +0,0 @@
#include "../subghz_remote_app_i.h"
#include "../helpers/subrem_custom_event.h"
void subrem_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhzRemoteApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void subrem_scene_start_on_enter(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Open Map File",
SubmenuIndexSubRemOpenMapFile,
subrem_scene_start_submenu_callback,
app);
#if FURI_DEBUG
submenu_add_item(
submenu,
"Remote_Debug",
SubmenuIndexSubRemRemoteView,
subrem_scene_start_submenu_callback,
app);
#endif
submenu_add_item(
submenu,
"Edit Map File",
SubmenuIndexSubRemEditMapFile,
subrem_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"New Map File",
SubmenuIndexSubRemNewMapFile,
subrem_scene_start_submenu_callback,
app);
// submenu_add_item(
// submenu,
// "About",
// SubmenuIndexSubGhzRemoteAbout,
// subrem_scene_start_submenu_callback,
// app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDSubmenu);
}
bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
SubGhzRemoteApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSubRemOpenMapFile) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile);
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
consumed = true;
}
#if FURI_DEBUG
else if(event.event == SubmenuIndexSubRemRemoteView) {
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
consumed = true;
}
#endif
else if(event.event == SubmenuIndexSubRemEditMapFile) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemEditMapFile);
scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
consumed = true;
} else if(event.event == SubmenuIndexSubRemNewMapFile) {
scene_manager_set_scene_state(
app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemNewMapFile);
scene_manager_next_scene(app->scene_manager, SubRemSceneEnterNewName);
consumed = true;
}
// } else if(event.event == SubmenuIndexSubRemAbout) {
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
// consumed = true;
// }
}
return consumed;
}
void subrem_scene_start_on_exit(void* context) {
furi_assert(context);
SubGhzRemoteApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -1,204 +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);
}
static void subghz_remote_make_app_folder(SubGhzRemoteApp* app) {
furi_assert(app);
Storage* storage = furi_record_open(RECORD_STORAGE);
// Migrate old users data
storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER);
if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) {
// FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER);
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
}
furi_record_close(RECORD_STORAGE);
}
SubGhzRemoteApp* subghz_remote_app_alloc() {
SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp));
furi_hal_power_suppress_charge_enter();
app->file_path = furi_string_alloc();
furi_string_set(app->file_path, SUBREM_APP_FOLDER);
// GUI
app->gui = furi_record_open(RECORD_GUI);
// View Dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&subrem_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, subghz_remote_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, subghz_remote_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, subghz_remote_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewIDSubmenu, submenu_get_view(app->submenu));
// Dialog
app->dialogs = furi_record_open(RECORD_DIALOGS);
// TextInput
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewIDTextInput, text_input_get_view(app->text_input));
// Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubRemViewIDWidget, widget_get_view(app->widget));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, SubRemViewIDPopup, popup_get_view(app->popup));
// Remote view
app->subrem_remote_view = subrem_view_remote_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewIDRemote,
subrem_view_remote_get_view(app->subrem_remote_view));
// Edit Menu view
app->subrem_edit_menu = subrem_view_edit_menu_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubRemViewIDEditMenu,
subrem_view_edit_menu_get_view(app->subrem_edit_menu));
app->map_preset = malloc(sizeof(SubRemMapPreset));
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
app->map_preset->subs_preset[i] = subrem_sub_file_preset_alloc();
}
app->txrx = subghz_txrx_alloc();
subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app);
app->map_not_saved = false;
return app;
}
void subghz_remote_app_free(SubGhzRemoteApp* app) {
furi_assert(app);
furi_hal_power_suppress_charge_exit();
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDSubmenu);
submenu_free(app->submenu);
// Dialog
furi_record_close(RECORD_DIALOGS);
// TextInput
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDTextInput);
text_input_free(app->text_input);
// Widget
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDWidget);
widget_free(app->widget);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDPopup);
popup_free(app->popup);
// Remote view
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote);
subrem_view_remote_free(app->subrem_remote_view);
// Edit view
view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDEditMenu);
subrem_view_edit_menu_free(app->subrem_edit_menu);
scene_manager_free(app->scene_manager);
view_dispatcher_free(app->view_dispatcher);
subghz_txrx_free(app->txrx);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
subrem_sub_file_preset_free(app->map_preset->subs_preset[i]);
}
free(app->map_preset);
// Notifications
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Close records
furi_record_close(RECORD_GUI);
// Path strings
furi_string_free(app->file_path);
free(app);
}
int32_t subghz_remote_app(void* arg) {
SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc();
subghz_remote_make_app_folder(subghz_remote_app);
bool map_loaded = false;
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);
subghz_remote_app_free(subghz_remote_app);
return 0;
}

View File

@@ -1,320 +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));
}
bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset) {
furi_assert(app);
furi_assert(sub_preset);
bool ret = false;
subrem_tx_stop_sub(app, true);
if(sub_preset->type == SubGhzProtocolTypeUnknown) {
ret = false;
} else {
FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label));
subghz_txrx_load_decoder_by_name_protocol(
app->txrx, furi_string_get_cstr(sub_preset->protocaol_name));
subghz_txrx_set_preset(
app->txrx,
furi_string_get_cstr(sub_preset->freq_preset.name),
sub_preset->freq_preset.frequency,
NULL,
0);
subghz_custom_btns_reset();
if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) {
ret = true;
}
}
return ret;
}
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
furi_assert(app);
SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub];
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
subghz_txrx_stop(app->txrx);
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
subghz_txrx_reset_dynamic_and_custom_btns(app->txrx);
}
subghz_custom_btns_reset();
return true;
}
return false;
}
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) {
furi_assert(app);
FuriString* file_path = furi_string_alloc();
SubRemLoadMapState ret = SubRemLoadMapStateBack;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_subrem_10px);
browser_options.base_path = SUBREM_APP_FOLDER;
// Input events and views are managed by file_select
if(!dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options)) {
} else {
ret = subrem_map_file_load(app, furi_string_get_cstr(app->file_path));
}
furi_string_free(file_path);
return ret;
}
bool subrem_save_map_to_file(SubGhzRemoteApp* app) {
furi_assert(app);
const char* file_name = furi_string_get_cstr(app->file_path);
bool saved = false;
FlipperFormat* fff_data = flipper_format_string_alloc();
SubRemSubFilePreset* sub_preset;
flipper_format_write_header_cstr(
fff_data, SUBREM_APP_APP_FILE_TYPE, SUBREM_APP_APP_FILE_VERSION);
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = app->map_preset->subs_preset[i];
if(!furi_string_empty(sub_preset->file_path)) {
flipper_format_write_string(fff_data, map_file_labels[i][0], sub_preset->file_path);
}
}
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
sub_preset = app->map_preset->subs_preset[i];
if(!furi_string_empty(sub_preset->file_path)) {
flipper_format_write_string(fff_data, map_file_labels[i][1], sub_preset->label);
}
}
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* flipper_format_stream = flipper_format_get_raw_stream(fff_data);
do {
if(!storage_simply_remove(storage, file_name)) {
break;
}
//ToDo check Write
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
stream_save_to_file(flipper_format_stream, storage, file_name, FSOM_CREATE_ALWAYS);
saved = true;
} while(0);
furi_record_close(RECORD_STORAGE);
flipper_format_free(fff_data);
return saved;
}

View File

@@ -1,68 +0,0 @@
#pragma once
#include "helpers/subrem_types.h"
#include "helpers/subrem_presets.h"
#include "scenes/subrem_scene.h"
#include "helpers/txrx/subghz_txrx.h"
#include <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);
bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset);
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path);
void subrem_map_preset_reset(SubRemMapPreset* map_preset);
bool subrem_save_map_to_file(SubGhzRemoteApp* app);
void subrem_save_active_sub(void* context);

View File

@@ -1,290 +0,0 @@
#include "edit_menu.h"
#include "../subghz_remote_app_i.h"
#include <input/input.h>
#include <gui/elements.h>
#define subrem_view_edit_menu_MAX_LABEL_LENGTH 12
#define FRAME_HEIGHT 12
struct SubRemViewEditMenu {
View* view;
SubRemViewEditMenuCallback callback;
void* context;
};
typedef struct {
FuriString* label;
FuriString* file_path;
SubRemLoadSubState sub_state;
uint8_t chusen;
} SubRemViewEditMenuModel;
void subrem_view_edit_menu_set_callback(
SubRemViewEditMenu* subrem_view_edit_menu,
SubRemViewEditMenuCallback callback,
void* context) {
furi_assert(subrem_view_edit_menu);
subrem_view_edit_menu->callback = callback;
subrem_view_edit_menu->context = context;
}
void subrem_view_edit_menu_add_data_to_show(
SubRemViewEditMenu* subrem_view_edit_remote,
uint8_t index,
FuriString* label,
FuriString* path,
SubRemLoadSubState state) {
furi_assert(subrem_view_edit_remote);
with_view_model(
subrem_view_edit_remote->view,
SubRemViewEditMenuModel * model,
{
model->chusen = index;
if(!furi_string_empty(label)) {
furi_string_set(model->label, label);
} else {
furi_string_set(model->label, "Empty label");
}
furi_string_set(model->file_path, path);
model->sub_state = state;
},
true);
}
uint8_t subrem_view_edit_menu_get_index(SubRemViewEditMenu* subrem_view_edit_remote) {
furi_assert(subrem_view_edit_remote);
uint8_t index;
with_view_model(
subrem_view_edit_remote->view,
SubRemViewEditMenuModel * model,
{ index = model->chusen; },
true);
return index;
}
void subrem_view_edit_menu_draw(Canvas* canvas, SubRemViewEditMenuModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_clear(canvas);
// Draw bottom btn
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Back");
elements_button_center(canvas, "Edit");
elements_button_right(canvas, "Save");
// Draw top frame
canvas_draw_line(canvas, 1, 0, 125, 0);
canvas_draw_box(canvas, 0, 1, 127, FRAME_HEIGHT - 2);
canvas_draw_line(canvas, 1, FRAME_HEIGHT - 1, 125, FRAME_HEIGHT - 1);
canvas_set_color(canvas, ColorWhite);
// Draw btn name
canvas_set_font(canvas, FontPrimary);
switch(model->chusen) {
case SubRemSubKeyNameUp:
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "UP");
break;
case SubRemSubKeyNameDown:
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "DOWN");
break;
case SubRemSubKeyNameLeft:
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "LEFT");
break;
case SubRemSubKeyNameRight:
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "RIGHT");
break;
case SubRemSubKeyNameOk:
canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "OK");
break;
default:
break;
}
// Draw Label
canvas_set_font(canvas, FontSecondary);
elements_text_box(
canvas,
38,
2,
127 - 38,
FRAME_HEIGHT,
AlignCenter,
AlignBottom,
furi_string_empty(model->label) ? "Empty label" : furi_string_get_cstr(model->label),
true);
// Draw arrow
canvas_set_color(canvas, ColorBlack);
if(model->chusen != 0) {
canvas_draw_icon(canvas, 119, 13, &I_Pin_arrow_up_7x9);
}
if(model->chusen != 4) {
canvas_draw_icon_ex(canvas, 119, 42, &I_Pin_arrow_up_7x9, IconRotation180);
}
// Draw file_path
if(model->sub_state == SubRemLoadSubStateOK) {
canvas_set_font(canvas, FontSecondary);
elements_text_box(
canvas,
1,
FRAME_HEIGHT + 1,
118,
(63 - FRAME_HEIGHT * 2),
AlignLeft,
AlignTop,
furi_string_get_cstr(model->file_path),
false);
} else if(furi_string_empty(model->file_path)) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 1, FRAME_HEIGHT * 2 - 2, "Button not set");
} else {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 1, FRAME_HEIGHT * 2 - 2, "ERR:");
canvas_set_font(canvas, FontSecondary);
switch(model->sub_state) {
case SubRemLoadSubStateErrorNoFile:
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "File not found");
break;
case SubRemLoadSubStateErrorFreq:
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Bad frequency");
break;
case SubRemLoadSubStateErrorMod:
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Bad modulation");
break;
case SubRemLoadSubStateErrorProtocol:
canvas_draw_str(canvas, 26, FRAME_HEIGHT * 2 - 2, "Unsupported protocol");
break;
default:
break;
}
elements_text_box(
canvas,
1,
FRAME_HEIGHT * 2,
118,
30,
AlignLeft,
AlignTop,
furi_string_get_cstr(model->file_path),
false);
}
}
bool subrem_view_edit_menu_input(InputEvent* event, void* context) {
furi_assert(context);
SubRemViewEditMenu* subrem_view_edit_menu = context;
if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
event->type == InputTypeShort) {
subrem_view_edit_menu->callback(
SubRemCustomEventViewEditMenuBack, subrem_view_edit_menu->context);
return true;
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
with_view_model(
subrem_view_edit_menu->view,
SubRemViewEditMenuModel * model,
{
if(model->chusen > 0) {
model->chusen -= 1;
};
},
true);
subrem_view_edit_menu->callback(
SubRemCustomEventViewEditMenuUP, subrem_view_edit_menu->context);
return true;
} else if(event->key == InputKeyDown && event->type == InputTypeShort) {
with_view_model(
subrem_view_edit_menu->view,
SubRemViewEditMenuModel * model,
{
if(model->chusen < 4) {
model->chusen += 1;
};
},
true);
subrem_view_edit_menu->callback(
SubRemCustomEventViewEditMenuDOWN, subrem_view_edit_menu->context);
return true;
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
subrem_view_edit_menu->callback(
SubRemCustomEventViewEditMenuEdit, subrem_view_edit_menu->context);
return true;
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
subrem_view_edit_menu->callback(
SubRemCustomEventViewEditMenuSave, subrem_view_edit_menu->context);
return true;
}
return true;
}
void subrem_view_edit_menu_enter(void* context) {
furi_assert(context);
}
void subrem_view_edit_menu_exit(void* context) {
furi_assert(context);
}
SubRemViewEditMenu* subrem_view_edit_menu_alloc() {
SubRemViewEditMenu* subrem_view_edit_menu = malloc(sizeof(SubRemViewEditMenu));
// View allocation and configuration
subrem_view_edit_menu->view = view_alloc();
view_allocate_model(
subrem_view_edit_menu->view, ViewModelTypeLocking, sizeof(SubRemViewEditMenuModel));
view_set_context(subrem_view_edit_menu->view, subrem_view_edit_menu);
view_set_draw_callback(
subrem_view_edit_menu->view, (ViewDrawCallback)subrem_view_edit_menu_draw);
view_set_input_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_input);
view_set_enter_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_enter);
view_set_exit_callback(subrem_view_edit_menu->view, subrem_view_edit_menu_exit);
with_view_model(
subrem_view_edit_menu->view,
SubRemViewEditMenuModel * model,
{
model->label = furi_string_alloc(); // furi_string_alloc_set_str("LABEL");
model->file_path = furi_string_alloc(); // furi_string_alloc_set_str("FILE_PATH");
model->chusen = 0;
},
true);
return subrem_view_edit_menu;
}
void subrem_view_edit_menu_free(SubRemViewEditMenu* subghz_edit_menu) {
furi_assert(subghz_edit_menu);
with_view_model(
subghz_edit_menu->view,
SubRemViewEditMenuModel * model,
{
furi_string_free(model->label);
furi_string_free(model->file_path);
},
true);
view_free(subghz_edit_menu->view);
free(subghz_edit_menu);
}
View* subrem_view_edit_menu_get_view(SubRemViewEditMenu* subrem_view_edit_menu) {
furi_assert(subrem_view_edit_menu);
return subrem_view_edit_menu->view;
}

View File

@@ -1,29 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subrem_custom_event.h"
#include "../helpers/subrem_presets.h"
typedef struct SubRemViewEditMenu SubRemViewEditMenu;
typedef void (*SubRemViewEditMenuCallback)(SubRemCustomEvent event, void* context);
void subrem_view_edit_menu_set_callback(
SubRemViewEditMenu* subrem_view_edit_menu,
SubRemViewEditMenuCallback callback,
void* context);
SubRemViewEditMenu* subrem_view_edit_menu_alloc();
void subrem_view_edit_menu_free(SubRemViewEditMenu* subrem_view_edit_menu);
View* subrem_view_edit_menu_get_view(SubRemViewEditMenu* subrem_view_edit_menu);
void subrem_view_edit_menu_add_data_to_show(
SubRemViewEditMenu* subrem_view_edit_remote,
uint8_t index,
FuriString* label,
FuriString* path,
SubRemLoadSubState state);
uint8_t subrem_view_edit_menu_get_index(SubRemViewEditMenu* subrem_view_edit_remote);

View File

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

View File

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

View File

@@ -10,7 +10,7 @@
#include "hmac_sha256.h"
#include "micro-ecc/uECC.h"
#define TAG "U2F"
#define TAG "U2f"
#define WORKER_TAG TAG "Worker"
#define U2F_CMD_REGISTER 0x01

View File

@@ -5,7 +5,7 @@
#include <furi_hal_random.h>
#include <flipper_format/flipper_format.h>
#define TAG "U2F"
#define TAG "U2f"
#define U2F_DATA_FOLDER EXT_PATH("u2f/")
#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der"

View File

@@ -10,7 +10,7 @@
#include <furi_hal_console.h>
#define TAG "U2FHID"
#define TAG "U2fHid"
#define WORKER_TAG TAG "Worker"
#define U2F_HID_MAX_PAYLOAD_LEN ((HID_U2F_PACKET_LEN - 7) + 128 * (HID_U2F_PACKET_LEN - 5))

View File

@@ -101,7 +101,6 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
char buffer[20];
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
// TODO FL-3515: never do that, may cause visual glitches
view_port_set_width(
desktop->clock_viewport,
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
@@ -126,8 +125,6 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
return true;
case DesktopGlobalAfterAppFinished:
animation_manager_load_and_continue_animation(desktop->animation_manager);
// TODO FL-3497: Implement a message mechanism for loading settings and (optionally)
// locking and unlocking
DESKTOP_SETTINGS_LOAD(&desktop->settings);
desktop_clock_reconfigure(desktop);

View File

@@ -4,7 +4,6 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_rtc.h>
#include <stdint.h>
#include <u8g2_glue.h>

View File

@@ -290,7 +290,8 @@ void elements_multiline_text_aligned(
} else if((y + font_height) > canvas_height(canvas)) {
line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
} else {
line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
// Account for the added "-" in length
line = furi_string_alloc_printf("%.*s-\n", chars_fit - 1, start);
}
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));
furi_string_free(line);

View File

@@ -361,10 +361,11 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
furi_assert(view_port);
furi_check(layer < GuiLayerMAX);
// Only fullscreen supports Vertical orientation for now
furi_assert(
ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port);
furi_check(
(layer == GuiLayerFullscreen) ||
((view_port->orientation != ViewPortOrientationVertical) &&
(view_port->orientation != ViewPortOrientationVerticalFlip)));
((view_port_orientation != ViewPortOrientationVertical) &&
(view_port_orientation != ViewPortOrientationVerticalFlip)));
gui_lock(gui);
// Verify that view port is not yet added

View File

@@ -325,6 +325,7 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model)
static void text_input_handle_left(TextInput* text_input, TextInputModel* model) {
UNUSED(text_input);
if(model->cursor_select) {
model->clear_default_text = false;
if(model->cursor_pos > 0) {
model->cursor_pos = CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u);
}
@@ -338,6 +339,7 @@ static void text_input_handle_left(TextInput* text_input, TextInputModel* model)
static void text_input_handle_right(TextInput* text_input, TextInputModel* model) {
UNUSED(text_input);
if(model->cursor_select) {
model->clear_default_text = false;
model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u);
} else if(model->selected_column < get_row_size(model->selected_row) - 1) {
model->selected_column++;
@@ -347,7 +349,10 @@ static void text_input_handle_right(TextInput* text_input, TextInputModel* model
}
static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, InputType type) {
if(model->cursor_select) return;
if(model->cursor_select) {
model->clear_default_text = !model->clear_default_text;
return;
}
bool shift = type == InputTypeLong;
bool repeat = type == InputTypeRepeat;
char selected = get_selected_char(model);

View File

@@ -272,7 +272,6 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
} else if(view_dispatcher->navigation_event_callback) {
// Dispatch navigation event
if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) {
// TODO FL-3514: should we allow view_dispatcher to stop without navigation_event_callback?
view_dispatcher_stop(view_dispatcher);
return;
}

View File

@@ -2,13 +2,10 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_rtc.h>
#include "gui.h"
#include "gui_i.h"
// TODO FL-3498: add mutex to view_port ops
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
_Static_assert(
(ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&
@@ -95,52 +92,73 @@ ViewPort* view_port_alloc() {
ViewPort* view_port = malloc(sizeof(ViewPort));
view_port->orientation = ViewPortOrientationHorizontal;
view_port->is_enabled = true;
view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
return view_port;
}
void view_port_free(ViewPort* view_port) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui == NULL);
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
furi_mutex_free(view_port->mutex);
free(view_port);
}
void view_port_set_width(ViewPort* view_port, uint8_t width) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->width = width;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
uint8_t view_port_get_width(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->width;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
uint8_t width = view_port->width;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return width;
}
void view_port_set_height(ViewPort* view_port, uint8_t height) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->height = height;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
uint8_t view_port_get_height(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->height;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
uint8_t height = view_port->height;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return height;
}
void view_port_enabled_set(ViewPort* view_port, bool enabled) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
if(view_port->is_enabled != enabled) {
view_port->is_enabled = enabled;
if(view_port->gui) gui_update(view_port->gui);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
bool view_port_is_enabled(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->is_enabled;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
bool is_enabled = view_port->is_enabled;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return is_enabled;
}
void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->draw_callback = callback;
view_port->draw_callback_context = context;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_input_callback_set(
@@ -148,34 +166,43 @@ void view_port_input_callback_set(
ViewPortInputCallback callback,
void* context) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->input_callback = callback;
view_port->input_callback_context = context;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_update(ViewPort* view_port) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_gui_set(ViewPort* view_port, Gui* gui) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->gui = gui;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_draw(ViewPort* view_port, Canvas* canvas) {
furi_assert(view_port);
furi_assert(canvas);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui);
if(view_port->draw_callback) {
view_port_setup_canvas_orientation(view_port, canvas);
view_port->draw_callback(canvas, view_port->draw_callback_context);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_input(ViewPort* view_port, InputEvent* event) {
furi_assert(view_port);
furi_assert(event);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui);
if(view_port->input_callback) {
@@ -183,13 +210,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) {
view_port_map_input(event, orientation);
view_port->input_callback(event, view_port->input_callback_context);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->orientation = orientation;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) {
return view_port->orientation;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
ViewPortOrientation orientation = view_port->orientation;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return orientation;
}

View File

@@ -10,6 +10,7 @@
struct ViewPort {
Gui* gui;
FuriMutex* mutex;
bool is_enabled;
ViewPortOrientation orientation;

View File

@@ -15,6 +15,8 @@
#include <stdio.h>
#include <m-dict.h>
#include <bt/bt_service/bt.h>
#define TAG "RpcSrv"
typedef enum {
@@ -316,6 +318,15 @@ static int32_t rpc_session_worker(void* context) {
session->closed_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
if(session->owner == RpcOwnerBle) {
// Disconnect BLE session
FURI_LOG_E("RPC", "BLE session closed due to a decode error");
Bt* bt = furi_record_open(RECORD_BT);
bt_set_profile(bt, BtProfileSerial);
furi_record_close(RECORD_BT);
FURI_LOG_E("RPC", "Finished disconnecting the BLE session");
}
}
}

View File

@@ -242,6 +242,23 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context)
rpc_send_and_release(session, &response);
}
static bool rpc_system_storage_list_filter(
const PB_Storage_ListRequest* request,
const FileInfo* fileinfo,
const char* name) {
bool result = false;
do {
if(!path_contains_only_ascii(name)) break;
if(request->filter_max_size) {
if(fileinfo->size > request->filter_max_size) break;
}
result = true;
} while(false);
return result;
}
static void rpc_system_storage_list_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
@@ -253,9 +270,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
RpcSession* session = rpc_storage->session;
furi_assert(session);
const PB_Storage_ListRequest* list_request = &request->content.storage_list_request;
rpc_system_storage_reset_state(rpc_storage, session, true);
if(!strcmp(request->content.storage_list_request.path, "/")) {
if(!strcmp(list_request->path, "/")) {
rpc_system_storage_list_root(request, context);
return;
}
@@ -271,7 +290,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
};
PB_Storage_ListResponse* list = &response.content.storage_list_response;
bool include_md5 = request->content.storage_list_request.include_md5;
bool include_md5 = list_request->include_md5;
FuriString* md5 = furi_string_alloc();
FuriString* md5_path = furi_string_alloc();
File* file = storage_file_alloc(fs_api);
@@ -279,7 +298,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
bool finish = false;
int i = 0;
if(!storage_dir_open(dir, request->content.storage_list_request.path)) {
if(!storage_dir_open(dir, list_request->path)) {
response.command_status = rpc_system_storage_get_file_error(dir);
response.which_content = PB_Main_empty_tag;
finish = true;
@@ -289,7 +308,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
FileInfo fileinfo;
char* name = malloc(MAX_NAME_LENGTH + 1);
if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
if(path_contains_only_ascii(name)) {
if(rpc_system_storage_list_filter(list_request, &fileinfo, name)) {
if(i == COUNT_OF(list->file)) {
list->file_count = i;
response.has_next = true;
@@ -303,11 +322,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
list->file[i].name = name;
if(include_md5 && !file_info_is_dir(&fileinfo)) {
furi_string_printf( //-V576
md5_path,
"%s/%s",
request->content.storage_list_request.path,
name);
furi_string_printf(md5_path, "%s/%s", list_request->path, name); //-V576
if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) {
char* md5sum = list->file[i].md5sum;

View File

@@ -12,7 +12,7 @@
#define ICON_SD_MOUNTED &I_SDcardMounted_11x8
#define ICON_SD_ERROR &I_SDcardFail_11x8
#define TAG RECORD_STORAGE
#define TAG "Storage"
static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
furi_assert(canvas);

View File

@@ -334,12 +334,20 @@ const char* storage_file_get_error_desc(File* file);
*/
FS_Error storage_sd_format(Storage* api);
/** Will unmount the SD card
/** Will unmount the SD card.
* Will return FSE_NOT_READY if the SD card is not mounted.
* Will return FSE_DENIED if there are open files on the SD card.
* @param api pointer to the api
* @return FS_Error operation result
*/
FS_Error storage_sd_unmount(Storage* api);
/** Will mount the SD card
* @param api pointer to the api
* @return FS_Error operation result
*/
FS_Error storage_sd_mount(Storage* api);
/** Retrieves SD card information
* @param api pointer to the api
* @param info pointer to the info

View File

@@ -11,7 +11,7 @@
#define MAX_EXT_LEN 16
#define FILE_BUFFER_SIZE 512
#define TAG "StorageAPI"
#define TAG "StorageApi"
#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();
@@ -781,6 +781,14 @@ FS_Error storage_sd_unmount(Storage* storage) {
return S_RETURN_ERROR;
}
FS_Error storage_sd_mount(Storage* storage) {
S_API_PROLOGUE;
SAData data = {};
S_API_MESSAGE(StorageCommandSDMount);
S_API_EPILOGUE;
return S_RETURN_ERROR;
}
FS_Error storage_sd_info(Storage* storage, SDInfo* info) {
S_API_PROLOGUE;
SAData data = {

View File

@@ -1,6 +1,8 @@
#include "storage_glue.h"
#include <furi_hal.h>
#define TAG "StorageGlue"
/****************** storage file ******************/
void storage_file_init(StorageFile* obj) {
@@ -149,3 +151,8 @@ bool storage_pop_storage_file(File* file, StorageData* storage) {
return result;
}
size_t storage_open_files_count(StorageData* storage) {
size_t count = StorageFileList_size(storage->files);
return count;
}

View File

@@ -68,6 +68,8 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage);
void storage_push_storage_file(File* file, FuriString* path, StorageData* storage);
bool storage_pop_storage_file(File* file, StorageData* storage);
size_t storage_open_files_count(StorageData* storage);
#ifdef __cplusplus
}
#endif

View File

@@ -141,6 +141,7 @@ typedef enum {
StorageCommandSDInfo,
StorageCommandSDStatus,
StorageCommandCommonResolvePath,
StorageCommandSDMount,
} StorageCommand;
typedef struct {

View File

@@ -418,12 +418,38 @@ static FS_Error storage_process_sd_format(Storage* app) {
static FS_Error storage_process_sd_unmount(Storage* app) {
FS_Error ret = FSE_OK;
if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {
ret = FSE_NOT_READY;
} else {
sd_unmount_card(&app->storage[ST_EXT]);
storage_data_timestamp(&app->storage[ST_EXT]);
}
do {
StorageData* storage = &app->storage[ST_EXT];
if(storage_data_status(storage) == StorageStatusNotReady) {
ret = FSE_NOT_READY;
break;
}
if(storage_open_files_count(storage)) {
ret = FSE_DENIED;
break;
}
sd_unmount_card(storage);
storage_data_timestamp(storage);
} while(false);
return ret;
}
static FS_Error storage_process_sd_mount(Storage* app) {
FS_Error ret = FSE_OK;
do {
StorageData* storage = &app->storage[ST_EXT];
if(storage_data_status(storage) != StorageStatusNotReady) {
ret = FSE_NOT_READY;
break;
}
ret = sd_mount_card(storage, true);
storage_data_timestamp(storage);
} while(false);
return ret;
}
@@ -630,6 +656,9 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
case StorageCommandSDUnmount:
message->return_data->error_value = storage_process_sd_unmount(app);
break;
case StorageCommandSDMount:
message->return_data->error_value = storage_process_sd_mount(app);
break;
case StorageCommandSDInfo:
message->return_data->error_value =
storage_process_sd_info(app, message->data->sdinfo.info);

View File

@@ -24,7 +24,7 @@ static FS_Error storage_ext_parse_error(SDError error);
/******************* Core Functions *******************/
static bool sd_mount_card(StorageData* storage, bool notify) {
static bool sd_mount_card_internal(StorageData* storage, bool notify) {
bool result = false;
uint8_t counter = sd_max_mount_retry_count();
uint8_t bsp_result;
@@ -106,6 +106,32 @@ FS_Error sd_unmount_card(StorageData* storage) {
return storage_ext_parse_error(error);
}
FS_Error sd_mount_card(StorageData* storage, bool notify) {
sd_mount_card_internal(storage, notify);
FS_Error error;
if(storage->status != StorageStatusOK) {
FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage));
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_error(notification);
furi_record_close(RECORD_NOTIFICATION);
}
error = FSE_INTERNAL;
} else {
FURI_LOG_I(TAG, "card mounted");
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_success(notification);
furi_record_close(RECORD_NOTIFICATION);
}
error = FSE_OK;
}
return error;
}
FS_Error sd_format_card(StorageData* storage) {
#ifdef FURI_RAM_EXEC
UNUSED(storage);
@@ -222,25 +248,8 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
if(sd_data->sd_was_present) {
if(hal_sd_detect()) {
FURI_LOG_I(TAG, "card detected");
sd_mount_card(storage, notify);
if(storage->status != StorageStatusOK) {
FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage));
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_error(notification);
furi_record_close(RECORD_NOTIFICATION);
}
} else {
FURI_LOG_I(TAG, "card mounted");
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_success(notification);
furi_record_close(RECORD_NOTIFICATION);
}
}
sd_data->sd_was_present = false;
sd_mount_card(storage, notify);
if(!hal_sd_detect()) {
FURI_LOG_I(TAG, "card removed while mounting");

View File

@@ -8,6 +8,7 @@ extern "C" {
#endif
void storage_ext_init(StorageData* storage);
FS_Error sd_mount_card(StorageData* storage, bool notify);
FS_Error sd_unmount_card(StorageData* storage);
FS_Error sd_format_card(StorageData* storage);
FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info);

View File

@@ -31,12 +31,24 @@ void storage_settings_scene_start_on_enter(void* context) {
StorageSettingsStartSubmenuIndexSDInfo,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Unmount SD Card",
StorageSettingsStartSubmenuIndexUnmount,
storage_settings_scene_start_submenu_callback,
app);
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status != FSE_OK) {
submenu_add_item(
submenu,
"Mount SD Card",
StorageSettingsStartSubmenuIndexUnmount,
storage_settings_scene_start_submenu_callback,
app);
} else {
submenu_add_item(
submenu,
"Unmount SD Card",
StorageSettingsStartSubmenuIndexUnmount,
storage_settings_scene_start_submenu_callback,
app);
}
submenu_add_item(
submenu,
"Format SD Card",

View File

@@ -12,13 +12,17 @@ void storage_settings_scene_unmount_confirm_on_enter(void* context) {
DialogEx* dialog_ex = app->dialog_ex;
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status == FSE_NOT_READY) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_header(dialog_ex, "Mount SD Card?", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
dialog_ex_set_center_button_text(dialog_ex, "Ok");
dialog_ex,
"This may turn off power\nfor external modules",
64,
32,
AlignCenter,
AlignCenter);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Mount");
} else {
dialog_ex_set_header(dialog_ex, "Unmount SD Card?", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(

View File

@@ -9,22 +9,41 @@ static void
void storage_settings_scene_unmounted_on_enter(void* context) {
StorageSettings* app = context;
FS_Error error = storage_sd_unmount(app->fs_api);
DialogEx* dialog_ex = app->dialog_ex;
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status == FSE_NOT_READY) {
FS_Error error = storage_sd_mount(app->fs_api);
if(error == FSE_OK) {
dialog_ex_set_header(dialog_ex, "SD Card Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Flipper can use\nSD card now.", 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_green_100);
} else {
dialog_ex_set_header(dialog_ex, "Cannot Mount SD Card", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_red_100);
}
} else {
FS_Error error = storage_sd_unmount(app->fs_api);
if(error == FSE_OK) {
dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_green_100);
} else {
dialog_ex_set_header(
dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_red_100);
}
}
dialog_ex_set_center_button_text(dialog_ex, "OK");
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
if(error == FSE_OK) {
dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_green_100);
} else {
dialog_ex_set_header(dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_red_100);
}
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback);

Some files were not shown because too many files have changed in this diff Show More