mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
Compare commits
215 Commits
subghz_pro
...
unlshd-034
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05489fda7d | ||
|
|
319108b11c | ||
|
|
ee41413c6a | ||
|
|
71daa3e0f8 | ||
|
|
1249ce9b94 | ||
|
|
da9b968fc9 | ||
|
|
b0af6f4b53 | ||
|
|
7a413f390d | ||
|
|
16df48bac6 | ||
|
|
3b2d0486a0 | ||
|
|
ed06aa48e9 | ||
|
|
e941ef5952 | ||
|
|
5d9174ac9d | ||
|
|
5be15152eb | ||
|
|
90958a6d23 | ||
|
|
ab91dc1882 | ||
|
|
cad2f2a065 | ||
|
|
086be92f69 | ||
|
|
28eb4d1060 | ||
|
|
2ff937a278 | ||
|
|
cdc44c502e | ||
|
|
b37c3ccf92 | ||
|
|
74259c2276 | ||
|
|
3dcd5538d0 | ||
|
|
eb5dae1cda | ||
|
|
f0471a62e5 | ||
|
|
9819306731 | ||
|
|
b872a12517 | ||
|
|
eefca9f498 | ||
|
|
e0905597fc | ||
|
|
6511252140 | ||
|
|
27000f1763 | ||
|
|
ada343b7df | ||
|
|
9dd1fb64b7 | ||
|
|
c0e0403b44 | ||
|
|
6d0c3eb3b6 | ||
|
|
f25af91d23 | ||
|
|
507c8582ca | ||
|
|
d780752d9e | ||
|
|
2f567f3c3f | ||
|
|
f612357150 | ||
|
|
f1f2718598 | ||
|
|
4ab832cc46 | ||
|
|
920bee0532 | ||
|
|
1a9544778b | ||
|
|
dbee87a6c6 | ||
|
|
7e80ed6693 | ||
|
|
db0c34f63e | ||
|
|
056f2eb7d5 | ||
|
|
fe84e9521c | ||
|
|
91e2b466d3 | ||
|
|
b90060e574 | ||
|
|
5d4057f722 | ||
|
|
0b7d205253 | ||
|
|
72ca6b25e9 | ||
|
|
4be64b6206 | ||
|
|
6cc5119c64 | ||
|
|
42d27d04f6 | ||
|
|
fed4c28925 | ||
|
|
24f084d282 | ||
|
|
12b760e2e3 | ||
|
|
43ef3d9bff | ||
|
|
3bdd171ce2 | ||
|
|
6ff0a5f318 | ||
|
|
f3ab4bc292 | ||
|
|
806428efeb | ||
|
|
4359e2eaa9 | ||
|
|
777a4d109d | ||
|
|
f8dc1939cd | ||
|
|
1b8cf6a5b1 | ||
|
|
1b12526357 | ||
|
|
1f6382e93d | ||
|
|
ab6b3f8ed3 | ||
|
|
cc52253e22 | ||
|
|
cf6dc9f895 | ||
|
|
8deb29a8ff | ||
|
|
e0f9697750 | ||
|
|
c3a6ba3c02 | ||
|
|
75a8f0a7b4 | ||
|
|
caa4ba67d6 | ||
|
|
9f6f391354 | ||
|
|
24726ab8a3 | ||
|
|
6bd5e22872 | ||
|
|
cbb09b6812 | ||
|
|
9c9688dd5b | ||
|
|
21c52df090 | ||
|
|
2c95a7cba4 | ||
|
|
568176d775 | ||
|
|
04250632d7 | ||
|
|
9ae58f5462 | ||
|
|
e6d1bcc421 | ||
|
|
4439a83733 | ||
|
|
0f8d7dd6db | ||
|
|
182296d8af | ||
|
|
cd14380dba | ||
|
|
5b0c5a82c0 | ||
|
|
478390de19 | ||
|
|
b054912167 | ||
|
|
09edf66a2a | ||
|
|
1d55aee39c | ||
|
|
3efb7d4050 | ||
|
|
0c06e54831 | ||
|
|
a61286bd43 | ||
|
|
9bda3e62ee | ||
|
|
905273e066 | ||
|
|
16fe62e98d | ||
|
|
958797062d | ||
|
|
2bb76e09c6 | ||
|
|
33dd256dfb | ||
|
|
295fd3d0c0 | ||
|
|
efdf24d711 | ||
|
|
12c1ec37a2 | ||
|
|
03f889962b | ||
|
|
802035d92e | ||
|
|
0bc995bfab | ||
|
|
a19768e376 | ||
|
|
0bf0267edd | ||
|
|
b3559bf058 | ||
|
|
2b8f55322e | ||
|
|
b15a15411f | ||
|
|
39329eb422 | ||
|
|
115257ea59 | ||
|
|
a71abedd25 | ||
|
|
b53924c27a | ||
|
|
543346f885 | ||
|
|
a435959ee3 | ||
|
|
d33b092a9d | ||
|
|
f676072e15 | ||
|
|
098d6944c4 | ||
|
|
172c0e077f | ||
|
|
4dbb55d740 | ||
|
|
ba09da107a | ||
|
|
eaf965c66f | ||
|
|
e999c35749 | ||
|
|
203adabc46 | ||
|
|
82ad44a863 | ||
|
|
a24d0f1958 | ||
|
|
86da6a7ffe | ||
|
|
36e7b9185a | ||
|
|
f8eda660d2 | ||
|
|
f3e3e828aa | ||
|
|
c230d09dad | ||
|
|
72fd448541 | ||
|
|
07ff0c7d97 | ||
|
|
76f84b50e6 | ||
|
|
1f3e937471 | ||
|
|
32f11f59b0 | ||
|
|
202a97eb74 | ||
|
|
f9db06b781 | ||
|
|
5132b16305 | ||
|
|
a0b02b0110 | ||
|
|
36debf25fb | ||
|
|
d16d1f43fc | ||
|
|
11b3484f4b | ||
|
|
2bcb15b8bc | ||
|
|
9690dba7fe | ||
|
|
663eb6cd6d | ||
|
|
e9aa2d3449 | ||
|
|
7d4bffb575 | ||
|
|
b89902942b | ||
|
|
0a3ff7f85a | ||
|
|
b15c4afea1 | ||
|
|
738e0df4f4 | ||
|
|
3de6ae07b7 | ||
|
|
78afaab7e8 | ||
|
|
80a64d8e1a | ||
|
|
c7fbc8323b | ||
|
|
335f8b9aff | ||
|
|
009c9b1b71 | ||
|
|
ab515aeebb | ||
|
|
487d03eca4 | ||
|
|
32b74b968e | ||
|
|
1e98361299 | ||
|
|
ee2f6581bb | ||
|
|
5b0f74bffa | ||
|
|
faac423772 | ||
|
|
aae37121d6 | ||
|
|
660f97f241 | ||
|
|
387e9431f5 | ||
|
|
fd46bd1886 | ||
|
|
883f67d6d1 | ||
|
|
a52f64acfd | ||
|
|
4f4ccaa727 | ||
|
|
85f6ee2870 | ||
|
|
d81653461c | ||
|
|
55cfccafaf | ||
|
|
58e9acc19e | ||
|
|
6bcb9a60f7 | ||
|
|
5a02a51bd9 | ||
|
|
4aae197cf7 | ||
|
|
2fdebb639b | ||
|
|
25e8947282 | ||
|
|
34f0aced2f | ||
|
|
da68f2e4ed | ||
|
|
8f16dbb8e7 | ||
|
|
49e458f1b5 | ||
|
|
5cf46d2aa9 | ||
|
|
beedf54e75 | ||
|
|
70ccb89c3d | ||
|
|
5ea43a2a4b | ||
|
|
41f60dbbf4 | ||
|
|
827341ec08 | ||
|
|
5c36043d03 | ||
|
|
cf5811f8d9 | ||
|
|
ec6a169bf8 | ||
|
|
f1dec87c1b | ||
|
|
ab29951a99 | ||
|
|
bbe9f88bbe | ||
|
|
9188bf0013 | ||
|
|
f33ed59567 | ||
|
|
3fd8c80861 | ||
|
|
7b8ac3a5a0 | ||
|
|
6fef957001 | ||
|
|
0de1c9df89 | ||
|
|
a0e8cfbe97 |
@@ -36,13 +36,6 @@ Min level: 1
|
||||
Max level: 1
|
||||
Weight: 3
|
||||
|
||||
Name: L1_Happy_holidays_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 14
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 4
|
||||
|
||||
Name: L1_Read_books_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
|
||||
@@ -198,7 +198,7 @@ steps:
|
||||
[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
|
||||
[-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
|
||||
[-Download latest extra apps pack-](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip)
|
||||
|
||||
|
||||
[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})
|
||||
@@ -220,7 +220,7 @@ steps:
|
||||
commands:
|
||||
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||
- chmod +x ./discord.sh
|
||||
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
|
||||
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
|
||||
|
||||
- name: "Send extra pack build to telegram"
|
||||
image: appleboy/drone-telegram
|
||||
|
||||
@@ -44,3 +44,6 @@
|
||||
|
||||
# Functions that always return the same error code
|
||||
//-V:picopass_device_decrypt:1048
|
||||
|
||||
# Examples
|
||||
//V_EXCLUDE_PATH applications/examples/
|
||||
5
.vscode/example/launch.json
vendored
5
.vscode/example/launch.json
vendored
@@ -11,9 +11,10 @@
|
||||
"args": {
|
||||
"useSingleResult": true,
|
||||
"env": {
|
||||
"PATH": "${workspaceFolder};${env:PATH}"
|
||||
"PATH": "${workspaceFolder};${env:PATH}",
|
||||
"FBT_QUIET": 1
|
||||
},
|
||||
"command": "./fbt get_blackmagic",
|
||||
"command": "fbt get_blackmagic",
|
||||
"description": "Get Blackmagic device",
|
||||
}
|
||||
}
|
||||
|
||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,14 +1,41 @@
|
||||
### New changes
|
||||
* SubGHz: **Nice ON2E (Nice One)** support (by @assasinfil | PR #335)
|
||||
* SubGHz: Remove 467.75 From freq analyzer since it has too much noise (Frequency is still can be used, just excluded from FA to avoid false detections)
|
||||
* Archive and FileBrowser: **Fixed more navigation issues** (by @Willy-JL | PR #334)
|
||||
* Plugins -> SubGHz Bruteforcer: Fix Linear Delta 3 repeats (now its more stable and we will be sure signal is received correctly)
|
||||
* Plugins: Updated TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
|
||||
* OFW: **Fix Cyfral & Metakom emulation (My temp fix removed and proper fix from OFW applied)**
|
||||
* OFW: BadUSB: disable CDC mode, USB mode switch fix
|
||||
* OFW: Updater visual fixes
|
||||
* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues!
|
||||
* SubGHz: Default custom buttons layout for non standard remotes (for example your remote has broken buttons and transmit only 0xC, now you can use other buttons)
|
||||
* SubGHz: Fix issues with external module 5v power (now all works automatically, enabling +5v manually not required) (**Only for modules that work with 5v->3.3v converter!!!!!**)
|
||||
* SubGHz: Option to disable automatic 5v power for external modules - (5v is enabled by default, if you are using module without converter you can set this option to OFF)
|
||||
* SubGHz: Fix and update subghz protocols to use new error system
|
||||
* SubGHz: Fix default frequency being overwritten bug (Add manually fixes)
|
||||
* SubGHz: Fix 464Mhz and (390MHz for external module only) was showing up in Frequency analyzer all the time due to noise
|
||||
* iButton: Fix ibutton app - add manually - duplicate names
|
||||
* Plugins: NFC Magic fix - reinit nfc at app start
|
||||
* Plugins: Update **Unitemp - Temperature sensors reader** (DHT11/22, DS18B20, BMP280, HTU21x and more) [(by quen0n)](https://github.com/quen0n/unitemp-flipperzero)
|
||||
* Plugins: Update **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe)
|
||||
* Plugins: Massive plugins refactoring - not full refactoring, only small issues is fixed and moved all plugins to furi mutex instead of valuemutex
|
||||
* Plugins: Many small issues was found and fixed due mutex upgrade
|
||||
* Plugins: `Extra pack` updated and fixed (valuemutex to furi_mutex upgrade)
|
||||
* Plugins: SubGHz playlist - rewind (skip or play previous file) [(by alvarotorijano)](https://github.com/alvarotorijano/playListMod/blob/main/playlistMod.c)
|
||||
* Plugins: Properly rename unirf remix to subghz remote - And automatically migrate user files to new folder (unirf -> subghz_remote)
|
||||
* Plugins: Fix unirf freeze (protocol deserialize status ok) (by @Willy-JL | PR #375)
|
||||
* Plugins: Blackjack game: fix bug counting more than one ace (by @403-Fruit | PR #374)
|
||||
* Plugins: Update POCSAG Pager app to new error system
|
||||
* Plugins: Update iButton Fuzzer to new iButton system
|
||||
* Infrared: Update universal remote assets (by @amec0e | PR #378)
|
||||
* OFW: PicoPass: auth cleanup
|
||||
* OFW: More UI fixes and improvements
|
||||
* OFW: NFC: Support reading Mifare Classic key B from sector trailer, reading sector with B key where A key can't read block, Nfc Magic app not using NFC folder by default (in file select)
|
||||
* OFW: Remove ValueMutex -> **Breaking API change, api was changed from 17.x to 18.x**
|
||||
* OFW: Support reseting iCx cards
|
||||
* OFW: Fixed picopass load save file overrun
|
||||
* OFW: Fix SD card CID parsing
|
||||
* OFW: Archive browser: update path on dir leave
|
||||
* OFW: SubGhz: better and more verbose error handling in protocols, stricter CAME validation -> **Breaking API change, api was changed from 16.x to 17.x**
|
||||
* OFW: iButton system and app refactoring (+new protocols) -> **Breaking API change, api was changed from 15.x to 16.x**
|
||||
**(this will make your manually copied plugins not work, update them in same way you installed them, or delete `apps` folder and then install firmware, if you using extra pack builds (with `e` in version) all apps in _Extra will be updated automatically)**
|
||||
|
||||
#### [🎲 Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
|
||||
* OFW: New pin reset splashscreen
|
||||
* OFW: Getter for application data path
|
||||
|
||||
#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip)
|
||||
|
||||
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
|
||||
61
ReadMe.md
61
ReadMe.md
@@ -12,8 +12,6 @@
|
||||
|
||||
Our goal is to make all features possible on this device without any limitations!
|
||||
|
||||
Please help us implement emulation for all Sub-GHz dynamic (rolling code) protocols!
|
||||
|
||||
<br>
|
||||
|
||||
### This software is for experimental purposes only and is not meant for any illegal activity/purposes. <br> We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. <br> Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
|
||||
@@ -27,7 +25,7 @@ Our Discord Community:
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Dev builds
|
||||
## Dev builds (unstable)
|
||||
- https://dev.unleashedflip.com/
|
||||
- https://t.me/kotnehleb
|
||||
## Releases in Telegram
|
||||
@@ -37,7 +35,7 @@ Our Discord Community:
|
||||
* Sub-GHz regional TX restrictions removed
|
||||
* Sub-GHz frequency range can be extended in settings file (Warning: It can damage Flipper's hardware)
|
||||
* Many rolling code protocols now have the ability to save & send captured signals
|
||||
* FAAC SLH (Spa) & BFT Mitto (secure with seed) manual creation
|
||||
* FAAC SLH (Spa) & BFT Mitto (keeloq secure with seed) manual creation
|
||||
* Sub-GHz static code brute-force plugin
|
||||
* LFRFID Fuzzer plugin
|
||||
* Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
|
||||
@@ -45,18 +43,30 @@ 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.)
|
||||
* BadUSB keyboard layouts
|
||||
* Customizable Flipper name
|
||||
- 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 -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
|
||||
- Sub-GHz -> Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||
- Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||
- Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
|
||||
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
|
||||
* Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes
|
||||
* Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu
|
||||
* Sub-GHz -> External CC1101 module support
|
||||
* SubGHz -> **Hold right in received signal list to delete selected signal**
|
||||
* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis** - 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 -> Debug mode counter increase settings (+1 -> +5, +10, default: +1)
|
||||
* SubGHz -> Debug PIN output settings for protocol development
|
||||
* Infrared -> Debug TX PIN output settings
|
||||
* Other small fixes and changes throughout
|
||||
* See other changes in changelog and in readme below
|
||||
* See other changes in readme below
|
||||
|
||||
Also check the changelog in releases for latest updates!
|
||||
|
||||
### Current modified and new Sub-GHz protocols list:
|
||||
Thanks to Official team (to thier SubGHz Developer, Skorp) for implementing decoders for these protocols.
|
||||
Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols.
|
||||
|
||||
Encoders/sending made by Eng1n33r & @xMasterX:
|
||||
|
||||
@@ -70,10 +80,11 @@ Encoders/sending made by Eng1n33r & @xMasterX:
|
||||
- Keeloq: FAAC RC,XT
|
||||
- Keeloq: Mutancode
|
||||
- Keeloq: Normstahl
|
||||
- Keeloq: Beninca
|
||||
- CAME Atomo
|
||||
- Nice Flor S
|
||||
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||
- BFT Mitto [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)]
|
||||
- Security+ v1 & v2
|
||||
- Star Line
|
||||
|
||||
@@ -118,7 +129,6 @@ You can support us by using links or addresses below:
|
||||
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
|
||||
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
|
||||
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
|
||||
- USB Keyboard plugin [(by huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
||||
- WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player)
|
||||
- Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey
|
||||
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
|
||||
@@ -142,6 +152,7 @@ You can support us by using links or addresses below:
|
||||
- Text Viewer [(by kowalski7cc & kyhwana)](https://github.com/kowalski7cc/flipper-zero-text-viewer/tree/refactor-text-app)
|
||||
- **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main)
|
||||
- **ProtoView** [(by antirez)](https://github.com/antirez/protoview)
|
||||
- **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe)
|
||||
|
||||
Games:
|
||||
- DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
|
||||
@@ -157,15 +168,6 @@ Games:
|
||||
- BlackJack [(by teeebor)](https://github.com/teeebor/flipper_games)
|
||||
- 2048 game [(by eugene-kirzhanov)](https://github.com/eugene-kirzhanov/flipper-zero-2048-game)
|
||||
|
||||
### Other changes
|
||||
|
||||
- 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 -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
|
||||
- Sub-GHz -> Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||
- Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||
- Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
|
||||
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
|
||||
|
||||
# Instructions
|
||||
## [- How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
@@ -178,12 +180,20 @@ Games:
|
||||
|
||||
## [- How to change Flipper name](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/CustomFlipperName.md)
|
||||
|
||||
### **Sub-GHz**
|
||||
|
||||
## [- Transmission is blocked? - How to extend Sub-GHz frequency range](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md)
|
||||
|
||||
## [- How to add extra Sub-GHz frequencies](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md)
|
||||
|
||||
## [- How to use Flipper as new remote (Nice FlorS, BFT Mitto, Somfy Telis)](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
|
||||
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
||||
|
||||
### **Plugins**
|
||||
|
||||
## [- 🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/unleashed-extra-pack)
|
||||
|
||||
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
||||
|
||||
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md)
|
||||
|
||||
## [- Barcode Generator](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/BarcodeGenerator.md)
|
||||
@@ -218,20 +228,12 @@ Games:
|
||||
|
||||
## [- How to use: [GPIO] SentrySafe plugin](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SentrySafe.md)
|
||||
|
||||
### **Sub-GHz**
|
||||
|
||||
## [- Transmission is blocked? - How to extend Sub-GHz frequency range](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md)
|
||||
|
||||
## [- How to add extra Sub-GHz frequencies](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# Where I can find IR, Sub-GHz, ... files, DBs, and other stuff?
|
||||
## [UberGuidoZ Playground - Large collection of files - Github](https://github.com/UberGuidoZ/Flipper)
|
||||
## [Awesome Flipper Zero - Github](https://github.com/djsime1/awesome-flipperzero)
|
||||
## [CAME-12bit, NICE-12bit, Linear-10bit, PT-2240 - Sub-GHz fixed code bruteforce](https://github.com/tobiabocchi/flipperzero-bruteforce)
|
||||
## [SMC5326, UNILARM - Sub-GHz fixed code bruteforce](https://github.com/Hong5489/flipperzero-gate-bruteforce)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -239,6 +241,9 @@ Games:
|
||||
# Links
|
||||
|
||||
* Unofficial Discord: [discord.unleashedflip.com](https://discord.unleashedflip.com)
|
||||
* Hello world - plugin tutorial (English): [https://github.com/DroomOne/Flipper-Plugin-Tutorial](https://github.com/DroomOne/Flipper-Plugin-Tutorial)
|
||||
* Hello world - plugin tutorial (in Russian): [https://yakovlev.me/hello-flipper-zero/](https://yakovlev.me/hello-flipper-zero/)
|
||||
* CLion IDE - How to setup workspace for flipper firmware development: [https://krasovs.ky/2022/11/01/flipper-zero-clion.html](https://krasovs.ky/2022/11/01/flipper-zero-clion.html)
|
||||
* Docs by atmanos / How to write your own app (outdated API): [https://flipper.atmanos.com/docs/overview/intro](https://flipper.atmanos.com/docs/overview/intro)
|
||||
|
||||
* Official Docs: [http://docs.flipperzero.one](http://docs.flipperzero.one)
|
||||
|
||||
@@ -139,7 +139,7 @@ if GetOption("fullenv") or any(
|
||||
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
||||
distenv.Default(basic_dist)
|
||||
|
||||
dist_dir = distenv.GetProjetDirName()
|
||||
dist_dir = distenv.GetProjectDirName()
|
||||
fap_dist = [
|
||||
distenv.Install(
|
||||
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
|
||||
|
||||
@@ -11,6 +11,7 @@ typedef struct {
|
||||
uint16_t left;
|
||||
uint16_t right;
|
||||
uint16_t ok;
|
||||
FuriMutex* mutex;
|
||||
} KeypadTestState;
|
||||
|
||||
static void keypad_test_reset_state(KeypadTestState* state) {
|
||||
@@ -22,7 +23,8 @@ static void keypad_test_reset_state(KeypadTestState* state) {
|
||||
}
|
||||
|
||||
static void keypad_test_render_callback(Canvas* canvas, void* ctx) {
|
||||
KeypadTestState* state = (KeypadTestState*)acquire_mutex((ValueMutex*)ctx, 25);
|
||||
KeypadTestState* state = ctx;
|
||||
furi_mutex_acquire(state->mutex, FuriWaitForever);
|
||||
canvas_clear(canvas);
|
||||
char strings[5][20];
|
||||
|
||||
@@ -51,7 +53,7 @@ static void keypad_test_render_callback(Canvas* canvas, void* ctx) {
|
||||
|
||||
canvas_draw_str(canvas, 10, 63, "[back] - reset, hold to exit");
|
||||
|
||||
release_mutex((ValueMutex*)ctx, state);
|
||||
furi_mutex_release(state->mutex);
|
||||
}
|
||||
|
||||
static void keypad_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
@@ -64,17 +66,17 @@ int32_t keypad_test_app(void* p) {
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
furi_check(event_queue);
|
||||
|
||||
KeypadTestState _state = {{false, false, false, false, false}, 0, 0, 0, 0, 0};
|
||||
KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL};
|
||||
state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) {
|
||||
if(!state.mutex) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
|
||||
view_port_draw_callback_set(view_port, keypad_test_render_callback, &state_mutex);
|
||||
view_port_draw_callback_set(view_port, keypad_test_render_callback, &state);
|
||||
view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
@@ -83,7 +85,7 @@ int32_t keypad_test_app(void* p) {
|
||||
|
||||
InputEvent event;
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
KeypadTestState* state = (KeypadTestState*)acquire_mutex_block(&state_mutex);
|
||||
furi_mutex_acquire(state.mutex, FuriWaitForever);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"key: %s type: %s",
|
||||
@@ -92,54 +94,54 @@ int32_t keypad_test_app(void* p) {
|
||||
|
||||
if(event.key == InputKeyRight) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[0] = true;
|
||||
state.press[0] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[0] = false;
|
||||
state.press[0] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->right;
|
||||
++state.right;
|
||||
}
|
||||
} else if(event.key == InputKeyLeft) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[1] = true;
|
||||
state.press[1] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[1] = false;
|
||||
state.press[1] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->left;
|
||||
++state.left;
|
||||
}
|
||||
} else if(event.key == InputKeyUp) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[2] = true;
|
||||
state.press[2] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[2] = false;
|
||||
state.press[2] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->up;
|
||||
++state.up;
|
||||
}
|
||||
} else if(event.key == InputKeyDown) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[3] = true;
|
||||
state.press[3] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[3] = false;
|
||||
state.press[3] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->down;
|
||||
++state.down;
|
||||
}
|
||||
} else if(event.key == InputKeyOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[4] = true;
|
||||
state.press[4] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[4] = false;
|
||||
state.press[4] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->ok;
|
||||
++state.ok;
|
||||
}
|
||||
} else if(event.key == InputKeyBack) {
|
||||
if(event.type == InputTypeLong) {
|
||||
release_mutex(&state_mutex, state);
|
||||
furi_mutex_release(state.mutex);
|
||||
break;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
keypad_test_reset_state(state);
|
||||
keypad_test_reset_state(&state);
|
||||
}
|
||||
}
|
||||
|
||||
release_mutex(&state_mutex, state);
|
||||
furi_mutex_release(state.mutex);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
@@ -147,7 +149,7 @@ int32_t keypad_test_app(void* p) {
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
furi_mutex_free(state.mutex);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
|
||||
@@ -53,15 +53,17 @@ static void (*text_box_test_render[])(Canvas* canvas) = {
|
||||
|
||||
typedef struct {
|
||||
uint32_t idx;
|
||||
FuriMutex* mutex;
|
||||
} TextBoxTestState;
|
||||
|
||||
static void text_box_test_render_callback(Canvas* canvas, void* ctx) {
|
||||
TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
TextBoxTestState* state = ctx;
|
||||
furi_mutex_acquire(state->mutex, FuriWaitForever);
|
||||
canvas_clear(canvas);
|
||||
|
||||
text_box_test_render[state->idx](canvas);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, state);
|
||||
furi_mutex_release(state->mutex);
|
||||
}
|
||||
|
||||
static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
@@ -74,17 +76,17 @@ int32_t text_box_test_app(void* p) {
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
furi_check(event_queue);
|
||||
|
||||
TextBoxTestState _state = {.idx = 0};
|
||||
TextBoxTestState state = {.idx = 0, .mutex = NULL};
|
||||
state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) {
|
||||
if(!state.mutex) {
|
||||
FURI_LOG_E(TAG, "Cannot create mutex");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
|
||||
view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex);
|
||||
view_port_draw_callback_set(view_port, text_box_test_render_callback, &state);
|
||||
view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
@@ -94,24 +96,24 @@ int32_t text_box_test_app(void* p) {
|
||||
uint32_t test_renders_num = COUNT_OF(text_box_test_render);
|
||||
InputEvent event;
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
TextBoxTestState* state = acquire_mutex_block(&state_mutex);
|
||||
furi_mutex_acquire(state.mutex, FuriWaitForever);
|
||||
|
||||
if(event.type == InputTypeShort) {
|
||||
if(event.key == InputKeyRight) {
|
||||
if(state->idx < test_renders_num - 1) {
|
||||
state->idx++;
|
||||
if(state.idx < test_renders_num - 1) {
|
||||
state.idx++;
|
||||
}
|
||||
} else if(event.key == InputKeyLeft) {
|
||||
if(state->idx > 0) {
|
||||
state->idx--;
|
||||
if(state.idx > 0) {
|
||||
state.idx--;
|
||||
}
|
||||
} else if(event.key == InputKeyBack) {
|
||||
release_mutex(&state_mutex, state);
|
||||
furi_mutex_release(state.mutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
release_mutex(&state_mutex, state);
|
||||
furi_mutex_release(state.mutex);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
@@ -119,7 +121,7 @@ int32_t text_box_test_app(void* p) {
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
furi_mutex_free(state.mutex);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
// v2 tests
|
||||
void test_furi_create_open();
|
||||
void test_furi_valuemutex();
|
||||
void test_furi_concurrent_access();
|
||||
void test_furi_pubsub();
|
||||
|
||||
@@ -30,10 +29,6 @@ MU_TEST(mu_test_furi_create_open) {
|
||||
test_furi_create_open();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_valuemutex) {
|
||||
test_furi_valuemutex();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_pubsub) {
|
||||
test_furi_pubsub();
|
||||
}
|
||||
@@ -51,7 +46,6 @@ MU_TEST_SUITE(test_suite) {
|
||||
|
||||
// v2 tests
|
||||
MU_RUN_TEST(mu_test_furi_create_open);
|
||||
MU_RUN_TEST(mu_test_furi_valuemutex);
|
||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
|
||||
void test_furi_valuemutex() {
|
||||
const int init_value = 0xdeadbeef;
|
||||
const int changed_value = 0x12345678;
|
||||
|
||||
int value = init_value;
|
||||
bool result;
|
||||
ValueMutex valuemutex;
|
||||
|
||||
// init mutex case
|
||||
result = init_mutex(&valuemutex, &value, sizeof(value));
|
||||
mu_assert(result, "init mutex failed");
|
||||
|
||||
// acquire mutex case
|
||||
int* value_pointer = acquire_mutex(&valuemutex, 100);
|
||||
mu_assert_pointers_eq(value_pointer, &value);
|
||||
|
||||
// second acquire mutex case
|
||||
int* value_pointer_second = acquire_mutex(&valuemutex, 100);
|
||||
mu_assert_pointers_eq(value_pointer_second, NULL);
|
||||
|
||||
// change value case
|
||||
*value_pointer = changed_value;
|
||||
mu_assert_int_eq(value, changed_value);
|
||||
|
||||
// release mutex case
|
||||
result = release_mutex(&valuemutex, &value);
|
||||
mu_assert(result, "release mutex failed");
|
||||
|
||||
// TODO
|
||||
//acquire mutex blocking case
|
||||
//write mutex blocking case
|
||||
//read mutex blocking case
|
||||
|
||||
mu_check(delete_mutex(&valuemutex));
|
||||
}
|
||||
@@ -3,56 +3,63 @@
|
||||
#include "../minunit.h"
|
||||
|
||||
static void power_test_deinit(void) {
|
||||
// Try to reset to default charging voltage
|
||||
furi_hal_power_set_battery_charging_voltage(4.208f);
|
||||
// Try to reset to default charge voltage limit
|
||||
furi_hal_power_set_battery_charge_voltage_limit(4.208f);
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_exact) {
|
||||
// Power of 16mV charge voltages get applied exactly
|
||||
MU_TEST(test_power_charge_voltage_limit_exact) {
|
||||
// Power of 16mV charge voltage limits get applied exactly
|
||||
// (bq25896 charge controller works in 16mV increments)
|
||||
//
|
||||
// This test may need adapted if other charge controllers are used in the future.
|
||||
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
|
||||
float charge_volt = (float)charge_mv / 1000.0f;
|
||||
furi_hal_power_set_battery_charging_voltage(charge_volt);
|
||||
mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(charge_volt);
|
||||
mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_floating_imprecision) {
|
||||
MU_TEST(test_power_charge_voltage_limit_floating_imprecision) {
|
||||
// 4.016f should act as 4.016 V, even with floating point imprecision
|
||||
furi_hal_power_set_battery_charging_voltage(4.016f);
|
||||
mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(4.016f);
|
||||
mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_inexact) {
|
||||
// Charge voltages that are not power of 16mV get truncated down
|
||||
furi_hal_power_set_battery_charging_voltage(3.841f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||
MU_TEST(test_power_charge_voltage_limit_inexact) {
|
||||
// Charge voltage limits that are not power of 16mV get truncated down
|
||||
furi_hal_power_set_battery_charge_voltage_limit(3.841f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
|
||||
furi_hal_power_set_battery_charging_voltage(3.900f);
|
||||
mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(3.900f);
|
||||
mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
|
||||
furi_hal_power_set_battery_charging_voltage(4.200f);
|
||||
mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(4.200f);
|
||||
mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_invalid_clamped) {
|
||||
// Out-of-range charge voltages get clamped to 3.840 V and 4.208 V
|
||||
furi_hal_power_set_battery_charging_voltage(3.808f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||
MU_TEST(test_power_charge_voltage_limit_invalid_clamped) {
|
||||
// Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V
|
||||
furi_hal_power_set_battery_charge_voltage_limit(3.808f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(1.0f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
|
||||
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
|
||||
// unhappy battery if this fails.
|
||||
furi_hal_power_set_battery_charging_voltage(4.240f);
|
||||
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage());
|
||||
furi_hal_power_set_battery_charge_voltage_limit(4.240f);
|
||||
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
// Likewise, picking a number that the uint8_t wraparound in the driver would result in a
|
||||
// VREG value under 23 if this test fails.
|
||||
// E.g. (uint8_t)((8105-3840)/16) -> 10
|
||||
furi_hal_power_set_battery_charge_voltage_limit(8.105f);
|
||||
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_power_suite) {
|
||||
MU_RUN_TEST(test_power_charge_voltage_exact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_floating_imprecision);
|
||||
MU_RUN_TEST(test_power_charge_voltage_inexact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_invalid_clamped);
|
||||
MU_RUN_TEST(test_power_charge_voltage_limit_exact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision);
|
||||
MU_RUN_TEST(test_power_charge_voltage_limit_inexact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped);
|
||||
power_test_deinit();
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) {
|
||||
size_t size = strlen(clean_dir) + strlen(name) + 1 + 1;
|
||||
char* fullname = malloc(size);
|
||||
snprintf(fullname, size, "%s/%s", clean_dir, name);
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
clean_directory(fs_api, fullname);
|
||||
}
|
||||
FS_Error error = storage_common_remove(fs_api, fullname);
|
||||
@@ -608,9 +608,8 @@ static void test_rpc_storage_list_create_expected_list(
|
||||
}
|
||||
|
||||
if(path_contains_only_ascii(name)) {
|
||||
list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ?
|
||||
PB_Storage_File_FileType_DIR :
|
||||
PB_Storage_File_FileType_FILE;
|
||||
list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :
|
||||
PB_Storage_File_FileType_FILE;
|
||||
list->file[i].size = fileinfo.size;
|
||||
list->file[i].data = NULL;
|
||||
/* memory free inside rpc_encode_and_send() -> pb_release() */
|
||||
@@ -873,7 +872,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
|
||||
if(error == FSE_OK) {
|
||||
response->which_content = PB_Main_storage_stat_response_tag;
|
||||
response->content.storage_stat_response.has_file = true;
|
||||
response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
|
||||
response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ?
|
||||
PB_Storage_File_FileType_DIR :
|
||||
PB_Storage_File_FileType_FILE;
|
||||
response->content.storage_stat_response.file.size = fileinfo.size;
|
||||
|
||||
@@ -179,7 +179,7 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) {
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
@@ -204,7 +204,7 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
@@ -219,7 +219,7 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein
|
||||
UNUSED(ctx);
|
||||
|
||||
// only files
|
||||
if(!(fileinfo->flags & FSF_DIRECTORY)) {
|
||||
if(!file_info_is_dir(fileinfo)) {
|
||||
// with ".test" in name
|
||||
if(strstr(name, ".test") != NULL) {
|
||||
return true;
|
||||
@@ -243,7 +243,7 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) {
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
|
||||
@@ -2,9 +2,40 @@
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
// DO NOT USE THIS IN PRODUCTION CODE
|
||||
// This is a hack to access internal storage functions and definitions
|
||||
#include <storage/storage_i.h>
|
||||
|
||||
#define UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/" path)
|
||||
|
||||
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
|
||||
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
|
||||
|
||||
#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir")
|
||||
|
||||
static bool storage_file_create(Storage* storage, const char* path, const char* data) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
bool result = false;
|
||||
do {
|
||||
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_NEW)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(storage_file_write(file, data, strlen(data)) != strlen(data)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!storage_file_close(file)) {
|
||||
break;
|
||||
}
|
||||
|
||||
result = true;
|
||||
} while(0);
|
||||
|
||||
storage_file_free(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void storage_file_open_lock_setup() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
@@ -115,7 +146,7 @@ static int32_t storage_dir_locker(void* ctx) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
|
||||
furi_semaphore_release(semaphore);
|
||||
furi_delay_ms(1000);
|
||||
furi_delay_ms(100);
|
||||
|
||||
furi_check(storage_dir_close(file));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
@@ -152,9 +183,21 @@ MU_TEST(storage_dir_open_lock) {
|
||||
mu_assert(result, "cannot open locked dir");
|
||||
}
|
||||
|
||||
MU_TEST(storage_dir_exists_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
mu_check(!storage_dir_exists(storage, STORAGE_TEST_DIR));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, STORAGE_TEST_DIR));
|
||||
mu_check(storage_dir_exists(storage, STORAGE_TEST_DIR));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, STORAGE_TEST_DIR));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(storage_dir) {
|
||||
MU_RUN_TEST(storage_dir_open_close);
|
||||
MU_RUN_TEST(storage_dir_open_lock);
|
||||
MU_RUN_TEST(storage_dir_exists_test);
|
||||
}
|
||||
|
||||
static const char* const storage_copy_test_paths[] = {
|
||||
@@ -303,9 +346,256 @@ MU_TEST_SUITE(storage_rename) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
#define APPSDATA_APP_PATH(path) APPS_DATA_PATH "/" path
|
||||
|
||||
static const char* storage_test_apps[] = {
|
||||
"-_twilight_-",
|
||||
"-_rainbow_-",
|
||||
"-_pinkie_-",
|
||||
"-_apple_-",
|
||||
"-_flutter_-",
|
||||
"-_rare_-",
|
||||
};
|
||||
|
||||
static size_t storage_test_apps_count = COUNT_OF(storage_test_apps);
|
||||
|
||||
static int32_t storage_test_app(void* arg) {
|
||||
UNUSED(arg);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_remove(storage, "/app/test");
|
||||
int32_t ret = storage_file_create(storage, "/app/test", "test");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MU_TEST(test_storage_data_path_apps) {
|
||||
for(size_t i = 0; i < storage_test_apps_count; i++) {
|
||||
FuriThread* thread =
|
||||
furi_thread_alloc_ex(storage_test_apps[i], 1024, storage_test_app, NULL);
|
||||
furi_thread_set_appid(thread, storage_test_apps[i]);
|
||||
furi_thread_start(thread);
|
||||
furi_thread_join(thread);
|
||||
|
||||
mu_assert_int_eq(true, furi_thread_get_return_code(thread));
|
||||
|
||||
// Check if app data dir and file exists
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* expected = furi_string_alloc();
|
||||
furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
|
||||
|
||||
mu_check(storage_dir_exists(storage, furi_string_get_cstr(expected)));
|
||||
furi_string_cat(expected, "/test");
|
||||
mu_check(storage_file_exists(storage, furi_string_get_cstr(expected)));
|
||||
|
||||
furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
|
||||
storage_simply_remove_recursive(storage, furi_string_get_cstr(expected));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
furi_string_free(expected);
|
||||
furi_thread_free(thread);
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_storage_data_path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
File* file = storage_file_alloc(storage);
|
||||
mu_check(storage_dir_open(file, "/app"));
|
||||
mu_check(storage_dir_close(file));
|
||||
storage_file_free(file);
|
||||
|
||||
// check that appsdata folder exists
|
||||
mu_check(storage_dir_exists(storage, APPS_DATA_PATH));
|
||||
|
||||
// check that cli folder exists
|
||||
mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli")));
|
||||
|
||||
storage_simply_remove(storage, APPSDATA_APP_PATH("cli"));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST(test_storage_common_migrate) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// Setup test folders
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
|
||||
// Test migration from non existing
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
// Test migration from existing folder to non existing
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
|
||||
mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
|
||||
// Test migration from existing folder to existing folder
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file11")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file21.ext")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext1.ext")));
|
||||
mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
|
||||
// Test migration from empty folder to existing file
|
||||
// Expected result: FSE_OK, folder removed, file untouched
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test1"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
|
||||
// Test migration from empty folder to existing folder
|
||||
// Expected result: FSE_OK, old folder removed, new folder untouched
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
|
||||
// Test migration from existing file to non existing, no extension
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
|
||||
// Test migration from existing file to non existing, with extension
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
|
||||
mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
|
||||
|
||||
// Test migration from existing file to existing file, no extension
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test2"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
|
||||
|
||||
// Test migration from existing file to existing file, with extension
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new.file"), "test2"));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
|
||||
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
|
||||
mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1.file")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1.file"));
|
||||
|
||||
// Test migration from existing file to existing folder
|
||||
mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK,
|
||||
storage_common_migrate(
|
||||
storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
|
||||
|
||||
mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
|
||||
mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
|
||||
mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
|
||||
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
|
||||
storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_data_path) {
|
||||
MU_RUN_TEST(test_storage_data_path);
|
||||
MU_RUN_TEST(test_storage_data_path_apps);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_storage_common) {
|
||||
MU_RUN_TEST(test_storage_common_migrate);
|
||||
}
|
||||
|
||||
int run_minunit_test_storage() {
|
||||
MU_RUN_SUITE(storage_file);
|
||||
MU_RUN_SUITE(storage_dir);
|
||||
MU_RUN_SUITE(storage_rename);
|
||||
MU_RUN_SUITE(test_data_path);
|
||||
MU_RUN_SUITE(test_storage_common);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
18
applications/examples/example_apps_data/README.md
Normal file
18
applications/examples/example_apps_data/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Apps Data folder Example
|
||||
|
||||
This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth.
|
||||
|
||||
## What is the Apps Data Folder?
|
||||
|
||||
The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware.
|
||||
|
||||
The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
|
||||
The Apps Data folder is located only on the external storage, the SD card.
|
||||
|
||||
For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/app` alias instead.
|
||||
|
||||
## How to get the path to the Apps Data folder?
|
||||
|
||||
You can use `/app` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/app/config.txt`. But this way is not recommended, because even the `/app` alias can change in the future.
|
||||
|
||||
We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
|
||||
9
applications/examples/example_apps_data/application.fam
Normal file
9
applications/examples/example_apps_data/application.fam
Normal file
@@ -0,0 +1,9 @@
|
||||
App(
|
||||
appid="example_apps_data",
|
||||
name="Example: Apps Data",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_apps_data_main",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
40
applications/examples/example_apps_data/example_apps_data.c
Normal file
40
applications/examples/example_apps_data/example_apps_data.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
// Define log tag
|
||||
#define TAG "example_apps_data"
|
||||
|
||||
// Application entry point
|
||||
int32_t example_apps_data_main(void* p) {
|
||||
// Mark argument as unused
|
||||
UNUSED(p);
|
||||
|
||||
// Open storage
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// Allocate file
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
// Get the path to the current application data folder
|
||||
// That is: /ext/apps_data/<app_name>
|
||||
// And it will create folders in the path if they don't exist
|
||||
// In this example it will create /ext/apps_data/example_apps_data
|
||||
// And file will be /ext/apps_data/example_apps_data/test.txt
|
||||
|
||||
// Open file, write data and close it
|
||||
if(!storage_file_open(file, APP_DATA_PATH("test.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
FURI_LOG_E(TAG, "Failed to open file");
|
||||
}
|
||||
if(!storage_file_write(file, "Hello World!", strlen("Hello World!"))) {
|
||||
FURI_LOG_E(TAG, "Failed to write to file");
|
||||
}
|
||||
storage_file_close(file);
|
||||
|
||||
// Deallocate file
|
||||
storage_file_free(file);
|
||||
|
||||
// Close storage
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ App(
|
||||
"fap_loader",
|
||||
"archive",
|
||||
"clock",
|
||||
"unirfremix",
|
||||
"subghz_remote",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -451,7 +451,7 @@ static bool archive_is_dir_exists(FuriString* path) {
|
||||
FileInfo file_info;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
if(file_info.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&file_info)) {
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
@@ -532,12 +532,16 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) {
|
||||
browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);
|
||||
|
||||
furi_string_set(browser->path, path);
|
||||
|
||||
file_browser_worker_folder_enter(browser->worker, path, idx_temp);
|
||||
}
|
||||
|
||||
void archive_leave_dir(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
size_t dirname_start = furi_string_search_rchar(browser->path, '/');
|
||||
furi_string_left(browser->path, dirname_start);
|
||||
|
||||
file_browser_worker_folder_exit(browser->worker);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ bool archive_favorites_read(void* context) {
|
||||
if(storage_file_exists(storage, furi_string_get_cstr(buffer))) {
|
||||
storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info);
|
||||
archive_add_file_item(
|
||||
browser, (file_info.flags & FSF_DIRECTORY), furi_string_get_cstr(buffer));
|
||||
browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer));
|
||||
file_count++;
|
||||
} else {
|
||||
need_refresh = true;
|
||||
|
||||
@@ -96,7 +96,7 @@ void archive_delete_file(void* context, const char* format, ...) {
|
||||
|
||||
bool res = false;
|
||||
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
res = storage_simply_remove_recursive(fs_api, furi_string_get_cstr(filename));
|
||||
} else {
|
||||
res = (storage_common_remove(fs_api, furi_string_get_cstr(filename)) == FSE_OK);
|
||||
|
||||
@@ -142,10 +142,6 @@ void bad_usb_app_free(BadUsbApp* app) {
|
||||
app->bad_usb_script = NULL;
|
||||
}
|
||||
|
||||
if(app->usb_if_prev) {
|
||||
furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL));
|
||||
}
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
|
||||
bad_usb_free(app->bad_usb_view);
|
||||
@@ -172,6 +168,10 @@ void bad_usb_app_free(BadUsbApp* app) {
|
||||
furi_string_free(app->file_path);
|
||||
furi_string_free(app->keyboard_layout);
|
||||
|
||||
if(app->usb_if_prev) {
|
||||
furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL));
|
||||
}
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ struct BadUsbScript {
|
||||
FuriString* file_path;
|
||||
uint32_t defdelay;
|
||||
uint16_t layout[128];
|
||||
uint32_t stringdelay;
|
||||
FuriThread* thread;
|
||||
uint8_t file_buf[FILE_BUFFER_LEN + 1];
|
||||
uint8_t buf_start;
|
||||
@@ -113,6 +114,8 @@ static const char ducky_cmd_delay[] = {"DELAY "};
|
||||
static const char ducky_cmd_string[] = {"STRING "};
|
||||
static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "};
|
||||
static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "};
|
||||
static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "};
|
||||
static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "};
|
||||
static const char ducky_cmd_repeat[] = {"REPEAT "};
|
||||
static const char ducky_cmd_sysrq[] = {"SYSRQ "};
|
||||
|
||||
@@ -211,14 +214,19 @@ static bool ducky_altstring(const char* param) {
|
||||
|
||||
static bool ducky_string(BadUsbScript* bad_usb, const char* param) {
|
||||
uint32_t i = 0;
|
||||
|
||||
while(param[i] != '\0') {
|
||||
uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
furi_hal_hid_kb_press(keycode);
|
||||
furi_hal_hid_kb_release(keycode);
|
||||
if(bad_usb->stringdelay > 0) {
|
||||
furi_delay_ms(bad_usb->stringdelay);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
bad_usb->stringdelay = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -277,6 +285,20 @@ static int32_t
|
||||
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||
}
|
||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||
} else if(
|
||||
(strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) ||
|
||||
(strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) {
|
||||
//STRINGDELAY, finally it's here
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
state = ducky_get_number(line_tmp, &bad_usb->stringdelay);
|
||||
if((state) && (bad_usb->stringdelay > 0)) {
|
||||
return state;
|
||||
}
|
||||
if(error != NULL) {
|
||||
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||
}
|
||||
return SCRIPT_STATE_ERROR;
|
||||
|
||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||
// STRING
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
@@ -484,6 +506,19 @@ static void bad_usb_hid_state_callback(bool state, void* context) {
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect);
|
||||
}
|
||||
|
||||
static uint32_t bad_usb_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_usb_worker(void* context) {
|
||||
BadUsbScript* bad_usb = context;
|
||||
|
||||
@@ -520,11 +555,9 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
|
||||
FuriFlagWaitAny,
|
||||
FuriWaitForever);
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) {
|
||||
@@ -535,11 +568,9 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadUsbStateIdle) { // State: ready to start
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
|
||||
FuriFlagWaitAny,
|
||||
FuriWaitForever);
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) { // Start executing script
|
||||
@@ -548,6 +579,7 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->buf_len = 0;
|
||||
bad_usb->st.line_cur = 0;
|
||||
bad_usb->defdelay = 0;
|
||||
bad_usb->stringdelay = 0;
|
||||
bad_usb->repeat_cnt = 0;
|
||||
bad_usb->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
@@ -558,11 +590,9 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
|
||||
FuriFlagWaitAny,
|
||||
FuriWaitForever);
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) { // Start executing script
|
||||
@@ -571,12 +601,22 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->buf_len = 0;
|
||||
bad_usb->st.line_cur = 0;
|
||||
bad_usb->defdelay = 0;
|
||||
bad_usb->stringdelay = 0;
|
||||
bad_usb->repeat_cnt = 0;
|
||||
bad_usb->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
// extra time for PC to recognize Flipper as keyboard
|
||||
furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
|
||||
worker_state = BadUsbStateRunning;
|
||||
flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle,
|
||||
FuriFlagWaitAny | FuriFlagNoClear,
|
||||
1500);
|
||||
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
||||
// If nothing happened - start script execution
|
||||
worker_state = BadUsbStateRunning;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
worker_state = BadUsbStateIdle;
|
||||
furi_thread_flags_clear(WorkerEvtToggle);
|
||||
}
|
||||
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
|
||||
worker_state = BadUsbStateNotConnected;
|
||||
}
|
||||
@@ -586,6 +626,7 @@ static int32_t bad_usb_worker(void* context) {
|
||||
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) {
|
||||
@@ -629,9 +670,9 @@ static int32_t bad_usb_worker(void* context) {
|
||||
} else if(
|
||||
(worker_state == BadUsbStateFileError) ||
|
||||
(worker_state == BadUsbStateScriptError)) { // State: error
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
uint32_t flags =
|
||||
bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ void bad_usb_scene_config_on_enter(void* context) {
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Keyboard layout",
|
||||
"Keyboard Layout",
|
||||
SubmenuIndexKeyboardLayout,
|
||||
bad_usb_scene_config_submenu_callback,
|
||||
bad_usb);
|
||||
|
||||
@@ -22,7 +22,6 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
||||
void bad_usb_scene_file_select_on_enter(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
|
||||
furi_hal_usb_disable();
|
||||
if(bad_usb->bad_usb_script) {
|
||||
bad_usb_script_close(bad_usb->bad_usb_script);
|
||||
bad_usb->bad_usb_script = NULL;
|
||||
@@ -34,7 +33,6 @@ void bad_usb_scene_file_select_on_enter(void* context) {
|
||||
|
||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
|
||||
} else {
|
||||
furi_hal_usb_enable();
|
||||
view_dispatcher_stop(bad_usb->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InputKeyLeft) {
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
|
||||
if(bad_usb_is_idle_state(app->bad_usb_view)) {
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyOk) {
|
||||
bad_usb_script_toggle(app->bad_usb_script);
|
||||
|
||||
@@ -48,17 +48,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
|
||||
(model->state.state == BadUsbStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
elements_button_left(canvas, "Config");
|
||||
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
elements_button_center(canvas, "Cancel");
|
||||
}
|
||||
|
||||
if((model->state.state == BadUsbStateNotConnected) ||
|
||||
(model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
|
||||
elements_button_left(canvas, "Config");
|
||||
}
|
||||
|
||||
if(model->state.state == BadUsbStateNotConnected) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
@@ -203,6 +199,7 @@ void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) {
|
||||
{ strlcpy(model->layout, layout, MAX_NAME_LEN); },
|
||||
true);
|
||||
}
|
||||
|
||||
void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
|
||||
furi_assert(st);
|
||||
with_view_model(
|
||||
@@ -214,3 +211,19 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
bool bad_usb_is_idle_state(BadUsb* bad_usb) {
|
||||
bool is_idle = false;
|
||||
with_view_model(
|
||||
bad_usb->view,
|
||||
BadUsbModel * model,
|
||||
{
|
||||
if((model->state.state == BadUsbStateIdle) ||
|
||||
(model->state.state == BadUsbStateDone) ||
|
||||
(model->state.state == BadUsbStateNotConnected)) {
|
||||
is_idle = true;
|
||||
}
|
||||
},
|
||||
false);
|
||||
return is_idle;
|
||||
}
|
||||
|
||||
@@ -19,3 +19,5 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name);
|
||||
void bad_usb_set_layout(BadUsb* bad_usb, const char* layout);
|
||||
|
||||
void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st);
|
||||
|
||||
bool bad_usb_is_idle_state(BadUsb* bad_usb);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <storage/storage.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include "elf_cpp/elf_hashtable.h"
|
||||
#include "fap_loader_app.h"
|
||||
@@ -105,6 +106,12 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
|
||||
FURI_LOG_I(TAG, "FAP Loader is starting app");
|
||||
|
||||
FuriThread* thread = flipper_application_spawn(loader->app, NULL);
|
||||
|
||||
FuriString* app_name = furi_string_alloc();
|
||||
path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name);
|
||||
furi_thread_set_appid(thread, furi_string_get_cstr(app_name));
|
||||
furi_string_free(app_name);
|
||||
|
||||
furi_thread_start(thread);
|
||||
furi_thread_join(thread);
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#include "ibutton.h"
|
||||
#include "assets_icons.h"
|
||||
#include "ibutton_i.h"
|
||||
#include "ibutton/scenes/ibutton_scene.h"
|
||||
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <rpc/rpc_app.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "iButtonApp"
|
||||
@@ -34,50 +30,13 @@ static const NotificationSequence* ibutton_notification_sequences[] = {
|
||||
};
|
||||
|
||||
static void ibutton_make_app_folder(iButton* ibutton) {
|
||||
if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) {
|
||||
dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder");
|
||||
}
|
||||
}
|
||||
|
||||
bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog) {
|
||||
FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
|
||||
bool result = false;
|
||||
FuriString* data;
|
||||
data = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, furi_string_get_cstr(key_path))) break;
|
||||
|
||||
// header
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(file, data, &version)) break;
|
||||
if(furi_string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break;
|
||||
if(version != 1) break;
|
||||
|
||||
// key type
|
||||
iButtonKeyType type;
|
||||
if(!flipper_format_read_string(file, "Key type", data)) break;
|
||||
if(!ibutton_key_get_type_by_string(furi_string_get_cstr(data), &type)) break;
|
||||
|
||||
// key data
|
||||
uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
|
||||
if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
|
||||
break;
|
||||
|
||||
ibutton_key_set_type(ibutton->key, type);
|
||||
ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE);
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_string_free(data);
|
||||
|
||||
if((!result) && (show_dialog)) {
|
||||
dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
|
||||
}
|
||||
|
||||
return result;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
@@ -87,14 +46,14 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context)
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
|
||||
ibutton->rpc_ctx = NULL;
|
||||
rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
|
||||
ibutton->rpc = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
} else {
|
||||
rpc_system_app_confirm(ibutton->rpc_ctx, event, false);
|
||||
rpc_system_app_confirm(ibutton->rpc, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,13 +94,13 @@ iButton* ibutton_alloc() {
|
||||
|
||||
ibutton->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
ibutton->storage = furi_record_open(RECORD_STORAGE);
|
||||
ibutton->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
ibutton->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
ibutton->key = ibutton_key_alloc();
|
||||
ibutton->key_worker = ibutton_worker_alloc();
|
||||
ibutton_worker_start_thread(ibutton->key_worker);
|
||||
ibutton->protocols = ibutton_protocols_alloc();
|
||||
ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols));
|
||||
ibutton->worker = ibutton_worker_alloc(ibutton->protocols);
|
||||
ibutton_worker_start_thread(ibutton->worker);
|
||||
|
||||
ibutton->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@@ -163,9 +122,9 @@ iButton* ibutton_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget));
|
||||
|
||||
ibutton->dialog_ex = dialog_ex_alloc();
|
||||
ibutton->loading = loading_alloc();
|
||||
view_dispatcher_add_view(
|
||||
ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex));
|
||||
ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading));
|
||||
|
||||
return ibutton;
|
||||
}
|
||||
@@ -173,8 +132,8 @@ iButton* ibutton_alloc() {
|
||||
void ibutton_free(iButton* ibutton) {
|
||||
furi_assert(ibutton);
|
||||
|
||||
view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx);
|
||||
dialog_ex_free(ibutton->dialog_ex);
|
||||
view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading);
|
||||
loading_free(ibutton->loading);
|
||||
|
||||
view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
widget_free(ibutton->widget);
|
||||
@@ -194,9 +153,6 @@ void ibutton_free(iButton* ibutton) {
|
||||
view_dispatcher_free(ibutton->view_dispatcher);
|
||||
scene_manager_free(ibutton->scene_manager);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
ibutton->storage = NULL;
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
ibutton->notifications = NULL;
|
||||
|
||||
@@ -206,103 +162,83 @@ void ibutton_free(iButton* ibutton) {
|
||||
furi_record_close(RECORD_GUI);
|
||||
ibutton->gui = NULL;
|
||||
|
||||
ibutton_worker_stop_thread(ibutton->key_worker);
|
||||
ibutton_worker_free(ibutton->key_worker);
|
||||
ibutton_worker_stop_thread(ibutton->worker);
|
||||
ibutton_worker_free(ibutton->worker);
|
||||
ibutton_key_free(ibutton->key);
|
||||
ibutton_protocols_free(ibutton->protocols);
|
||||
|
||||
furi_string_free(ibutton->file_path);
|
||||
|
||||
free(ibutton);
|
||||
}
|
||||
|
||||
bool ibutton_file_select(iButton* ibutton) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
|
||||
browser_options.base_path = IBUTTON_APP_FOLDER;
|
||||
bool ibutton_load_key(iButton* ibutton) {
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
|
||||
|
||||
bool success = dialog_file_browser_show(
|
||||
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options);
|
||||
const bool success = ibutton_protocols_load(
|
||||
ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path));
|
||||
|
||||
if(success) {
|
||||
success = ibutton_load_key_data(ibutton, ibutton->file_path, true);
|
||||
if(!success) {
|
||||
dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
|
||||
|
||||
} else {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
path_extract_filename(ibutton->file_path, tmp, true);
|
||||
strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE);
|
||||
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ibutton_save_key(iButton* ibutton, const char* key_name) {
|
||||
// Create ibutton directory if necessary
|
||||
bool ibutton_select_and_load_key(iButton* ibutton) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
|
||||
browser_options.base_path = IBUTTON_APP_FOLDER;
|
||||
|
||||
if(furi_string_empty(ibutton->file_path)) {
|
||||
furi_string_set(ibutton->file_path, browser_options.base_path);
|
||||
}
|
||||
|
||||
return dialog_file_browser_show(
|
||||
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) &&
|
||||
ibutton_load_key(ibutton);
|
||||
}
|
||||
|
||||
bool ibutton_save_key(iButton* ibutton) {
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
|
||||
|
||||
ibutton_make_app_folder(ibutton);
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
|
||||
iButtonKey* key = ibutton->key;
|
||||
const bool success =
|
||||
ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path));
|
||||
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
// Check if we has old key
|
||||
if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
// First remove old key
|
||||
ibutton_delete_key(ibutton);
|
||||
|
||||
// Remove old key name from path
|
||||
size_t filename_start = furi_string_search_rchar(ibutton->file_path, '/');
|
||||
furi_string_left(ibutton->file_path, filename_start);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION);
|
||||
|
||||
// Open file for write
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(ibutton->file_path))) break;
|
||||
|
||||
// Write header
|
||||
if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break;
|
||||
|
||||
// Write key type
|
||||
if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
|
||||
break;
|
||||
const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
|
||||
if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
|
||||
|
||||
// Write data
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
|
||||
break;
|
||||
result = true;
|
||||
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
|
||||
if(!result) { //-V547
|
||||
if(!success) {
|
||||
dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file");
|
||||
}
|
||||
|
||||
return result;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ibutton_delete_key(iButton* ibutton) {
|
||||
bool result = false;
|
||||
result = storage_simply_remove(ibutton->storage, furi_string_get_cstr(ibutton->file_path));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
ibutton_reset_key(ibutton);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ibutton_text_store_set(iButton* ibutton, const char* text, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ibutton_text_store_clear(iButton* ibutton) {
|
||||
memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1);
|
||||
void ibutton_reset_key(iButton* ibutton) {
|
||||
memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1);
|
||||
furi_string_reset(ibutton->file_path);
|
||||
ibutton_key_reset(ibutton->key);
|
||||
}
|
||||
|
||||
void ibutton_notification_message(iButton* ibutton, uint32_t message) {
|
||||
@@ -310,36 +246,44 @@ void ibutton_notification_message(iButton* ibutton, uint32_t message) {
|
||||
notification_message(ibutton->notifications, ibutton_notification_sequences[message]);
|
||||
}
|
||||
|
||||
int32_t ibutton_app(void* p) {
|
||||
void ibutton_submenu_callback(void* context, uint32_t index) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void ibutton_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
iButton* ibutton = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ibutton_app(void* arg) {
|
||||
iButton* ibutton = ibutton_alloc();
|
||||
|
||||
ibutton_make_app_folder(ibutton);
|
||||
|
||||
bool key_loaded = false;
|
||||
bool rpc_mode = false;
|
||||
|
||||
if(p && strlen(p)) {
|
||||
uint32_t rpc_ctx = 0;
|
||||
if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
|
||||
if((arg != NULL) && (strlen(arg) != 0)) {
|
||||
if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) {
|
||||
FURI_LOG_D(TAG, "Running in RPC mode");
|
||||
ibutton->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_mode = true;
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton);
|
||||
rpc_system_app_send_started(ibutton->rpc_ctx);
|
||||
|
||||
rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton);
|
||||
rpc_system_app_send_started(ibutton->rpc);
|
||||
|
||||
} else {
|
||||
furi_string_set(ibutton->file_path, (const char*)p);
|
||||
if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) {
|
||||
key_loaded = true;
|
||||
// TODO: Display an error if the key from p could not be loaded
|
||||
}
|
||||
furi_string_set(ibutton->file_path, (const char*)arg);
|
||||
key_loaded = ibutton_load_key(ibutton);
|
||||
}
|
||||
}
|
||||
|
||||
if(rpc_mode) {
|
||||
if(ibutton->rpc != NULL) {
|
||||
view_dispatcher_attach_to_gui(
|
||||
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc);
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
|
||||
|
||||
} else {
|
||||
view_dispatcher_attach_to_gui(
|
||||
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
|
||||
@@ -353,9 +297,9 @@ int32_t ibutton_app(void* p) {
|
||||
|
||||
view_dispatcher_run(ibutton->view_dispatcher);
|
||||
|
||||
if(ibutton->rpc_ctx) {
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(ibutton->rpc_ctx);
|
||||
if(ibutton->rpc) {
|
||||
rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
|
||||
rpc_system_app_send_exited(ibutton->rpc);
|
||||
}
|
||||
ibutton_free(ibutton);
|
||||
return 0;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <one_wire/ibutton/ibutton_worker.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#include <one_wire/one_wire_host.h>
|
||||
|
||||
#include <one_wire/ibutton/ibutton_key.h>
|
||||
#include <one_wire/ibutton/ibutton_worker.h>
|
||||
#include <one_wire/ibutton/ibutton_protocols.h>
|
||||
|
||||
static void ibutton_cli(Cli* cli, FuriString* args, void* context);
|
||||
static void onewire_cli(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
@@ -22,7 +26,7 @@ void ibutton_on_system_start() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ibutton_cli_print_usage() {
|
||||
static void ibutton_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("ikey read\r\n");
|
||||
printf("ikey emulate <key_type> <key_data>\r\n");
|
||||
@@ -34,30 +38,52 @@ void ibutton_cli_print_usage() {
|
||||
printf("\t<key_data> are hex-formatted\r\n");
|
||||
};
|
||||
|
||||
bool ibutton_cli_get_key_type(FuriString* data, iButtonKeyType* type) {
|
||||
static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) {
|
||||
bool result = false;
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
if(furi_string_cmp_str(data, "Dallas") == 0 || furi_string_cmp_str(data, "dallas") == 0) {
|
||||
result = true;
|
||||
*type = iButtonKeyDS1990;
|
||||
} else if(furi_string_cmp_str(data, "Cyfral") == 0 || furi_string_cmp_str(data, "cyfral") == 0) {
|
||||
result = true;
|
||||
*type = iButtonKeyCyfral;
|
||||
} else if(furi_string_cmp_str(data, "Metakom") == 0 || furi_string_cmp_str(data, "metakom") == 0) {
|
||||
result = true;
|
||||
*type = iButtonKeyMetakom;
|
||||
}
|
||||
do {
|
||||
// Read protocol name
|
||||
if(!args_read_string_and_trim(args, name)) break;
|
||||
|
||||
// Make the protocol name uppercase
|
||||
const char first = furi_string_get_char(name, 0);
|
||||
furi_string_set_char(name, 0, toupper((int)first));
|
||||
|
||||
const iButtonProtocolId id =
|
||||
ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name));
|
||||
if(id == iButtonProtocolIdInvalid) break;
|
||||
|
||||
ibutton_key_set_protocol_id(key, id);
|
||||
|
||||
// Get the data pointer
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(protocols, key, &data);
|
||||
|
||||
// Read data
|
||||
if(!args_read_hex_bytes(args, data.ptr, data.size)) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ibutton_cli_print_key_data(iButtonKey* key) {
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
iButtonKeyType type = ibutton_key_get_type(key);
|
||||
static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) {
|
||||
const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key));
|
||||
|
||||
printf("%s ", ibutton_key_get_string_by_type(type));
|
||||
for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) {
|
||||
printf("%02X", key_data[i]);
|
||||
if(strncmp(name, "DS", 2) == 0) {
|
||||
name = "Dallas";
|
||||
}
|
||||
|
||||
printf("%s ", name);
|
||||
|
||||
iButtonEditableData data;
|
||||
ibutton_protocols_get_editable_data(protocols, key, &data);
|
||||
|
||||
for(size_t i = 0; i < data.size; i++) {
|
||||
printf("%02X", data.ptr[i]);
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
@@ -71,9 +97,10 @@ static void ibutton_cli_worker_read_cb(void* context) {
|
||||
furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);
|
||||
}
|
||||
|
||||
void ibutton_cli_read(Cli* cli) {
|
||||
iButtonKey* key = ibutton_key_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc();
|
||||
static void ibutton_cli_read(Cli* cli) {
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
FuriEventFlag* event = furi_event_flag_alloc();
|
||||
|
||||
ibutton_worker_start_thread(worker);
|
||||
@@ -81,32 +108,25 @@ void ibutton_cli_read(Cli* cli) {
|
||||
|
||||
printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n");
|
||||
ibutton_worker_read_start(worker, key);
|
||||
|
||||
while(true) {
|
||||
uint32_t flags =
|
||||
furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
|
||||
|
||||
if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
|
||||
ibutton_cli_print_key_data(key);
|
||||
|
||||
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
|
||||
if(!ibutton_key_dallas_crc_is_valid(key)) {
|
||||
printf("Warning: invalid CRC\r\n");
|
||||
}
|
||||
|
||||
if(!ibutton_key_dallas_is_1990_key(key)) {
|
||||
printf("Warning: not a key\r\n");
|
||||
}
|
||||
}
|
||||
ibutton_cli_print_key(protocols, key);
|
||||
break;
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
}
|
||||
ibutton_worker_stop(worker);
|
||||
|
||||
ibutton_worker_stop(worker);
|
||||
ibutton_worker_stop_thread(worker);
|
||||
ibutton_worker_free(worker);
|
||||
|
||||
ibutton_key_free(key);
|
||||
ibutton_worker_free(worker);
|
||||
ibutton_protocols_free(protocols);
|
||||
|
||||
furi_event_flag_free(event);
|
||||
};
|
||||
@@ -124,48 +144,33 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult
|
||||
}
|
||||
|
||||
void ibutton_cli_write(Cli* cli, FuriString* args) {
|
||||
iButtonKey* key = ibutton_key_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc();
|
||||
iButtonKeyType type;
|
||||
iButtonWriteContext write_context;
|
||||
uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
|
||||
FuriString* data;
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
|
||||
iButtonWriteContext write_context;
|
||||
write_context.event = furi_event_flag_alloc();
|
||||
|
||||
data = furi_string_alloc();
|
||||
ibutton_worker_start_thread(worker);
|
||||
ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, data)) {
|
||||
if(!ibutton_cli_parse_key(protocols, key, args)) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!ibutton_cli_get_key_type(data, &type)) {
|
||||
if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) &
|
||||
iButtonProtocolFeatureWriteBlank)) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(type != iButtonKeyDS1990) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
ibutton_key_set_type(key, type);
|
||||
ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
|
||||
|
||||
printf("Writing key ");
|
||||
ibutton_cli_print_key_data(key);
|
||||
ibutton_cli_print_key(protocols, key);
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
ibutton_worker_write_start(worker, key);
|
||||
ibutton_worker_write_blank_start(worker, key);
|
||||
while(true) {
|
||||
uint32_t flags = furi_event_flag_wait(
|
||||
write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
|
||||
@@ -183,64 +188,53 @@ void ibutton_cli_write(Cli* cli, FuriString* args) {
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
}
|
||||
ibutton_worker_stop(worker);
|
||||
} while(false);
|
||||
|
||||
furi_string_free(data);
|
||||
ibutton_worker_stop(worker);
|
||||
ibutton_worker_stop_thread(worker);
|
||||
ibutton_worker_free(worker);
|
||||
|
||||
ibutton_key_free(key);
|
||||
ibutton_worker_free(worker);
|
||||
ibutton_protocols_free(protocols);
|
||||
|
||||
furi_event_flag_free(write_context.event);
|
||||
};
|
||||
}
|
||||
|
||||
void ibutton_cli_emulate(Cli* cli, FuriString* args) {
|
||||
iButtonKey* key = ibutton_key_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc();
|
||||
iButtonKeyType type;
|
||||
uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
|
||||
FuriString* data;
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
|
||||
data = furi_string_alloc();
|
||||
ibutton_worker_start_thread(worker);
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, data)) {
|
||||
if(!ibutton_cli_parse_key(protocols, key, args)) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!ibutton_cli_get_key_type(data, &type)) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
|
||||
ibutton_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
ibutton_key_set_type(key, type);
|
||||
ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
|
||||
|
||||
printf("Emulating key ");
|
||||
ibutton_cli_print_key_data(key);
|
||||
ibutton_cli_print_key(protocols, key);
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
ibutton_worker_emulate_start(worker, key);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
furi_delay_ms(100);
|
||||
};
|
||||
ibutton_worker_stop(worker);
|
||||
|
||||
} while(false);
|
||||
|
||||
furi_string_free(data);
|
||||
ibutton_worker_stop(worker);
|
||||
ibutton_worker_stop_thread(worker);
|
||||
ibutton_worker_free(worker);
|
||||
|
||||
ibutton_key_free(key);
|
||||
ibutton_worker_free(worker);
|
||||
ibutton_protocols_free(protocols);
|
||||
};
|
||||
|
||||
static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
|
||||
void ibutton_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -264,7 +258,7 @@ static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
void onewire_cli_print_usage() {
|
||||
static void onewire_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("onewire search\r\n");
|
||||
};
|
||||
@@ -281,7 +275,7 @@ static void onewire_cli_search(Cli* cli) {
|
||||
furi_hal_power_enable_otg();
|
||||
|
||||
while(!done) {
|
||||
if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) {
|
||||
if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
|
||||
printf("Search finished\r\n");
|
||||
onewire_host_reset_search(onewire);
|
||||
done = true;
|
||||
|
||||
@@ -6,6 +6,7 @@ enum iButtonCustomEvent {
|
||||
|
||||
iButtonCustomEventBack,
|
||||
iButtonCustomEventTextEditResult,
|
||||
iButtonCustomEventByteEditChanged,
|
||||
iButtonCustomEventByteEditResult,
|
||||
iButtonCustomEventWorkerEmulated,
|
||||
iButtonCustomEventWorkerRead,
|
||||
|
||||
@@ -4,31 +4,40 @@
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
#include <one_wire/ibutton/ibutton_worker.h>
|
||||
#include <one_wire/ibutton/ibutton_protocols.h>
|
||||
|
||||
#include <rpc/rpc_app.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/byte_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/loading.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "ibutton_custom_event.h"
|
||||
#include "scenes/ibutton_scene.h"
|
||||
|
||||
#define IBUTTON_FILE_NAME_SIZE 100
|
||||
#define IBUTTON_TEXT_STORE_SIZE 128
|
||||
|
||||
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
|
||||
#define IBUTTON_APP_EXTENSION ".ibtn"
|
||||
#define IBUTTON_APP_FILE_TYPE "Flipper iButton key"
|
||||
|
||||
#define IBUTTON_KEY_NAME_SIZE 22
|
||||
|
||||
typedef enum {
|
||||
iButtonWriteModeInvalid,
|
||||
iButtonWriteModeBlank,
|
||||
iButtonWriteModeCopy,
|
||||
} iButtonWriteMode;
|
||||
|
||||
struct iButton {
|
||||
SceneManager* scene_manager;
|
||||
@@ -38,21 +47,22 @@ struct iButton {
|
||||
Storage* storage;
|
||||
DialogsApp* dialogs;
|
||||
NotificationApp* notifications;
|
||||
RpcAppSystem* rpc;
|
||||
|
||||
iButtonWorker* key_worker;
|
||||
iButtonKey* key;
|
||||
iButtonWorker* worker;
|
||||
iButtonProtocols* protocols;
|
||||
iButtonWriteMode write_mode;
|
||||
|
||||
FuriString* file_path;
|
||||
char text_store[IBUTTON_TEXT_STORE_SIZE + 1];
|
||||
char key_name[IBUTTON_KEY_NAME_SIZE + 1];
|
||||
|
||||
Submenu* submenu;
|
||||
ByteInput* byte_input;
|
||||
TextInput* text_input;
|
||||
Popup* popup;
|
||||
Widget* widget;
|
||||
DialogEx* dialog_ex;
|
||||
|
||||
void* rpc_ctx;
|
||||
Loading* loading;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -61,7 +71,7 @@ typedef enum {
|
||||
iButtonViewTextInput,
|
||||
iButtonViewPopup,
|
||||
iButtonViewWidget,
|
||||
iButtonViewDialogEx,
|
||||
iButtonViewLoading,
|
||||
} iButtonView;
|
||||
|
||||
typedef enum {
|
||||
@@ -78,10 +88,12 @@ typedef enum {
|
||||
iButtonNotificationMessageBlinkStop,
|
||||
} iButtonNotificationMessage;
|
||||
|
||||
bool ibutton_file_select(iButton* ibutton);
|
||||
bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog);
|
||||
bool ibutton_save_key(iButton* ibutton, const char* key_name);
|
||||
bool ibutton_select_and_load_key(iButton* ibutton);
|
||||
bool ibutton_load_key(iButton* ibutton);
|
||||
bool ibutton_save_key(iButton* ibutton);
|
||||
bool ibutton_delete_key(iButton* ibutton);
|
||||
void ibutton_text_store_set(iButton* ibutton, const char* text, ...);
|
||||
void ibutton_text_store_clear(iButton* ibutton);
|
||||
void ibutton_reset_key(iButton* ibutton);
|
||||
void ibutton_notification_message(iButton* ibutton, uint32_t message);
|
||||
|
||||
void ibutton_submenu_callback(void* context, uint32_t index);
|
||||
void ibutton_widget_callback(GuiButtonType result, InputType type, void* context);
|
||||
|
||||
@@ -1,54 +1,57 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexCyfral,
|
||||
SubmenuIndexDallas,
|
||||
SubmenuIndexMetakom,
|
||||
};
|
||||
|
||||
void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void ibutton_scene_add_type_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Submenu* submenu = ibutton->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton);
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType));
|
||||
for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count();
|
||||
++protocol_id) {
|
||||
if((strcmp(
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) &&
|
||||
(strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") !=
|
||||
0)) {
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s %s",
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
} else {
|
||||
furi_string_printf(
|
||||
tmp, "%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
}
|
||||
|
||||
submenu_add_item(
|
||||
submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context);
|
||||
}
|
||||
|
||||
const uint32_t prev_protocol_id =
|
||||
scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType);
|
||||
submenu_set_selected_item(submenu, prev_protocol_id);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
iButtonKey* key = ibutton->key;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, event.event);
|
||||
consumed = true;
|
||||
if(event.event == SubmenuIndexCyfral) {
|
||||
ibutton_key_set_type(key, iButtonKeyCyfral);
|
||||
} else if(event.event == SubmenuIndexDallas) {
|
||||
ibutton_key_set_type(key, iButtonKeyDS1990);
|
||||
} else if(event.event == SubmenuIndexMetakom) {
|
||||
ibutton_key_set_type(key, iButtonKeyMetakom);
|
||||
} else {
|
||||
furi_crash("Unknown key type");
|
||||
}
|
||||
const iButtonProtocolId protocol_id = event.event;
|
||||
|
||||
furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
|
||||
ibutton_key_clear_data(key);
|
||||
ibutton_key_reset(key);
|
||||
ibutton_key_set_protocol_id(key, protocol_id);
|
||||
ibutton_protocols_apply_edits(ibutton->protocols, key);
|
||||
|
||||
scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id);
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
@@ -1,42 +1,52 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
void ibutton_scene_add_type_byte_input_callback(void* context) {
|
||||
static void ibutton_scene_add_type_byte_input_callback(void* context) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);
|
||||
}
|
||||
|
||||
static void ibutton_scene_add_type_byte_changed_callback(void* context) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged);
|
||||
}
|
||||
|
||||
void ibutton_scene_add_value_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
iButtonKey* key = ibutton->key;
|
||||
uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE);
|
||||
byte_input_set_header_text(ibutton->byte_input, "Enter the key");
|
||||
|
||||
scene_manager_set_scene_state(
|
||||
ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data);
|
||||
memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
|
||||
iButtonEditableData editable_data;
|
||||
ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data);
|
||||
|
||||
byte_input_set_result_callback(
|
||||
ibutton->byte_input,
|
||||
ibutton_scene_add_type_byte_input_callback,
|
||||
NULL,
|
||||
ibutton,
|
||||
new_key_data,
|
||||
ibutton_key_get_data_size(key));
|
||||
ibutton_scene_add_type_byte_changed_callback,
|
||||
context,
|
||||
editable_data.ptr,
|
||||
editable_data.size);
|
||||
|
||||
byte_input_set_header_text(ibutton->byte_input, "Enter the key");
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput);
|
||||
}
|
||||
|
||||
bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
uint8_t* new_key_data =
|
||||
(uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventByteEditResult) {
|
||||
ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE);
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
|
||||
} else if(event.event == iButtonCustomEventByteEditChanged) {
|
||||
ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
// User cancelled editing, reload the key from storage
|
||||
if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) {
|
||||
if(!ibutton_load_key(ibutton)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, iButtonSceneStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +55,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void ibutton_scene_add_value_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
uint8_t* new_key_data =
|
||||
(uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
|
||||
|
||||
byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(ibutton->byte_input, NULL);
|
||||
free(new_key_data);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ ADD_SCENE(ibutton, info, Info)
|
||||
ADD_SCENE(ibutton, read, Read)
|
||||
ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu)
|
||||
ADD_SCENE(ibutton, read_success, ReadSuccess)
|
||||
ADD_SCENE(ibutton, read_crc_error, ReadCRCError)
|
||||
ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError)
|
||||
ADD_SCENE(ibutton, read_error, ReadError)
|
||||
ADD_SCENE(ibutton, select_key, SelectKey)
|
||||
ADD_SCENE(ibutton, add_type, AddType)
|
||||
ADD_SCENE(ibutton, add_value, AddValue)
|
||||
@@ -18,4 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
|
||||
ADD_SCENE(ibutton, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
|
||||
ADD_SCENE(ibutton, exit_confirm, ExitConfirm)
|
||||
ADD_SCENE(ibutton, view_data, ViewData)
|
||||
ADD_SCENE(ibutton, rpc, Rpc)
|
||||
|
||||
@@ -1,74 +1,29 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <toolbox/path.h>
|
||||
|
||||
static void ibutton_scene_delete_confirm_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
iButton* ibutton = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void ibutton_scene_delete_confirm_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
iButtonKey* key = ibutton->key;
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", furi_string_get_cstr(key_name));
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, true);
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton);
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeRight,
|
||||
"Delete",
|
||||
ibutton_scene_delete_confirm_widget_callback,
|
||||
ibutton);
|
||||
widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
|
||||
|
||||
switch(ibutton_key_get_type(key)) {
|
||||
case iButtonKeyDS1990:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"%02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7]);
|
||||
widget_add_string_element(
|
||||
widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Dallas");
|
||||
break;
|
||||
|
||||
case iButtonKeyCyfral:
|
||||
ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
|
||||
widget_add_string_element(
|
||||
widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
|
||||
break;
|
||||
|
||||
case iButtonKeyMetakom:
|
||||
ibutton_text_store_set(
|
||||
ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
|
||||
widget_add_string_element(
|
||||
widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Metakom");
|
||||
break;
|
||||
}
|
||||
furi_string_printf(tmp, "Delete %s?", ibutton->key_name);
|
||||
widget_add_string_element(
|
||||
widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
|
||||
widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
|
||||
furi_string_reset(tmp);
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
furi_string_free(key_name);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -81,8 +36,10 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
if(ibutton_delete_key(ibutton)) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess);
|
||||
} else {
|
||||
dialog_message_show_storage_error(ibutton->dialogs, "Cannot delete\nkey file");
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
//TODO: What if the key could not be deleted?
|
||||
} else if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
@@ -93,6 +50,5 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
void ibutton_scene_delete_confirm_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
ibutton_text_store_clear(ibutton);
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
||||
|
||||
@@ -14,61 +14,32 @@ static void ibutton_scene_emulate_callback(void* context, bool emulated) {
|
||||
|
||||
void ibutton_scene_emulate_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
iButtonKey* key = ibutton->key;
|
||||
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
Widget* widget = ibutton->widget;
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
|
||||
// check that stored key has name
|
||||
if(!furi_string_empty(key_name)) {
|
||||
ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
|
||||
} else {
|
||||
// if not, show key data
|
||||
switch(ibutton_key_get_type(key)) {
|
||||
case iButtonKeyDS1990:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"%02X %02X %02X %02X\n%02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7]);
|
||||
break;
|
||||
case iButtonKeyCyfral:
|
||||
ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
|
||||
break;
|
||||
case iButtonKeyMetakom:
|
||||
ibutton_text_store_set(
|
||||
ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s\n[%s]",
|
||||
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
widget_add_text_box_element(
|
||||
widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
|
||||
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
ibutton_worker_emulate_set_callback(
|
||||
ibutton->key_worker, ibutton_scene_emulate_callback, ibutton);
|
||||
ibutton_worker_emulate_start(ibutton->key_worker, key);
|
||||
|
||||
furi_string_free(key_name);
|
||||
ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
|
||||
ibutton_worker_emulate_start(ibutton->worker, key);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -78,8 +49,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate);
|
||||
if(cnt > 0) {
|
||||
cnt--;
|
||||
if(cnt == 0) {
|
||||
if(--cnt == 0) {
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);
|
||||
}
|
||||
scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt);
|
||||
@@ -101,7 +71,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void ibutton_scene_emulate_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
ibutton_worker_stop(ibutton->key_worker);
|
||||
ibutton_worker_stop(ibutton->worker);
|
||||
widget_reset(ibutton->widget);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ void ibutton_scene_exit_confirm_on_enter(void* context) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Stay", ibutton_scene_exit_confirm_widget_callback, ibutton);
|
||||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu?");
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton Menu?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
|
||||
|
||||
|
||||
@@ -1,66 +1,54 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <toolbox/path.h>
|
||||
|
||||
void ibutton_scene_info_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
iButtonKey* key = ibutton->key;
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
|
||||
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"\e#%s [%s]\e#",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
|
||||
ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 23, AlignCenter, AlignCenter, ibutton->text_store, true);
|
||||
widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
|
||||
|
||||
switch(ibutton_key_get_type(key)) {
|
||||
case iButtonKeyDS1990:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"%02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7]);
|
||||
widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Dallas");
|
||||
break;
|
||||
furi_string_reset(tmp);
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
|
||||
|
||||
case iButtonKeyMetakom:
|
||||
ibutton_text_store_set(
|
||||
ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
|
||||
widget_add_string_element(
|
||||
widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Metakom");
|
||||
break;
|
||||
widget_add_string_multiline_element(
|
||||
widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
|
||||
case iButtonKeyCyfral:
|
||||
ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
|
||||
widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Cyfral");
|
||||
break;
|
||||
if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
|
||||
iButtonProtocolFeatureExtData) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
|
||||
}
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 64, 50, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
furi_string_free(key_name);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
iButton* ibutton = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_info_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
ibutton_text_store_clear(ibutton);
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
||||
|
||||
@@ -10,14 +10,13 @@ void ibutton_scene_read_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Popup* popup = ibutton->popup;
|
||||
iButtonKey* key = ibutton->key;
|
||||
iButtonWorker* worker = ibutton->key_worker;
|
||||
iButtonWorker* worker = ibutton->worker;
|
||||
|
||||
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
|
||||
|
||||
ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton);
|
||||
ibutton_worker_read_start(worker, key);
|
||||
@@ -35,25 +34,14 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventWorkerRead) {
|
||||
bool success = false;
|
||||
iButtonKey* key = ibutton->key;
|
||||
|
||||
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
|
||||
if(!ibutton_key_dallas_crc_is_valid(key)) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError);
|
||||
} else if(!ibutton_key_dallas_is_1990_key(key)) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError);
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
|
||||
if(success) {
|
||||
if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) {
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
|
||||
|
||||
} else {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +52,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
|
||||
void ibutton_scene_read_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Popup* popup = ibutton->popup;
|
||||
ibutton_worker_stop(ibutton->key_worker);
|
||||
ibutton_worker_stop(ibutton->worker);
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <one_wire/maxim_crc.h>
|
||||
|
||||
static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void ibutton_scene_read_crc_error_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
iButtonKey* key = ibutton->key;
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7],
|
||||
maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "More");
|
||||
dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback);
|
||||
dialog_ex_set_context(dialog_ex, ibutton);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageError);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == DialogExResultRight) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_read_crc_error_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
|
||||
ibutton_text_store_clear(ibutton);
|
||||
|
||||
dialog_ex_reset(dialog_ex);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
|
||||
}
|
||||
58
applications/main/ibutton/scenes/ibutton_scene_read_error.c
Normal file
58
applications/main/ibutton/scenes/ibutton_scene_read_error.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <one_wire/maxim_crc.h>
|
||||
|
||||
void ibutton_scene_read_error_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
iButtonKey* key = ibutton->key;
|
||||
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error");
|
||||
|
||||
ibutton_protocols_render_error(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageError);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_read_error_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
typedef enum {
|
||||
SubmenuIndexSave,
|
||||
SubmenuIndexEmulate,
|
||||
SubmenuIndexWrite,
|
||||
SubmenuIndexViewData,
|
||||
SubmenuIndexWriteBlank,
|
||||
SubmenuIndexWriteCopy,
|
||||
} SubmenuIndex;
|
||||
|
||||
void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {
|
||||
@@ -16,6 +18,9 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Submenu* submenu = ibutton->submenu;
|
||||
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key);
|
||||
const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id);
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
@@ -24,36 +29,66 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
SubmenuIndexEmulate,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
|
||||
|
||||
if(features & iButtonProtocolFeatureExtData) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write",
|
||||
SubmenuIndexWrite,
|
||||
"View Data",
|
||||
SubmenuIndexViewData,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Blank",
|
||||
SubmenuIndexWriteBlank,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Copy",
|
||||
SubmenuIndexWriteCopy,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(
|
||||
ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event);
|
||||
scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event);
|
||||
consumed = true;
|
||||
|
||||
if(event.event == SubmenuIndexSave) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
|
||||
} else if(event.event == SubmenuIndexEmulate) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
|
||||
} else if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
|
||||
} else if(event.event == SubmenuIndexViewData) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneViewData);
|
||||
} else if(event.event == SubmenuIndexWriteBlank) {
|
||||
ibutton->write_mode = iButtonWriteModeBlank;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneWrite);
|
||||
} else if(event.event == SubmenuIndexWriteCopy) {
|
||||
ibutton->write_mode = iButtonWriteModeCopy;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneWrite);
|
||||
}
|
||||
} else if(event.event == SceneManagerEventTypeBack) {
|
||||
scene_manager_set_scene_state(
|
||||
ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave);
|
||||
// Event is not consumed
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <one_wire/maxim_crc.h>
|
||||
|
||||
static void
|
||||
ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void ibutton_scene_read_not_key_error_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
iButtonKey* key = ibutton->key;
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7],
|
||||
maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "More");
|
||||
dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback);
|
||||
dialog_ex_set_context(dialog_ex, ibutton);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageError);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == DialogExResultRight) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_read_not_key_error_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
|
||||
ibutton_text_store_clear(ibutton);
|
||||
|
||||
dialog_ex_reset(dialog_ex);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
|
||||
}
|
||||
@@ -1,55 +1,40 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void ibutton_scene_read_success_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
iButtonKey* key = ibutton->key;
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
switch(ibutton_key_get_type(key)) {
|
||||
case iButtonKeyDS1990:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7]);
|
||||
break;
|
||||
case iButtonKeyCyfral:
|
||||
ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]);
|
||||
break;
|
||||
case iButtonKeyMetakom:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"Metakom\n%02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3]);
|
||||
break;
|
||||
}
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "More");
|
||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
|
||||
dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback);
|
||||
dialog_ex_set_context(dialog_ex, ibutton);
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s[%s]",
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id));
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
|
||||
furi_string_reset(tmp);
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
|
||||
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -62,9 +47,9 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == DialogExResultRight) {
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
} else if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm);
|
||||
}
|
||||
}
|
||||
@@ -74,11 +59,8 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
void ibutton_scene_read_success_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
DialogEx* dialog_ex = ibutton->dialog_ex;
|
||||
|
||||
ibutton_text_store_clear(ibutton);
|
||||
|
||||
dialog_ex_reset(dialog_ex);
|
||||
widget_reset(ibutton->widget);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <rpc/rpc_app.h>
|
||||
|
||||
void ibutton_scene_rpc_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
@@ -17,8 +15,6 @@ void ibutton_scene_rpc_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
iButton* ibutton = context;
|
||||
Popup* popup = ibutton->popup;
|
||||
|
||||
@@ -26,40 +22,32 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx);
|
||||
bool result = false;
|
||||
if(arg && (furi_string_empty(ibutton->file_path))) {
|
||||
furi_string_set(ibutton->file_path, arg);
|
||||
if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) {
|
||||
ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
|
||||
if(!furi_string_empty(key_name)) {
|
||||
ibutton_text_store_set(
|
||||
ibutton, "emulating\n%s", furi_string_get_cstr(key_name));
|
||||
} else {
|
||||
ibutton_text_store_set(ibutton, "emulating");
|
||||
}
|
||||
popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
bool result = false;
|
||||
const char* file_path = rpc_system_app_get_data(ibutton->rpc);
|
||||
|
||||
if(file_path && (furi_string_empty(ibutton->file_path))) {
|
||||
furi_string_set(ibutton->file_path, file_path);
|
||||
|
||||
if(ibutton_load_key(ibutton)) {
|
||||
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
|
||||
|
||||
furi_string_free(key_name);
|
||||
result = true;
|
||||
} else {
|
||||
furi_string_reset(ibutton->file_path);
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
|
||||
rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
|
||||
|
||||
} else if(event.event == iButtonCustomEventRpcExit) {
|
||||
rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true);
|
||||
scene_manager_stop(ibutton->scene_manager);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
|
||||
} else if(event.event == iButtonCustomEventRpcSessionClose) {
|
||||
scene_manager_stop(ibutton->scene_manager);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include <lib/toolbox/random_name.h>
|
||||
|
||||
#include <toolbox/random_name.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static void ibutton_scene_save_name_text_input_callback(void* context) {
|
||||
@@ -12,17 +14,10 @@ void ibutton_scene_save_name_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
TextInput* text_input = ibutton->text_input;
|
||||
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
const bool is_new_file = furi_string_empty(ibutton->file_path);
|
||||
|
||||
const bool key_name_is_empty = furi_string_empty(key_name);
|
||||
if(key_name_is_empty) {
|
||||
set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE);
|
||||
} else {
|
||||
ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
|
||||
if(is_new_file) {
|
||||
set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE);
|
||||
}
|
||||
|
||||
text_input_set_header_text(text_input, "Name the key");
|
||||
@@ -30,23 +25,15 @@ void ibutton_scene_save_name_on_enter(void* context) {
|
||||
text_input,
|
||||
ibutton_scene_save_name_text_input_callback,
|
||||
ibutton,
|
||||
ibutton->text_store,
|
||||
ibutton->key_name,
|
||||
IBUTTON_KEY_NAME_SIZE,
|
||||
key_name_is_empty);
|
||||
is_new_file);
|
||||
|
||||
FuriString* folder_path;
|
||||
folder_path = furi_string_alloc();
|
||||
|
||||
path_extract_dirname(furi_string_get_cstr(ibutton->file_path), folder_path);
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, furi_string_get_cstr(key_name));
|
||||
ValidatorIsFile* validator_is_file =
|
||||
validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name);
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);
|
||||
|
||||
furi_string_free(key_name);
|
||||
furi_string_free(folder_path);
|
||||
}
|
||||
|
||||
bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -56,8 +43,16 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventTextEditResult) {
|
||||
if(ibutton_save_key(ibutton, ibutton->text_store)) {
|
||||
furi_string_printf(
|
||||
ibutton->file_path,
|
||||
"%s/%s%s",
|
||||
IBUTTON_APP_FOLDER,
|
||||
ibutton->key_name,
|
||||
IBUTTON_APP_EXTENSION);
|
||||
|
||||
if(ibutton_save_key(ibutton)) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);
|
||||
|
||||
if(scene_manager_has_previous_scene(
|
||||
ibutton->scene_manager, iButtonSceneSavedKeyMenu)) {
|
||||
// Nothing, do not count editing as saving
|
||||
@@ -67,6 +62,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
} else {
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonSave);
|
||||
}
|
||||
|
||||
} else {
|
||||
const uint32_t possible_scenes[] = {
|
||||
iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
|
||||
|
||||
@@ -3,72 +3,70 @@
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexEmulate,
|
||||
SubmenuIndexWrite,
|
||||
SubmenuIndexWriteBlank,
|
||||
SubmenuIndexWriteCopy,
|
||||
SubmenuIndexEdit,
|
||||
SubmenuIndexDelete,
|
||||
SubmenuIndexInfo,
|
||||
};
|
||||
|
||||
void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void ibutton_scene_saved_key_menu_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Submenu* submenu = ibutton->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Emulate",
|
||||
SubmenuIndexEmulate,
|
||||
ibutton_scene_saved_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
|
||||
const uint32_t features = ibutton_protocols_get_features(
|
||||
ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key));
|
||||
|
||||
submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton);
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write",
|
||||
SubmenuIndexWrite,
|
||||
ibutton_scene_saved_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Delete",
|
||||
SubmenuIndexDelete,
|
||||
ibutton_scene_saved_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
submenu_add_item(
|
||||
submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton);
|
||||
}
|
||||
|
||||
submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);
|
||||
submenu_add_item(submenu, "Delete", SubmenuIndexDelete, ibutton_submenu_callback, ibutton);
|
||||
submenu_add_item(submenu, "Info", SubmenuIndexInfo, ibutton_submenu_callback, ibutton);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu));
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
|
||||
}
|
||||
|
||||
bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(
|
||||
ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event);
|
||||
scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event);
|
||||
consumed = true;
|
||||
if(event.event == SubmenuIndexEmulate) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
|
||||
} else if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
|
||||
} else if(event.event == SubmenuIndexWriteBlank) {
|
||||
ibutton->write_mode = iButtonWriteModeBlank;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneWrite);
|
||||
} else if(event.event == SubmenuIndexWriteCopy) {
|
||||
ibutton->write_mode = iButtonWriteModeCopy;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneWrite);
|
||||
} else if(event.event == SubmenuIndexEdit) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneAddValue);
|
||||
} else if(event.event == SubmenuIndexDelete) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm);
|
||||
} else if(event.event == SubmenuIndexInfo) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneInfo);
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_set_scene_state(
|
||||
scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate);
|
||||
// Event is not consumed
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
void ibutton_scene_select_key_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
|
||||
if(!ibutton_file_select(ibutton)) {
|
||||
if(ibutton_select_and_load_key(ibutton)) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
ibutton->scene_manager, iButtonSceneStart);
|
||||
} else {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,21 +8,15 @@ enum SubmenuIndex {
|
||||
SubmenuIndexAdd,
|
||||
};
|
||||
|
||||
void ibutton_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void ibutton_scene_start_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Submenu* submenu = ibutton->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton);
|
||||
submenu_add_item(
|
||||
submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton);
|
||||
ibutton_reset_key(ibutton);
|
||||
|
||||
submenu_add_item(submenu, "Read", SubmenuIndexRead, ibutton_submenu_callback, ibutton);
|
||||
submenu_add_item(submenu, "Saved", SubmenuIndexSaved, ibutton_submenu_callback, ibutton);
|
||||
submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, ibutton_submenu_callback, ibutton);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart));
|
||||
@@ -41,7 +35,6 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);
|
||||
DOLPHIN_DEED(DolphinDeedIbuttonRead);
|
||||
} else if(event.event == SubmenuIndexSaved) {
|
||||
furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);
|
||||
} else if(event.event == SubmenuIndexAdd) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType);
|
||||
|
||||
26
applications/main/ibutton/scenes/ibutton_scene_view_data.c
Normal file
26
applications/main/ibutton/scenes/ibutton_scene_view_data.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
void ibutton_scene_view_data_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
iButtonKey* key = ibutton->key;
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
ibutton_protocols_render_data(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp));
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ibutton_scene_view_data_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "../ibutton_i.h"
|
||||
#include "toolbox/path.h"
|
||||
|
||||
typedef enum {
|
||||
iButtonSceneWriteStateDefault,
|
||||
@@ -13,61 +12,46 @@ static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult
|
||||
|
||||
void ibutton_scene_write_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
furi_assert(ibutton->write_mode != iButtonWriteModeInvalid);
|
||||
|
||||
iButtonKey* key = ibutton->key;
|
||||
iButtonWorker* worker = ibutton->worker;
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
|
||||
|
||||
Widget* widget = ibutton->widget;
|
||||
iButtonWorker* worker = ibutton->key_worker;
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
const uint8_t* key_data = ibutton_key_get_data_p(key);
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
|
||||
FuriString* key_name;
|
||||
key_name = furi_string_alloc();
|
||||
if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s\n[%s]",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
|
||||
// check that stored key has name
|
||||
if(!furi_string_empty(key_name)) {
|
||||
ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
|
||||
} else {
|
||||
// if not, show key data
|
||||
switch(ibutton_key_get_type(key)) {
|
||||
case iButtonKeyDS1990:
|
||||
ibutton_text_store_set(
|
||||
ibutton,
|
||||
"%02X %02X %02X %02X\n%02X %02X %02X %02X",
|
||||
key_data[0],
|
||||
key_data[1],
|
||||
key_data[2],
|
||||
key_data[3],
|
||||
key_data[4],
|
||||
key_data[5],
|
||||
key_data[6],
|
||||
key_data[7]);
|
||||
break;
|
||||
case iButtonKeyCyfral:
|
||||
ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
|
||||
break;
|
||||
case iButtonKeyMetakom:
|
||||
ibutton_text_store_set(
|
||||
ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
|
||||
break;
|
||||
}
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
|
||||
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
|
||||
|
||||
furi_string_set(tmp, "iButton\nwriting ");
|
||||
|
||||
if(ibutton->write_mode == iButtonWriteModeBlank) {
|
||||
furi_string_cat(tmp, "Blank");
|
||||
ibutton_worker_write_blank_start(worker, key);
|
||||
|
||||
} else if(ibutton->write_mode == iButtonWriteModeCopy) {
|
||||
furi_string_cat(tmp, "Copy");
|
||||
ibutton_worker_write_copy_start(worker, key);
|
||||
}
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nwriting");
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
widget_add_text_box_element(
|
||||
widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
|
||||
ibutton_worker_write_start(worker, key);
|
||||
|
||||
furi_string_free(key_name);
|
||||
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -94,7 +78,9 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void ibutton_scene_write_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
ibutton_worker_stop(ibutton->key_worker);
|
||||
ibutton->write_mode = iButtonWriteModeInvalid;
|
||||
|
||||
ibutton_worker_stop(ibutton->worker);
|
||||
widget_reset(ibutton->widget);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <string.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define INFRARED_TX_MIN_INTERVAL_MS 50U
|
||||
|
||||
static const NotificationSequence* infrared_notification_sequences[] = {
|
||||
&sequence_success,
|
||||
&sequence_set_only_green_255,
|
||||
@@ -148,6 +150,12 @@ static Infrared* infrared_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
|
||||
|
||||
infrared->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
infrared->view_dispatcher,
|
||||
InfraredViewVariableItemList,
|
||||
variable_item_list_get_view(infrared->variable_item_list));
|
||||
|
||||
infrared->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
|
||||
@@ -195,6 +203,9 @@ static void infrared_free(Infrared* infrared) {
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
|
||||
text_input_free(infrared->text_input);
|
||||
|
||||
view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
|
||||
variable_item_list_free(infrared->variable_item_list);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
|
||||
dialog_ex_free(infrared->dialog_ex);
|
||||
|
||||
@@ -299,10 +310,13 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
|
||||
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
if(infrared->app_state.is_transmitting) {
|
||||
FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active");
|
||||
return;
|
||||
} else {
|
||||
infrared->app_state.is_transmitting = true;
|
||||
}
|
||||
|
||||
const uint32_t time_elapsed = furi_get_tick() - infrared->app_state.last_transmit_time;
|
||||
|
||||
if(time_elapsed < INFRARED_TX_MIN_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
@@ -319,6 +333,8 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
infrared_worker_tx_set_get_signal_callback(
|
||||
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
|
||||
infrared_worker_tx_start(infrared->worker);
|
||||
|
||||
infrared->app_state.is_transmitting = true;
|
||||
}
|
||||
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
|
||||
@@ -328,26 +344,24 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(button);
|
||||
|
||||
infrared_tx_start_signal(infrared, signal);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
void infrared_tx_start_received(Infrared* infrared) {
|
||||
infrared_tx_start_signal(infrared, infrared->received_signal);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
void infrared_tx_stop(Infrared* infrared) {
|
||||
if(!infrared->app_state.is_transmitting) {
|
||||
FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped");
|
||||
return;
|
||||
} else {
|
||||
infrared->app_state.is_transmitting = false;
|
||||
}
|
||||
|
||||
infrared_worker_tx_stop(infrared->worker);
|
||||
infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL);
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
|
||||
infrared->app_state.is_transmitting = false;
|
||||
infrared->app_state.last_transmit_time = furi_get_tick();
|
||||
}
|
||||
|
||||
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
@@ -69,6 +70,7 @@ typedef struct {
|
||||
InfraredEditTarget edit_target : 8;
|
||||
InfraredEditMode edit_mode : 8;
|
||||
int32_t current_button_index;
|
||||
uint32_t last_transmit_time;
|
||||
} InfraredAppState;
|
||||
|
||||
struct Infrared {
|
||||
@@ -86,6 +88,7 @@ struct Infrared {
|
||||
|
||||
Submenu* submenu;
|
||||
TextInput* text_input;
|
||||
VariableItemList* variable_item_list;
|
||||
DialogEx* dialog_ex;
|
||||
ButtonMenu* button_menu;
|
||||
Popup* popup;
|
||||
@@ -107,6 +110,7 @@ struct Infrared {
|
||||
typedef enum {
|
||||
InfraredViewSubmenu,
|
||||
InfraredViewTextInput,
|
||||
InfraredViewVariableItemList,
|
||||
InfraredViewDialogEx,
|
||||
InfraredViewButtonMenu,
|
||||
InfraredViewPopup,
|
||||
|
||||
@@ -10,13 +10,13 @@ void infrared_scene_ask_back_on_enter(void* context) {
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 11, AlignCenter, AlignTop);
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 11, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
|
||||
@@ -9,9 +9,9 @@ void infrared_scene_ask_retry_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
|
||||
@@ -21,4 +21,5 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio)
|
||||
ADD_SCENE(infrared, universal_projector, UniversalProjector)
|
||||
ADD_SCENE(infrared, debug, Debug)
|
||||
ADD_SCENE(infrared, error_databases, ErrorDatabases)
|
||||
ADD_SCENE(infrared, debug_settings, DebugSettings)
|
||||
ADD_SCENE(infrared, rpc, Rpc)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "../infrared_i.h"
|
||||
#include <furi_hal_infrared.h>
|
||||
|
||||
uint8_t value_index_ir;
|
||||
|
||||
#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const))
|
||||
const char* const infrared_debug_cfg_variables_text[] = {
|
||||
"Internal",
|
||||
"2 (A7)",
|
||||
};
|
||||
|
||||
static void infrared_scene_debug_settings_changed(VariableItem* item) {
|
||||
Infrared* infrared = variable_item_get_context(item);
|
||||
value_index_ir = variable_item_get_current_value_index(item);
|
||||
UNUSED(infrared);
|
||||
|
||||
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
|
||||
|
||||
furi_hal_infrared_set_debug_out(value_index_ir);
|
||||
}
|
||||
static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_debug_settings_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
|
||||
VariableItemList* variable_item_list = infrared->variable_item_list;
|
||||
|
||||
value_index_ir = furi_hal_infrared_get_debug_out_status();
|
||||
VariableItem* item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
"Send signal to",
|
||||
DEB_PINS_COUNT,
|
||||
infrared_scene_debug_settings_changed,
|
||||
infrared);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
|
||||
|
||||
variable_item_set_current_value_index(item, value_index_ir);
|
||||
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList);
|
||||
}
|
||||
|
||||
bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
UNUSED(event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void infrared_scene_debug_settings_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
variable_item_list_reset(infrared->variable_item_list);
|
||||
}
|
||||
@@ -5,7 +5,8 @@ enum SubmenuIndex {
|
||||
SubmenuIndexLearnNewRemote,
|
||||
SubmenuIndexLearnNewRemoteRaw,
|
||||
SubmenuIndexSavedRemotes,
|
||||
SubmenuIndexDebug
|
||||
SubmenuIndexDebug,
|
||||
SubmenuIndexDebugSettings
|
||||
};
|
||||
|
||||
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
@@ -45,7 +46,17 @@ void infrared_scene_start_on_enter(void* context) {
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
submenu_add_item(
|
||||
submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);
|
||||
submenu,
|
||||
"Debug RX",
|
||||
SubmenuIndexDebug,
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Debug Settings",
|
||||
SubmenuIndexDebugSettings,
|
||||
infrared_scene_start_submenu_callback,
|
||||
infrared);
|
||||
}
|
||||
|
||||
const uint32_t submenu_index =
|
||||
@@ -86,6 +97,9 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(submenu_index == SubmenuIndexDebug) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
|
||||
consumed = true;
|
||||
} else if(submenu_index == SubmenuIndexDebugSettings) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Anonumous instance */
|
||||
/** Anonymous instance */
|
||||
typedef struct InfraredProgressView InfraredProgressView;
|
||||
|
||||
/** Callback for back button handling */
|
||||
|
||||
@@ -14,7 +14,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
|
||||
T55xxTiming* t55xxtiming = malloc(sizeof(T55xxTiming));
|
||||
Popup* popup = app->popup;
|
||||
char curr_buf[32] = {};
|
||||
//TODO: use .txt file in resourses for passwords.
|
||||
//TODO: use .txt file in resources for passwords.
|
||||
const uint32_t default_passwords[] = {
|
||||
0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F,
|
||||
0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752,
|
||||
|
||||
@@ -7,7 +7,7 @@ void lfrfid_scene_retry_confirm_on_enter(void* context) {
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app);
|
||||
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app);
|
||||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @param aid - AID number array
|
||||
* @param aid_len - AID length
|
||||
* @param aid_name - string to keep AID name
|
||||
* @return - true if AID found, false otherwies
|
||||
* @return - true if AID found, false otherwise
|
||||
*/
|
||||
bool nfc_emv_parser_get_aid_name(
|
||||
Storage* storage,
|
||||
@@ -21,7 +21,7 @@ bool nfc_emv_parser_get_aid_name(
|
||||
* @param storage Storage instance
|
||||
* @param country_code - ISO 3166 country code
|
||||
* @param country_name - string to keep country name
|
||||
* @return - true if country found, false otherwies
|
||||
* @return - true if country found, false otherwise
|
||||
*/
|
||||
bool nfc_emv_parser_get_country_name(
|
||||
Storage* storage,
|
||||
@@ -32,7 +32,7 @@ bool nfc_emv_parser_get_country_name(
|
||||
* @param storage Storage instance
|
||||
* @param currency_code - ISO 3166 currency code
|
||||
* @param currency_name - string to keep currency name
|
||||
* @return - true if currency found, false otherwies
|
||||
* @return - true if currency found, false otherwise
|
||||
*/
|
||||
bool nfc_emv_parser_get_currency_name(
|
||||
Storage* storage,
|
||||
|
||||
@@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) {
|
||||
while(!cmd_exit) {
|
||||
cmd_exit |= cli_cmd_interrupt_received(cli);
|
||||
if(furi_hal_nfc_detect(&dev_data, 400)) {
|
||||
printf("found: %s ", nfc_get_dev_type(dev_data.type));
|
||||
printf("Found: %s ", nfc_get_dev_type(dev_data.type));
|
||||
printf("UID length: %d, UID:", dev_data.uid_len);
|
||||
for(size_t i = 0; i < dev_data.uid_len; i++) {
|
||||
printf("%02X", dev_data.uid[i]);
|
||||
|
||||
@@ -29,6 +29,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu)
|
||||
ADD_SCENE(nfc, mf_desfire_data, MfDesfireData)
|
||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||
ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess)
|
||||
ADD_SCENE(nfc, mf_classic_data, MfClassicData)
|
||||
ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
|
||||
ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
|
||||
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
|
||||
|
||||
106
applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
Normal file
106
applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_mf_classic_data_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
MfClassicType type = nfc->dev->dev_data.mf_classic_data.type;
|
||||
MfClassicData* data = &nfc->dev->dev_data.mf_classic_data;
|
||||
TextBox* text_box = nfc->text_box;
|
||||
|
||||
text_box_set_font(text_box, TextBoxFontHex);
|
||||
|
||||
int card_blocks = 0;
|
||||
if(type == MfClassicType1k) {
|
||||
card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4;
|
||||
} else if(type == MfClassicType4k) {
|
||||
// 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each
|
||||
card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16;
|
||||
} else if(type == MfClassicTypeMini) {
|
||||
card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4;
|
||||
}
|
||||
|
||||
int bytes_written = 0;
|
||||
for(int block_num = 0; block_num < card_blocks; block_num++) {
|
||||
bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
|
||||
if(is_sec_trailer) {
|
||||
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
|
||||
MfClassicSectorTrailer* sec_tr =
|
||||
mf_classic_get_sector_trailer_by_sector(data, sector_num);
|
||||
// Key A
|
||||
for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) {
|
||||
if((bytes_written % 8 == 0) && (bytes_written != 0)) {
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
}
|
||||
if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) {
|
||||
furi_string_cat_printf(
|
||||
nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]);
|
||||
} else {
|
||||
furi_string_cat_printf(nfc->text_box_store, "???? ");
|
||||
}
|
||||
bytes_written += 2;
|
||||
}
|
||||
// Access bytes
|
||||
for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) {
|
||||
if((bytes_written % 8 == 0) && (bytes_written != 0)) {
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
}
|
||||
if(mf_classic_is_block_read(data, block_num)) {
|
||||
furi_string_cat_printf(
|
||||
nfc->text_box_store,
|
||||
"%02X%02X ",
|
||||
sec_tr->access_bits[i],
|
||||
sec_tr->access_bits[i + 1]);
|
||||
} else {
|
||||
furi_string_cat_printf(nfc->text_box_store, "???? ");
|
||||
}
|
||||
bytes_written += 2;
|
||||
}
|
||||
// Key B
|
||||
for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) {
|
||||
if((bytes_written % 8 == 0) && (bytes_written != 0)) {
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
}
|
||||
if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) {
|
||||
furi_string_cat_printf(
|
||||
nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]);
|
||||
} else {
|
||||
furi_string_cat_printf(nfc->text_box_store, "???? ");
|
||||
}
|
||||
bytes_written += 2;
|
||||
}
|
||||
} else {
|
||||
// Write data block
|
||||
for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) {
|
||||
if((bytes_written % 8 == 0) && (bytes_written != 0)) {
|
||||
furi_string_push_back(nfc->text_box_store, '\n');
|
||||
}
|
||||
if(mf_classic_is_block_read(data, block_num)) {
|
||||
furi_string_cat_printf(
|
||||
nfc->text_box_store,
|
||||
"%02X%02X ",
|
||||
data->block[block_num].value[i],
|
||||
data->block[block_num].value[i + 1]);
|
||||
} else {
|
||||
furi_string_cat_printf(nfc->text_box_store, "???? ");
|
||||
}
|
||||
bytes_written += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_data_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clean view
|
||||
text_box_reset(nfc->text_box);
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) {
|
||||
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Detect reader",
|
||||
"Detect Reader",
|
||||
SubmenuIndexDetectReader,
|
||||
nfc_scene_mf_classic_menu_submenu_callback,
|
||||
nfc);
|
||||
|
||||
@@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
||||
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
||||
NfcProtocol protocol = dev_data->protocol;
|
||||
uint8_t text_scroll_height = 0;
|
||||
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) {
|
||||
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) ||
|
||||
(protocol == NfcDeviceProtocolMifareClassic)) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
|
||||
text_scroll_height = 52;
|
||||
@@ -136,6 +137,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
|
||||
consumed = true;
|
||||
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed =
|
||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ void nfc_scene_retry_confirm_on_enter(void* context) {
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Stay");
|
||||
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data will be\nlost!", 64, 25, AlignCenter, AlignTop);
|
||||
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
|
||||
dialog_ex_set_context(dialog_ex, nfc);
|
||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback);
|
||||
|
||||
|
||||
@@ -52,20 +52,20 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Detect reader",
|
||||
"Detect Reader",
|
||||
SubmenuIndexDetectReader,
|
||||
nfc_scene_saved_menu_submenu_callback,
|
||||
nfc);
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write To Initial Card",
|
||||
"Write to Initial Card",
|
||||
SubmenuIndexWrite,
|
||||
nfc_scene_saved_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Update From Initial Card",
|
||||
"Update from Initial Card",
|
||||
SubmenuIndexUpdate,
|
||||
nfc_scene_saved_menu_submenu_callback,
|
||||
nfc);
|
||||
@@ -76,13 +76,13 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
!mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock With Reader",
|
||||
"Unlock with Reader",
|
||||
SubmenuIndexMfUlUnlockByReader,
|
||||
nfc_scene_saved_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock With Password",
|
||||
"Unlock with Password",
|
||||
SubmenuIndexMfUlUnlockByPassword,
|
||||
nfc_scene_saved_menu_submenu_callback,
|
||||
nfc);
|
||||
@@ -151,6 +151,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
application_info_present = nfc_supported_card_verify_and_parse(dev_data);
|
||||
}
|
||||
|
||||
FURI_LOG_I("nfc", "application_info_present: %d", application_info_present);
|
||||
|
||||
if(application_info_present) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@ struct SubGhzChatWorker {
|
||||
SubGhzTxRxWorker* subghz_txrx;
|
||||
|
||||
volatile bool worker_running;
|
||||
volatile bool worker_stoping;
|
||||
volatile bool worker_stopping;
|
||||
FuriMessageQueue* event_queue;
|
||||
uint32_t last_time_rx_data;
|
||||
|
||||
|
||||
@@ -8,11 +8,14 @@ typedef enum {
|
||||
//SubmenuIndex
|
||||
SubmenuIndexFaacSLH_433,
|
||||
SubmenuIndexFaacSLH_868,
|
||||
SubmenuIndexBFT,
|
||||
SubmenuIndexBFTClone,
|
||||
SubmenuIndexBFTMitto,
|
||||
SubmenuIndexSomfyTelis,
|
||||
SubmenuIndexPricenton,
|
||||
SubmenuIndexNiceFlo12bit,
|
||||
SubmenuIndexNiceFlo24bit,
|
||||
SubmenuIndexNiceFlorS_433_92,
|
||||
SubmenuIndexNiceOne_433_92,
|
||||
SubmenuIndexNiceSmilo_433_92,
|
||||
SubmenuIndexCAME12bit,
|
||||
SubmenuIndexCAME24bit,
|
||||
@@ -64,6 +67,7 @@ typedef enum {
|
||||
SubGhzCustomEventViewReceiverBack,
|
||||
SubGhzCustomEventViewReceiverOffDisplay,
|
||||
SubGhzCustomEventViewReceiverUnlock,
|
||||
SubGhzCustomEventViewReceiverDeleteItem,
|
||||
|
||||
SubGhzCustomEventViewReadRAWBack,
|
||||
SubGhzCustomEventViewReadRAWIDLE,
|
||||
|
||||
@@ -117,15 +117,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
||||
|
||||
// First stage: coarse scan
|
||||
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
|
||||
uint32_t current_frequnecy = subghz_setting_get_frequency(instance->setting, i);
|
||||
if(furi_hal_subghz_is_frequency_valid(current_frequnecy) &&
|
||||
(current_frequnecy != 467750000) &&
|
||||
uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
|
||||
if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
|
||||
(current_frequency != 467750000) && (current_frequency != 464000000) &&
|
||||
!((furi_hal_subghz.radio_type == SubGhzRadioExternal) &&
|
||||
(current_frequnecy >= 311900000 && current_frequnecy <= 312200000))) {
|
||||
((current_frequency == 390000000) || (current_frequency == 312000000) ||
|
||||
(current_frequency == 312100000) || (current_frequency == 312200000) ||
|
||||
(current_frequency == 440175000)))) {
|
||||
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
|
||||
frequency =
|
||||
cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, current_frequnecy);
|
||||
cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, current_frequency);
|
||||
|
||||
cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
|
||||
do {
|
||||
@@ -330,4 +332,4 @@ void subghz_frequency_analyzer_worker_set_trigger_level(
|
||||
|
||||
float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) {
|
||||
return instance->trigger_level;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ typedef enum {
|
||||
/** SubGhzHopperState state */
|
||||
typedef enum {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunnig,
|
||||
SubGhzHopperStateRunning,
|
||||
SubGhzHopperStatePause,
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
@@ -54,6 +54,7 @@ typedef enum {
|
||||
SubGhzLoadKeyStateOK,
|
||||
SubGhzLoadKeyStateParseErr,
|
||||
SubGhzLoadKeyStateOnlyRx,
|
||||
SubGhzLoadKeyStateProtocolDescriptionErr,
|
||||
} SubGhzLoadKeyState;
|
||||
|
||||
/** SubGhzLock */
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
|
||||
uint8_t value_index;
|
||||
uint8_t value_index2;
|
||||
uint8_t value_index_exm;
|
||||
uint8_t value_index_dpin;
|
||||
uint8_t value_index_cnt;
|
||||
uint8_t value_index_pwr;
|
||||
|
||||
#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const))
|
||||
const char* const radio_modules_variables_text[] = {
|
||||
@@ -10,18 +12,34 @@ const char* const radio_modules_variables_text[] = {
|
||||
"External",
|
||||
};
|
||||
|
||||
#define EXT_MOD_POWER_COUNT 2
|
||||
const char* const ext_mod_power_text[EXT_MOD_POWER_COUNT] = {
|
||||
"ON",
|
||||
"OFF",
|
||||
};
|
||||
|
||||
#define DEBUG_P_COUNT 2
|
||||
const char* const debug_pin_text[DEBUG_P_COUNT] = {
|
||||
"OFF",
|
||||
"17(1W)",
|
||||
};
|
||||
|
||||
#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) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
value_index = variable_item_get_current_value_index(item);
|
||||
value_index_exm = variable_item_get_current_value_index(item);
|
||||
UNUSED(subghz);
|
||||
|
||||
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
|
||||
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
|
||||
}
|
||||
static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
|
||||
SubGhz* subghz = context;
|
||||
@@ -37,31 +55,114 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
|
||||
subghz->txrx->debug_pin_state = index == 1;
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, debug_counter_text[index]);
|
||||
|
||||
switch(index) {
|
||||
case 0:
|
||||
furi_hal_subghz_set_rolling_counter_mult(1);
|
||||
break;
|
||||
case 1:
|
||||
furi_hal_subghz_set_rolling_counter_mult(2);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, ext_mod_power_text[index]);
|
||||
|
||||
furi_hal_subghz_set_external_power_disable(index == 1);
|
||||
if(index == 1) {
|
||||
furi_hal_subghz_disable_ext_power();
|
||||
} else {
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_scene_ext_module_settings_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
VariableItemList* variable_item_list = subghz->variable_item_list;
|
||||
|
||||
value_index = furi_hal_subghz.radio_type;
|
||||
value_index_exm = furi_hal_subghz.radio_type;
|
||||
VariableItem* item = variable_item_list_add(
|
||||
variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz);
|
||||
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
|
||||
variable_item_set_current_value_index(item, value_index_exm);
|
||||
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ext Radio 5v",
|
||||
EXT_MOD_POWER_COUNT,
|
||||
subghz_scene_receiver_config_set_ext_mod_power,
|
||||
subghz);
|
||||
value_index_pwr = furi_hal_subghz_get_external_power_disable();
|
||||
variable_item_set_current_value_index(item, value_index_pwr);
|
||||
variable_item_set_current_value_text(item, ext_mod_power_text[value_index_pwr]);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Debug Pin:",
|
||||
"Debug Pin",
|
||||
DEBUG_P_COUNT,
|
||||
subghz_scene_receiver_config_set_debug_pin,
|
||||
subghz);
|
||||
value_index2 = subghz->txrx->debug_pin_state;
|
||||
variable_item_set_current_value_index(item, value_index2);
|
||||
variable_item_set_current_value_text(item, debug_pin_text[value_index2]);
|
||||
value_index_dpin = subghz->txrx->debug_pin_state;
|
||||
variable_item_set_current_value_index(item, value_index_dpin);
|
||||
variable_item_set_current_value_text(item, debug_pin_text[value_index_dpin]);
|
||||
|
||||
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_cnt = 0;
|
||||
break;
|
||||
case 2:
|
||||
value_index_cnt = 1;
|
||||
break;
|
||||
case 3:
|
||||
value_index_cnt = 2;
|
||||
break;
|
||||
case 4:
|
||||
value_index_cnt = 3;
|
||||
break;
|
||||
case 5:
|
||||
value_index_cnt = 4;
|
||||
break;
|
||||
case 10:
|
||||
value_index_cnt = 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
variable_item_set_current_value_index(item, value_index_cnt);
|
||||
variable_item_set_current_value_text(item, debug_counter_text[value_index_cnt]);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
|
||||
@@ -73,12 +174,14 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent
|
||||
UNUSED(event);
|
||||
|
||||
// Set selected radio module
|
||||
furi_hal_subghz_set_radio_type(value_index);
|
||||
furi_hal_subghz_set_radio_type(value_index_exm);
|
||||
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
|
||||
// Check if module is present, if no -> show error
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
value_index = 0;
|
||||
furi_hal_subghz_set_radio_type(value_index);
|
||||
value_index_exm = 0;
|
||||
furi_hal_subghz_set_radio_type(value_index_exm);
|
||||
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz menu?");
|
||||
subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?");
|
||||
widget_add_string_multiline_element(
|
||||
subghz->widget,
|
||||
64,
|
||||
@@ -24,7 +24,7 @@ void subghz_scene_need_saving_on_enter(void* context) {
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
FontSecondary,
|
||||
"All unsaved will be\nlost.");
|
||||
"All unsaved data\nwill be lost!");
|
||||
|
||||
widget_add_button_element(
|
||||
subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz);
|
||||
|
||||
@@ -368,7 +368,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
float rssi = furi_hal_subghz_get_rssi();
|
||||
|
||||
if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) {
|
||||
if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) {
|
||||
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
|
||||
subghz_protocol_raw_save_to_file_pause(
|
||||
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
|
||||
@@ -421,4 +421,4 @@ void subghz_scene_read_raw_on_exit(void* context) {
|
||||
|
||||
//filter restoration
|
||||
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
|
||||
consumed = true;
|
||||
break;
|
||||
case SubGhzCustomEventViewReceiverDeleteItem:
|
||||
subghz->txrx->idx_menu_chosen =
|
||||
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||
|
||||
subghz_history_delete_item(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
|
||||
subghz_view_receiver_delete_element_callback(subghz->subghz_receiver);
|
||||
|
||||
subghz_scene_receiver_update_statusbar(subghz);
|
||||
consumed = true;
|
||||
break;
|
||||
case SubGhzCustomEventViewReceiverConfig:
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz->txrx->idx_menu_chosen =
|
||||
|
||||
@@ -8,11 +8,11 @@ enum SubGhzSettingIndex {
|
||||
SubGhzSettingIndexBinRAW,
|
||||
SubGhzSettingIndexSound,
|
||||
SubGhzSettingIndexLock,
|
||||
SubGhzSettingIndexRAWThesholdRSSI,
|
||||
SubGhzSettingIndexRAWThresholdRSSI,
|
||||
};
|
||||
|
||||
#define RAW_THRESHOLD_RSSI_COUNT 11
|
||||
const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
const char* const raw_threshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
"-----",
|
||||
"-85.0",
|
||||
"-80.0",
|
||||
@@ -26,7 +26,7 @@ const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
"-40.0",
|
||||
|
||||
};
|
||||
const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
-90.0f,
|
||||
-85.0f,
|
||||
-80.0f,
|
||||
@@ -47,7 +47,7 @@ const char* const hopping_text[HOPPING_COUNT] = {
|
||||
};
|
||||
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunnig,
|
||||
SubGhzHopperStateRunning,
|
||||
};
|
||||
|
||||
#define SPEAKER_COUNT 2
|
||||
@@ -213,8 +213,8 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]);
|
||||
subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index];
|
||||
variable_item_set_current_value_text(item, raw_threshold_rssi_text[index]);
|
||||
subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||
@@ -320,9 +320,9 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
subghz_scene_receiver_config_set_raw_threshold_rssi,
|
||||
subghz);
|
||||
value_index = value_index_float(
|
||||
subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT);
|
||||
subghz->txrx->raw_threshold_rssi, raw_threshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);
|
||||
variable_item_set_current_value_text(item, raw_threshold_rssi_text[value_index]);
|
||||
}
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
#include <lib/subghz/protocols/keeloq.h>
|
||||
#include <lib/subghz/protocols/star_line.h>
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
|
||||
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -24,8 +27,9 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
|
||||
subghz->txrx->receiver,
|
||||
subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
||||
|
||||
if(subghz->txrx->decoder_result) {
|
||||
// In this case flipper format was changed to short file content
|
||||
//todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
|
||||
subghz_protocol_decoder_base_deserialize(
|
||||
subghz->txrx->decoder_result,
|
||||
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
||||
@@ -135,7 +139,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz,
|
||||
subghz_history_get_raw_data(
|
||||
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_tx_stop(subghz);
|
||||
}
|
||||
@@ -148,7 +151,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz_rx(subghz, subghz->txrx->preset->frequency);
|
||||
}
|
||||
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunning;
|
||||
}
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
} else {
|
||||
@@ -175,7 +178,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz_rx(subghz, subghz->txrx->preset->frequency);
|
||||
}
|
||||
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunning;
|
||||
}
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
}
|
||||
@@ -233,6 +236,10 @@ void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
widget_reset(subghz->widget);
|
||||
keeloq_reset_mfname();
|
||||
keeloq_reset_kl_type();
|
||||
keeloq_reset_original_btn();
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "../subghz_i.h"
|
||||
#include <lib/subghz/protocols/keeloq.h>
|
||||
#include <lib/subghz/protocols/star_line.h>
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
|
||||
typedef enum {
|
||||
SubGhzRpcStateIdle,
|
||||
@@ -110,6 +113,10 @@ void subghz_scene_rpc_on_exit(void* context) {
|
||||
|
||||
keeloq_reset_mfname();
|
||||
keeloq_reset_kl_type();
|
||||
keeloq_reset_original_btn();
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ void subghz_scene_set_seed_bft_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// Setup view
|
||||
// roguemaster don't steal!!!
|
||||
// RogueMaster don't steal!!!
|
||||
ByteInput* byte_input = subghz->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter SEED in hex");
|
||||
byte_input_set_result_callback(
|
||||
|
||||
@@ -65,7 +65,7 @@ bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event)
|
||||
seed,
|
||||
"FAAC_SLH",
|
||||
subghz->txrx->preset);
|
||||
// rogueemaster dont steal!
|
||||
// RogueMaster dont steal!
|
||||
uint8_t seed_data[sizeof(uint32_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF;
|
||||
|
||||
@@ -36,8 +36,9 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
|
||||
do {
|
||||
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
|
||||
stream_clean(fff_data_stream);
|
||||
if(!subghz_protocol_decoder_base_serialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset)) {
|
||||
if(subghz_protocol_decoder_base_serialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) !=
|
||||
SubGhzProtocolStatusOk) {
|
||||
FURI_LOG_E(TAG, "Unable to serialize");
|
||||
break;
|
||||
}
|
||||
@@ -79,10 +80,22 @@ void subghz_scene_set_type_on_enter(void* context) {
|
||||
SubmenuIndexFaacSLH_433,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"BFT [Manual] 433MHz",
|
||||
SubmenuIndexBFTClone,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"BFT Mitto 433MHz",
|
||||
SubmenuIndexBFT,
|
||||
SubmenuIndexBFTMitto,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Somfy Telis 433MHz",
|
||||
SubmenuIndexSomfyTelis,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
@@ -115,6 +128,12 @@ void subghz_scene_set_type_on_enter(void* context) {
|
||||
SubmenuIndexNiceFlorS_433_92,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Nice One 433MHz",
|
||||
SubmenuIndexNiceOne_433_92,
|
||||
subghz_scene_set_type_submenu_callback,
|
||||
subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"CAME 12bit 433MHz",
|
||||
@@ -230,7 +249,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubmenuIndexFaacSLH_433:
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac);
|
||||
break;
|
||||
case SubmenuIndexBFT:
|
||||
case SubmenuIndexBFTClone:
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft);
|
||||
break;
|
||||
case SubmenuIndexPricenton:
|
||||
@@ -306,11 +325,69 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexBFTMitto:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_keeloq_bft_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
subghz->txrx->fff_data,
|
||||
key & 0x000FFFFF,
|
||||
0x2,
|
||||
0x0002,
|
||||
key & 0x000FFFFF,
|
||||
"BFT",
|
||||
subghz->txrx->preset);
|
||||
|
||||
uint8_t seed_data[sizeof(uint32_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
seed_data[sizeof(uint32_t) - i - 1] = ((key & 0x000FFFFF) >> i * 8) & 0xFF;
|
||||
}
|
||||
|
||||
flipper_format_write_hex(
|
||||
subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t));
|
||||
|
||||
flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT");
|
||||
|
||||
generated_protocol = true;
|
||||
} else {
|
||||
generated_protocol = false;
|
||||
}
|
||||
subghz_transmitter_free(subghz->txrx->transmitter);
|
||||
if(!generated_protocol) {
|
||||
furi_string_set(
|
||||
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexSomfyTelis:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_somfy_telis_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
subghz->txrx->fff_data,
|
||||
key & 0x00FFFFFF,
|
||||
0x2,
|
||||
0x0003,
|
||||
subghz->txrx->preset);
|
||||
generated_protocol = true;
|
||||
} else {
|
||||
generated_protocol = false;
|
||||
}
|
||||
subghz_transmitter_free(subghz->txrx->transmitter);
|
||||
if(!generated_protocol) {
|
||||
furi_string_set(
|
||||
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexDoorHan_433_92:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||
subghz_preset_init(
|
||||
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_keeloq_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
@@ -358,8 +435,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubmenuIndexNiceFlorS_433_92:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
|
||||
subghz_preset_init(
|
||||
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_nice_flor_s_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
@@ -367,7 +443,32 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
key & 0x0FFFFFFF,
|
||||
0x1,
|
||||
0x0003,
|
||||
subghz->txrx->preset);
|
||||
subghz->txrx->preset,
|
||||
false);
|
||||
generated_protocol = true;
|
||||
} else {
|
||||
generated_protocol = false;
|
||||
}
|
||||
subghz_transmitter_free(subghz->txrx->transmitter);
|
||||
if(!generated_protocol) {
|
||||
furi_string_set(
|
||||
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexNiceOne_433_92:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_nice_flor_s_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
subghz->txrx->fff_data,
|
||||
key & 0x0FFFFFFF,
|
||||
0x1,
|
||||
0x0003,
|
||||
subghz->txrx->preset,
|
||||
true);
|
||||
generated_protocol = true;
|
||||
} else {
|
||||
generated_protocol = false;
|
||||
@@ -382,8 +483,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubmenuIndexNiceSmilo_433_92:
|
||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||
subghz_preset_init(
|
||||
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
|
||||
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
|
||||
if(subghz->txrx->transmitter) {
|
||||
subghz_protocol_keeloq_create_data(
|
||||
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
|
||||
|
||||
@@ -75,43 +75,46 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings);
|
||||
return true;
|
||||
|
||||
} else if(!furi_hal_subghz_check_radio()) {
|
||||
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexReadRAW) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexRead) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexSaved) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexAddManually) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexFrequencyAnalyzer) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
|
||||
DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexTest) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest);
|
||||
return true;
|
||||
} else {
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexReadRAW) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexRead) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexSaved) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexFrequencyAnalyzer) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
|
||||
DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexTest) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#include "../views/transmitter.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <lib/subghz/protocols/keeloq.h>
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/star_line.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
|
||||
void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -11,9 +14,8 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
|
||||
}
|
||||
|
||||
bool subghz_scene_transmitter_update_data_show(void* context) {
|
||||
//ToDo Fix
|
||||
SubGhz* subghz = context;
|
||||
|
||||
bool ret = false;
|
||||
if(subghz->txrx->decoder_result) {
|
||||
FuriString* key_str;
|
||||
FuriString* frequency_str;
|
||||
@@ -24,30 +26,29 @@ bool subghz_scene_transmitter_update_data_show(void* context) {
|
||||
modulation_str = furi_string_alloc();
|
||||
uint8_t show_button = 0;
|
||||
|
||||
subghz_protocol_decoder_base_deserialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data);
|
||||
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
|
||||
if(subghz_protocol_decoder_base_deserialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) {
|
||||
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
|
||||
|
||||
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
|
||||
SubGhzProtocolFlag_Send) {
|
||||
show_button = 1;
|
||||
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
|
||||
SubGhzProtocolFlag_Send) {
|
||||
show_button = 1;
|
||||
}
|
||||
|
||||
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
|
||||
subghz_view_transmitter_add_data_to_show(
|
||||
subghz->subghz_transmitter,
|
||||
furi_string_get_cstr(key_str),
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
show_button);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
|
||||
subghz_view_transmitter_add_data_to_show(
|
||||
subghz->subghz_transmitter,
|
||||
furi_string_get_cstr(key_str),
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
show_button);
|
||||
|
||||
furi_string_free(frequency_str);
|
||||
furi_string_free(modulation_str);
|
||||
furi_string_free(key_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_scene_transmitter_on_enter(void* context) {
|
||||
@@ -89,6 +90,27 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
}
|
||||
if(keeloq_get_custom_btn() != 0) {
|
||||
keeloq_set_btn(0);
|
||||
alutech_set_btn(0);
|
||||
nice_flors_set_btn(0);
|
||||
somfy_telis_set_btn(0);
|
||||
uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
// Calling restore!
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz);
|
||||
}
|
||||
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
|
||||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
||||
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||
}
|
||||
}
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubGhzCustomEventViewTransmitterBack) {
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
@@ -113,6 +135,10 @@ void subghz_scene_transmitter_on_exit(void* context) {
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
keeloq_reset_mfname();
|
||||
keeloq_reset_kl_type();
|
||||
keeloq_reset_original_btn();
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
||||
subghz->txrx->history = subghz_history_alloc();
|
||||
}
|
||||
|
||||
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
|
||||
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
subghz->txrx->worker = subghz_worker_alloc();
|
||||
|
||||
subghz->txrx->fff_data = flipper_format_string_alloc();
|
||||
@@ -421,6 +421,9 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) {
|
||||
|
||||
// The rest
|
||||
free(subghz);
|
||||
|
||||
// Disable power for External CC1101 if it was enabled and module is connected
|
||||
furi_hal_subghz_disable_ext_power();
|
||||
}
|
||||
|
||||
int32_t subghz_app(void* p) {
|
||||
|
||||
@@ -88,6 +88,28 @@ void subghz_history_reset(SubGhzHistory* instance) {
|
||||
instance->code_last_hash_data = 0;
|
||||
}
|
||||
|
||||
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) {
|
||||
furi_assert(instance);
|
||||
|
||||
SubGhzHistoryItemArray_it_t it;
|
||||
//SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id);
|
||||
SubGhzHistoryItemArray_it_last(it, instance->history->data);
|
||||
while(!SubGhzHistoryItemArray_end_p(it)) {
|
||||
SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it);
|
||||
|
||||
if(it->index == (size_t)(item_id)) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->preset->name);
|
||||
free(item->preset);
|
||||
flipper_format_free(item->flipper_string);
|
||||
item->type = 0;
|
||||
SubGhzHistoryItemArray_remove(instance->history->data, it);
|
||||
}
|
||||
SubGhzHistoryItemArray_previous(it);
|
||||
}
|
||||
instance->last_index_write--;
|
||||
}
|
||||
|
||||
uint16_t subghz_history_get_item(SubGhzHistory* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->last_index_write;
|
||||
|
||||
@@ -27,6 +27,8 @@ void subghz_history_free(SubGhzHistory* instance);
|
||||
*/
|
||||
void subghz_history_reset(SubGhzHistory* instance);
|
||||
|
||||
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id);
|
||||
|
||||
/** Get frequency to history[idx]
|
||||
*
|
||||
* @param instance - SubGhzHistory instance
|
||||
@@ -80,7 +82,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
|
||||
*
|
||||
* @param instance - SubGhzHistory instance
|
||||
* @param output - FuriString* output
|
||||
* @return bool - is FUUL
|
||||
* @return bool - is FULL
|
||||
*/
|
||||
bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output);
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ uint32_t subghz_history_rand_range(uint32_t min, uint32_t max);
|
||||
*
|
||||
* @param file - Stream*
|
||||
* @param is_negative_start - first value is negative or positive?
|
||||
* @param current_position - 0 if started from begining
|
||||
* @param current_position - 0 if started from beginning
|
||||
* @param empty_line - add RAW_Data to this line
|
||||
* @return true
|
||||
* @return false
|
||||
@@ -160,4 +160,4 @@ bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool str
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
|
||||
bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
|
||||
|
||||
@@ -154,7 +154,6 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
//ToDo FIX
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable Repeat");
|
||||
break;
|
||||
@@ -164,7 +163,8 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
|
||||
subghz->txrx->environment, furi_string_get_cstr(temp_str));
|
||||
|
||||
if(subghz->txrx->transmitter) {
|
||||
if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) {
|
||||
if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) ==
|
||||
SubGhzProtocolStatusOk) {
|
||||
if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) {
|
||||
subghz_begin(
|
||||
subghz,
|
||||
@@ -187,7 +187,12 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
|
||||
//Start TX
|
||||
furi_hal_subghz_start_async_tx(
|
||||
subghz_transmitter_yield, subghz->txrx->transmitter);
|
||||
} else {
|
||||
subghz_dialog_message_show_only_rx(subghz);
|
||||
}
|
||||
} else {
|
||||
dialog_message_show_storage_error(
|
||||
subghz->dialogs, "Error in protocol\nparameters\ndescription");
|
||||
}
|
||||
}
|
||||
if(!ret) {
|
||||
@@ -335,8 +340,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
|
||||
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
|
||||
subghz->txrx->receiver, furi_string_get_cstr(temp_str));
|
||||
if(subghz->txrx->decoder_result) {
|
||||
if(!subghz_protocol_decoder_base_deserialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data)) {
|
||||
SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
|
||||
subghz->txrx->decoder_result, subghz->txrx->fff_data);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -357,6 +364,12 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
|
||||
dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile");
|
||||
}
|
||||
return false;
|
||||
case SubGhzLoadKeyStateProtocolDescriptionErr:
|
||||
if(show_dialog) {
|
||||
dialog_message_show_storage_error(
|
||||
subghz->dialogs, "Error in protocol\nparameters\ndescription");
|
||||
}
|
||||
return false;
|
||||
|
||||
case SubGhzLoadKeyStateOnlyRx:
|
||||
if(show_dialog) {
|
||||
@@ -575,7 +588,7 @@ void subghz_hopper_update(SubGhz* subghz) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunning;
|
||||
}
|
||||
// Select next frequency
|
||||
if(subghz->txrx->hopper_idx_frequency <
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define MENU_ITEMS 4u
|
||||
#define UNLOCK_CNT 3
|
||||
|
||||
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
|
||||
typedef struct {
|
||||
FuriString* item_str;
|
||||
@@ -80,10 +80,10 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
|
||||
instance->view,
|
||||
SubGhzViewReceiverModel * model,
|
||||
{
|
||||
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
|
||||
if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
|
||||
model->u_rssi = 0;
|
||||
} else {
|
||||
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
|
||||
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
|
||||
}
|
||||
},
|
||||
true);
|
||||
@@ -427,6 +427,34 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
true);
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
|
||||
} else if(event->key == InputKeyRight && event->type == InputTypeLong) {
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
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);
|
||||
item->type = 0;
|
||||
SubGhzReceiverMenuItemArray_remove(model->history->data, it);
|
||||
}
|
||||
|
||||
SubGhzReceiverMenuItemArray_previous(it);
|
||||
}
|
||||
|
||||
// Callback
|
||||
subghz_receiver->callback(
|
||||
SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context);
|
||||
}
|
||||
},
|
||||
true);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
@@ -540,12 +568,34 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
|
||||
|
||||
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
uint32_t idx = 0;
|
||||
uint16_t idx = 0;
|
||||
with_view_model(
|
||||
subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
SubGhzViewReceiverModel * model,
|
||||
{
|
||||
if(model->history_item == 5) {
|
||||
if(model->idx >= 2) {
|
||||
model->idx = model->history_item - 1;
|
||||
}
|
||||
}
|
||||
model->history_item--;
|
||||
|
||||
if(model->idx != 0) {
|
||||
model->idx--;
|
||||
}
|
||||
},
|
||||
true);
|
||||
furi_delay_ms(200);
|
||||
}
|
||||
|
||||
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
|
||||
furi_assert(subghz_receiver);
|
||||
with_view_model(
|
||||
|
||||
@@ -46,4 +46,6 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
|
||||
|
||||
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
|
||||
|
||||
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver);
|
||||
|
||||
void subghz_view_receiver_exit(void* context);
|
||||
|
||||
@@ -23,7 +23,7 @@ typedef struct {
|
||||
FuriString* sample_write;
|
||||
FuriString* file_name;
|
||||
uint8_t* rssi_history;
|
||||
uint8_t rssi_curret;
|
||||
uint8_t rssi_current;
|
||||
bool rssi_history_end;
|
||||
uint8_t ind_write;
|
||||
uint8_t ind_sin;
|
||||
@@ -62,17 +62,17 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra
|
||||
furi_assert(instance);
|
||||
uint8_t u_rssi = 0;
|
||||
|
||||
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
|
||||
if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
|
||||
u_rssi = 0;
|
||||
} else {
|
||||
u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
|
||||
u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
SubGhzReadRAWModel * model,
|
||||
{
|
||||
model->rssi_curret = u_rssi;
|
||||
model->rssi_current = u_rssi;
|
||||
if(trace) {
|
||||
model->rssi_history[model->ind_write++] = u_rssi;
|
||||
} else {
|
||||
@@ -206,10 +206,10 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
|
||||
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);
|
||||
}
|
||||
canvas_draw_line(
|
||||
canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret);
|
||||
canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_current);
|
||||
if(model->ind_write > 3) {
|
||||
canvas_draw_line(
|
||||
canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret);
|
||||
canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_current);
|
||||
|
||||
for(uint8_t i = 13; i < 47; i += width * 2) {
|
||||
canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width);
|
||||
@@ -231,13 +231,13 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
|
||||
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
|
||||
47,
|
||||
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
|
||||
47 - model->rssi_curret);
|
||||
47 - model->rssi_current);
|
||||
canvas_draw_line(
|
||||
canvas,
|
||||
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
|
||||
47,
|
||||
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
|
||||
47 - model->rssi_curret);
|
||||
47 - model->rssi_current);
|
||||
|
||||
for(uint8_t i = 13; i < 47; i += width * 2) {
|
||||
canvas_draw_line(
|
||||
@@ -266,9 +266,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod
|
||||
uint8_t x = 118;
|
||||
uint8_t y = 48;
|
||||
|
||||
if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) {
|
||||
if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {
|
||||
uint8_t x = 118;
|
||||
y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
|
||||
y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
|
||||
|
||||
uint8_t width = 3;
|
||||
for(uint8_t i = 0; i < x; i += width * 2) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
|
||||
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
|
||||
typedef struct SubGhzReadRAW SubGhzReadRAW;
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <lib/subghz/protocols/keeloq.h>
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
|
||||
struct SubGhzViewTransmitter {
|
||||
View* view;
|
||||
SubGhzViewTransmitterCallback callback;
|
||||
@@ -15,6 +20,8 @@ typedef struct {
|
||||
FuriString* preset_str;
|
||||
FuriString* key_str;
|
||||
uint8_t show_button;
|
||||
FuriString* temp_button_id;
|
||||
bool draw_temp_button;
|
||||
} SubGhzViewTransmitterModel;
|
||||
|
||||
void subghz_view_transmitter_set_callback(
|
||||
@@ -89,6 +96,12 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
|
||||
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
|
||||
|
||||
if(model->draw_temp_button) {
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id));
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
}
|
||||
|
||||
if(model->show_button) {
|
||||
canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
|
||||
subghz_view_transmitter_button_right(canvas, "Send");
|
||||
@@ -108,7 +121,9 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
|
||||
furi_string_reset(model->frequency_str);
|
||||
furi_string_reset(model->preset_str);
|
||||
furi_string_reset(model->key_str);
|
||||
furi_string_reset(model->temp_button_id);
|
||||
model->show_button = 0;
|
||||
model->draw_temp_button = false;
|
||||
},
|
||||
false);
|
||||
return false;
|
||||
@@ -125,6 +140,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
|
||||
true);
|
||||
|
||||
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
|
||||
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;
|
||||
@@ -134,6 +157,141 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Temp Buttons (UP)
|
||||
if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
|
||||
keeloq_set_btn(1);
|
||||
alutech_set_btn(1);
|
||||
nice_flors_set_btn(1);
|
||||
somfy_telis_set_btn(1);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(keeloq_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(alutech_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(nice_flors_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", nice_flors_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(somfy_telis_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
|
||||
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) {
|
||||
keeloq_set_btn(2);
|
||||
alutech_set_btn(2);
|
||||
nice_flors_set_btn(2);
|
||||
somfy_telis_set_btn(2);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(keeloq_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(alutech_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(nice_flors_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", nice_flors_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(somfy_telis_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
|
||||
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) {
|
||||
keeloq_set_btn(3);
|
||||
alutech_set_btn(3);
|
||||
nice_flors_set_btn(3);
|
||||
somfy_telis_set_btn(3);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(keeloq_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(alutech_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(nice_flors_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", nice_flors_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(somfy_telis_get_original_btn() != 0) {
|
||||
furi_string_printf(
|
||||
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
|
||||
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) {
|
||||
keeloq_set_btn(4);
|
||||
alutech_set_btn(4);
|
||||
with_view_model(
|
||||
subghz_transmitter->view,
|
||||
SubGhzViewTransmitterModel * model,
|
||||
{
|
||||
furi_string_reset(model->temp_button_id);
|
||||
if(keeloq_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
|
||||
model->draw_temp_button = true;
|
||||
} else if(alutech_get_original_btn() != 0) {
|
||||
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -166,6 +324,7 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() {
|
||||
model->frequency_str = furi_string_alloc();
|
||||
model->preset_str = furi_string_alloc();
|
||||
model->key_str = furi_string_alloc();
|
||||
model->temp_button_id = furi_string_alloc();
|
||||
},
|
||||
true);
|
||||
return subghz_transmitter;
|
||||
@@ -181,6 +340,7 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {
|
||||
furi_string_free(model->frequency_str);
|
||||
furi_string_free(model->preset_str);
|
||||
furi_string_free(model->key_str);
|
||||
furi_string_free(model->temp_button_id);
|
||||
},
|
||||
true);
|
||||
view_free(subghz_transmitter->view);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user