mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-01 22:08:55 -07:00
Merge branch 'UFW_dev' into subghz_remote_new
This commit is contained in:
+1
-1
@@ -85,7 +85,7 @@ steps:
|
||||
- zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e
|
||||
- zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n
|
||||
- zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG}
|
||||
- tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts debug
|
||||
- tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts
|
||||
- rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG}
|
||||
- rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG}
|
||||
- rm -rf artifacts-default/f7-update-${DRONE_TAG}
|
||||
|
||||
Vendored
+8
-8
@@ -13,7 +13,7 @@
|
||||
"label": "[Debug] Build",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (ST-Link)",
|
||||
@@ -25,7 +25,7 @@
|
||||
"label": "[Debug] Flash (ST-Link)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (blackmagic)",
|
||||
@@ -37,7 +37,7 @@
|
||||
"label": "[Debug] Flash (blackmagic)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash_blackmagic"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_blackmagic"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (JLink)",
|
||||
@@ -49,7 +49,7 @@
|
||||
"label": "[Debug] Flash (JLink)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 jflash"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 jflash"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Build update bundle",
|
||||
@@ -61,7 +61,7 @@
|
||||
"label": "[Debug] Build update bundle",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt updater_package"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack updater_package"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Build updater",
|
||||
@@ -73,13 +73,13 @@
|
||||
"label": "[Debug] Build updater",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt updater_all"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack updater_all"
|
||||
},
|
||||
{
|
||||
"label": "[Debug] Flash (USB, w/o resources)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash_usb"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_usb"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (USB, w/o resources)",
|
||||
@@ -97,7 +97,7 @@
|
||||
"label": "[Debug] Flash (USB, with resources)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash_usb_full"
|
||||
"command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_usb_full"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (USB, with resources)",
|
||||
|
||||
+29
-1
@@ -1,5 +1,33 @@
|
||||
### New changes
|
||||
### New changes
|
||||
* **Warning! After installing, Desktop settings (Favoutite apps, PIN Code, AutoLock time..) will be resetted to default due to OFW changes, Please set your PIN code, Favourite apps again in Settings->Desktop**
|
||||
* New way of changing device name -> **Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
|
||||
* Plugins: BadBT plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb)
|
||||
* Plugins: WiFi Marauder -> Added sniff pmkid on selected aps from 0.10.4 update (by @clipboard1)
|
||||
* Plugins: SubGHz Bruteforcer -> Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay
|
||||
* Plugins: UART Terminal -> Fix crashes on plugin load with RX connected
|
||||
* NFC: Mifare mini with SAK 0x89 support
|
||||
* SubGHz: **CAME Atomo - Add manually support and custom buttons support**
|
||||
* SubGHz: Fix crashes when deleting signals using right arrow button in `Read` mode
|
||||
* SubGHz: Restore Rx indication after deletion after Memory is FULL (by @wosk | PR #464)
|
||||
* SubGHz: **App refactoring** (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461)
|
||||
* SubGHz: Using scene manager functions in DecodeRAW (by @gid9798 | PR #462)
|
||||
* SubGHz: Protocols and custom buttons refactoring (by @gid9798 | PR #465)
|
||||
* SubGHz: Move `counter increase` setting out of debug, change max value
|
||||
* GUI: Submenu locked elements (by @Willy-JL and @giacomoferretti)
|
||||
* GUI: Text Input improvements, added cursor and ability to set minimal length (by @Willy-JL)
|
||||
* BT API: Functions that allow to change bt mac address and device broadcasted name (by @Willy-JL and XFW contributors)
|
||||
* Infrared: `External output` move out of debug and add power option for external modules
|
||||
* Infrared: Updated universal remote assets (by @amec0e | PR #474)
|
||||
* Extra pack: Some app fixes
|
||||
* FBT: Fix vscode example config for debug builds - please run `./fbt vscode_dist` again if you had issues with debug builds size
|
||||
* OFW PR 2316: NFC V support (by @g3gg0 & @nvx)
|
||||
* OFW PR 2669: nfc: Fix MFUL tearing flags read (by @GMMan)
|
||||
* OFW PR 2666: BadUSB: Add fr-FR-mac key layout (by @FelixLgr)
|
||||
* OFW: api: added lib/nfc/protocols/nfc_util.h
|
||||
* OFW: fix PIN retry count reset on reboot
|
||||
* OFW: fbt: allow strings for fap_version field in app manifests
|
||||
* OFW: Rpc: add desktop service. Desktop: refactor locking routine. **Now PIN lock is actually cannot be bypassed by reboot!** / **Desktop settings will be reset, please set your PIN and favourite apps again!**
|
||||
* OFW: Part 2 of hooking C2 IPC
|
||||
* OFW: ble: attempt to handle hardfaulted c2
|
||||
* OFW: Add Mfkey32 application
|
||||
* OFW: Added DigitalSequence and PulseReader
|
||||
|
||||
@@ -43,7 +43,8 @@ Our Discord Community:
|
||||
* Picopass/iClass plugin included in releases
|
||||
* Recompiled IR TV Universal Remote for ALL buttons
|
||||
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
|
||||
* Customizable Flipper name
|
||||
* Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
|
||||
* Text Input UI element -> Cursor feature (by @Willy-JL)
|
||||
- BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
||||
- Sub-GHz -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307)
|
||||
- Sub-GHz -> `Add manually` menu extended with new protocols
|
||||
@@ -58,8 +59,8 @@ Our Discord Community:
|
||||
* SubGHz -> Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk)
|
||||
* Sub-GHz -> External CC1101 module support (Hardware SPI used)
|
||||
* SubGHz -> **Hold right in received signal list to delete selected signal**
|
||||
* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0** - now you can use arrow buttons to send signal with different button code
|
||||
* SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis)
|
||||
* SubGHz -> **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
|
||||
* SubGHz -> 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)
|
||||
* SubGHz -> Debug mode counter increase settings (+1 -> +5, +10, default: +1)
|
||||
* SubGHz -> Debug PIN output settings for protocol development
|
||||
* Infrared -> Debug TX PIN output settings
|
||||
@@ -75,6 +76,7 @@ Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported man
|
||||
|
||||
Encoders or sending made by @xMasterX:
|
||||
- Nero Radio 57bit (+ 56bit encoder improvements)
|
||||
- CAME 12bit/24bit encoder fixes (already merged in OFW)
|
||||
- Keeloq: HCS101
|
||||
- Keeloq: AN-Motors
|
||||
- Keeloq: JCM Tech
|
||||
@@ -89,20 +91,20 @@ Encoders or sending made by @xMasterX:
|
||||
- Keeloq: CAME Space
|
||||
- Keeloq: Aprimatic (model TR and similar)
|
||||
|
||||
Encoders/sending made by @Eng1n33r & @xMasterX:
|
||||
- CAME Atomo
|
||||
- Nice Flor S
|
||||
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: Nano#8998)]
|
||||
- Keeloq: BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)] -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- Star Line
|
||||
- Security+ v1 & v2
|
||||
- Security+ v1 & v2 (encoders was made in OFW)
|
||||
|
||||
Encoders made by @assasinfil & @xMasterX:
|
||||
- Somfy Telis
|
||||
- Somfy Telis -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- Somfy Keytis
|
||||
- KingGates Stylo 4k
|
||||
- Alutech AT-4N
|
||||
- Nice ON2E (Nice One)
|
||||
- Alutech AT-4N -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- Nice ON2E (Nice One) -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
|
||||
## Please support development of the project
|
||||
The majority of this project is developed and maintained by me, @xMasterX.
|
||||
@@ -162,6 +164,7 @@ You can support us by using links or addresses below:
|
||||
- **ProtoView** [(by antirez)](https://github.com/antirez/protoview)
|
||||
- **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe)
|
||||
- IR Scope [(by kallanreed)](https://github.com/DarkFlippers/unleashed-firmware/pull/407)
|
||||
- BadBT plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb)
|
||||
|
||||
Games:
|
||||
- DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
App(
|
||||
appid="bad_bt",
|
||||
name="Bad BT",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="bad_bt_app",
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_libs=["assets"],
|
||||
fap_category="Tools",
|
||||
fap_icon="images/badbt_10px.png",
|
||||
fap_icon_assets="images",
|
||||
fap_icon_assets_symbol="bad_bt",
|
||||
)
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
#include "bad_bt_app.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
|
||||
#include <bt/bt_service/bt_i.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
|
||||
#define BAD_BT_SETTINGS_FILE_NAME ".badbt.settings"
|
||||
#define BAD_BT_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badbt")
|
||||
#define BAD_BT_APP_PATH_BOUND_KEYS_FILE BAD_BT_APP_PATH_BOUND_KEYS_FOLDER "/.badbt.keys"
|
||||
|
||||
#define BAD_BT_SETTINGS_PATH BAD_BT_APP_BASE_CONFIG_FOLDER "/" BAD_BT_SETTINGS_FILE_NAME
|
||||
|
||||
static bool bad_bt_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
BadBtApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool bad_bt_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BadBtApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void bad_bt_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BadBtApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void bad_bt_load_settings(BadBtApp* app) {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
strcpy(app->config.bt_name, "");
|
||||
memcpy(
|
||||
app->config.bt_mac,
|
||||
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
|
||||
BAD_BT_MAC_ADDRESS_LEN);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
if(flipper_format_file_open_existing(file, BAD_BT_SETTINGS_PATH)) {
|
||||
FuriString* tmp_str = furi_string_alloc();
|
||||
if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
}
|
||||
if(!flipper_format_read_bool(file, "BT_Remember", &(app->bt_remember), 1)) {
|
||||
app->bt_remember = false;
|
||||
}
|
||||
if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) {
|
||||
strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str));
|
||||
} else {
|
||||
strcpy(app->config.bt_name, "");
|
||||
}
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN)) {
|
||||
memcpy(
|
||||
app->config.bt_mac,
|
||||
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
|
||||
BAD_BT_MAC_ADDRESS_LEN);
|
||||
}
|
||||
furi_string_free(tmp_str);
|
||||
flipper_format_file_close(file);
|
||||
}
|
||||
flipper_format_free(file);
|
||||
|
||||
if(!furi_string_empty(app->keyboard_layout)) {
|
||||
FileInfo layout_file_info;
|
||||
FS_Error file_check_err = storage_common_stat(
|
||||
storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
|
||||
if(file_check_err != FSE_OK) {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
return;
|
||||
}
|
||||
if(layout_file_info.size != 256) {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
}
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void bad_bt_save_settings(BadBtApp* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
if(flipper_format_file_open_always(file, BAD_BT_SETTINGS_PATH)) {
|
||||
flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout);
|
||||
flipper_format_write_bool(file, "BT_Remember", &(app->bt_remember), 1);
|
||||
flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name);
|
||||
flipper_format_write_hex(
|
||||
file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN);
|
||||
flipper_format_file_close(file);
|
||||
}
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void bad_bt_reload_worker(BadBtApp* app) {
|
||||
bad_bt_script_close(app->bad_bt_script);
|
||||
app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app);
|
||||
bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout);
|
||||
}
|
||||
|
||||
void bad_kb_config_refresh_menu(BadBtApp* app) {
|
||||
scene_manager_next_scene(app->scene_manager, BadBtSceneConfig);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
}
|
||||
|
||||
int32_t bad_bt_config_switch_mode(BadBtApp* app) {
|
||||
bad_bt_reload_worker(app);
|
||||
furi_hal_bt_start_advertising();
|
||||
bad_kb_config_refresh_menu(app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bad_bt_config_switch_remember_mode(BadBtApp* app) {
|
||||
if(app->bt_remember) {
|
||||
furi_hal_bt_set_profile_pairing_method(
|
||||
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
|
||||
bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS);
|
||||
bt_enable_peer_key_update(app->bt);
|
||||
} else {
|
||||
furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
|
||||
bt_set_profile_mac_address(app->bt, app->config.bt_mac);
|
||||
bt_disable_peer_key_update(app->bt);
|
||||
}
|
||||
bad_bt_reload_worker(app);
|
||||
}
|
||||
|
||||
int32_t bad_bt_connection_init(BadBtApp* app) {
|
||||
// Set original name and mac address in prev config
|
||||
strcpy(
|
||||
app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard));
|
||||
|
||||
memcpy(app->prev_config.bt_mac, furi_hal_version_get_ble_mac(), BAD_BT_MAC_ADDRESS_LEN);
|
||||
|
||||
bt_timeout = bt_hid_delays[LevelRssi39_0];
|
||||
bt_disconnect(app->bt);
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_storage_path(app->bt, BAD_BT_APP_PATH_BOUND_KEYS_FILE);
|
||||
if(strcmp(app->config.bt_name, "") != 0) {
|
||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name);
|
||||
}
|
||||
if(app->bt_remember) {
|
||||
furi_hal_bt_set_profile_mac_addr(
|
||||
FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS);
|
||||
furi_hal_bt_set_profile_pairing_method(
|
||||
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
|
||||
} else {
|
||||
if(memcmp(
|
||||
app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) !=
|
||||
0) {
|
||||
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac);
|
||||
}
|
||||
furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
|
||||
}
|
||||
bt_set_profile(app->bt, BtProfileHidKeyboard);
|
||||
if(strcmp(app->config.bt_name, "") == 0) {
|
||||
strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard));
|
||||
}
|
||||
if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) ==
|
||||
0) {
|
||||
memcpy(
|
||||
app->config.bt_mac,
|
||||
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
|
||||
BAD_BT_MAC_ADDRESS_LEN);
|
||||
}
|
||||
|
||||
furi_hal_bt_start_advertising();
|
||||
if(app->bt_remember) {
|
||||
bt_enable_peer_key_update(app->bt);
|
||||
} else {
|
||||
bt_disable_peer_key_update(app->bt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bad_bt_connection_deinit(BadBtApp* app) {
|
||||
bt_disconnect(app->bt);
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_default_path(app->bt);
|
||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name);
|
||||
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac);
|
||||
furi_hal_bt_set_profile_pairing_method(
|
||||
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
|
||||
bt_set_profile(app->bt, BtProfileSerial);
|
||||
bt_enable_peer_key_update(app->bt);
|
||||
}
|
||||
|
||||
BadBtApp* bad_bt_app_alloc(char* arg) {
|
||||
BadBtApp* app = malloc(sizeof(BadBtApp));
|
||||
|
||||
app->bad_bt_script = NULL;
|
||||
|
||||
app->file_path = furi_string_alloc();
|
||||
app->keyboard_layout = furi_string_alloc();
|
||||
if(arg && strlen(arg)) {
|
||||
furi_string_set(app->file_path, arg);
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_simply_mkdir(storage, BAD_BT_APP_BASE_CONFIG_FOLDER);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
bad_bt_load_settings(app);
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&bad_bt_scene_handlers, app);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, bad_bt_app_tick_event_callback, 500);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, bad_bt_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, bad_bt_app_back_event_callback);
|
||||
|
||||
Bt* bt = furi_record_open(RECORD_BT);
|
||||
app->bt = bt;
|
||||
app->bt->suppress_pin_screen = true;
|
||||
|
||||
// Custom Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBtAppViewError, widget_get_view(app->widget));
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBtAppViewConfig, variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
app->bad_bt_view = bad_bt_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBtAppViewWork, bad_bt_get_view(app->bad_bt_view));
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBtAppViewConfigName, text_input_get_view(app->text_input));
|
||||
|
||||
app->byte_input = byte_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBtAppViewConfigMac, byte_input_get_view(app->byte_input));
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->conn_init_thread = furi_thread_alloc_ex(
|
||||
"BadBtConnInit", 1024, (FuriThreadCallback)bad_bt_connection_init, app);
|
||||
furi_thread_start(app->conn_init_thread);
|
||||
if(!furi_string_empty(app->file_path)) {
|
||||
app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app);
|
||||
bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout);
|
||||
scene_manager_next_scene(app->scene_manager, BadBtSceneWork);
|
||||
} else {
|
||||
furi_string_set(app->file_path, BAD_BT_APP_BASE_FOLDER);
|
||||
scene_manager_next_scene(app->scene_manager, BadBtSceneFileSelect);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void bad_bt_app_free(BadBtApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
if(app->bad_bt_script) {
|
||||
bad_bt_script_close(app->bad_bt_script);
|
||||
app->bad_bt_script = NULL;
|
||||
}
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewWork);
|
||||
bad_bt_free(app->bad_bt_view);
|
||||
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewError);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Variable item list
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfig);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
||||
// Text Input
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigName);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
// Byte Input
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigMac);
|
||||
byte_input_free(app->byte_input);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Restore bt config
|
||||
app->bt->suppress_pin_screen = false;
|
||||
if(app->conn_init_thread) {
|
||||
furi_thread_join(app->conn_init_thread);
|
||||
furi_thread_free(app->conn_init_thread);
|
||||
bad_bt_connection_deinit(app);
|
||||
}
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_record_close(RECORD_BT);
|
||||
|
||||
bad_bt_save_settings(app);
|
||||
|
||||
furi_string_free(app->file_path);
|
||||
furi_string_free(app->keyboard_layout);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t bad_bt_app(void* p) {
|
||||
BadBtApp* bad_bt_app = bad_bt_app_alloc((char*)p);
|
||||
|
||||
view_dispatcher_run(bad_bt_app->view_dispatcher);
|
||||
|
||||
bad_bt_app_free(bad_bt_app);
|
||||
return 0;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/bad_bt_scene.h"
|
||||
#include "helpers/ducky_script.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "bad_bt_icons.h"
|
||||
|
||||
#define BAD_BT_APP_BASE_FOLDER EXT_PATH("badusb")
|
||||
#define BAD_BT_APP_BASE_CONFIG_FOLDER EXT_PATH("badbt")
|
||||
#define BAD_BT_APP_PATH_LAYOUT_FOLDER BAD_BT_APP_BASE_FOLDER "/assets/layouts"
|
||||
#define BAD_BT_APP_SCRIPT_EXTENSION ".txt"
|
||||
#define BAD_BT_APP_LAYOUT_EXTENSION ".kl"
|
||||
|
||||
typedef enum BadBtCustomEvent {
|
||||
BadBtAppCustomEventTextEditResult,
|
||||
BadBtAppCustomEventByteInputDone,
|
||||
BadBtCustomEventErrorBack
|
||||
} BadBtCustomEvent;
|
||||
|
||||
typedef enum {
|
||||
BadBtAppViewError,
|
||||
BadBtAppViewWork,
|
||||
BadBtAppViewConfig,
|
||||
BadBtAppViewConfigMac,
|
||||
BadBtAppViewConfigName
|
||||
} BadBtAppView;
|
||||
|
||||
void bad_bt_config_switch_remember_mode(BadBtApp* app);
|
||||
|
||||
int32_t bad_bt_connection_init(BadBtApp* app);
|
||||
|
||||
void bad_bt_connection_deinit(BadBtApp* app);
|
||||
|
||||
void bad_kb_config_refresh_menu(BadBtApp* app);
|
||||
+777
@@ -0,0 +1,777 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <furi_hal_bt_hid.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
#include <storage/storage.h>
|
||||
#include "ducky_script.h"
|
||||
#include "ducky_script_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <toolbox/hex.h>
|
||||
#include "../bad_bt_app.h"
|
||||
|
||||
const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] =
|
||||
{0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4};
|
||||
const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] =
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define TAG "BadBT"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define BADBT_ASCII_TO_KEY(script, x) \
|
||||
(((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
|
||||
|
||||
/**
|
||||
* Delays for waiting between HID key press and key release
|
||||
*/
|
||||
const uint8_t bt_hid_delays[LevelRssiNum] = {
|
||||
30, // LevelRssi122_100
|
||||
25, // LevelRssi99_80
|
||||
20, // LevelRssi79_60
|
||||
17, // LevelRssi59_40
|
||||
14, // LevelRssi39_0
|
||||
};
|
||||
|
||||
uint8_t bt_timeout = 0;
|
||||
|
||||
static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
|
||||
uint8_t rssi;
|
||||
|
||||
if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError;
|
||||
|
||||
if(rssi <= 39)
|
||||
return LevelRssi39_0;
|
||||
else if(rssi <= 59)
|
||||
return LevelRssi59_40;
|
||||
else if(rssi <= 79)
|
||||
return LevelRssi79_60;
|
||||
else if(rssi <= 99)
|
||||
return LevelRssi99_80;
|
||||
else if(rssi <= 122)
|
||||
return LevelRssi122_100;
|
||||
|
||||
return LevelRssiError;
|
||||
}
|
||||
|
||||
static inline void update_bt_timeout(Bt* bt) {
|
||||
LevelRssiRange r = bt_remote_rssi_range(bt);
|
||||
if(r < LevelRssiNum) {
|
||||
bt_timeout = bt_hid_delays[r];
|
||||
FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtToggle = (1 << 0),
|
||||
WorkerEvtEnd = (1 << 1),
|
||||
WorkerEvtConnect = (1 << 2),
|
||||
WorkerEvtDisconnect = (1 << 3),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
static const char ducky_cmd_id[] = {"ID"};
|
||||
static const char ducky_cmd_bt_id[] = {"BT_ID"};
|
||||
|
||||
static const uint8_t numpad_keys[10] = {
|
||||
HID_KEYPAD_0,
|
||||
HID_KEYPAD_1,
|
||||
HID_KEYPAD_2,
|
||||
HID_KEYPAD_3,
|
||||
HID_KEYPAD_4,
|
||||
HID_KEYPAD_5,
|
||||
HID_KEYPAD_6,
|
||||
HID_KEYPAD_7,
|
||||
HID_KEYPAD_8,
|
||||
HID_KEYPAD_9,
|
||||
};
|
||||
|
||||
uint32_t ducky_get_command_len(const char* line) {
|
||||
uint32_t len = strlen(line);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(line[i] == ' ') return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ducky_is_line_end(const char chr) {
|
||||
return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
|
||||
}
|
||||
|
||||
uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars) {
|
||||
uint16_t keycode = ducky_get_keycode_by_name(param);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
if((accept_chars) && (strlen(param) > 0)) {
|
||||
return (BADBT_ASCII_TO_KEY(bad_bt, param[0]) & 0xFF);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ducky_get_number(const char* param, uint32_t* val) {
|
||||
uint32_t value = 0;
|
||||
if(sscanf(param, "%lu", &value) == 1) {
|
||||
*val = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ducky_numlock_on(BadBtScript* bad_bt) {
|
||||
UNUSED(bad_bt);
|
||||
if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
|
||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
|
||||
}
|
||||
}
|
||||
|
||||
bool ducky_numpad_press(BadBtScript* bad_bt, const char num) {
|
||||
UNUSED(bad_bt);
|
||||
if((num < '0') || (num > '9')) return false;
|
||||
|
||||
uint16_t key = numpad_keys[num - '0'];
|
||||
furi_hal_bt_hid_kb_press(key);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ducky_altchar(BadBtScript* bad_bt, const char* charcode) {
|
||||
uint8_t i = 0;
|
||||
bool state = false;
|
||||
|
||||
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT);
|
||||
|
||||
while(!ducky_is_line_end(charcode[i])) {
|
||||
state = ducky_numpad_press(bad_bt, charcode[i]);
|
||||
if(state == false) break;
|
||||
i++;
|
||||
}
|
||||
|
||||
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool ducky_altstring(BadBtScript* bad_bt, const char* param) {
|
||||
uint32_t i = 0;
|
||||
bool state = false;
|
||||
|
||||
while(param[i] != '\0') {
|
||||
if((param[i] < ' ') || (param[i] > '~')) {
|
||||
i++;
|
||||
continue; // Skip non-printable chars
|
||||
}
|
||||
|
||||
char temp_str[4];
|
||||
snprintf(temp_str, 4, "%u", param[i]);
|
||||
|
||||
state = ducky_altchar(bad_bt, temp_str);
|
||||
if(state == false) break;
|
||||
i++;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(bad_bt->st.error, sizeof(bad_bt->st.error), text, args);
|
||||
|
||||
va_end(args);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
}
|
||||
|
||||
bool ducky_string(BadBtScript* bad_bt, const char* param) {
|
||||
uint32_t i = 0;
|
||||
|
||||
while(param[i] != '\0') {
|
||||
if(param[i] != '\n') {
|
||||
uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, param[i]);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
furi_hal_bt_hid_kb_press(keycode);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(keycode);
|
||||
}
|
||||
} else {
|
||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
bad_bt->stringdelay = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ducky_string_next(BadBtScript* bad_bt) {
|
||||
if(bad_bt->string_print_pos >= furi_string_size(bad_bt->string_print)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char print_char = furi_string_get_char(bad_bt->string_print, bad_bt->string_print_pos);
|
||||
|
||||
if(print_char != '\n') {
|
||||
uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, print_char);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
furi_hal_bt_hid_kb_press(keycode);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(keycode);
|
||||
}
|
||||
} else {
|
||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN);
|
||||
}
|
||||
|
||||
bad_bt->string_print_pos++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int32_t ducky_parse_line(BadBtScript* bad_bt, FuriString* line) {
|
||||
uint32_t line_len = furi_string_size(line);
|
||||
const char* line_tmp = furi_string_get_cstr(line);
|
||||
|
||||
if(line_len == 0) {
|
||||
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||
}
|
||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
||||
|
||||
// Ducky Lang Functions
|
||||
int32_t cmd_result = ducky_execute_cmd(bad_bt, line_tmp);
|
||||
if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
|
||||
return cmd_result;
|
||||
}
|
||||
|
||||
// Special keys + modifiers
|
||||
uint16_t key = ducky_get_keycode(bad_bt, line_tmp, false);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_bt, "No keycode defined for %s", line_tmp);
|
||||
}
|
||||
if((key & 0xFF00) != 0) {
|
||||
// It's a modifier key
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
key |= ducky_get_keycode(bad_bt, line_tmp, true);
|
||||
}
|
||||
furi_hal_bt_hid_kb_press(key);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ducky_set_bt_id(BadBtScript* bad_bt, const char* line) {
|
||||
size_t line_len = strlen(line);
|
||||
size_t mac_len = BAD_BT_MAC_ADDRESS_LEN * 3;
|
||||
if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name
|
||||
|
||||
uint8_t mac[BAD_BT_MAC_ADDRESS_LEN];
|
||||
for(size_t i = 0; i < BAD_BT_MAC_ADDRESS_LEN; i++) {
|
||||
char a = line[i * 3];
|
||||
char b = line[i * 3 + 1];
|
||||
if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') ||
|
||||
(b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len);
|
||||
bt_set_profile_mac_address(bad_bt->bt, mac);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ducky_script_preload(BadBtScript* bad_bt, File* script_file) {
|
||||
uint8_t ret = 0;
|
||||
uint32_t line_len = 0;
|
||||
|
||||
furi_string_reset(bad_bt->line);
|
||||
|
||||
do {
|
||||
ret = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN);
|
||||
for(uint16_t i = 0; i < ret; i++) {
|
||||
if(bad_bt->file_buf[i] == '\n' && line_len > 0) {
|
||||
bad_bt->st.line_nb++;
|
||||
line_len = 0;
|
||||
} else {
|
||||
if(bad_bt->st.line_nb == 0) { // Save first line
|
||||
furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]);
|
||||
}
|
||||
line_len++;
|
||||
}
|
||||
}
|
||||
if(storage_file_eof(script_file)) {
|
||||
if(line_len > 0) {
|
||||
bad_bt->st.line_nb++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(ret > 0);
|
||||
|
||||
const char* line_tmp = furi_string_get_cstr(bad_bt->line);
|
||||
if(bad_bt->app->switch_mode_thread) {
|
||||
furi_thread_join(bad_bt->app->switch_mode_thread);
|
||||
furi_thread_free(bad_bt->app->switch_mode_thread);
|
||||
bad_bt->app->switch_mode_thread = NULL;
|
||||
}
|
||||
// Looking for ID or BT_ID command at first line
|
||||
bad_bt->set_usb_id = false;
|
||||
bad_bt->set_bt_id = false;
|
||||
bad_bt->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0;
|
||||
// TODO: We setting has_usb_id to its value but ignoring it for now and not using anywhere here, may be used in a future to detect script type
|
||||
bad_bt->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0;
|
||||
if(bad_bt->has_bt_id) {
|
||||
if(!bad_bt->app->bt_remember) {
|
||||
bad_bt->set_bt_id = ducky_set_bt_id(bad_bt, &line_tmp[strlen(ducky_cmd_bt_id) + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
bad_kb_config_refresh_menu(bad_bt->app);
|
||||
|
||||
if(!bad_bt->set_bt_id) {
|
||||
const char* bt_name = bad_bt->app->config.bt_name;
|
||||
const uint8_t* bt_mac = bad_bt->app->bt_remember ? (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS :
|
||||
bad_bt->app->config.bt_mac;
|
||||
bool reset_name = strncmp(
|
||||
bt_name,
|
||||
furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard),
|
||||
BAD_BT_ADV_NAME_MAX_LEN);
|
||||
bool reset_mac = memcmp(
|
||||
bt_mac,
|
||||
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
|
||||
BAD_BT_MAC_ADDRESS_LEN);
|
||||
if(reset_name && reset_mac) {
|
||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name);
|
||||
} else if(reset_name) {
|
||||
bt_set_profile_adv_name(bad_bt->bt, bt_name);
|
||||
}
|
||||
if(reset_mac) {
|
||||
bt_set_profile_mac_address(bad_bt->bt, bt_mac);
|
||||
}
|
||||
}
|
||||
|
||||
storage_file_seek(script_file, 0, true);
|
||||
furi_string_reset(bad_bt->line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t ducky_script_execute_next(BadBtScript* bad_bt, File* script_file) {
|
||||
int32_t delay_val = 0;
|
||||
|
||||
if(bad_bt->repeat_cnt > 0) {
|
||||
bad_bt->repeat_cnt--;
|
||||
delay_val = ducky_parse_line(bad_bt, bad_bt->line_prev);
|
||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||
return 0;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
|
||||
return delay_val;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
|
||||
return delay_val;
|
||||
} else if(delay_val < 0) { // Script error
|
||||
bad_bt->st.error_line = bad_bt->st.line_cur - 1;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur - 1U);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
} else {
|
||||
return (delay_val + bad_bt->defdelay);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_set(bad_bt->line_prev, bad_bt->line);
|
||||
furi_string_reset(bad_bt->line);
|
||||
|
||||
while(1) {
|
||||
if(bad_bt->buf_len == 0) {
|
||||
bad_bt->buf_len = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN);
|
||||
if(storage_file_eof(script_file)) {
|
||||
if((bad_bt->buf_len < FILE_BUFFER_LEN) && (bad_bt->file_end == false)) {
|
||||
bad_bt->file_buf[bad_bt->buf_len] = '\n';
|
||||
bad_bt->buf_len++;
|
||||
bad_bt->file_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
bad_bt->buf_start = 0;
|
||||
if(bad_bt->buf_len == 0) return SCRIPT_STATE_END;
|
||||
}
|
||||
for(uint8_t i = bad_bt->buf_start; i < (bad_bt->buf_start + bad_bt->buf_len); i++) {
|
||||
if(bad_bt->file_buf[i] == '\n' && furi_string_size(bad_bt->line) > 0) {
|
||||
bad_bt->st.line_cur++;
|
||||
bad_bt->buf_len = bad_bt->buf_len + bad_bt->buf_start - (i + 1);
|
||||
bad_bt->buf_start = i + 1;
|
||||
furi_string_trim(bad_bt->line);
|
||||
delay_val = ducky_parse_line(bad_bt, bad_bt->line);
|
||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||
return 0;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
|
||||
return delay_val;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
|
||||
return delay_val;
|
||||
} else if(delay_val < 0) {
|
||||
bad_bt->st.error_line = bad_bt->st.line_cur;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
} else {
|
||||
return (delay_val + bad_bt->defdelay);
|
||||
}
|
||||
} else {
|
||||
furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]);
|
||||
}
|
||||
}
|
||||
bad_bt->buf_len = 0;
|
||||
if(bad_bt->file_end) return SCRIPT_STATE_END;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bad_bt_bt_hid_state_callback(BtStatus status, void* context) {
|
||||
furi_assert(context);
|
||||
BadBtScript* bad_bt = context;
|
||||
bool state = (status == BtStatusConnected);
|
||||
|
||||
if(state == true) {
|
||||
LevelRssiRange r = bt_remote_rssi_range(bad_bt->bt);
|
||||
if(r != LevelRssiError) {
|
||||
bt_timeout = bt_hid_delays[r];
|
||||
}
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtConnect);
|
||||
} else {
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t bad_bt_flags_get(uint32_t flags_mask, uint32_t timeout) {
|
||||
uint32_t flags = furi_thread_flags_get();
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
if(flags == 0) {
|
||||
flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);
|
||||
furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));
|
||||
} else {
|
||||
uint32_t state = furi_thread_flags_clear(flags);
|
||||
furi_check((state & FuriFlagError) == 0);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int32_t bad_bt_worker(void* context) {
|
||||
BadBtScript* bad_bt = context;
|
||||
|
||||
BadBtWorkerState worker_state = BadBtStateInit;
|
||||
int32_t delay_val = 0;
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "Init");
|
||||
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
bad_bt->line = furi_string_alloc();
|
||||
bad_bt->line_prev = furi_string_alloc();
|
||||
bad_bt->string_print = furi_string_alloc();
|
||||
|
||||
bt_set_status_changed_callback(bad_bt->bt, bad_bt_bt_hid_state_callback, bad_bt);
|
||||
|
||||
while(1) {
|
||||
if(worker_state == BadBtStateInit) { // State: initialization
|
||||
if(storage_file_open(
|
||||
script_file,
|
||||
furi_string_get_cstr(bad_bt->file_path),
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
if((ducky_script_preload(bad_bt, script_file)) && (bad_bt->st.line_nb > 0)) {
|
||||
if(furi_hal_bt_is_connected()) {
|
||||
worker_state = BadBtStateIdle; // Ready to run
|
||||
} else {
|
||||
worker_state = BadBtStateNotConnected; // Not connected
|
||||
}
|
||||
|
||||
} else {
|
||||
worker_state = BadBtStateScriptError; // Script preload error
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(WORKER_TAG, "File open error");
|
||||
worker_state = BadBtStateFileError; // File open error
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBtStateNotConnected) { // State: Not connected
|
||||
uint32_t flags = bad_bt_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) {
|
||||
worker_state = BadBtStateIdle; // Ready to run
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
worker_state = BadBtStateWillRun; // Will run when connected
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBtStateIdle) { // State: ready to start
|
||||
uint32_t flags = bad_bt_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) { // Start executing script
|
||||
delay_val = 0;
|
||||
bad_bt->buf_len = 0;
|
||||
bad_bt->st.line_cur = 0;
|
||||
bad_bt->defdelay = 0;
|
||||
bad_bt->stringdelay = 0;
|
||||
bad_bt->repeat_cnt = 0;
|
||||
bad_bt->key_hold_nb = 0;
|
||||
bad_bt->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout);
|
||||
worker_state = BadBtStateRunning;
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBtStateNotConnected; // Disconnected
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBtStateWillRun) { // State: start on connection
|
||||
uint32_t flags = bad_bt_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) { // Start executing script
|
||||
delay_val = 0;
|
||||
bad_bt->buf_len = 0;
|
||||
bad_bt->st.line_cur = 0;
|
||||
bad_bt->defdelay = 0;
|
||||
bad_bt->stringdelay = 0;
|
||||
bad_bt->repeat_cnt = 0;
|
||||
bad_bt->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
// extra time for PC to recognize Flipper as keyboard
|
||||
flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle,
|
||||
FuriFlagWaitAny | FuriFlagNoClear,
|
||||
1500);
|
||||
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
||||
// If nothing happened - start script execution
|
||||
worker_state = BadBtStateRunning;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
worker_state = BadBtStateIdle;
|
||||
furi_thread_flags_clear(WorkerEvtToggle);
|
||||
}
|
||||
|
||||
update_bt_timeout(bad_bt->bt);
|
||||
|
||||
bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout);
|
||||
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
|
||||
worker_state = BadBtStateNotConnected;
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBtStateRunning) { // State: running
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
|
||||
|
||||
delay_val -= delay_cur;
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
worker_state = BadBtStateIdle; // Stop executing script
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBtStateNotConnected; // Disconnected
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
continue;
|
||||
} else if(
|
||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||
if(delay_val > 0) {
|
||||
bad_bt->st.delay_remain--;
|
||||
continue;
|
||||
}
|
||||
bad_bt->st.state = BadBtStateRunning;
|
||||
delay_val = ducky_script_execute_next(bad_bt, script_file);
|
||||
if(delay_val == SCRIPT_STATE_ERROR) { // Script error
|
||||
delay_val = 0;
|
||||
worker_state = BadBtStateScriptError;
|
||||
bad_bt->st.state = worker_state;
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
|
||||
} else if(delay_val == SCRIPT_STATE_END) { // End of script
|
||||
delay_val = 0;
|
||||
worker_state = BadBtStateIdle;
|
||||
bad_bt->st.state = BadBtStateDone;
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
|
||||
continue;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
|
||||
delay_val = bad_bt->defdelay;
|
||||
bad_bt->string_print_pos = 0;
|
||||
worker_state = BadBtStateStringDelay;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
|
||||
worker_state = BadBtStateWaitForBtn;
|
||||
bad_bt->st.state = BadBtStateWaitForBtn; // Show long delays
|
||||
} else if(delay_val > 1000) {
|
||||
bad_bt->st.state = BadBtStateDelay; // Show long delays
|
||||
bad_bt->st.delay_remain = delay_val / 1000;
|
||||
}
|
||||
} else {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
}
|
||||
} else if(worker_state == BadBtStateWaitForBtn) { // State: Wait for button Press
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
delay_val = 0;
|
||||
worker_state = BadBtStateRunning;
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBtStateNotConnected; // Disconnected
|
||||
furi_hal_hid_kb_release_all();
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
continue;
|
||||
}
|
||||
} else if(worker_state == BadBtStateStringDelay) { // State: print string with delays
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
|
||||
FuriFlagWaitAny,
|
||||
bad_bt->stringdelay);
|
||||
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
worker_state = BadBtStateIdle; // Stop executing script
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBtStateNotConnected; // Disconnected
|
||||
|
||||
furi_hal_bt_hid_kb_release_all();
|
||||
}
|
||||
bad_bt->st.state = worker_state;
|
||||
continue;
|
||||
} else if(
|
||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||
bool string_end = ducky_string_next(bad_bt);
|
||||
if(string_end) {
|
||||
bad_bt->stringdelay = 0;
|
||||
worker_state = BadBtStateRunning;
|
||||
}
|
||||
} else {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
}
|
||||
} else if(
|
||||
(worker_state == BadBtStateFileError) ||
|
||||
(worker_state == BadBtStateScriptError)) { // State: error
|
||||
uint32_t flags =
|
||||
bad_bt_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_bt_timeout(bad_bt->bt);
|
||||
}
|
||||
|
||||
bt_set_status_changed_callback(bad_bt->bt, NULL, NULL);
|
||||
|
||||
storage_file_close(script_file);
|
||||
storage_file_free(script_file);
|
||||
furi_string_free(bad_bt->line);
|
||||
furi_string_free(bad_bt->line_prev);
|
||||
furi_string_free(bad_bt->string_print);
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "End");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bad_bt_script_set_default_keyboard_layout(BadBtScript* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
furi_string_set_str(bad_bt->keyboard_layout, "");
|
||||
memset(bad_bt->layout, HID_KEYBOARD_NONE, sizeof(bad_bt->layout));
|
||||
memcpy(bad_bt->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_bt->layout)));
|
||||
}
|
||||
|
||||
BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app) {
|
||||
furi_assert(file_path);
|
||||
|
||||
BadBtScript* bad_bt = malloc(sizeof(BadBtScript));
|
||||
bad_bt->app = app;
|
||||
bad_bt->file_path = furi_string_alloc();
|
||||
furi_string_set(bad_bt->file_path, file_path);
|
||||
bad_bt->keyboard_layout = furi_string_alloc();
|
||||
bad_bt_script_set_default_keyboard_layout(bad_bt);
|
||||
|
||||
bad_bt->st.state = BadBtStateInit;
|
||||
bad_bt->st.error[0] = '\0';
|
||||
|
||||
bad_bt->bt = bt;
|
||||
|
||||
bad_bt->thread = furi_thread_alloc_ex("BadBtWorker", 2048, bad_bt_worker, bad_bt);
|
||||
furi_thread_start(bad_bt->thread);
|
||||
return bad_bt;
|
||||
}
|
||||
|
||||
void bad_bt_script_close(BadBtScript* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtEnd);
|
||||
furi_thread_join(bad_bt->thread);
|
||||
furi_thread_free(bad_bt->thread);
|
||||
furi_string_free(bad_bt->file_path);
|
||||
furi_string_free(bad_bt->keyboard_layout);
|
||||
free(bad_bt);
|
||||
}
|
||||
|
||||
void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path) {
|
||||
furi_assert(bad_bt);
|
||||
|
||||
if((bad_bt->st.state == BadBtStateRunning) || (bad_bt->st.state == BadBtStateDelay)) {
|
||||
// do not update keyboard layout while a script is running
|
||||
return;
|
||||
}
|
||||
|
||||
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
if(!furi_string_empty(layout_path)) { //-V1051
|
||||
furi_string_set(bad_bt->keyboard_layout, layout_path);
|
||||
if(storage_file_open(
|
||||
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
uint16_t layout[128];
|
||||
if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
|
||||
memcpy(bad_bt->layout, layout, sizeof(layout));
|
||||
}
|
||||
}
|
||||
storage_file_close(layout_file);
|
||||
} else {
|
||||
bad_bt_script_set_default_keyboard_layout(bad_bt);
|
||||
}
|
||||
storage_file_free(layout_file);
|
||||
}
|
||||
|
||||
void bad_bt_script_toggle(BadBtScript* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtToggle);
|
||||
}
|
||||
|
||||
BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
return &(bad_bt->st);
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <bt/bt_service/bt_i.h>
|
||||
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/byte_input.h>
|
||||
#include "../views/bad_bt_view.h"
|
||||
|
||||
#define FILE_BUFFER_LEN 16
|
||||
|
||||
typedef enum {
|
||||
LevelRssi122_100,
|
||||
LevelRssi99_80,
|
||||
LevelRssi79_60,
|
||||
LevelRssi59_40,
|
||||
LevelRssi39_0,
|
||||
LevelRssiNum,
|
||||
LevelRssiError = 0xFF,
|
||||
} LevelRssiRange;
|
||||
|
||||
extern const uint8_t bt_hid_delays[LevelRssiNum];
|
||||
|
||||
extern uint8_t bt_timeout;
|
||||
|
||||
typedef enum {
|
||||
BadBtStateInit,
|
||||
BadBtStateNotConnected,
|
||||
BadBtStateIdle,
|
||||
BadBtStateWillRun,
|
||||
BadBtStateRunning,
|
||||
BadBtStateDelay,
|
||||
BadBtStateStringDelay,
|
||||
BadBtStateWaitForBtn,
|
||||
BadBtStateDone,
|
||||
BadBtStateScriptError,
|
||||
BadBtStateFileError,
|
||||
} BadBtWorkerState;
|
||||
|
||||
struct BadBtState {
|
||||
BadBtWorkerState state;
|
||||
uint32_t pin;
|
||||
uint16_t line_cur;
|
||||
uint16_t line_nb;
|
||||
uint32_t delay_remain;
|
||||
uint16_t error_line;
|
||||
char error[64];
|
||||
};
|
||||
|
||||
typedef struct BadBtApp BadBtApp;
|
||||
|
||||
typedef struct {
|
||||
FuriHalUsbHidConfig hid_cfg;
|
||||
FuriThread* thread;
|
||||
BadBtState st;
|
||||
|
||||
FuriString* file_path;
|
||||
FuriString* keyboard_layout;
|
||||
uint8_t file_buf[FILE_BUFFER_LEN + 1];
|
||||
uint8_t buf_start;
|
||||
uint8_t buf_len;
|
||||
bool file_end;
|
||||
|
||||
uint32_t defdelay;
|
||||
uint32_t stringdelay;
|
||||
uint16_t layout[128];
|
||||
|
||||
FuriString* line;
|
||||
FuriString* line_prev;
|
||||
uint32_t repeat_cnt;
|
||||
uint8_t key_hold_nb;
|
||||
|
||||
bool set_usb_id;
|
||||
bool set_bt_id;
|
||||
bool has_usb_id;
|
||||
bool has_bt_id;
|
||||
|
||||
FuriString* string_print;
|
||||
size_t string_print_pos;
|
||||
|
||||
Bt* bt;
|
||||
BadBtApp* app;
|
||||
} BadBtScript;
|
||||
|
||||
BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app);
|
||||
|
||||
void bad_bt_script_close(BadBtScript* bad_bt);
|
||||
|
||||
void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path);
|
||||
|
||||
void bad_bt_script_start(BadBtScript* bad_bt);
|
||||
|
||||
void bad_bt_script_stop(BadBtScript* bad_bt);
|
||||
|
||||
void bad_bt_script_toggle(BadBtScript* bad_bt);
|
||||
|
||||
BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt);
|
||||
|
||||
#define BAD_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH
|
||||
#define BAD_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
|
||||
|
||||
// this is the MAC address used when we do not forget paired device (BOUND STATE)
|
||||
extern const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN];
|
||||
extern const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN];
|
||||
|
||||
typedef enum {
|
||||
BadBtAppErrorNoFiles,
|
||||
BadBtAppErrorCloseRpc,
|
||||
} BadBtAppError;
|
||||
|
||||
typedef struct {
|
||||
char bt_name[BAD_BT_ADV_NAME_MAX_LEN];
|
||||
uint8_t bt_mac[BAD_BT_MAC_ADDRESS_LEN];
|
||||
GapPairing bt_mode;
|
||||
} BadBtConfig;
|
||||
|
||||
struct BadBtApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
VariableItemList* var_item_list;
|
||||
TextInput* text_input;
|
||||
ByteInput* byte_input;
|
||||
|
||||
BadBtAppError error;
|
||||
FuriString* file_path;
|
||||
FuriString* keyboard_layout;
|
||||
BadBt* bad_bt_view;
|
||||
BadBtScript* bad_bt_script;
|
||||
|
||||
Bt* bt;
|
||||
bool bt_remember;
|
||||
BadBtConfig config;
|
||||
BadBtConfig prev_config;
|
||||
FuriThread* conn_init_thread;
|
||||
FuriThread* switch_mode_thread;
|
||||
};
|
||||
|
||||
int32_t bad_bt_config_switch_mode(BadBtApp* app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,201 @@
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_bt_hid.h>
|
||||
#include "ducky_script.h"
|
||||
#include "ducky_script_i.h"
|
||||
|
||||
typedef int32_t (*DuckyCmdCallback)(BadBtScript* bad_bt, const char* line, int32_t param);
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
DuckyCmdCallback callback;
|
||||
int32_t param;
|
||||
} DuckyCmd;
|
||||
|
||||
static int32_t ducky_fnc_delay(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint32_t delay_val = 0;
|
||||
bool state = ducky_get_number(line, &delay_val);
|
||||
if((state) && (delay_val > 0)) {
|
||||
return (int32_t)delay_val;
|
||||
}
|
||||
|
||||
return ducky_error(bad_bt, "Invalid number %s", line);
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_defdelay(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_bt->defdelay);
|
||||
if(!state) {
|
||||
return ducky_error(bad_bt, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_strdelay(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_bt->stringdelay);
|
||||
if(!state) {
|
||||
return ducky_error(bad_bt, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_string(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
furi_string_set_str(bad_bt->string_print, line);
|
||||
if(param == 1) {
|
||||
furi_string_cat(bad_bt->string_print, "\n");
|
||||
}
|
||||
|
||||
if(bad_bt->stringdelay == 0) { // stringdelay not set - run command immidiately
|
||||
bool state = ducky_string(bad_bt, furi_string_get_cstr(bad_bt->string_print));
|
||||
if(!state) {
|
||||
return ducky_error(bad_bt, "Invalid string %s", line);
|
||||
}
|
||||
} else { // stringdelay is set - run command in thread to keep handling external events
|
||||
return SCRIPT_STATE_STRING_START;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_repeat(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_bt->repeat_cnt);
|
||||
if((!state) || (bad_bt->repeat_cnt == 0)) {
|
||||
return ducky_error(bad_bt, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_sysrq(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_bt, line, true);
|
||||
|
||||
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
|
||||
furi_hal_bt_hid_kb_press(key);
|
||||
furi_delay_ms(bt_timeout);
|
||||
furi_hal_bt_hid_kb_release(key);
|
||||
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_altchar(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
ducky_numlock_on(bad_bt);
|
||||
bool state = ducky_altchar(bad_bt, line);
|
||||
if(!state) {
|
||||
return ducky_error(bad_bt, "Invalid altchar %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_altstring(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
ducky_numlock_on(bad_bt);
|
||||
bool state = ducky_altstring(bad_bt, line);
|
||||
if(!state) {
|
||||
return ducky_error(bad_bt, "Invalid altstring %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_hold(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_bt, line, true);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_bt, "No keycode defined for %s", line);
|
||||
}
|
||||
bad_bt->key_hold_nb++;
|
||||
if(bad_bt->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {
|
||||
return ducky_error(bad_bt, "Too many keys are hold");
|
||||
}
|
||||
furi_hal_bt_hid_kb_press(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_release(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_bt, line, true);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_bt, "No keycode defined for %s", line);
|
||||
}
|
||||
if(bad_bt->key_hold_nb == 0) {
|
||||
return ducky_error(bad_bt, "No keys are hold");
|
||||
}
|
||||
bad_bt->key_hold_nb--;
|
||||
furi_hal_bt_hid_kb_release(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_waitforbutton(BadBtScript* bad_bt, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
UNUSED(bad_bt);
|
||||
UNUSED(line);
|
||||
|
||||
return SCRIPT_STATE_WAIT_FOR_BTN;
|
||||
}
|
||||
|
||||
static const DuckyCmd ducky_commands[] = {
|
||||
{"REM", NULL, -1},
|
||||
{"ID", NULL, -1},
|
||||
{"BT_ID", NULL, -1},
|
||||
{"DELAY", ducky_fnc_delay, -1},
|
||||
{"STRING", ducky_fnc_string, 0},
|
||||
{"STRINGLN", ducky_fnc_string, 1},
|
||||
{"DEFAULT_DELAY", ducky_fnc_defdelay, -1},
|
||||
{"DEFAULTDELAY", ducky_fnc_defdelay, -1},
|
||||
{"STRINGDELAY", ducky_fnc_strdelay, -1},
|
||||
{"STRING_DELAY", ducky_fnc_strdelay, -1},
|
||||
{"REPEAT", ducky_fnc_repeat, -1},
|
||||
{"SYSRQ", ducky_fnc_sysrq, -1},
|
||||
{"ALTCHAR", ducky_fnc_altchar, -1},
|
||||
{"ALTSTRING", ducky_fnc_altstring, -1},
|
||||
{"ALTCODE", ducky_fnc_altstring, -1},
|
||||
{"HOLD", ducky_fnc_hold, -1},
|
||||
{"RELEASE", ducky_fnc_release, -1},
|
||||
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
|
||||
};
|
||||
|
||||
#define TAG "BadBT"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line) {
|
||||
size_t cmd_word_len = strcspn(line, " ");
|
||||
for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
|
||||
size_t cmd_compare_len = strlen(ducky_commands[i].name);
|
||||
|
||||
if(cmd_compare_len != cmd_word_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {
|
||||
if(ducky_commands[i].callback == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((ducky_commands[i].callback)(bad_bt, line, ducky_commands[i].param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SCRIPT_STATE_CMD_UNKNOWN;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "ducky_script.h"
|
||||
|
||||
#define SCRIPT_STATE_ERROR (-1)
|
||||
#define SCRIPT_STATE_END (-2)
|
||||
#define SCRIPT_STATE_NEXT_LINE (-3)
|
||||
#define SCRIPT_STATE_CMD_UNKNOWN (-4)
|
||||
#define SCRIPT_STATE_STRING_START (-5)
|
||||
#define SCRIPT_STATE_WAIT_FOR_BTN (-6)
|
||||
|
||||
uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars);
|
||||
|
||||
uint32_t ducky_get_command_len(const char* line);
|
||||
|
||||
bool ducky_is_line_end(const char chr);
|
||||
|
||||
uint16_t ducky_get_keycode_by_name(const char* param);
|
||||
|
||||
bool ducky_get_number(const char* param, uint32_t* val);
|
||||
|
||||
void ducky_numlock_on(BadBtScript* bad_bt);
|
||||
|
||||
bool ducky_numpad_press(BadBtScript* bad_bt, const char num);
|
||||
|
||||
bool ducky_altchar(BadBtScript* bad_bt, const char* charcode);
|
||||
|
||||
bool ducky_altstring(BadBtScript* bad_bt, const char* param);
|
||||
|
||||
bool ducky_string(BadBtScript* bad_bt, const char* param);
|
||||
|
||||
int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line);
|
||||
|
||||
int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <furi_hal.h>
|
||||
#include "ducky_script_i.h"
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
uint16_t keycode;
|
||||
} DuckyKey;
|
||||
|
||||
static const DuckyKey ducky_keys[] = {
|
||||
{"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
|
||||
{"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
|
||||
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
|
||||
{"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
|
||||
|
||||
{"CTRL", KEY_MOD_LEFT_CTRL},
|
||||
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
||||
{"SHIFT", KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT", KEY_MOD_LEFT_ALT},
|
||||
{"GUI", KEY_MOD_LEFT_GUI},
|
||||
{"WINDOWS", KEY_MOD_LEFT_GUI},
|
||||
|
||||
{"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
|
||||
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
|
||||
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
|
||||
{"LEFT", HID_KEYBOARD_LEFT_ARROW},
|
||||
{"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
|
||||
{"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
|
||||
{"UPARROW", HID_KEYBOARD_UP_ARROW},
|
||||
{"UP", HID_KEYBOARD_UP_ARROW},
|
||||
|
||||
{"ENTER", HID_KEYBOARD_RETURN},
|
||||
{"BREAK", HID_KEYBOARD_PAUSE},
|
||||
{"PAUSE", HID_KEYBOARD_PAUSE},
|
||||
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
|
||||
{"DELETE", HID_KEYBOARD_DELETE_FORWARD},
|
||||
{"BACKSPACE", HID_KEYBOARD_DELETE},
|
||||
{"END", HID_KEYBOARD_END},
|
||||
{"ESC", HID_KEYBOARD_ESCAPE},
|
||||
{"ESCAPE", HID_KEYBOARD_ESCAPE},
|
||||
{"HOME", HID_KEYBOARD_HOME},
|
||||
{"INSERT", HID_KEYBOARD_INSERT},
|
||||
{"NUMLOCK", HID_KEYPAD_NUMLOCK},
|
||||
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
|
||||
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
|
||||
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
|
||||
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
||||
{"SPACE", HID_KEYBOARD_SPACEBAR},
|
||||
{"TAB", HID_KEYBOARD_TAB},
|
||||
{"MENU", HID_KEYBOARD_APPLICATION},
|
||||
{"APP", HID_KEYBOARD_APPLICATION},
|
||||
|
||||
{"F1", HID_KEYBOARD_F1},
|
||||
{"F2", HID_KEYBOARD_F2},
|
||||
{"F3", HID_KEYBOARD_F3},
|
||||
{"F4", HID_KEYBOARD_F4},
|
||||
{"F5", HID_KEYBOARD_F5},
|
||||
{"F6", HID_KEYBOARD_F6},
|
||||
{"F7", HID_KEYBOARD_F7},
|
||||
{"F8", HID_KEYBOARD_F8},
|
||||
{"F9", HID_KEYBOARD_F9},
|
||||
{"F10", HID_KEYBOARD_F10},
|
||||
{"F11", HID_KEYBOARD_F11},
|
||||
{"F12", HID_KEYBOARD_F12},
|
||||
};
|
||||
|
||||
uint16_t ducky_get_keycode_by_name(const char* param) {
|
||||
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
||||
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
||||
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||
return ducky_keys[i].keycode;
|
||||
}
|
||||
}
|
||||
|
||||
return HID_KEYBOARD_NONE;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,30 @@
|
||||
#include "bad_bt_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const bad_bt_scene_on_enter_handlers[])(void*) = {
|
||||
#include "bad_bt_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 bad_bt_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "bad_bt_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 bad_bt_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "bad_bt_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers bad_bt_scene_handlers = {
|
||||
.on_enter_handlers = bad_bt_scene_on_enter_handlers,
|
||||
.on_event_handlers = bad_bt_scene_on_event_handlers,
|
||||
.on_exit_handlers = bad_bt_scene_on_exit_handlers,
|
||||
.scene_num = BadBtSceneNum,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) BadBtScene##id,
|
||||
typedef enum {
|
||||
#include "bad_bt_scene_config.h"
|
||||
BadBtSceneNum,
|
||||
} BadBtScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers bad_bt_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "bad_bt_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 "bad_bt_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 "bad_bt_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "../bad_bt_app.h"
|
||||
#include "../helpers/ducky_script.h"
|
||||
#include "furi_hal_power.h"
|
||||
|
||||
enum VarItemListIndex {
|
||||
VarItemListIndexKeyboardLayout,
|
||||
VarItemListIndexBtRemember,
|
||||
VarItemListIndexBtDeviceName,
|
||||
VarItemListIndexBtMacAddress,
|
||||
VarItemListIndexRandomizeBtMac,
|
||||
};
|
||||
|
||||
void bad_bt_scene_config_bt_remember_callback(VariableItem* item) {
|
||||
BadBtApp* bad_bt = variable_item_get_context(item);
|
||||
bad_bt->bt_remember = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF");
|
||||
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, VarItemListIndexBtRemember);
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_var_item_list_callback(void* context, uint32_t index) {
|
||||
BadBtApp* bad_bt = context;
|
||||
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_on_enter(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
VariableItemList* var_item_list = bad_bt->var_item_list;
|
||||
VariableItem* item;
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_bt);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "BT Remember", 2, bad_bt_scene_config_bt_remember_callback, bad_bt);
|
||||
variable_item_set_current_value_index(item, bad_bt->bt_remember);
|
||||
variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF");
|
||||
|
||||
item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_bt);
|
||||
if(bad_bt->bad_bt_script->set_bt_id) {
|
||||
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!");
|
||||
}
|
||||
|
||||
item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_bt);
|
||||
if(bad_bt->bt_remember) {
|
||||
variable_item_set_locked(item, true, "Remember\nmust be Off!");
|
||||
} else if(bad_bt->bad_bt_script->set_bt_id) {
|
||||
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
|
||||
}
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_bt);
|
||||
if(bad_bt->bt_remember) {
|
||||
variable_item_set_locked(item, true, "Remember\nmust be Off!");
|
||||
} else if(bad_bt->bad_bt_script->set_bt_id) {
|
||||
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
|
||||
}
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, bad_bt_scene_config_var_item_list_callback, bad_bt);
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list, scene_manager_get_scene_state(bad_bt->scene_manager, BadBtSceneConfig));
|
||||
|
||||
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfig);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_config_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBtApp* bad_bt = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(bad_bt->scene_manager, BadBtSceneConfig, event.event);
|
||||
consumed = true;
|
||||
switch(event.event) {
|
||||
case VarItemListIndexKeyboardLayout:
|
||||
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigLayout);
|
||||
break;
|
||||
case VarItemListIndexBtRemember:
|
||||
bad_bt_config_switch_remember_mode(bad_bt);
|
||||
scene_manager_previous_scene(bad_bt->scene_manager);
|
||||
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfig);
|
||||
break;
|
||||
case VarItemListIndexBtDeviceName:
|
||||
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigName);
|
||||
break;
|
||||
case VarItemListIndexBtMacAddress:
|
||||
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigMac);
|
||||
break;
|
||||
case VarItemListIndexRandomizeBtMac:
|
||||
furi_hal_random_fill_buf(bad_bt->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN);
|
||||
bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_on_exit(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
VariableItemList* var_item_list = bad_bt->var_item_list;
|
||||
|
||||
variable_item_list_reset(var_item_list);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
ADD_SCENE(bad_bt, file_select, FileSelect)
|
||||
ADD_SCENE(bad_bt, work, Work)
|
||||
ADD_SCENE(bad_bt, error, Error)
|
||||
ADD_SCENE(bad_bt, config, Config)
|
||||
ADD_SCENE(bad_bt, config_layout, ConfigLayout)
|
||||
ADD_SCENE(bad_bt, config_name, ConfigName)
|
||||
ADD_SCENE(bad_bt, config_mac, ConfigMac)
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "../bad_bt_app.h"
|
||||
#include "furi_hal_power.h"
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_bt_layout_select(BadBtApp* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
|
||||
FuriString* predefined_path;
|
||||
predefined_path = furi_string_alloc();
|
||||
if(!furi_string_empty(bad_bt->keyboard_layout)) {
|
||||
furi_string_set(predefined_path, bad_bt->keyboard_layout);
|
||||
} else {
|
||||
furi_string_set(predefined_path, BAD_BT_APP_PATH_LAYOUT_FOLDER);
|
||||
}
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_BT_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
|
||||
browser_options.base_path = BAD_BT_APP_PATH_LAYOUT_FOLDER;
|
||||
browser_options.skip_assets = false;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
bool res = dialog_file_browser_show(
|
||||
bad_bt->dialogs, bad_bt->keyboard_layout, predefined_path, &browser_options);
|
||||
|
||||
furi_string_free(predefined_path);
|
||||
return res;
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_layout_on_enter(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
if(bad_bt_layout_select(bad_bt)) {
|
||||
bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout);
|
||||
}
|
||||
scene_manager_previous_scene(bad_bt->scene_manager);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_layout_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "../bad_bt_app.h"
|
||||
|
||||
#define TAG "BadBtConfigMac"
|
||||
|
||||
void bad_bt_scene_config_mac_byte_input_callback(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_mac_on_enter(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = bad_bt->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Set BT MAC address");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
bad_bt_scene_config_mac_byte_input_callback,
|
||||
NULL,
|
||||
bad_bt,
|
||||
bad_bt->config.bt_mac,
|
||||
GAP_MAC_ADDR_SIZE);
|
||||
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigMac);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBtApp* bad_bt = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == BadBtAppCustomEventByteInputDone) {
|
||||
bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac);
|
||||
scene_manager_previous_scene(bad_bt->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_mac_on_exit(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(bad_bt->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(bad_bt->byte_input, "");
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "../bad_bt_app.h"
|
||||
|
||||
static void bad_bt_scene_config_name_text_input_callback(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventTextEditResult);
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_name_on_enter(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
TextInput* text_input = bad_bt->text_input;
|
||||
|
||||
text_input_set_header_text(text_input, "Set BT device name");
|
||||
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
bad_bt_scene_config_name_text_input_callback,
|
||||
bad_bt,
|
||||
bad_bt->config.bt_name,
|
||||
BAD_BT_ADV_NAME_MAX_LEN,
|
||||
true);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigName);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_config_name_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBtApp* bad_bt = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == BadBtAppCustomEventTextEditResult) {
|
||||
bt_set_profile_adv_name(bad_bt->bt, bad_bt->config.bt_name);
|
||||
}
|
||||
scene_manager_previous_scene(bad_bt->scene_manager);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_bt_scene_config_name_on_exit(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
TextInput* text_input = bad_bt->text_input;
|
||||
|
||||
text_input_reset(text_input);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "../bad_bt_app.h"
|
||||
|
||||
static void
|
||||
bad_bt_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
BadBtApp* app = context;
|
||||
|
||||
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, BadBtCustomEventErrorBack);
|
||||
}
|
||||
}
|
||||
|
||||
void bad_bt_scene_error_on_enter(void* context) {
|
||||
BadBtApp* app = context;
|
||||
|
||||
if(app->error == BadBtAppErrorNoFiles) {
|
||||
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
81,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", bad_bt_scene_error_event_callback, app);
|
||||
} else if(app->error == BadBtAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
3,
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewError);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_error_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBtApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == BadBtCustomEventErrorBack) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_bt_scene_error_on_exit(void* context) {
|
||||
BadBtApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "../bad_bt_app.h"
|
||||
#include <furi_hal_power.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_bt_file_select(BadBtApp* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_BT_APP_SCRIPT_EXTENSION, &I_badbt_10px);
|
||||
browser_options.base_path = BAD_BT_APP_BASE_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
bool res = dialog_file_browser_show(
|
||||
bad_bt->dialogs, bad_bt->file_path, bad_bt->file_path, &browser_options);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void bad_bt_scene_file_select_on_enter(void* context) {
|
||||
BadBtApp* bad_bt = context;
|
||||
|
||||
if(bad_bt->bad_bt_script) {
|
||||
bad_bt_script_close(bad_bt->bad_bt_script);
|
||||
bad_bt->bad_bt_script = NULL;
|
||||
}
|
||||
|
||||
if(bad_bt_file_select(bad_bt)) {
|
||||
bad_bt->bad_bt_script = bad_bt_script_open(bad_bt->file_path, bad_bt->bt, bad_bt);
|
||||
bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout);
|
||||
|
||||
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneWork);
|
||||
} else {
|
||||
view_dispatcher_stop(bad_bt->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
bool bad_bt_scene_file_select_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
// BadBtApp* bad_bt = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
void bad_bt_scene_file_select_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
// BadBtApp* bad_bt = context;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "../helpers/ducky_script.h"
|
||||
#include "../bad_bt_app.h"
|
||||
#include "../views/bad_bt_view.h"
|
||||
#include <furi_hal.h>
|
||||
#include "toolbox/path.h"
|
||||
|
||||
void bad_bt_scene_work_button_callback(InputKey key, void* context) {
|
||||
furi_assert(context);
|
||||
BadBtApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, key);
|
||||
}
|
||||
|
||||
bool bad_bt_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBtApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InputKeyLeft) {
|
||||
if(bad_bt_is_idle_state(app->bad_bt_view)) {
|
||||
scene_manager_next_scene(app->scene_manager, BadBtSceneConfig);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyOk) {
|
||||
bad_bt_script_toggle(app->bad_bt_script);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script));
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_bt_scene_work_on_enter(void* context) {
|
||||
BadBtApp* app = context;
|
||||
|
||||
FuriString* file_name;
|
||||
file_name = furi_string_alloc();
|
||||
path_extract_filename(app->file_path, file_name, true);
|
||||
bad_bt_set_file_name(app->bad_bt_view, furi_string_get_cstr(file_name));
|
||||
furi_string_free(file_name);
|
||||
|
||||
FuriString* layout;
|
||||
layout = furi_string_alloc();
|
||||
path_extract_filename(app->keyboard_layout, layout, true);
|
||||
bad_bt_set_layout(app->bad_bt_view, furi_string_get_cstr(layout));
|
||||
furi_string_free(layout);
|
||||
|
||||
bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script));
|
||||
|
||||
bad_bt_set_button_callback(app->bad_bt_view, bad_bt_scene_work_button_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewWork);
|
||||
}
|
||||
|
||||
void bad_bt_scene_work_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
#include "bad_bt_view.h"
|
||||
#include "../helpers/ducky_script.h"
|
||||
#include "../bad_bt_app.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define MAX_NAME_LEN 64
|
||||
|
||||
typedef struct {
|
||||
char file_name[MAX_NAME_LEN];
|
||||
char layout[MAX_NAME_LEN];
|
||||
BadBtState state;
|
||||
uint8_t anim_frame;
|
||||
} BadBtModel;
|
||||
|
||||
static void bad_bt_draw_callback(Canvas* canvas, void* _model) {
|
||||
BadBtModel* model = _model;
|
||||
|
||||
FuriString* disp_str;
|
||||
disp_str = furi_string_alloc_set("(BT) ");
|
||||
furi_string_cat_str(disp_str, model->file_name);
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
|
||||
|
||||
if(strlen(model->layout) == 0) {
|
||||
furi_string_set(disp_str, "(default)");
|
||||
} else {
|
||||
furi_string_reset(disp_str);
|
||||
furi_string_push_back(disp_str, '(');
|
||||
for(size_t i = 0; i < strlen(model->layout); i++)
|
||||
furi_string_push_back(disp_str, model->layout[i]);
|
||||
furi_string_push_back(disp_str, ')');
|
||||
}
|
||||
if(model->state.pin) {
|
||||
furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin);
|
||||
}
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_draw_str(
|
||||
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
|
||||
|
||||
furi_string_reset(disp_str);
|
||||
|
||||
if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) ||
|
||||
(model->state.state == BadBtStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
elements_button_left(canvas, "Config");
|
||||
} else if((model->state.state == BadBtStateRunning) || (model->state.state == BadBtStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
} else if(model->state.state == BadBtStateWaitForBtn) {
|
||||
elements_button_center(canvas, "Press to continue");
|
||||
} else if(model->state.state == BadBtStateWillRun) {
|
||||
elements_button_center(canvas, "Cancel");
|
||||
}
|
||||
|
||||
if(model->state.state == BadBtStateNotConnected) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device");
|
||||
} else if(model->state.state == BadBtStateWillRun) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
|
||||
} else if(model->state.state == BadBtStateFileError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
|
||||
} else if(model->state.state == BadBtStateScriptError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
furi_string_printf(disp_str, "line %u", model->state.error_line);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
furi_string_set_str(disp_str, model->state.error);
|
||||
elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else if(model->state.state == BadBtStateIdle) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadBtStateRunning) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadBtStateDone) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadBtStateDelay) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
}
|
||||
|
||||
furi_string_free(disp_str);
|
||||
}
|
||||
|
||||
static bool bad_bt_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
BadBt* bad_bt = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
|
||||
consumed = true;
|
||||
furi_assert(bad_bt->callback);
|
||||
bad_bt->callback(event->key, bad_bt->context);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
BadBt* bad_bt_alloc() {
|
||||
BadBt* bad_bt = malloc(sizeof(BadBt));
|
||||
|
||||
bad_bt->view = view_alloc();
|
||||
view_allocate_model(bad_bt->view, ViewModelTypeLocking, sizeof(BadBtModel));
|
||||
view_set_context(bad_bt->view, bad_bt);
|
||||
view_set_draw_callback(bad_bt->view, bad_bt_draw_callback);
|
||||
view_set_input_callback(bad_bt->view, bad_bt_input_callback);
|
||||
|
||||
return bad_bt;
|
||||
}
|
||||
|
||||
void bad_bt_free(BadBt* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
view_free(bad_bt->view);
|
||||
free(bad_bt);
|
||||
}
|
||||
|
||||
View* bad_bt_get_view(BadBt* bad_bt) {
|
||||
furi_assert(bad_bt);
|
||||
return bad_bt->view;
|
||||
}
|
||||
|
||||
void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context) {
|
||||
furi_assert(bad_bt);
|
||||
furi_assert(callback);
|
||||
with_view_model(
|
||||
bad_bt->view,
|
||||
BadBtModel * model,
|
||||
{
|
||||
UNUSED(model);
|
||||
bad_bt->callback = callback;
|
||||
bad_bt->context = context;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void bad_bt_set_file_name(BadBt* bad_bt, const char* name) {
|
||||
furi_assert(name);
|
||||
with_view_model(
|
||||
bad_bt->view, BadBtModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true);
|
||||
}
|
||||
|
||||
void bad_bt_set_layout(BadBt* bad_bt, const char* layout) {
|
||||
furi_assert(layout);
|
||||
with_view_model(
|
||||
bad_bt->view, BadBtModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true);
|
||||
}
|
||||
|
||||
void bad_bt_set_state(BadBt* bad_bt, BadBtState* st) {
|
||||
furi_assert(st);
|
||||
uint32_t pin = 0;
|
||||
if(bad_bt->context != NULL) {
|
||||
BadBtApp* app = bad_bt->context;
|
||||
if(app->bt != NULL) {
|
||||
pin = app->bt->pin;
|
||||
}
|
||||
}
|
||||
st->pin = pin;
|
||||
with_view_model(
|
||||
bad_bt->view,
|
||||
BadBtModel * model,
|
||||
{
|
||||
memcpy(&(model->state), st, sizeof(BadBtState));
|
||||
model->anim_frame ^= 1;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
bool bad_bt_is_idle_state(BadBt* bad_bt) {
|
||||
bool is_idle = false;
|
||||
with_view_model(
|
||||
bad_bt->view,
|
||||
BadBtModel * model,
|
||||
{
|
||||
if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) ||
|
||||
(model->state.state == BadBtStateNotConnected)) {
|
||||
is_idle = true;
|
||||
}
|
||||
},
|
||||
false);
|
||||
return is_idle;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef void (*BadBtButtonCallback)(InputKey key, void* context);
|
||||
|
||||
typedef struct {
|
||||
View* view;
|
||||
BadBtButtonCallback callback;
|
||||
void* context;
|
||||
} BadBt;
|
||||
|
||||
typedef struct BadBtState BadBtState;
|
||||
|
||||
BadBt* bad_bt_alloc();
|
||||
|
||||
void bad_bt_free(BadBt* bad_bt);
|
||||
|
||||
View* bad_bt_get_view(BadBt* bad_bt);
|
||||
|
||||
void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context);
|
||||
|
||||
void bad_bt_set_file_name(BadBt* bad_bt, const char* name);
|
||||
|
||||
void bad_bt_set_layout(BadBt* bad_bt, const char* layout);
|
||||
|
||||
void bad_bt_set_state(BadBt* bad_bt, BadBtState* st);
|
||||
|
||||
bool bad_bt_is_idle_state(BadBt* bad_bt);
|
||||
Vendored
+1
-1
Submodule applications/external/subbrute updated: c94efdf88a...d1317392a4
+1
-1
@@ -11,4 +11,4 @@
|
||||
// End of list
|
||||
|
||||
// Target firmware to build for
|
||||
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_OFFICIAL_DEV
|
||||
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
|
||||
+10
-10
@@ -38,8 +38,6 @@ void uart_terminal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context)
|
||||
static int32_t uart_worker(void* context) {
|
||||
UART_TerminalUart* uart = (void*)context;
|
||||
|
||||
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
|
||||
|
||||
while(1) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||
@@ -64,6 +62,16 @@ void uart_terminal_uart_tx(uint8_t* data, size_t len) {
|
||||
|
||||
UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) {
|
||||
UART_TerminalUart* uart = malloc(sizeof(UART_TerminalUart));
|
||||
uart->app = app;
|
||||
// Init all rx stream and thread early to avoid crashes
|
||||
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
|
||||
uart->rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(uart->rx_thread, "UART_TerminalUartRxThread");
|
||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
||||
furi_thread_set_context(uart->rx_thread, uart);
|
||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
||||
|
||||
furi_thread_start(uart->rx_thread);
|
||||
|
||||
furi_hal_console_disable();
|
||||
if(app->BAUDRATE == 0) {
|
||||
@@ -72,14 +80,6 @@ UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) {
|
||||
furi_hal_uart_set_br(UART_CH, app->BAUDRATE);
|
||||
furi_hal_uart_set_irq_cb(UART_CH, uart_terminal_uart_on_irq_cb, uart);
|
||||
|
||||
uart->app = app;
|
||||
uart->rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(uart->rx_thread, "UART_TerminalUartRxThread");
|
||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
||||
furi_thread_set_context(uart->rx_thread, uart);
|
||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
||||
|
||||
furi_thread_start(uart->rx_thread);
|
||||
return uart;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -97,11 +97,11 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
|
||||
NO_ARGS,
|
||||
FOCUS_CONSOLE_END,
|
||||
SHOW_STOPSCAN_TIP},
|
||||
{"Sniff PMKID on channel",
|
||||
{""},
|
||||
1,
|
||||
{"sniffpmkid -c"},
|
||||
INPUT_ARGS,
|
||||
{"Sniff PMKID",
|
||||
{"ap", "channel"},
|
||||
2,
|
||||
{"sniffpmkid -d -l", "sniffpmkid -c"},
|
||||
TOGGLE_ARGS,
|
||||
FOCUS_CONSOLE_END,
|
||||
SHOW_STOPSCAN_TIP},
|
||||
{"Channel",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WIFI_MARAUDER_APP_VERSION "v0.3.4"
|
||||
#define WIFI_MARAUDER_APP_VERSION "v0.3.5"
|
||||
|
||||
typedef struct WifiMarauderApp WifiMarauderApp;
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ App(
|
||||
name="Basic applications for main menu",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"gpio",
|
||||
#"gpio",
|
||||
#"ibutton",
|
||||
"infrared",
|
||||
#"infrared",
|
||||
"lfrfid",
|
||||
"nfc",
|
||||
"subghz",
|
||||
|
||||
@@ -186,11 +186,16 @@ int32_t lfrfid_app(void* p) {
|
||||
DOLPHIN_DEED(DolphinDeedRfidEmulate);
|
||||
} else {
|
||||
furi_string_set(app->file_path, args);
|
||||
lfrfid_load_key_data(app, app->file_path, true);
|
||||
view_dispatcher_attach_to_gui(
|
||||
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedRfidEmulate);
|
||||
if(lfrfid_load_key_data(app, app->file_path, true)) {
|
||||
view_dispatcher_attach_to_gui(
|
||||
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedRfidEmulate);
|
||||
} else {
|
||||
// TODO: exit properly
|
||||
lfrfid_free(app);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -12,4 +12,6 @@ enum NfcCustomEvent {
|
||||
NfcCustomEventDictAttackSkip,
|
||||
NfcCustomEventRpcLoad,
|
||||
NfcCustomEventRpcSessionClose,
|
||||
NfcCustomEventUpdateLog,
|
||||
NfcCustomEventSaveShadow,
|
||||
};
|
||||
|
||||
@@ -290,6 +290,9 @@ int32_t nfc_app(void* p) {
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
|
||||
@@ -14,6 +14,12 @@ ADD_SCENE(nfc, file_select, FileSelect)
|
||||
ADD_SCENE(nfc, emulate_uid, EmulateUid)
|
||||
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
|
||||
ADD_SCENE(nfc, nfca_menu, NfcaMenu)
|
||||
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
|
||||
ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu)
|
||||
ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput)
|
||||
ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock)
|
||||
ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate)
|
||||
ADD_SCENE(nfc, nfcv_sniff, NfcVSniff)
|
||||
ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
|
||||
ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData)
|
||||
ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)
|
||||
@@ -46,14 +52,18 @@ ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess)
|
||||
ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard)
|
||||
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
|
||||
ADD_SCENE(nfc, emv_menu, EmvMenu)
|
||||
#if FURI_DEBUG
|
||||
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
|
||||
#endif
|
||||
ADD_SCENE(nfc, device_info, DeviceInfo)
|
||||
ADD_SCENE(nfc, delete, Delete)
|
||||
ADD_SCENE(nfc, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm)
|
||||
ADD_SCENE(nfc, restore_original, RestoreOriginal)
|
||||
#if FURI_DEBUG
|
||||
ADD_SCENE(nfc, debug, Debug)
|
||||
ADD_SCENE(nfc, field, Field)
|
||||
#endif
|
||||
ADD_SCENE(nfc, dict_not_found, DictNotFound)
|
||||
ADD_SCENE(nfc, rpc, Rpc)
|
||||
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if FURI_DEBUG
|
||||
#include "../nfc_i.h"
|
||||
|
||||
enum SubmenuDebugIndex {
|
||||
@@ -52,3 +53,4 @@ void nfc_scene_debug_on_exit(void* context) {
|
||||
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
||||
#endif
|
||||
@@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||
|
||||
NfcProtocol protocol = nfc->dev->dev_data.protocol;
|
||||
const char* nfc_type = "NFC-A";
|
||||
|
||||
if(protocol == NfcDeviceProtocolEMV) {
|
||||
furi_string_set(temp_str, "EMV bank card");
|
||||
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
||||
@@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type));
|
||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
furi_string_set(temp_str, "MIFARE DESFire");
|
||||
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||
furi_string_set(temp_str, "ISO15693 tag");
|
||||
nfc_type = "NFC-V";
|
||||
} else {
|
||||
furi_string_set(temp_str, "Unknown ISO tag");
|
||||
}
|
||||
widget_add_string_element(
|
||||
nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||
widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A");
|
||||
widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type);
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if FURI_DEBUG
|
||||
#include "../nfc_i.h"
|
||||
#include <core/common_defines.h>
|
||||
|
||||
@@ -32,3 +33,4 @@ void nfc_scene_emulate_apdu_sequence_on_exit(void* context) {
|
||||
|
||||
nfc_blink_stop(nfc);
|
||||
}
|
||||
#endif
|
||||
@@ -4,6 +4,8 @@ enum SubmenuIndex {
|
||||
SubmenuIndexReadCardType,
|
||||
SubmenuIndexMfClassicKeys,
|
||||
SubmenuIndexMfUltralightUnlock,
|
||||
SubmenuIndexNfcVUnlock,
|
||||
SubmenuIndexNfcVSniff,
|
||||
};
|
||||
|
||||
void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
|
||||
@@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) {
|
||||
SubmenuIndexMfUltralightUnlock,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock SLIX-L",
|
||||
SubmenuIndexNfcVUnlock,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Listen NfcV Reader",
|
||||
SubmenuIndexNfcVSniff,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions));
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
@@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVUnlock) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVSniff) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if FURI_DEBUG
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_field_on_enter(void* context) {
|
||||
@@ -31,3 +32,4 @@ void nfc_scene_field_on_exit(void* context) {
|
||||
notification_internal_message(nfc->notifications, &sequence_reset_blue);
|
||||
popup_reset(nfc->popup);
|
||||
}
|
||||
#endif
|
||||
@@ -7,6 +7,17 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nfc_scene_nfc_data_info_get_key(uint8_t* data) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint32_t pos = 0; pos < 4; pos++) {
|
||||
value <<= 8;
|
||||
value |= data[pos];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void nfc_scene_nfc_data_info_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Widget* widget = nfc->widget;
|
||||
@@ -15,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
||||
NfcProtocol protocol = dev_data->protocol;
|
||||
uint8_t text_scroll_height = 0;
|
||||
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) ||
|
||||
(protocol == NfcDeviceProtocolMifareClassic)) {
|
||||
(protocol == NfcDeviceProtocolMifareClassic) || (protocol == NfcDeviceProtocolNfcV)) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
|
||||
text_scroll_height = 52;
|
||||
@@ -41,19 +52,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
||||
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
|
||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
|
||||
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n");
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n");
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
|
||||
}
|
||||
|
||||
// Set tag iso data
|
||||
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
|
||||
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
if(protocol == NfcDeviceProtocolNfcV) {
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
|
||||
furi_string_cat_printf(temp_str, "UID:\n");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\n");
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"DSFID: %02X %s\n",
|
||||
nfcv_data->dsfid,
|
||||
(nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"AFI: %02X %s\n",
|
||||
nfcv_data->afi,
|
||||
(nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
|
||||
furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref);
|
||||
furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
|
||||
furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size);
|
||||
|
||||
int maxBlocks = nfcv_data->block_num;
|
||||
if(maxBlocks > 32) {
|
||||
maxBlocks = 32;
|
||||
furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks);
|
||||
}
|
||||
|
||||
for(int block = 0; block < maxBlocks; block++) {
|
||||
const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : "";
|
||||
for(int pos = 0; pos < nfcv_data->block_size; pos++) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, " %s\n", status);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\n");
|
||||
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "Type: Plain\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-S\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Read %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Write %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-L\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX2\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Read %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Write %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08lX\n",
|
||||
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
|
||||
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
|
||||
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
|
||||
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
|
||||
|
||||
// Set application specific data
|
||||
if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
@@ -139,6 +296,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
|
||||
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200)
|
||||
|
||||
enum {
|
||||
NfcSceneNfcVEmulateStateWidget,
|
||||
NfcSceneNfcVEmulateStateTextBox,
|
||||
};
|
||||
|
||||
bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
|
||||
switch(event) {
|
||||
case NfcWorkerEventNfcVCommandExecuted:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog);
|
||||
break;
|
||||
case NfcWorkerEventNfcVContentChanged:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_textbox_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) {
|
||||
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
|
||||
Widget* widget = nfc->widget;
|
||||
widget_reset(widget);
|
||||
FuriString* info_str;
|
||||
info_str = furi_string_alloc();
|
||||
|
||||
widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
widget_add_string_element(
|
||||
widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV");
|
||||
if(strcmp(nfc->dev->dev_name, "")) {
|
||||
furi_string_printf(info_str, "%s", nfc->dev->dev_name);
|
||||
} else {
|
||||
for(uint8_t i = 0; i < data->uid_len; i++) {
|
||||
furi_string_cat_printf(info_str, "%02X ", data->uid[i]);
|
||||
}
|
||||
}
|
||||
furi_string_trim(info_str);
|
||||
widget_add_text_box_element(
|
||||
widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||
furi_string_free(info_str);
|
||||
if(data_received) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup Widget
|
||||
nfc_scene_nfcv_emulate_widget_config(nfc, false);
|
||||
// Setup TextBox
|
||||
TextBox* text_box = nfc->text_box;
|
||||
text_box_set_font(text_box, TextBoxFontHex);
|
||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||
text_box_set_text(text_box, "");
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
// Set Widget state and view
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
// Start worker
|
||||
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVEmulate,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_nfcv_emulate_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_emulate_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventUpdateLog) {
|
||||
// Add data button to widget if data is received for the first time
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
if(!furi_string_size(nfc->text_box_store)) {
|
||||
nfc_scene_nfcv_emulate_widget_config(nfc, true);
|
||||
}
|
||||
/* use the last n bytes from the log so there's enough space for the new log entry */
|
||||
size_t maxSize =
|
||||
NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1);
|
||||
if(furi_string_size(nfc->text_box_store) >= maxSize) {
|
||||
furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1));
|
||||
}
|
||||
furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command);
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||
|
||||
/* clear previously logged command */
|
||||
strcpy(nfcv_data->last_command, "");
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventSaveShadow) {
|
||||
if(furi_string_size(nfc->dev->load_path)) {
|
||||
nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state == NfcSceneNfcVEmulateStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Stop worker
|
||||
nfc_worker_stop(nfc->worker);
|
||||
|
||||
// Clear view
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
nfc_blink_stop(nfc);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void nfc_scene_nfcv_key_input_byte_input_callback(void* context) {
|
||||
Nfc* nfc = context;
|
||||
NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
memcpy(data->key_privacy, nfc->byte_input_store, 4);
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_key_input_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter The Password In Hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_scene_nfcv_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc,
|
||||
nfc->byte_input_store,
|
||||
4);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_key_input_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc->byte_input, "");
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexSave,
|
||||
SubmenuIndexEmulate,
|
||||
};
|
||||
|
||||
void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_menu_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
||||
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
||||
|
||||
submenu_set_selected_item(
|
||||
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu));
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexSave) {
|
||||
nfc->dev->format = NfcDeviceSaveFormatNfcV;
|
||||
// Clear device name
|
||||
nfc_device_set_name(nfc->dev, "");
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexEmulate) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
|
||||
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
|
||||
} else {
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event);
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_menu_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear view
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800)
|
||||
|
||||
enum {
|
||||
NfcSceneNfcVSniffStateWidget,
|
||||
NfcSceneNfcVSniffStateTextBox,
|
||||
};
|
||||
|
||||
bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
|
||||
switch(event) {
|
||||
case NfcWorkerEventNfcVCommandExecuted:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog);
|
||||
break;
|
||||
case NfcWorkerEventNfcVContentChanged:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_textbox_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) {
|
||||
Widget* widget = nfc->widget;
|
||||
widget_reset(widget);
|
||||
FuriString* info_str;
|
||||
info_str = furi_string_alloc();
|
||||
|
||||
widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV");
|
||||
furi_string_trim(info_str);
|
||||
widget_add_text_box_element(
|
||||
widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||
furi_string_free(info_str);
|
||||
if(data_received) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup Widget
|
||||
nfc_scene_nfcv_sniff_widget_config(nfc, false);
|
||||
// Setup TextBox
|
||||
TextBox* text_box = nfc->text_box;
|
||||
text_box_set_font(text_box, TextBoxFontHex);
|
||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||
text_box_set_text(text_box, "");
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
// Set Widget state and view
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
// Start worker
|
||||
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVSniff,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_nfcv_sniff_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_emulate_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventUpdateLog) {
|
||||
// Add data button to widget if data is received for the first time
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
if(!furi_string_size(nfc->text_box_store)) {
|
||||
nfc_scene_nfcv_sniff_widget_config(nfc, true);
|
||||
}
|
||||
/* use the last n bytes from the log so there's enough space for the new log entry */
|
||||
size_t maxSize =
|
||||
NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1);
|
||||
if(furi_string_size(nfc->text_box_store) >= maxSize) {
|
||||
furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1));
|
||||
}
|
||||
furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command);
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||
|
||||
/* clear previously logged command */
|
||||
strcpy(nfcv_data->last_command, "");
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventSaveShadow) {
|
||||
if(furi_string_size(nfc->dev->load_path)) {
|
||||
nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state == NfcSceneNfcVSniffStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Stop worker
|
||||
nfc_worker_stop(nfc->worker);
|
||||
|
||||
// Clear view
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
nfc_blink_stop(nfc);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
typedef enum {
|
||||
NfcSceneNfcVUnlockStateIdle,
|
||||
NfcSceneNfcVUnlockStateDetecting,
|
||||
NfcSceneNfcVUnlockStateUnlocked,
|
||||
NfcSceneNfcVUnlockStateAlreadyUnlocked,
|
||||
NfcSceneNfcVUnlockStateNotSupportedCard,
|
||||
} NfcSceneNfcVUnlockState;
|
||||
|
||||
static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
Nfc* nfc = context;
|
||||
NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
if(event == NfcWorkerEventNfcVPassKey) {
|
||||
memcpy(data->key_privacy, nfc->byte_input_store, 4);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_popup_callback(void* context) {
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) {
|
||||
FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data);
|
||||
NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data);
|
||||
|
||||
uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
if(curr_state != state) {
|
||||
Popup* popup = nfc->popup;
|
||||
if(state == NfcSceneNfcVUnlockStateDetecting) {
|
||||
popup_reset(popup);
|
||||
popup_set_text(
|
||||
popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else if(state == NfcSceneNfcVUnlockStateUnlocked) {
|
||||
popup_reset(popup);
|
||||
|
||||
if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) {
|
||||
snprintf(
|
||||
nfc->dev->dev_name,
|
||||
sizeof(nfc->dev->dev_name),
|
||||
"SLIX_%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
nfc_data->uid[0],
|
||||
nfc_data->uid[1],
|
||||
nfc_data->uid[2],
|
||||
nfc_data->uid[3],
|
||||
nfc_data->uid[4],
|
||||
nfc_data->uid[5],
|
||||
nfc_data->uid[6],
|
||||
nfc_data->uid[7]);
|
||||
|
||||
nfc->dev->format = NfcDeviceSaveFormatNfcV;
|
||||
|
||||
if(nfc_save_file(nfc)) {
|
||||
popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(
|
||||
popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop);
|
||||
}
|
||||
} else {
|
||||
popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
notification_message(nfc->notifications, &sequence_single_vibro);
|
||||
//notification_message(nfc->notifications, &sequence_success);
|
||||
|
||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
||||
popup_set_context(popup, nfc);
|
||||
popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
|
||||
} else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) {
|
||||
popup_reset(popup);
|
||||
|
||||
popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
||||
popup_set_context(popup, nfc);
|
||||
popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
} else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) {
|
||||
popup_reset(popup);
|
||||
popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop);
|
||||
popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop);
|
||||
popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48);
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
nfc_device_clear(nfc->dev);
|
||||
// Setup view
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
||||
// Start worker
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVUnlockAndSave,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_nfcv_unlock_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_read_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcWorkerEventCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventAborted) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventNoCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventWrongCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Stop worker
|
||||
nfc_worker_stop(nfc->worker);
|
||||
// Clear view
|
||||
popup_reset(nfc->popup);
|
||||
nfc_blink_stop(nfc);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexNfcVUnlockMenuManual,
|
||||
SubmenuIndexNfcVUnlockMenuTonieBox,
|
||||
};
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Enter PWD Manually",
|
||||
SubmenuIndexNfcVUnlockMenuManual,
|
||||
nfc_scene_nfcv_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Auth As TonieBox",
|
||||
SubmenuIndexNfcVUnlockMenuTonieBox,
|
||||
nfc_scene_nfcv_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_set_selected_item(submenu, state);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexNfcVUnlockMenuManual) {
|
||||
nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) {
|
||||
nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
||||
@@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventReadNfcV) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventReadMfUltralight) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
// Set unlock password input to 0xFFFFFFFF only on fresh read
|
||||
|
||||
@@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_rpc_emulate_callback,
|
||||
nfc);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVEmulate,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_rpc_emulate_callback,
|
||||
nfc);
|
||||
} else {
|
||||
nfc_worker_start(
|
||||
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);
|
||||
|
||||
@@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
}
|
||||
} else if(
|
||||
nfc->dev->format == NfcDeviceSaveFormatMifareUl ||
|
||||
nfc->dev->format == NfcDeviceSaveFormatNfcV ||
|
||||
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
submenu_add_item(
|
||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||
@@ -117,6 +118,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ enum SubmenuIndex {
|
||||
SubmenuIndexSaved,
|
||||
SubmenuIndexExtraAction,
|
||||
SubmenuIndexAddManually,
|
||||
#if FURI_DEBUG
|
||||
SubmenuIndexDebug,
|
||||
#endif
|
||||
};
|
||||
|
||||
void nfc_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
@@ -29,12 +31,12 @@ void nfc_scene_start_on_enter(void* context) {
|
||||
submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc);
|
||||
submenu_add_item(
|
||||
submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc);
|
||||
|
||||
#if FURI_DEBUG
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
submenu_add_item(
|
||||
submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);
|
||||
}
|
||||
|
||||
#endif
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
|
||||
|
||||
@@ -82,11 +84,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexDebug) {
|
||||
}
|
||||
#if FURI_DEBUG
|
||||
else if(event.event == SubmenuIndexDebug) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
|
||||
consumed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ App(
|
||||
"cli",
|
||||
"dialogs",
|
||||
],
|
||||
provides=["subghz_start"],
|
||||
provides=[
|
||||
"subghz_start",
|
||||
"subghz_load_dangerous_settings",
|
||||
],
|
||||
icon="A_Sub1ghz_14",
|
||||
stack_size=3 * 1024,
|
||||
order=10,
|
||||
@@ -23,3 +26,11 @@ App(
|
||||
requires=["subghz"],
|
||||
order=40,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="subghz_load_dangerous_settings",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="subghz_dangerous_freq",
|
||||
requires=["storage", "subghz"],
|
||||
order=650,
|
||||
)
|
||||
|
||||
@@ -36,6 +36,8 @@ typedef enum {
|
||||
SubmenuIndexCAME24bit868,
|
||||
SubmenuIndexCAMETwee,
|
||||
SubmenuIndexCAMESpace,
|
||||
SubmenuIndexCameAtomo433,
|
||||
SubmenuIndexCameAtomo868,
|
||||
SubmenuIndexPricenton433,
|
||||
SubmenuIndexPricenton315,
|
||||
SubmenuIndexBETT_433,
|
||||
|
||||
@@ -266,6 +266,34 @@ bool subghz_txrx_gen_alutech_at_4n_protocol(
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_came_atomo_protocol(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint32_t serial,
|
||||
uint16_t cnt) {
|
||||
SubGhzTxRx* txrx = context;
|
||||
|
||||
bool res = false;
|
||||
|
||||
txrx->transmitter =
|
||||
subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_CAME_ATOMO_NAME);
|
||||
subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0);
|
||||
|
||||
if(txrx->transmitter && subghz_protocol_came_atomo_create_data(
|
||||
subghz_transmitter_get_protocol_instance(txrx->transmitter),
|
||||
txrx->fff_data,
|
||||
serial,
|
||||
cnt,
|
||||
txrx->preset)) {
|
||||
res = true;
|
||||
}
|
||||
|
||||
subghz_transmitter_free(txrx->transmitter);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_somfy_telis_protocol(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
|
||||
@@ -108,6 +108,13 @@ bool subghz_txrx_gen_somfy_telis_protocol(
|
||||
uint8_t btn,
|
||||
uint16_t cnt);
|
||||
|
||||
bool subghz_txrx_gen_came_atomo_protocol(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint32_t serial,
|
||||
uint16_t cnt);
|
||||
|
||||
/**
|
||||
* Generate data SecPlus v2 protocol
|
||||
*
|
||||
|
||||
@@ -91,3 +91,9 @@ typedef enum {
|
||||
SubGhzViewReceiverModeLive,
|
||||
SubGhzViewReceiverModeFile,
|
||||
} SubGhzViewReceiverMode;
|
||||
|
||||
typedef enum {
|
||||
SubGhzDecodeRawStateStart,
|
||||
SubGhzDecodeRawStateLoading,
|
||||
SubGhzDecodeRawStateLoaded,
|
||||
} SubGhzDecodeRawState;
|
||||
@@ -120,7 +120,8 @@ bool subghz_scene_decode_raw_next(SubGhz* subghz) {
|
||||
uint32_t duration = level_duration_get_duration(level_duration);
|
||||
subghz_receiver_decode(receiver, level, duration);
|
||||
} else {
|
||||
subghz->decode_raw_state = SubGhzDecodeRawStateLoaded;
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoaded);
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
|
||||
subghz_view_receiver_add_data_progress(subghz->subghz_receiver, "Done!");
|
||||
@@ -155,11 +156,13 @@ void subghz_scene_decode_raw_on_enter(void* context) {
|
||||
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_Decodable);
|
||||
|
||||
if(subghz->decode_raw_state == SubGhzDecodeRawStateStart) {
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW) ==
|
||||
SubGhzDecodeRawStateStart) {
|
||||
//Decode RAW to history
|
||||
subghz_history_reset(subghz->history);
|
||||
if(subghz_scene_decode_raw_start(subghz)) {
|
||||
subghz->decode_raw_state = SubGhzDecodeRawStateLoading;
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoading);
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
}
|
||||
} else {
|
||||
@@ -192,10 +195,9 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case SubGhzCustomEventViewReceiverBack:
|
||||
subghz->decode_raw_state = SubGhzDecodeRawStateStart;
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart);
|
||||
subghz->idx_menu_chosen = 0;
|
||||
subghz->in_decoder_scene = false;
|
||||
subghz->in_decoder_scene_skip = false;
|
||||
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
|
||||
|
||||
@@ -214,7 +216,6 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubGhzCustomEventViewReceiverOK:
|
||||
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz->in_decoder_scene = true;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
|
||||
consumed = true;
|
||||
break;
|
||||
@@ -242,7 +243,7 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch(subghz->decode_raw_state) {
|
||||
switch(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
case SubGhzDecodeRawStateLoading:
|
||||
subghz_scene_decode_raw_next(subghz);
|
||||
break;
|
||||
|
||||
@@ -25,11 +25,14 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = {
|
||||
"17(1W)",
|
||||
};
|
||||
|
||||
#define DEBUG_COUNTER_COUNT 3
|
||||
#define DEBUG_COUNTER_COUNT 6
|
||||
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
|
||||
"+1",
|
||||
"+2",
|
||||
"+3",
|
||||
"+4",
|
||||
"+5",
|
||||
"+10",
|
||||
};
|
||||
|
||||
static void subghz_scene_ext_module_changed(VariableItem* item) {
|
||||
@@ -71,6 +74,15 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
|
||||
case 2:
|
||||
furi_hal_subghz_set_rolling_counter_mult(3);
|
||||
break;
|
||||
case 3:
|
||||
furi_hal_subghz_set_rolling_counter_mult(4);
|
||||
break;
|
||||
case 4:
|
||||
furi_hal_subghz_set_rolling_counter_mult(5);
|
||||
break;
|
||||
case 5:
|
||||
furi_hal_subghz_set_rolling_counter_mult(10);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -140,24 +152,58 @@ void subghz_scene_radio_settings_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, timestamp_names_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Counter incr.",
|
||||
DEBUG_COUNTER_COUNT,
|
||||
subghz_scene_receiver_config_set_debug_counter,
|
||||
subghz);
|
||||
switch(furi_hal_subghz_get_rolling_counter_mult()) {
|
||||
case 1:
|
||||
value_index = 0;
|
||||
break;
|
||||
case 2:
|
||||
value_index = 1;
|
||||
break;
|
||||
case 3:
|
||||
value_index = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Counter incr.",
|
||||
DEBUG_COUNTER_COUNT,
|
||||
subghz_scene_receiver_config_set_debug_counter,
|
||||
subghz);
|
||||
switch(furi_hal_subghz_get_rolling_counter_mult()) {
|
||||
case 1:
|
||||
value_index = 0;
|
||||
break;
|
||||
case 2:
|
||||
value_index = 1;
|
||||
break;
|
||||
case 3:
|
||||
value_index = 2;
|
||||
break;
|
||||
case 4:
|
||||
value_index = 3;
|
||||
break;
|
||||
case 5:
|
||||
value_index = 4;
|
||||
break;
|
||||
case 10:
|
||||
value_index = 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Counter incr.",
|
||||
3,
|
||||
subghz_scene_receiver_config_set_debug_counter,
|
||||
subghz);
|
||||
switch(furi_hal_subghz_get_rolling_counter_mult()) {
|
||||
case 1:
|
||||
value_index = 0;
|
||||
break;
|
||||
case 2:
|
||||
value_index = 1;
|
||||
break;
|
||||
case 3:
|
||||
value_index = 2;
|
||||
break;
|
||||
default:
|
||||
// Reset to default value
|
||||
value_index = 0;
|
||||
furi_hal_subghz_set_rolling_counter_mult(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, debug_counter_text[value_index]);
|
||||
|
||||
@@ -227,12 +227,20 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
break;
|
||||
case SubGhzCustomEventViewReceiverDeleteItem:
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||
|
||||
subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver);
|
||||
|
||||
subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen);
|
||||
subghz_view_receiver_delete_element_callback(subghz->subghz_receiver);
|
||||
|
||||
subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver);
|
||||
|
||||
subghz_scene_receiver_update_statusbar(subghz);
|
||||
if(subghz_history_get_last_index(subghz->history) == 0) {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case SubGhzCustomEventViewReceiverConfig:
|
||||
|
||||
@@ -141,7 +141,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz_scene_receiver_info_draw_widget(subghz);
|
||||
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
if(!subghz->in_decoder_scene) {
|
||||
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
@@ -161,9 +161,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
|
||||
subghz_file_name_clear(subghz);
|
||||
|
||||
if(subghz->in_decoder_scene) {
|
||||
subghz->in_decoder_scene_skip = true;
|
||||
}
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
}
|
||||
return true;
|
||||
@@ -192,9 +189,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
if(subghz->in_decoder_scene && !subghz->in_decoder_scene_skip) {
|
||||
subghz->in_decoder_scene = false;
|
||||
}
|
||||
|
||||
widget_reset(subghz->widget);
|
||||
keeloq_reset_mfname();
|
||||
keeloq_reset_kl_type();
|
||||
|
||||
@@ -127,7 +127,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
if(!(strcmp(subghz->file_name_tmp, "") == 0) ||
|
||||
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
SubGhzCustomEventManagerNoSet) {
|
||||
if(!subghz->in_decoder_scene) {
|
||||
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
furi_string_set(subghz->file_path, subghz->file_path_tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
|
||||
SubGhz* subghz = context;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubGhzCustomEventSceneSaveSuccess) {
|
||||
if(!subghz->in_decoder_scene) {
|
||||
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneReceiver)) {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave);
|
||||
@@ -38,7 +38,9 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
subghz->decode_raw_state = SubGhzDecodeRawStateStart;
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart);
|
||||
|
||||
subghz->idx_menu_chosen = 0;
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
|
||||
|
||||
@@ -64,11 +66,6 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
|
||||
void subghz_scene_save_success_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(subghz->in_decoder_scene) {
|
||||
subghz->in_decoder_scene = false;
|
||||
subghz->in_decoder_scene_skip = false;
|
||||
}
|
||||
|
||||
// Clear view
|
||||
Popup* popup = subghz->popup;
|
||||
|
||||
|
||||
@@ -199,6 +199,18 @@ void subghz_scene_set_type_on_enter(void* context) {
|
||||
SubmenuIndexCAMETwee,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"CAME Atomo 433MHz",
|
||||
SubmenuIndexCameAtomo433,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"CAME Atomo 868MHz",
|
||||
SubmenuIndexCameAtomo868,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"KL: CAME Space 433MHz",
|
||||
@@ -534,6 +546,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexCameAtomo433:
|
||||
generated_protocol = subghz_txrx_gen_came_atomo_protocol(
|
||||
subghz->txrx, "AM650", 433920000, (key & 0x0FFFFFFF) | 0x10000000, 0x0003);
|
||||
break;
|
||||
case SubmenuIndexCameAtomo868:
|
||||
generated_protocol = subghz_txrx_gen_came_atomo_protocol(
|
||||
subghz->txrx, "AM650", 868350000, (key & 0x0FFFFFFF) | 0x10000000, 0x0003);
|
||||
break;
|
||||
case SubmenuIndexBFTMitto:
|
||||
generated_protocol = subghz_txrx_gen_keeloq_bft_protocol(
|
||||
subghz->txrx,
|
||||
|
||||
@@ -76,8 +76,8 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
if(subghz_custom_btn_get() != 0) {
|
||||
subghz_custom_btn_set(0);
|
||||
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();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
// Calling restore!
|
||||
|
||||
@@ -60,9 +60,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
||||
// GUI
|
||||
subghz->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
subghz->in_decoder_scene = false;
|
||||
subghz->in_decoder_scene_skip = false;
|
||||
|
||||
// View Dispatcher
|
||||
subghz->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(subghz->view_dispatcher);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <firmware/targets/f7/furi_hal/furi_hal_subghz_i.h>
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
void subghz_dangerous_freq() {
|
||||
bool is_extended_i = false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) {
|
||||
flipper_format_read_bool(
|
||||
fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended_i, 1);
|
||||
}
|
||||
|
||||
furi_hal_subghz_set_dangerous_frequency(is_extended_i);
|
||||
|
||||
flipper_format_free(fff_data_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
@@ -54,12 +54,6 @@ typedef struct {
|
||||
uint8_t seed[4];
|
||||
} SecureData;
|
||||
|
||||
typedef enum {
|
||||
SubGhzDecodeRawStateStart,
|
||||
SubGhzDecodeRawStateLoading,
|
||||
SubGhzDecodeRawStateLoaded,
|
||||
} SubGhzDecodeRawState;
|
||||
|
||||
struct SubGhz {
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
@@ -98,16 +92,12 @@ struct SubGhz {
|
||||
FuriString* error_str;
|
||||
SubGhzLock lock;
|
||||
|
||||
bool in_decoder_scene;
|
||||
bool in_decoder_scene_skip;
|
||||
|
||||
bool ignore_starline;
|
||||
bool ignore_auto_alarms;
|
||||
bool ignore_magellan;
|
||||
|
||||
SecureData* secure_data;
|
||||
|
||||
SubGhzDecodeRawState decode_raw_state;
|
||||
SubGhzFileEncoderWorker* decode_raw_file_worker_encoder;
|
||||
|
||||
SubGhzThresholdRssi* threshold_rssi;
|
||||
|
||||
@@ -71,6 +71,7 @@ typedef struct {
|
||||
SubGhzViewReceiverMode mode;
|
||||
uint8_t u_rssi;
|
||||
size_t scroll_counter;
|
||||
bool nodraw;
|
||||
} SubGhzViewReceiverModel;
|
||||
|
||||
void subghz_view_receiver_set_mode(
|
||||
@@ -241,39 +242,47 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||
bool scrollbar = model->history_item > 4;
|
||||
FuriString* str_buff = furi_string_alloc();
|
||||
|
||||
SubGhzReceiverMenuItem* item_menu;
|
||||
if(!model->nodraw) {
|
||||
SubGhzReceiverMenuItem* item_menu;
|
||||
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
|
||||
item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
|
||||
furi_string_set(str_buff, item_menu->item_str);
|
||||
size_t scroll_counter = model->scroll_counter;
|
||||
if(model->idx == idx) {
|
||||
subghz_view_receiver_draw_frame(canvas, i, scrollbar);
|
||||
if(scroll_counter < SCROLL_DELAY) {
|
||||
// Show time of signal one moment
|
||||
furi_string_set(str_buff, item_menu->time);
|
||||
scroll_counter = 0;
|
||||
} else {
|
||||
scroll_counter -= SCROLL_DELAY;
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
|
||||
item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
|
||||
if(item_menu == NULL) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
scroll_counter = 0;
|
||||
if(item_menu->type == 0) {
|
||||
break;
|
||||
}
|
||||
furi_string_set(str_buff, item_menu->item_str);
|
||||
size_t scroll_counter = model->scroll_counter;
|
||||
if(model->idx == idx) {
|
||||
subghz_view_receiver_draw_frame(canvas, i, scrollbar);
|
||||
if(scroll_counter < SCROLL_DELAY) {
|
||||
// Show time of signal one moment
|
||||
furi_string_set(str_buff, item_menu->time);
|
||||
scroll_counter = 0;
|
||||
} else {
|
||||
scroll_counter -= SCROLL_DELAY;
|
||||
}
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
scroll_counter = 0;
|
||||
}
|
||||
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||
elements_scrollable_text_line(
|
||||
canvas,
|
||||
15,
|
||||
9 + i * FRAME_HEIGHT,
|
||||
(scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX),
|
||||
str_buff,
|
||||
scroll_counter,
|
||||
(model->idx != idx));
|
||||
furi_string_reset(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||
elements_scrollable_text_line(
|
||||
canvas,
|
||||
15,
|
||||
9 + i * FRAME_HEIGHT,
|
||||
(scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX),
|
||||
str_buff,
|
||||
scroll_counter,
|
||||
(model->idx != idx));
|
||||
furi_string_reset(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
furi_string_free(str_buff);
|
||||
|
||||
@@ -464,29 +473,12 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
SubGhzViewReceiverModel * model,
|
||||
{
|
||||
if(model->history_item != 0) {
|
||||
SubGhzReceiverMenuItemArray_it_t it;
|
||||
// SubGhzReceiverMenuItem* target_item =
|
||||
// SubGhzReceiverMenuItemArray_get(model->history->data, model->idx);
|
||||
SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
|
||||
while(!SubGhzReceiverMenuItemArray_end_p(it)) {
|
||||
SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
|
||||
|
||||
if(it->index == (size_t)(model->idx)) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->time);
|
||||
item->type = 0;
|
||||
SubGhzReceiverMenuItemArray_remove(model->history->data, it);
|
||||
}
|
||||
|
||||
SubGhzReceiverMenuItemArray_previous(it);
|
||||
}
|
||||
|
||||
// Callback
|
||||
subghz_receiver->callback(
|
||||
SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context);
|
||||
}
|
||||
},
|
||||
true);
|
||||
false);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
@@ -537,6 +529,7 @@ void subghz_view_receiver_exit(void* context) {
|
||||
model->idx = 0;
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
model->nodraw = false;
|
||||
},
|
||||
false);
|
||||
furi_timer_stop(subghz_receiver->timer);
|
||||
@@ -571,6 +564,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() {
|
||||
model->history_stat_str = furi_string_alloc();
|
||||
model->progress_str = furi_string_alloc();
|
||||
model->bar_show = SubGhzViewReceiverBarShowDefault;
|
||||
model->nodraw = false;
|
||||
model->history = malloc(sizeof(SubGhzReceiverHistory));
|
||||
SubGhzReceiverMenuItemArray_init(model->history->data);
|
||||
},
|
||||
@@ -628,6 +622,23 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec
|
||||
subghz_receiver->view,
|
||||
SubGhzViewReceiverModel * model,
|
||||
{
|
||||
SubGhzReceiverMenuItemArray_it_t it;
|
||||
// SubGhzReceiverMenuItem* target_item =
|
||||
// SubGhzReceiverMenuItemArray_get(model->history->data, model->idx);
|
||||
SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
|
||||
while(!SubGhzReceiverMenuItemArray_end_p(it)) {
|
||||
SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
|
||||
|
||||
if(it->index == (size_t)(model->idx)) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->time);
|
||||
item->type = 0;
|
||||
SubGhzReceiverMenuItemArray_remove(model->history->data, it);
|
||||
}
|
||||
|
||||
SubGhzReceiverMenuItemArray_previous(it);
|
||||
}
|
||||
|
||||
if(model->history_item == 5) {
|
||||
if(model->idx >= 2) {
|
||||
model->idx = model->history_item - 1;
|
||||
@@ -640,7 +651,20 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec
|
||||
}
|
||||
},
|
||||
true);
|
||||
furi_delay_ms(200);
|
||||
}
|
||||
|
||||
void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, SubGhzViewReceiverModel * model, { model->nodraw = false; }, true);
|
||||
}
|
||||
|
||||
void subghz_view_receiver_disable_draw_callback(SubGhzViewReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, SubGhzViewReceiverModel * model, { model->nodraw = true; }, true);
|
||||
}
|
||||
|
||||
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
|
||||
|
||||
@@ -49,4 +49,8 @@ void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint
|
||||
|
||||
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver);
|
||||
|
||||
void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver);
|
||||
|
||||
void subghz_view_receiver_disable_draw_callback(SubGhzViewReceiver* subghz_receiver);
|
||||
|
||||
void subghz_view_receiver_exit(void* context);
|
||||
|
||||
@@ -124,7 +124,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
|
||||
},
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
} // Finish "Back" key processing
|
||||
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
@@ -136,124 +136,67 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
|
||||
},
|
||||
true);
|
||||
|
||||
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(0);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
model->draw_temp_button = false;
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
}
|
||||
if(can_be_sent) {
|
||||
if(event->key == InputKeyOk && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
model->draw_temp_button = false;
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
} // Finish "OK" key processing
|
||||
|
||||
// Temp Buttons (UP)
|
||||
if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(1);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(subghz_custom_btn_get_original() != 0) {
|
||||
if(subghz_custom_btn_get() == 1) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
|
||||
model->draw_temp_button = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
}
|
||||
// Down
|
||||
if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(2);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(subghz_custom_btn_get_original() != 0) {
|
||||
if(subghz_custom_btn_get() == 2) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
|
||||
model->draw_temp_button = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
}
|
||||
// Left
|
||||
if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(3);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(subghz_custom_btn_get_original() != 0) {
|
||||
if(subghz_custom_btn_get() == 3) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
|
||||
model->draw_temp_button = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
}
|
||||
// Right
|
||||
if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) {
|
||||
subghz_custom_btn_set(4);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(subghz_custom_btn_get_original() != 0) {
|
||||
if(subghz_custom_btn_get() == 4) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", subghz_custom_btn_get_original());
|
||||
model->draw_temp_button = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
if(subghz_custom_btn_is_allowed()) {
|
||||
uint8_t temp_btn_id;
|
||||
if(event->key == InputKeyUp) {
|
||||
temp_btn_id = SUBGHZ_CUSTOM_BTN_UP;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
temp_btn_id = SUBGHZ_CUSTOM_BTN_DOWN;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
temp_btn_id = SUBGHZ_CUSTOM_BTN_LEFT;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
temp_btn_id = SUBGHZ_CUSTOM_BTN_RIGHT;
|
||||
} else {
|
||||
// Finish processing if the button is different
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == InputTypePress) {
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(subghz_custom_btn_get_original() != 0) {
|
||||
if(subghz_custom_btn_set(temp_btn_id)) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id,
|
||||
"%01X",
|
||||
subghz_custom_btn_get_original());
|
||||
model->draw_temp_button = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||
return true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
subghz_transmitter->callback(
|
||||
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -3,7 +3,10 @@ App(
|
||||
name="Sub-GHz Remote",
|
||||
apptype=FlipperAppType.APP,
|
||||
entry_point="subghz_remote_app",
|
||||
cdefines=["APP_SUBGHZREMOTE"],
|
||||
cdefines=[
|
||||
"APP_SUBGHZREMOTE",
|
||||
"SUBREM_LIGHT",
|
||||
],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
@@ -11,4 +14,4 @@ App(
|
||||
icon="A_SubGHzRemote_14",
|
||||
stack_size=4 * 1024,
|
||||
order=11,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//SubmenuIndex
|
||||
SubmenuIndexSubRemOpenMapFile,
|
||||
SubmenuIndexSubRemRemoteView,
|
||||
SubmenuIndexSubRemAbout,
|
||||
|
||||
//SubRemCustomEvent
|
||||
SubRemCustomEventViewRemoteStartUP = 100,
|
||||
SubRemCustomEventViewRemoteStartDOWN,
|
||||
SubRemCustomEventViewRemoteStartLEFT,
|
||||
SubRemCustomEventViewRemoteStartRIGHT,
|
||||
SubRemCustomEventViewRemoteStartOK,
|
||||
SubRemCustomEventViewRemoteBack,
|
||||
SubRemCustomEventViewRemoteStop,
|
||||
SubRemCustomEventViewRemoteForcedStop,
|
||||
} SubRemCustomEvent;
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
// TODO: File version/type logic
|
||||
// #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 {
|
||||
SubRemViewSubmenu,
|
||||
SubRemViewWidget,
|
||||
SubRemViewPopup,
|
||||
SubRemViewTextInput,
|
||||
SubRemViewIDRemote,
|
||||
} SubRemViewID;
|
||||
|
||||
typedef enum {
|
||||
SubRemLoadMapStateBack = 0,
|
||||
SubRemLoadMapStateError,
|
||||
SubRemLoadMapStateOK,
|
||||
} SubRemLoadMapState;
|
||||
@@ -0,0 +1,30 @@
|
||||
#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,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#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
|
||||
@@ -0,0 +1,3 @@
|
||||
ADD_SCENE(subrem, start, Start)
|
||||
ADD_SCENE(subrem, openmapfile, OpenMapFile)
|
||||
ADD_SCENE(subrem, remote, Remote)
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "../subghz_remote_app_i.h"
|
||||
|
||||
void subrem_scene_openmapfile_on_enter(void* context) {
|
||||
SubGhzRemoteApp* app = context;
|
||||
SubRemLoadMapState load_state = subrem_load_from_file(app);
|
||||
|
||||
if(load_state == SubRemLoadMapStateError) {
|
||||
#ifdef SUBREM_LIGHT
|
||||
dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file");
|
||||
#else
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter);
|
||||
dialog_message_set_text(message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(message, "Back", NULL, NULL);
|
||||
dialog_message_show(app->dialogs, message);
|
||||
|
||||
dialog_message_free(message);
|
||||
#endif
|
||||
}
|
||||
if(load_state == SubRemLoadMapStateOK) {
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
|
||||
} else {
|
||||
// TODO: Map Preset Reset
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool subrem_scene_openmapfile_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void subrem_scene_openmapfile_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
static bool subrem_scene_remote_update_data_show(void* context) {
|
||||
SubGhzRemoteApp* app = context;
|
||||
bool ret = false;
|
||||
|
||||
subrem_view_remote_add_data_to_show(
|
||||
app->subrem_remote_view,
|
||||
furi_string_get_cstr(app->subs_preset[0]->label),
|
||||
furi_string_get_cstr(app->subs_preset[1]->label),
|
||||
furi_string_get_cstr(app->subs_preset[2]->label),
|
||||
furi_string_get_cstr(app->subs_preset[3]->label),
|
||||
furi_string_get_cstr(app->subs_preset[4]->label));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subrem_scene_remote_on_enter(void* context) {
|
||||
SubGhzRemoteApp* app = context;
|
||||
|
||||
subrem_scene_remote_update_data_show(app);
|
||||
|
||||
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_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, SubRemSceneOpenMapFile)) {
|
||||
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 == SubRemCustomEventViewRemoteStartUP ||
|
||||
event.event == SubRemCustomEventViewRemoteStartDOWN ||
|
||||
event.event == SubRemCustomEventViewRemoteStartLEFT ||
|
||||
event.event == SubRemCustomEventViewRemoteStartRIGHT ||
|
||||
event.event == SubRemCustomEventViewRemoteStartOK) {
|
||||
// Start sending sub
|
||||
subrem_tx_stop_sub(app, true);
|
||||
app->chusen_sub = subrem_scene_remote_event_to_index(event.event);
|
||||
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateLoading);
|
||||
if(subrem_tx_start_sub(
|
||||
app,
|
||||
app->subs_preset[app->chusen_sub],
|
||||
subrem_scene_remote_raw_callback_end_tx)) {
|
||||
subrem_view_remote_set_presed_btn(app->subrem_remote_view, app->chusen_sub);
|
||||
subrem_view_remote_set_state(
|
||||
app->subrem_remote_view, SubRemViewRemoteStateSending);
|
||||
notification_message(app->notifications, &sequence_blink_start_magenta);
|
||||
} else {
|
||||
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubRemCustomEventViewRemoteForcedStop) {
|
||||
subrem_tx_stop_sub(app, true);
|
||||
subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0);
|
||||
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
|
||||
|
||||
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_presed_btn(app->subrem_remote_view, 0);
|
||||
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
|
||||
|
||||
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_presed_btn(app->subrem_remote_view, 0);
|
||||
subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle);
|
||||
|
||||
notification_message(app->notifications, &sequence_blink_stop);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#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,
|
||||
// "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, SubRemViewSubmenu);
|
||||
}
|
||||
|
||||
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_next_scene(app->scene_manager, SubRemSceneOpenMapFile);
|
||||
consumed = true;
|
||||
}
|
||||
// } else if(event.event == SubmenuIndexSubRemAbout) {
|
||||
// scene_manager_next_scene(app->scene_manager, SubRemSceneAbout);
|
||||
// consumed = true;
|
||||
// }
|
||||
#if FURI_DEBUG
|
||||
else if(event.event == SubmenuIndexSubRemRemoteView) {
|
||||
scene_manager_next_scene(app->scene_manager, SubRemSceneRemote);
|
||||
consumed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void subrem_scene_start_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzRemoteApp* app = context;
|
||||
submenu_reset(app->submenu);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,448 @@
|
||||
#include "subghz_remote_app_i.h"
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
|
||||
// #include <lib/subghz/protocols/keeloq.h>
|
||||
// #include <lib/subghz/protocols/star_line.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"},
|
||||
};
|
||||
|
||||
static bool
|
||||
subrem_set_preset_data(SubGhzSetting* setting, FreqPreset* freq_preset, const char* preset) {
|
||||
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";
|
||||
return false;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown preset");
|
||||
return false;
|
||||
}
|
||||
size_t preset_index = subghz_setting_get_inx_preset_by_name(setting, preset_name);
|
||||
freq_preset->data = subghz_setting_get_preset_data(setting, preset_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
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_set_str("N/A");
|
||||
|
||||
sub_preset->type = SubGhzProtocolTypeUnknown;
|
||||
|
||||
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);
|
||||
|
||||
free(sub_preset);
|
||||
}
|
||||
|
||||
static void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) {
|
||||
furi_assert(sub_preset);
|
||||
|
||||
furi_string_set_str(sub_preset->label, "N/A");
|
||||
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;
|
||||
}
|
||||
|
||||
void subrem_map_preset_reset(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
subrem_sub_file_preset_reset(app->subs_preset[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static bool subrem_map_preset_load(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) {
|
||||
furi_assert(app);
|
||||
bool ret = false;
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->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(!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
|
||||
path_extract_filename(sub_preset->file_path, sub_preset->label, true);
|
||||
} else {
|
||||
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));
|
||||
ret = true;
|
||||
}
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subrem_save_protocol_to_file(FlipperFormat* flipper_format, const char* dev_file_name) {
|
||||
furi_assert(flipper_format);
|
||||
furi_assert(dev_file_name);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
|
||||
|
||||
bool saved = false;
|
||||
FuriString* file_dir = furi_string_alloc();
|
||||
|
||||
path_extract_dirname(dev_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, dev_file_name)) {
|
||||
break;
|
||||
}
|
||||
//ToDo check Write
|
||||
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||
stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS);
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
furi_string_free(file_dir);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool subrem_tx_start_sub(
|
||||
SubGhzRemoteApp* app,
|
||||
SubRemSubFilePreset* sub_preset,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback) {
|
||||
furi_assert(app);
|
||||
furi_assert(sub_preset);
|
||||
bool ret = false;
|
||||
|
||||
subrem_tx_stop_sub(app, true);
|
||||
|
||||
if(sub_preset->type == SubGhzProtocolTypeUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label));
|
||||
|
||||
subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK);
|
||||
keeloq_reset_original_btn();
|
||||
subghz_custom_btns_reset();
|
||||
|
||||
do {
|
||||
flipper_format_rewind(sub_preset->fff_data); //
|
||||
|
||||
app->transmitter = subghz_transmitter_alloc_init(
|
||||
app->environment, furi_string_get_cstr(sub_preset->protocaol_name));
|
||||
|
||||
if(app->transmitter) {
|
||||
if(subghz_transmitter_deserialize(app->transmitter, sub_preset->fff_data) !=
|
||||
SubGhzProtocolStatusOk) {
|
||||
FURI_LOG_E(TAG, "Deserialize error!");
|
||||
break;
|
||||
}
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_idle();
|
||||
furi_hal_subghz_load_custom_preset(sub_preset->freq_preset.data);
|
||||
furi_hal_gpio_init(
|
||||
furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
furi_hal_subghz_idle();
|
||||
|
||||
furi_hal_subghz_set_frequency_and_path(sub_preset->freq_preset.frequency);
|
||||
furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
|
||||
furi_hal_gpio_init(
|
||||
furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
if(!furi_hal_subghz_tx()) {
|
||||
FURI_LOG_E(TAG, "Sending not allowed");
|
||||
break;
|
||||
}
|
||||
|
||||
if(sub_preset->type == SubGhzProtocolTypeRAW) {
|
||||
subghz_protocol_raw_file_encoder_worker_set_callback_end(
|
||||
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(
|
||||
app->transmitter),
|
||||
callback,
|
||||
app);
|
||||
}
|
||||
|
||||
furi_hal_subghz_start_async_tx(subghz_transmitter_yield, app->transmitter);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
app->tx_running = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void subghz_tx_stop(SubGhzRemoteApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
//Stop TX
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
|
||||
subghz_transmitter_stop(app->transmitter);
|
||||
subghz_transmitter_free(app->transmitter);
|
||||
furi_hal_subghz_idle();
|
||||
}
|
||||
|
||||
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) {
|
||||
furi_assert(app);
|
||||
SubRemSubFilePreset* sub_preset = app->subs_preset[app->chusen_sub];
|
||||
|
||||
if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) {
|
||||
// SubRemSubKeyTypeRawKey)) {
|
||||
if(app->tx_running) {
|
||||
subghz_tx_stop(app);
|
||||
|
||||
if(sub_preset->type == SubGhzProtocolTypeDynamic) {
|
||||
subrem_save_protocol_to_file(
|
||||
sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path));
|
||||
|
||||
keeloq_reset_mfname();
|
||||
keeloq_reset_kl_type();
|
||||
keeloq_reset_original_btn();
|
||||
subghz_custom_btns_reset();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
app->tx_running = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool subrem_map_preset_check(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) {
|
||||
furi_assert(app);
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t temp_data32;
|
||||
bool ret = false;
|
||||
bool sub_preset_loaded = false;
|
||||
SubRemSubFilePreset* sub_preset;
|
||||
|
||||
for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) {
|
||||
sub_preset = app->subs_preset[i];
|
||||
sub_preset_loaded = false;
|
||||
if(furi_string_empty(sub_preset->file_path)) {
|
||||
// FURI_LOG_I(TAG, "Empty file path");
|
||||
continue;
|
||||
}
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(
|
||||
fff_data_file, furi_string_get_cstr(sub_preset->file_path))) {
|
||||
FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path));
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//Load frequency
|
||||
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(app->setting);
|
||||
} else if(!furi_hal_subghz_is_tx_allowed(temp_data32)) {
|
||||
FURI_LOG_E(TAG, "This frequency can only be used for RX");
|
||||
break;
|
||||
} else {
|
||||
sub_preset->freq_preset.frequency = temp_data32;
|
||||
}
|
||||
|
||||
//Load preset
|
||||
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Preset");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!subrem_set_preset_data(
|
||||
app->setting, &sub_preset->freq_preset, furi_string_get_cstr(temp_str))) {
|
||||
FURI_LOG_E(TAG, "Cannot load preset.");
|
||||
break;
|
||||
}
|
||||
|
||||
//Load protocol
|
||||
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));
|
||||
} else {
|
||||
stream_copy_full(
|
||||
flipper_format_get_raw_stream(fff_data_file),
|
||||
flipper_format_get_raw_stream(fff_data));
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* protocol_registry_items =
|
||||
subghz_environment_get_protocol_registry(app->environment);
|
||||
|
||||
const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(
|
||||
protocol_registry_items, furi_string_get_cstr(temp_str));
|
||||
|
||||
if(!protocol) {
|
||||
FURI_LOG_E(TAG, "Protocol not found");
|
||||
break;
|
||||
} else if(protocol->flag & SubGhzProtocolFlag_Send) {
|
||||
if((protocol->type == SubGhzProtocolTypeStatic) ||
|
||||
(protocol->type == SubGhzProtocolTypeDynamic) ||
|
||||
// (protocol->type == SubGhzProtocolTypeBinRAW) || // TODO: BINRAW
|
||||
(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");
|
||||
}
|
||||
|
||||
sub_preset_loaded = true;
|
||||
ret |= true;
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label));
|
||||
#endif
|
||||
} while(false);
|
||||
|
||||
// TODO:
|
||||
// Load file state logic
|
||||
// Label depending on the state
|
||||
|
||||
if(!sub_preset_loaded) {
|
||||
furi_string_set_str(sub_preset->label, "N/A");
|
||||
}
|
||||
|
||||
flipper_format_file_close(fff_data_file);
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool 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);
|
||||
bool ret = false;
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Open Map File..");
|
||||
#endif
|
||||
subrem_map_preset_reset(app);
|
||||
|
||||
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
|
||||
FURI_LOG_E(TAG, "Could not open MAP file %s", file_path);
|
||||
} else {
|
||||
if(!subrem_map_preset_load(app, 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)) &&
|
||||
(subrem_map_preset_check(app, fff_data_file))) {
|
||||
FURI_LOG_I(TAG, "Load Map File Seccesful");
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Popup for error or return error type
|
||||
if(!ret) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_sub1_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 if(subrem_map_file_load(app, furi_string_get_cstr(app->file_path))) {
|
||||
ret = SubRemLoadMapStateOK;
|
||||
} else {
|
||||
ret = SubRemLoadMapStateError;
|
||||
}
|
||||
|
||||
furi_string_free(file_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/subrem_types.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "views/remote.h"
|
||||
|
||||
#include "scenes/subrem_scene.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 <notification/notification_messages.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <storage/storage.h>
|
||||
#include <gui/modules/popup.h>
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include <lib/subghz/protocols/raw.h>
|
||||
|
||||
#include <lib/subghz/subghz_setting.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
|
||||
#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote")
|
||||
#define SUBREM_MAX_LEN_NAME 64
|
||||
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
uint8_t* data;
|
||||
} FreqPreset;
|
||||
|
||||
// Sub File preset
|
||||
typedef struct {
|
||||
FlipperFormat* fff_data;
|
||||
FreqPreset freq_preset;
|
||||
FuriString* file_path;
|
||||
FuriString* protocaol_name;
|
||||
FuriString* label;
|
||||
SubGhzProtocolType type;
|
||||
} SubRemSubFilePreset;
|
||||
|
||||
SubRemSubFilePreset* subrem_sub_file_preset_alloc();
|
||||
|
||||
void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset);
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Submenu* submenu;
|
||||
FuriString* file_path;
|
||||
char file_name_tmp[SUBREM_MAX_LEN_NAME];
|
||||
|
||||
SubRemViewRemote* subrem_remote_view;
|
||||
|
||||
SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount];
|
||||
|
||||
SubGhzSetting* setting;
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzReceiver* receiver;
|
||||
SubGhzTransmitter* transmitter;
|
||||
|
||||
bool tx_running;
|
||||
|
||||
uint8_t chusen_sub;
|
||||
|
||||
// TODO: LoadFileError
|
||||
} SubGhzRemoteApp;
|
||||
|
||||
SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app);
|
||||
|
||||
bool subrem_tx_start_sub(
|
||||
SubGhzRemoteApp* app,
|
||||
SubRemSubFilePreset* sub_preset,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback);
|
||||
|
||||
bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced);
|
||||
@@ -0,0 +1,302 @@
|
||||
#include "remote.h"
|
||||
#include "../subghz_remote_app_i.h"
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 16
|
||||
|
||||
struct SubRemViewRemote {
|
||||
View* view;
|
||||
SubRemViewRemoteCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
// TODO: model
|
||||
typedef struct {
|
||||
// FuriString* up_label;
|
||||
// FuriString* down_label;
|
||||
// FuriString* left_label;
|
||||
// FuriString* right_label;
|
||||
// FuriString* ok_label;
|
||||
|
||||
char* up_label;
|
||||
char* down_label;
|
||||
char* left_label;
|
||||
char* right_label;
|
||||
char* ok_label;
|
||||
|
||||
SubRemViewRemoteState state;
|
||||
|
||||
uint8_t pressed_btn;
|
||||
} SubRemViewRemoteModel;
|
||||
|
||||
void subrem_view_remote_set_callback(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteCallback callback,
|
||||
void* context) {
|
||||
furi_assert(subrem_view_remote);
|
||||
|
||||
subrem_view_remote->callback = callback;
|
||||
subrem_view_remote->context = context;
|
||||
}
|
||||
|
||||
void subrem_view_remote_add_data_to_show(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
const char* up_label,
|
||||
const char* down_label,
|
||||
const char* left_label,
|
||||
const char* right_label,
|
||||
const char* ok_label) {
|
||||
furi_assert(subrem_view_remote);
|
||||
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
strncpy(model->up_label, up_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
strncpy(model->down_label, down_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
strncpy(model->left_label, left_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
strncpy(model->right_label, right_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
strncpy(model->ok_label, ok_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH);
|
||||
|
||||
// furi_string_set(model->up_label, up_label);
|
||||
// furi_string_set(model->down_label, down_label);
|
||||
// furi_string_set(model->left_label, left_label);
|
||||
// furi_string_set(model->right_label, right_label);
|
||||
// furi_string_set(model->ok_label, ok_label);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn) {
|
||||
furi_assert(subrem_view_remote);
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{ model->pressed_btn = presed_btn; },
|
||||
true);
|
||||
}
|
||||
|
||||
void subrem_view_remote_set_state(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteState state) {
|
||||
furi_assert(subrem_view_remote);
|
||||
with_view_model(
|
||||
subrem_view_remote->view, SubRemViewRemoteModel * model, { model->state = state; }, 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);
|
||||
canvas_draw_str(canvas, 10, 10, model->up_label);
|
||||
canvas_draw_str(canvas, 10, 20, model->down_label);
|
||||
canvas_draw_str(canvas, 10, 30, model->left_label);
|
||||
canvas_draw_str(canvas, 10, 40, model->right_label);
|
||||
canvas_draw_str(canvas, 10, 50, model->ok_label);
|
||||
|
||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
||||
// canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label));
|
||||
|
||||
canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit.");
|
||||
|
||||
//Status text and indicator
|
||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||
|
||||
if(model->state == SubRemViewRemoteStateIdle) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
//Repeat indicator
|
||||
//canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:");
|
||||
//canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14);
|
||||
//canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat));
|
||||
}
|
||||
|
||||
bool subrem_view_remote_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
SubRemViewRemote* subrem_view_remote = context;
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeLong) {
|
||||
with_view_model(
|
||||
subrem_view_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
strcpy(model->up_label, "N/A");
|
||||
strcpy(model->down_label, "N/A");
|
||||
strcpy(model->left_label, "N/A");
|
||||
strcpy(model->right_label, "N/A");
|
||||
strcpy(model->ok_label, "N/A");
|
||||
},
|
||||
false);
|
||||
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;
|
||||
|
||||
model->up_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
model->down_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
model->left_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
model->right_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
model->ok_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1);
|
||||
|
||||
strcpy(model->up_label, "N/A");
|
||||
strcpy(model->down_label, "N/A");
|
||||
strcpy(model->left_label, "N/A");
|
||||
strcpy(model->right_label, "N/A");
|
||||
strcpy(model->ok_label, "N/A");
|
||||
|
||||
// model->up_label = furi_string_alloc_set_str("N/A");
|
||||
// model->down_label = furi_string_alloc_set_str("N/A");
|
||||
// model->left_label = furi_string_alloc_set_str("N/A");
|
||||
// model->right_label = furi_string_alloc_set_str("N/A");
|
||||
// model->ok_label = furi_string_alloc_set_str("N/A");
|
||||
|
||||
model->pressed_btn = 0;
|
||||
},
|
||||
true);
|
||||
return subrem_view_remote;
|
||||
}
|
||||
|
||||
void subrem_view_remote_free(SubRemViewRemote* subghz_remote) {
|
||||
furi_assert(subghz_remote);
|
||||
|
||||
with_view_model(
|
||||
subghz_remote->view,
|
||||
SubRemViewRemoteModel * model,
|
||||
{
|
||||
free(model->up_label);
|
||||
free(model->down_label);
|
||||
free(model->left_label);
|
||||
free(model->right_label);
|
||||
free(model->ok_label);
|
||||
|
||||
// furi_string_free(model->up_label);
|
||||
// furi_string_free(model->down_label);
|
||||
// furi_string_free(model->left_label);
|
||||
// furi_string_free(model->right_label);
|
||||
// furi_string_free(model->ok_label);
|
||||
},
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/subrem_custom_event.h"
|
||||
|
||||
typedef enum {
|
||||
SubRemViewRemoteStateIdle,
|
||||
SubRemViewRemoteStateLoading,
|
||||
SubRemViewRemoteStateSending,
|
||||
} 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_add_data_to_show(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
const char* up_label,
|
||||
const char* down_label,
|
||||
const char* left_label,
|
||||
const char* right_label,
|
||||
const char* ok_label);
|
||||
|
||||
void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn);
|
||||
void subrem_view_remote_set_state(
|
||||
SubRemViewRemote* subrem_view_remote,
|
||||
SubRemViewRemoteState state);
|
||||
@@ -9,5 +9,6 @@ App(
|
||||
"desktop",
|
||||
"loader",
|
||||
"power",
|
||||
"namechanger_srv",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -61,6 +61,7 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {
|
||||
|
||||
static void bt_pin_code_show(Bt* bt, uint32_t pin_code) {
|
||||
bt->pin_code = pin_code;
|
||||
if(bt->suppress_pin_screen) return;
|
||||
notification_message(bt->notification, &sequence_display_backlight_on);
|
||||
gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);
|
||||
view_port_enabled_set(bt->pin_code_view_port, true);
|
||||
@@ -75,6 +76,8 @@ static void bt_pin_code_hide(Bt* bt) {
|
||||
|
||||
static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
|
||||
furi_assert(bt);
|
||||
bt->pin_code = pin;
|
||||
if(bt->suppress_pin_screen) return true;
|
||||
notification_message(bt->notification, &sequence_display_backlight_on);
|
||||
FuriString* pin_str;
|
||||
dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
|
||||
@@ -149,6 +152,8 @@ Bt* bt_alloc() {
|
||||
// API evnent
|
||||
bt->api_event = furi_event_flag_alloc();
|
||||
|
||||
bt->pin = 0;
|
||||
|
||||
return bt;
|
||||
}
|
||||
|
||||
@@ -214,6 +219,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Bt* bt = context;
|
||||
bool ret = false;
|
||||
bt->pin = 0;
|
||||
|
||||
if(event.type == GapEventTypeConnected) {
|
||||
// Update status bar
|
||||
@@ -270,12 +276,14 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
|
||||
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
ret = true;
|
||||
} else if(event.type == GapEventTypePinCodeShow) {
|
||||
bt->pin = event.data.pin_code;
|
||||
BtMessage message = {
|
||||
.type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code};
|
||||
furi_check(
|
||||
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
ret = true;
|
||||
} else if(event.type == GapEventTypePinCodeVerify) {
|
||||
bt->pin = event.data.pin_code;
|
||||
ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code);
|
||||
} else if(event.type == GapEventTypeUpdateMTU) {
|
||||
bt->max_packet_size = event.data.max_packet_size;
|
||||
@@ -368,6 +376,86 @@ static void bt_close_connection(Bt* bt) {
|
||||
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
|
||||
}
|
||||
|
||||
static inline FuriHalBtProfile get_hal_bt_profile(BtProfile profile) {
|
||||
if(profile == BtProfileHidKeyboard) {
|
||||
return FuriHalBtProfileHidKeyboard;
|
||||
} else {
|
||||
return FuriHalBtProfileSerial;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_restart(Bt* bt) {
|
||||
furi_hal_bt_change_app(get_hal_bt_profile(bt->profile), bt_on_gap_event_callback, bt);
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
|
||||
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) {
|
||||
furi_assert(bt);
|
||||
furi_assert(fmt);
|
||||
|
||||
char name[FURI_HAL_BT_ADV_NAME_LENGTH];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(name, sizeof(name), fmt, args);
|
||||
va_end(args);
|
||||
furi_hal_bt_set_profile_adv_name(get_hal_bt_profile(bt->profile), name);
|
||||
|
||||
bt_restart(bt);
|
||||
}
|
||||
|
||||
const char* bt_get_profile_adv_name(Bt* bt) {
|
||||
furi_assert(bt);
|
||||
return furi_hal_bt_get_profile_adv_name(get_hal_bt_profile(bt->profile));
|
||||
}
|
||||
|
||||
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) {
|
||||
furi_assert(bt);
|
||||
furi_assert(mac);
|
||||
|
||||
furi_hal_bt_set_profile_mac_addr(get_hal_bt_profile(bt->profile), mac);
|
||||
|
||||
bt_restart(bt);
|
||||
}
|
||||
|
||||
const uint8_t* bt_get_profile_mac_address(Bt* bt) {
|
||||
furi_assert(bt);
|
||||
return furi_hal_bt_get_profile_mac_addr(get_hal_bt_profile(bt->profile));
|
||||
}
|
||||
|
||||
bool bt_remote_rssi(Bt* bt, uint8_t* rssi) {
|
||||
furi_assert(bt);
|
||||
|
||||
uint8_t rssi_val;
|
||||
uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val);
|
||||
|
||||
if(since == 0) return false;
|
||||
|
||||
*rssi = rssi_val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) {
|
||||
furi_assert(bt);
|
||||
furi_hal_bt_set_profile_pairing_method(get_hal_bt_profile(bt->profile), pairing_method);
|
||||
bt_restart(bt);
|
||||
}
|
||||
|
||||
GapPairing bt_get_profile_pairing_method(Bt* bt) {
|
||||
furi_assert(bt);
|
||||
return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile));
|
||||
}
|
||||
|
||||
void bt_disable_peer_key_update(Bt* bt) {
|
||||
UNUSED(bt);
|
||||
furi_hal_bt_set_key_storage_change_callback(NULL, NULL);
|
||||
}
|
||||
|
||||
void bt_enable_peer_key_update(Bt* bt) {
|
||||
furi_assert(bt);
|
||||
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
|
||||
}
|
||||
|
||||
int32_t bt_srv(void* p) {
|
||||
UNUSED(p);
|
||||
Bt* bt = bt_alloc();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi_hal_bt.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -23,6 +25,11 @@ typedef enum {
|
||||
BtProfileHidKeyboard,
|
||||
} BtProfile;
|
||||
|
||||
typedef struct {
|
||||
uint8_t rssi;
|
||||
uint32_t since;
|
||||
} BtRssi;
|
||||
|
||||
typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
|
||||
|
||||
/** Change BLE Profile
|
||||
@@ -69,6 +76,32 @@ void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path);
|
||||
*/
|
||||
void bt_keys_storage_set_default_path(Bt* bt);
|
||||
|
||||
// New methods
|
||||
|
||||
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...);
|
||||
|
||||
const char* bt_get_profile_adv_name(Bt* bt);
|
||||
|
||||
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]);
|
||||
|
||||
const uint8_t* bt_get_profile_mac_address(Bt* bt);
|
||||
|
||||
bool bt_remote_rssi(Bt* bt, uint8_t* rssi);
|
||||
|
||||
void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method);
|
||||
GapPairing bt_get_profile_pairing_method(Bt* bt);
|
||||
|
||||
/** Stop saving new peer key to flash (in .bt.keys file)
|
||||
*
|
||||
*/
|
||||
void bt_disable_peer_key_update(Bt* bt);
|
||||
|
||||
/** Enable saving peer key to internal flash (enable by default)
|
||||
*
|
||||
* @note This function should be called if bt_disable_peer_key_update was called before
|
||||
*/
|
||||
void bt_enable_peer_key_update(Bt* bt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -76,4 +76,6 @@ struct Bt {
|
||||
FuriEventFlag* api_event;
|
||||
BtStatusChangedCallback status_changed_cb;
|
||||
void* status_changed_ctx;
|
||||
uint32_t pin;
|
||||
bool suppress_pin_screen;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <notification/notification_messages.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_vcp.h>
|
||||
|
||||
#include "animations/animation_manager.h"
|
||||
#include "desktop/scenes/desktop_scene.h"
|
||||
@@ -14,7 +16,7 @@
|
||||
#include "desktop/views/desktop_view_pin_input.h"
|
||||
#include "desktop/views/desktop_view_pin_timeout.h"
|
||||
#include "desktop_i.h"
|
||||
#include "helpers/pin_lock.h"
|
||||
#include "helpers/pin.h"
|
||||
#include "helpers/slideshow_filename.h"
|
||||
|
||||
#define TAG "Desktop"
|
||||
@@ -70,9 +72,6 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||
return true;
|
||||
case DesktopGlobalAutoLock:
|
||||
if(!loader_is_locked(desktop->loader)) {
|
||||
if(desktop->settings.auto_lock_with_pin && desktop->settings.pin_code.length > 0) {
|
||||
desktop_pin_lock(&desktop->settings);
|
||||
}
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
return true;
|
||||
@@ -135,6 +134,14 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) {
|
||||
}
|
||||
|
||||
void desktop_lock(Desktop* desktop) {
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
|
||||
if(desktop->settings.pin_code.length) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
|
||||
@@ -150,6 +157,14 @@ void desktop_unlock(Desktop* desktop) {
|
||||
desktop_view_locked_unlock(desktop->locked_view);
|
||||
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
desktop_auto_lock_arm(desktop);
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
|
||||
if(desktop->settings.pin_code.length) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
}
|
||||
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
|
||||
@@ -293,11 +308,14 @@ Desktop* desktop_alloc() {
|
||||
desktop->auto_lock_timer =
|
||||
furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop);
|
||||
|
||||
furi_record_create(RECORD_DESKTOP, desktop);
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
void desktop_free(Desktop* desktop) {
|
||||
furi_assert(desktop);
|
||||
furi_check(furi_record_destroy(RECORD_DESKTOP));
|
||||
|
||||
furi_pubsub_unsubscribe(
|
||||
loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription);
|
||||
@@ -355,6 +373,16 @@ static bool desktop_check_file_flag(const char* flag_path) {
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool desktop_api_is_locked(Desktop* instance) {
|
||||
furi_assert(instance);
|
||||
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
|
||||
}
|
||||
|
||||
void desktop_api_unlock(Desktop* instance) {
|
||||
furi_assert(instance);
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked);
|
||||
}
|
||||
|
||||
int32_t desktop_srv(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
@@ -378,14 +406,12 @@ int32_t desktop_srv(void* p) {
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
|
||||
desktop_pin_lock_init(&desktop->settings);
|
||||
|
||||
if(!desktop_pin_lock_is_locked()) {
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
||||
desktop_lock(desktop);
|
||||
} else {
|
||||
if(!loader_is_locked(desktop->loader)) {
|
||||
desktop_auto_lock_arm(desktop);
|
||||
}
|
||||
} else {
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
|
||||
if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Desktop Desktop;
|
||||
|
||||
#define RECORD_DESKTOP "desktop"
|
||||
|
||||
bool desktop_api_is_locked(Desktop* instance);
|
||||
|
||||
void desktop_api_unlock(Desktop* instance);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <toolbox/saved_struct.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define DESKTOP_SETTINGS_VER (8)
|
||||
#define DESKTOP_SETTINGS_VER (9)
|
||||
|
||||
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
|
||||
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||
@@ -60,9 +60,7 @@ typedef struct {
|
||||
FavoriteApp favorite_secondary;
|
||||
FavoriteApp favorite_tertiary;
|
||||
PinCode pin_code;
|
||||
uint8_t is_locked;
|
||||
uint32_t auto_lock_delay_ms;
|
||||
uint8_t displayBatteryPercentage;
|
||||
uint8_t dummy_mode;
|
||||
bool auto_lock_with_pin;
|
||||
} DesktopSettings;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "pin.h"
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <stddef.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "../desktop_i.h"
|
||||
|
||||
static const NotificationSequence sequence_pin_fail = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
|
||||
&message_delay_250,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const uint8_t desktop_helpers_fails_timeout[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
30,
|
||||
60,
|
||||
90,
|
||||
120,
|
||||
150,
|
||||
180,
|
||||
/* +60 for every next fail */
|
||||
};
|
||||
|
||||
void desktop_pin_lock_error_notify() {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_pin_fail);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout() {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
uint32_t pin_timeout = 0;
|
||||
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
|
||||
if(pin_fails <= max_index) {
|
||||
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
|
||||
} else {
|
||||
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
|
||||
}
|
||||
|
||||
return pin_timeout;
|
||||
}
|
||||
|
||||
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) {
|
||||
furi_assert(pin_code1);
|
||||
furi_assert(pin_code2);
|
||||
bool result = false;
|
||||
|
||||
if(pin_code1->length == pin_code2->length) {
|
||||
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../desktop.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
void desktop_pin_lock_error_notify();
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout();
|
||||
|
||||
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2);
|
||||
@@ -1,140 +0,0 @@
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <stddef.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "../helpers/pin_lock.h"
|
||||
#include "../desktop_i.h"
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_vcp.h>
|
||||
|
||||
static const NotificationSequence sequence_pin_fail = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
|
||||
&message_delay_250,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const uint8_t desktop_helpers_fails_timeout[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
30,
|
||||
60,
|
||||
90,
|
||||
120,
|
||||
150,
|
||||
180,
|
||||
/* +60 for every next fail */
|
||||
};
|
||||
|
||||
void desktop_pin_lock_error_notify() {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_pin_fail);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout() {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
uint32_t pin_timeout = 0;
|
||||
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
|
||||
if(pin_fails <= max_index) {
|
||||
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
|
||||
} else {
|
||||
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
|
||||
}
|
||||
|
||||
return pin_timeout;
|
||||
}
|
||||
|
||||
void desktop_pin_lock(DesktopSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
settings->is_locked = 1;
|
||||
DESKTOP_SETTINGS_SAVE(settings);
|
||||
}
|
||||
|
||||
void desktop_pin_unlock(DesktopSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
settings->is_locked = 0;
|
||||
DESKTOP_SETTINGS_SAVE(settings);
|
||||
}
|
||||
|
||||
void desktop_pin_lock_init(DesktopSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
if(settings->pin_code.length > 0) {
|
||||
if(settings->is_locked == 1) {
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
} else {
|
||||
if(desktop_pin_lock_is_locked()) {
|
||||
settings->is_locked = 1;
|
||||
DESKTOP_SETTINGS_SAVE(settings);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||
}
|
||||
|
||||
if(desktop_pin_lock_is_locked()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
}
|
||||
|
||||
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) {
|
||||
bool result = false;
|
||||
if(desktop_pins_are_equal(pin_set, pin_entered)) {
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
result = true;
|
||||
} else {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
furi_hal_rtc_set_pin_fails(pin_fails + 1);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool desktop_pin_lock_is_locked() {
|
||||
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
|
||||
}
|
||||
|
||||
bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) {
|
||||
furi_assert(pin_code1);
|
||||
furi_assert(pin_code2);
|
||||
bool result = false;
|
||||
|
||||
if(pin_code1->length == pin_code2->length) {
|
||||
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../desktop.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
void desktop_pin_lock_error_notify();
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout();
|
||||
|
||||
void desktop_pin_lock(DesktopSettings* settings);
|
||||
|
||||
void desktop_pin_unlock(DesktopSettings* settings);
|
||||
|
||||
bool desktop_pin_lock_is_locked();
|
||||
|
||||
void desktop_pin_lock_init(DesktopSettings* settings);
|
||||
|
||||
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered);
|
||||
|
||||
bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2);
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "../views/desktop_view_lock_menu.h"
|
||||
#include "desktop_scene_i.h"
|
||||
#include "desktop_scene.h"
|
||||
#include "../helpers/pin_lock.h"
|
||||
#include "../helpers/pin.h"
|
||||
|
||||
#define TAG "DesktopSceneLock"
|
||||
|
||||
@@ -25,7 +25,6 @@ void desktop_scene_lock_menu_on_enter(void* context) {
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
||||
desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0);
|
||||
desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode);
|
||||
desktop_lock_menu_set_stealth_mode_state(
|
||||
desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode));
|
||||
@@ -44,7 +43,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
if(check_pin_changed) {
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(desktop->settings.pin_code.length > 0) {
|
||||
desktop_lock_menu_set_pin_state(desktop->lock_menu, true);
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
}
|
||||
}
|
||||
@@ -55,21 +53,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
desktop_lock(desktop);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopLockMenuEventPinLock:
|
||||
if(desktop->settings.pin_code.length > 0) {
|
||||
desktop_pin_lock(&desktop->settings);
|
||||
desktop_lock(desktop);
|
||||
} else {
|
||||
LoaderStatus status =
|
||||
loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG);
|
||||
if(status == LoaderStatusOk) {
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unable to start desktop settings");
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopLockMenuEventDummyModeOn:
|
||||
desktop_set_dummy_mode_state(desktop, true);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "../desktop.h"
|
||||
#include "../desktop_i.h"
|
||||
#include "../helpers/pin_lock.h"
|
||||
#include "../helpers/pin.h"
|
||||
#include "../animations/animation_manager.h"
|
||||
#include "../views/desktop_events.h"
|
||||
#include "../views/desktop_view_pin_input.h"
|
||||
@@ -45,7 +45,7 @@ void desktop_scene_locked_on_enter(void* context) {
|
||||
bool switch_to_timeout_scene = false;
|
||||
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
|
||||
if(state == SCENE_LOCKED_FIRST_ENTER) {
|
||||
bool pin_locked = desktop_pin_lock_is_locked();
|
||||
bool pin_locked = desktop->settings.pin_code.length > 0;
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, true);
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_set_lockdown(gui, true);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "../views/desktop_view_main.h"
|
||||
#include "desktop_scene.h"
|
||||
#include "desktop_scene_i.h"
|
||||
#include "../helpers/pin_lock.h"
|
||||
|
||||
#define TAG "DesktopSrv"
|
||||
|
||||
@@ -108,13 +107,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
|
||||
case DesktopMainEventLock:
|
||||
if(desktop->settings.pin_code.length > 0) {
|
||||
desktop_pin_lock(&desktop->settings);
|
||||
desktop_lock(desktop);
|
||||
} else {
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
desktop_lock(desktop);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "../animations/animation_manager.h"
|
||||
#include "../views/desktop_events.h"
|
||||
#include "../views/desktop_view_pin_input.h"
|
||||
#include "../helpers/pin_lock.h"
|
||||
#include "../helpers/pin.h"
|
||||
#include "desktop_scene.h"
|
||||
#include "desktop_scene_i.h"
|
||||
|
||||
@@ -54,9 +54,11 @@ static void desktop_scene_pin_input_back_callback(void* context) {
|
||||
|
||||
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) {
|
||||
if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
|
||||
} else {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
furi_hal_rtc_set_pin_fails(pin_fails + 1);
|
||||
view_dispatcher_send_custom_event(
|
||||
desktop->view_dispatcher, DesktopPinInputEventUnlockFailed);
|
||||
}
|
||||
@@ -126,7 +128,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopPinInputEventUnlocked:
|
||||
desktop_pin_unlock(&desktop->settings);
|
||||
desktop_unlock(desktop);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
@@ -36,7 +36,6 @@ typedef enum {
|
||||
DesktopDebugEventExit,
|
||||
|
||||
DesktopLockMenuEventLock,
|
||||
DesktopLockMenuEventPinLock,
|
||||
DesktopLockMenuEventDummyModeOn,
|
||||
DesktopLockMenuEventDummyModeOff,
|
||||
DesktopLockMenuEventStealthModeOn,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user