Merge branch 'dev' into release

This commit is contained in:
MX 2024-11-11 21:37:49 +03:00
commit 5f866a5756
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
411 changed files with 23942 additions and 6424 deletions

View file

@ -114,7 +114,7 @@ steps:
from_secret: fbt_link
- name: "Bundle self-update packages"
image: kramos/alpine-zip
image: joshkeegan/zip
commands:
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz .
- cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.tgz .
@ -245,9 +245,11 @@ steps:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/2253303efc0e7211ac2777d2535054cbb872f1e0/discord.sh"
- chmod +x ./discord.sh
- sed -n '/## Main changes/,/## Other changes/p' CHANGELOG.md | sed -e 's/## Main changes//' -e 's/## Other changes//' > changelogcut.txt
- truncate -s -1 changelogcut.txt
- tail -c +2 changelogcut.txt > changelogready.txt
- head -c 1544 changelogcut.txt > changelogcutfin.txt
- truncate -s -1 changelogcutfin.txt
- tail -c +2 changelogcutfin.txt > changelogready.txt
- rm -f changelogcut.txt
- rm -f changelogcutfin.txt
- echo '' >> changelogready.txt
- echo '## [Read full changelog](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')' >> changelogready.txt
- sed -i 's/(releasever)/'${DRONE_TAG}'/g' .ci_files/release_msg_discord.txt
@ -417,7 +419,7 @@ steps:
from_secret: fbt_link
- name: "Bundle self-update packages"
image: kramos/alpine-zip
image: joshkeegan/zip
commands:
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz .
- cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz .
@ -529,14 +531,20 @@ steps:
commands:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/2253303efc0e7211ac2777d2535054cbb872f1e0/discord.sh"
- chmod +x ./discord.sh
- sed -n '/## Main changes/,/<br><br>/p' CHANGELOG.md | sed -e 's/<br><br>//' > changelogcut.txt
- truncate -s -1 changelogcut.txt
- sed -n '/## Main changes/,/## Other changes/p' CHANGELOG.md | sed -e 's/## Main changes//' -e 's/## Other changes//' > changelogcut.txt
- head -c 1544 changelogcut.txt > changelogcutfin.txt
- truncate -s -1 changelogcutfin.txt
- tail -c +2 changelogcutfin.txt > changelogready.txt
- rm -f changelogcut.txt
- rm -f changelogcutfin.txt
- echo '' >> changelogready.txt
- echo '## [Read full changelog](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md)' >> changelogready.txt
- sed -i 's/(buildnum)/'${DRONE_BUILD_NUMBER}'/g' .ci_files/devbuild_msg_discord.txt
- sed -i 's/(commitsha)/'${DRONE_COMMIT_SHA}'/g' .ci_files/devbuild_msg_discord.txt
- sed -i 's/(buildnum)/'${DRONE_BUILD_NUMBER}'/g' .ci_files/devbuild_msg_telegram.txt
- sed -i 's/(commitsha)/'${DRONE_COMMIT_SHA}'/g' .ci_files/devbuild_msg_telegram.txt
- cp .ci_files/devbuild_msg_telegram.txt tg_dev_message.tpl
- ./discord.sh --title "Changelog" --description "$(jq -Rs . <changelogcut.txt | cut -c 2- | rev | cut -c 2- | rev)" --timestamp --text "$(jq -Rs . <.ci_files/devbuild_msg_discord.txt | cut -c 2- | rev | cut -c 2- | rev)"
- ./discord.sh --title "Changelog" --description "$(jq -Rs . <changelogready.txt | cut -c 2- | rev | cut -c 2- | rev)" --timestamp --text "$(jq -Rs . <.ci_files/devbuild_msg_discord.txt | cut -c 2- | rev | cut -c 2- | rev)"
- name: "Send message to telegram"
image: appleboy/drone-telegram

5
.gitignore vendored
View file

@ -69,4 +69,7 @@ PVS-Studio.log
.gdbinit
/fbt_options_local.py
/fbt_options_local.py
# JS packages
node_modules/

View file

@ -1,46 +1,119 @@
## Main changes
- Current API: 78.1
- SubGHz:
- Add new protocols (by @xMasterX) (big thanks to @Skorpionm for help with GangQi and Hollarm protocols!):
- Marantec24 (static 24 bit) with add manually support
- GangQi (static 34 bit) with button parsing and add manually support (thanks to @mishamyte for captures and testing)
- Hollarm (static 42 bit) with button parsing and add manually support (thanks to @mishamyte for captures)
- Hay21 (dynamic 21 bit) with button parsing
- Princeton custom buttons support (0x1, 0x2, 0x4, 0x8, 0xF)
- 125kHz RFID:
- OFW: Fix detection of GProx II cards and false detection of other cards (by @Astrrra)
- OFW: Fix Guard GProxII False Positive and 36-bit Parsing (by @zinongli)
- OFW: GProxII Fix Writing and Rendering Conflict
- Frequency analyzer fixes and improvements:
- **Enforce int module** (like in OFW) usage due to lack of required hardware on external boards (PathIsolate (+rf switch for multiple paths)) and incorrect usage and/or understanding the purpose of frequency analyzer app by users, it should be used only to get frequency of the remote placed around 1-10cm around flipper's left corner
- **Fix possible GSM mobile towers signal interference** by limiting upper frequency to 920mhz max
- Fix buttons logic, **fix crash**
- Protocol improvements:
- **Keeloq: Monarch full support, with add manually option** (thanks @ashphx !)
- **Princeton support for second button encoding type** (8bit)
- GangQi fix serial check and remove broken check from UI
- Hollarm add more button codes (thanks to @mishamyte for captures)
- Misc:
- Add extra settings to disable GPIO pins control used for external modules amplifiers and/or LEDs (in radio settings menu with debug ON)
- NFC:
- Saflok parser improvements (by @zinongli & @xtruan & @zacharyweiss & @evilmog & @Arkwin)
- OFW: Fix crash on Ultralight unlock (by @Astrrra)
- OFW: FeliCa anti-collision fix
* OFW: Rename 'Detect Reader' to 'Extract MF Keys'
* OFW: Happy mode
* OFW: Infrared: Universal AC - Add Airwell AW-HKD012-N91
* OFW: Broken file interaction fixes
* OFW: Add the Procrastination animation
* OFW PR 3892: Fix USB-UART bridge exit screen stopping the bridge prematurely (by @portasynthinca3)
- Read Ultralight block by block (**fix password protected MFUL reading issue**) (by @mishamyte | PR #825 #826)
- **Update NDEF parser** (SLIX and MFC support) (by @luu176 and @jaylikesbunda and @Willy-JL)
- OFW PR 3822: **MIFARE Classic Key Recovery Improvements** (by @noproto)
- OFW PR 3930: NFC Emulation freeze fix (by @RebornedBrain)
- OFW: H World Hotel Chain Room Key Parser
- OFW: Parser for Tianjin Railway Transit
- New keys in system dict
- Infrared:
- **Add LEDs universal remote** (DB by @amec0e)
- Update universal remote assets (by @amec0e | PR #813 #816)
- JS:
- OFW: JS modules & SDK -> **Breaking API change**
- **Backporting custom features** (read about most of the changes after other changes section) (by @xMasterX and @Willy-JL)
- Add i2c & SPI module (by @jamisonderek)
* OFW: FuriHal, drivers: rework gauge initialization routine -> **Downgrade to older releases may break battery UI percent indicator, upgrade to this or newer version to restore**
* OFW: heap: increased size -> **More free RAM!!**
* OFW: New layout for BadUSB (es-LA)
* OFW: Require PIN on boot
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
* Docs: Improved the description steps to create a new remote BFT Mitto with more detailed and accurate instructions (by @chrostino | PR #805)
* OFW: FuriTimer: Use an event instead of a volatile bool to wait for deletion
* OFW: Threading, Timers improvements
* OFW: Replace all calls to strncpy with strlcpy, use strdup more, expose strlcat
* OFW: feat: add linux/gnome badusb demo resource files
* OFW: Exposed `view_dispatcher_get_event_loop`
* OFW: Infrared button operation fails now shows more informative messages
* OFW: Loader: Warn about missing SD card for main apps
* OFW: Desktop: Sanity check PIN length for good measure
* OFW: DialogEx: Fix NULL ptr crash
* OFW: Debug: use proper hook for handle_exit in flipperapps
* OFW: Clean up of LFS traces
* OFW: Proper integer parsing
* OFW: SubGhz: Fix RPC status for ButtonRelease event
* OFW: CCID: App changes
* OFW: 5V on GPIO control for ext. modules
* OFW: Gui: Add up and down button drawing functions to GUI elements
* OFW: Gui: change dialog_ex text ownership model
* OFW: Publishing T5577 page 1 block count macro
* SubGHz: Freq analyzer - Fix duplicated frequency lists and use user config for nearest frequency selector too
* SubGHz: Code cleanup and fix for rare dupicated (Data) field cases
* OFW: NFC TRT Parser: Additional checks to prevent false positives
* OFW PR 3992: Loader: Fix BusFault in handling of OOM (by @Willy-JL)
* OFW PR 3885: NFC: Add API to enforce ISO15693 mode (by @aaronjamt)
* OFW: NFC: iso14443_4a improvements (by @RebornedBrain)
* OFW: NFC: Plantain parser improvements (by @assasinfil) & fixes (by @mxcdoam)
* OFW: NFC: Moscow social card parser (by @assasinfil)
* OFW: fix: npm deps
* OFW: 目覚め時計 (Added alarm option and clock settings)
* OFW: JS: Backport and more additions & fixes
* OFW: nfc: add Caltrain zones for Clipper
* OFW: Update unit tests docs
* OFW: Fix JS memory corruption (in gpio module)
* OFW: Full-fledged JS SDK + npm packages
* OFW: FurEventLoop: add support for FuriEventFlag, simplify API
* OFW: lib: digital_signal: digital_sequence: add furi_hal.h wrapped in ifdefs
* OFW: Add warning about stealth mode in vibro CLI
* OFW: Small fixes in the wifi devboard docs
* OFW: BadUSB - Improve ChromeOS and GNOME demo scripts
* OFW: Small JS fixes
* OFW: Canvas: extended icon draw.
* OFW: Fixes Mouse Clicker Should have a "0" value setting for "as fast as possible"
* OFW: Wi-Fi Devboard documentation rework
* OFW: Furi: A Lot of Fixes
* OFW PR 3933: furi_hal_random: Wait for ready state and no errors before sampling (by @n1kolasM)
* OFW: nfc/clipper: Update BART station codes
* OFW: FuriThread: Improve state callbacks
* OFW: Documentation: update and cleanup
* OFW: Improve bit_buffer.h docs
* OFW: Prevent idle priority threads from potentially starving the FreeRTOS idle task
* OFW: IR universal remote additions
* OFW: Fix EM4100 T5577 writing block order (was already done in UL)
* OFW: kerel typo
* OFW: Folder rename fails
* OFW: Put errno into TCB
* OFW: Fix USB-UART bridge exit screen stopping the bridge prematurely
**More details on JS changes** (js changelog written by @Willy-JL , thanks!):
- Our custom JS SDK can be found on npm now: https://www.npmjs.com/org/darkflippers
- Non-exhaustive list of changes to help you fix your scripts:
- `badusb`:
- `setup()`: `mfr_name`, `prod_name`, `layout_path` parameters renamed to `mfrName`, `prodName`, `layoutPath`
- effort required to update old scripts using badusb: very minimal
- `dialog`:
- removed, now replaced by `gui/dialog` and `gui/file_picker` (see below)
- `event_loop`:
- new module, allows timer functionality, callbacks and event-driven programming, used heavily alongside gpio and gui modules
- `gpio`:
- fully overhauled, now you `get()` pin instances and perform actions on them like `.init()`
- now supports interrupts, callbacks and more cool things
- effort required to update old scripts using gpio: moderate
- `gui`:
- new module, fully overhauled, replaces dialog, keyboard, submenu, textbox modules
- higher barrier to entry than older modules (requires usage of `event_loop` and `gui.viewDispatcher`), but much more flexible, powerful and easier to extend
- includes all previously available js gui functionality (except `widget`), and also adds `gui/loading` and `gui/empty_screen` views
- currently `gui/file_picker` works different than other new view objects, it is a simple `.pickFile()` synchronous function, but this [may change later](https://github.com/flipperdevices/flipperzero-firmware/pull/3961#discussion_r1805579153)
- effort required to update old scripts using gui: extensive
- `keyboard`:
- removed, now replaced by `gui/text_input` and `gui/byte_input` (see above)
- `math`:
- `is_equal()` renamed to `isEqual()`
- `storage`:
- fully overhauled, now you `openFile()`s and perform actions on them like `.read()`
- now supports many more operations including different open modes, directories and much more
- effort required to update old scripts using storage: moderate
- `submenu`:
- removed, now replaced by `gui/submenu` (see above)
- `textbox`:
- removed, now replace by `gui/text_box` (see above)
- `widget`:
- only gui functionality not ported to new gui module, remains unchanged for now but likely to be ported later on
- globals:
- `__filepath` and `__dirpath` renamed to `__filename` and `__dirname` like in nodejs
- `to_string()` renamed and moved to number class as `n.toString()`, now supports optional base parameter
- `to_hex_string()` removed, now use `n.toString(16)`
- `parse_int()` renamed to `parseInt()`, now supports optional base parameter
- `to_upper_case()` and `to_lower_case()` renamed and moved to string class as `s.toUpperCase()` and `s.toLowerCase()`
- effort required to update old scripts using these: minimal
- Added type definitions (typescript files for type checking in IDE, Flipper does not run typescript)
- Documentation is incomplete and deprecated, from now on you should refer to type definitions (`applications/system/js_app/types`), those will always be correct
- Type definitions for extra modules we have that OFW doesn't will come later
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
@ -60,7 +133,7 @@
|cloudtips|only RU payments accepted|<div align="center"><a href="https://github.com/user-attachments/assets/5de31d6a-ef24-4d30-bd8e-c06af815332a"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|https://pay.cloudtips.ru/p/7b3e9d65|
|YooMoney|only RU payments accepted|<div align="center"><a href="https://github.com/user-attachments/assets/33454f79-074b-4349-b453-f94fdadc3c68"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|https://yoomoney.ru/fundraise/XA49mgQLPA0.221209|
|USDT|(TRC20)|<div align="center"><a href="https://github.com/user-attachments/assets/0500498d-18ed-412d-a1a4-8a66d0b6f057"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`|
|ETH|(BSC/ERC20-Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)|
|ETH|(BSC/ERC20-Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`|
|BTC||<div align="center"><a href="https://github.com/user-attachments/assets/5a904d45-947e-4b92-9f0f-7fbaaa7b37f8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`|
|SOL|(Solana/Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/ab33c5e0-dd59-497b-9c91-ceb89c36b34d"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`DSgwouAEgu8iP5yr7EHHDqMNYWZxAqXWsTEeqCAXGLj8`|
|DOGE||<div align="center"><a href="https://github.com/user-attachments/assets/2937edd0-5c85-4465-a444-14d4edb481c0"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`|

View file

@ -36,9 +36,14 @@
## FAQ (frequently asked questions)
[Follow this link to find answers to most asked questions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/FAQ.md)
## Our official domains
- https://flipperunleashed.com/ -> our main web page
- https://unleashedflip.com/ -> update server, direct .tgz update links for web updater or direct download
## Dev builds (unstable) (built automatically from dev branch)
- https://dev.unleashedflip.com/
- https://t.me/kotnehleb
## Releases in Telegram
- https://t.me/unleashed_fw
@ -112,7 +117,7 @@ Decoders/Encoders or emulation (+ programming mode) support made by @xMasterX:
- Hay21 (dynamic 21 bit) with button parsing
- Nero Radio 57bit (+ 56bit support)
- CAME 12bit/24bit encoder fixes (Fixes are now merged in OFW)
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !)
Protocols support made by Skorp (original implementation) and @xMasterX (current version):
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
@ -150,7 +155,7 @@ You can support us by using links or addresses below:
|cloudtips|only RU payments accepted|<div align="center"><a href="https://github.com/user-attachments/assets/5de31d6a-ef24-4d30-bd8e-c06af815332a"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|https://pay.cloudtips.ru/p/7b3e9d65|
|YooMoney|only RU payments accepted|<div align="center"><a href="https://github.com/user-attachments/assets/33454f79-074b-4349-b453-f94fdadc3c68"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|https://yoomoney.ru/fundraise/XA49mgQLPA0.221209|
|USDT|(TRC20)|<div align="center"><a href="https://github.com/user-attachments/assets/0500498d-18ed-412d-a1a4-8a66d0b6f057"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`|
|ETH|(BSC/ERC20-Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)|
|ETH|(BSC/ERC20-Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`|
|BTC||<div align="center"><a href="https://github.com/user-attachments/assets/5a904d45-947e-4b92-9f0f-7fbaaa7b37f8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`|
|SOL|(Solana/Tokens)|<div align="center"><a href="https://github.com/user-attachments/assets/ab33c5e0-dd59-497b-9c91-ceb89c36b34d"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`DSgwouAEgu8iP5yr7EHHDqMNYWZxAqXWsTEeqCAXGLj8`|
|DOGE||<div align="center"><a href="https://github.com/user-attachments/assets/2937edd0-5c85-4465-a444-14d4edb481c0"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div>|`D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`|

View file

@ -1,6 +1,5 @@
#include <furi.h>
#include <gui/gui.h>
#include <gui/canvas_i.h>
#include <input/input.h>
#define BUFFER_SIZE (32U)
@ -42,10 +41,11 @@ static DirectDraw* direct_draw_alloc(void) {
static void direct_draw_free(DirectDraw* instance) {
furi_pubsub_unsubscribe(instance->input, instance->input_subscription);
instance->canvas = NULL;
gui_direct_draw_release(instance->gui);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_INPUT_EVENTS);
free(instance);
}
static void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) {

View file

@ -82,7 +82,7 @@ static void view_port_input_callback(InputEvent* input_event, void* context) {
furi_message_queue_put(app->input_queue, input_event, 0);
}
static bool input_queue_callback(FuriEventLoopObject* object, void* context) {
static void input_queue_callback(FuriEventLoopObject* object, void* context) {
FuriMessageQueue* queue = object;
EventLoopBlinkTestApp* app = context;
@ -107,8 +107,6 @@ static bool input_queue_callback(FuriEventLoopObject* object, void* context) {
furi_event_loop_stop(app->event_loop);
}
}
return true;
}
static void blink_timer_callback(void* context) {

View file

@ -221,6 +221,14 @@ App(
requires=["unit_tests"],
)
App(
appid="test_js",
sources=["tests/common/*.c", "tests/js/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests", "js_app"],
)
App(
appid="test_strint",
sources=["tests/common/*.c", "tests/strint/*.c"],

View file

@ -0,0 +1,15 @@
let tests = require("tests");
let flipper = require("flipper");
tests.assert_eq(1337, 1337);
tests.assert_eq("hello", "hello");
tests.assert_eq("compatible", sdkCompatibilityStatus(0, 1));
tests.assert_eq("firmwareTooOld", sdkCompatibilityStatus(100500, 0));
tests.assert_eq("firmwareTooNew", sdkCompatibilityStatus(-100500, 0));
tests.assert_eq(true, doesSdkSupport(["baseline"]));
tests.assert_eq(false, doesSdkSupport(["abobus", "other-nonexistent-feature"]));
tests.assert_eq("flipperdevices", flipper.firmwareVendor);
tests.assert_eq(0, flipper.jsSdkVersion[0]);
tests.assert_eq(1, flipper.jsSdkVersion[1]);

View file

@ -0,0 +1,30 @@
let tests = require("tests");
let event_loop = require("event_loop");
let ext = {
i: 0,
received: false,
};
let queue = event_loop.queue(16);
event_loop.subscribe(queue.input, function (_, item, tests, ext) {
tests.assert_eq(123, item);
ext.received = true;
}, tests, ext);
event_loop.subscribe(event_loop.timer("periodic", 1), function (_, _item, queue, counter, ext) {
ext.i++;
queue.send(123);
if (counter === 10)
event_loop.stop();
return [queue, counter + 1, ext];
}, queue, 1, ext);
event_loop.subscribe(event_loop.timer("oneshot", 1000), function (_, _item, tests) {
tests.fail("event loop was not stopped");
}, tests);
event_loop.run();
tests.assert_eq(10, ext.i);
tests.assert_eq(true, ext.received);

View file

@ -0,0 +1,34 @@
let tests = require("tests");
let math = require("math");
// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16
// basics
tests.assert_float_close(5, math.abs(-5), math.EPSILON);
tests.assert_float_close(0.5, math.abs(-0.5), math.EPSILON);
tests.assert_float_close(5, math.abs(5), math.EPSILON);
tests.assert_float_close(0.5, math.abs(0.5), math.EPSILON);
tests.assert_float_close(3, math.cbrt(27), math.EPSILON);
tests.assert_float_close(6, math.ceil(5.3), math.EPSILON);
tests.assert_float_close(31, math.clz32(1), math.EPSILON);
tests.assert_float_close(5, math.floor(5.7), math.EPSILON);
tests.assert_float_close(5, math.max(3, 5), math.EPSILON);
tests.assert_float_close(3, math.min(3, 5), math.EPSILON);
tests.assert_float_close(-1, math.sign(-5), math.EPSILON);
tests.assert_float_close(5, math.trunc(5.7), math.EPSILON);
// trig
tests.assert_float_close(1.0471975511965976, math.acos(0.5), math.EPSILON);
tests.assert_float_close(1.3169578969248166, math.acosh(2), math.EPSILON);
tests.assert_float_close(0.5235987755982988, math.asin(0.5), math.EPSILON);
tests.assert_float_close(1.4436354751788103, math.asinh(2), math.EPSILON);
tests.assert_float_close(0.7853981633974483, math.atan(1), math.EPSILON);
tests.assert_float_close(0.7853981633974483, math.atan2(1, 1), math.EPSILON);
tests.assert_float_close(0.5493061443340549, math.atanh(0.5), math.EPSILON);
tests.assert_float_close(-1, math.cos(math.PI), math.EPSILON * 18); // Error 3.77475828372553223744e-15
tests.assert_float_close(1, math.sin(math.PI / 2), math.EPSILON * 4.5); // Error 9.99200722162640886381e-16
// powers
tests.assert_float_close(5, math.sqrt(25), math.EPSILON);
tests.assert_float_close(8, math.pow(2, 3), math.EPSILON);
tests.assert_float_close(2.718281828459045, math.exp(1), math.EPSILON * 2); // Error 4.44089209850062616169e-16

View file

@ -0,0 +1,136 @@
let storage = require("storage");
let tests = require("tests");
let baseDir = "/ext/.tmp/unit_tests";
tests.assert_eq(true, storage.rmrf(baseDir));
tests.assert_eq(true, storage.makeDirectory(baseDir));
// write
let file = storage.openFile(baseDir + "/helloworld", "w", "create_always");
tests.assert_eq(true, !!file);
tests.assert_eq(true, file.isOpen());
tests.assert_eq(13, file.write("Hello, World!"));
tests.assert_eq(true, file.close());
tests.assert_eq(false, file.isOpen());
// read
file = storage.openFile(baseDir + "/helloworld", "r", "open_existing");
tests.assert_eq(true, !!file);
tests.assert_eq(true, file.isOpen());
tests.assert_eq(13, file.size());
tests.assert_eq("Hello, World!", file.read("ascii", 128));
tests.assert_eq(true, file.close());
tests.assert_eq(false, file.isOpen());
// seek
file = storage.openFile(baseDir + "/helloworld", "r", "open_existing");
tests.assert_eq(true, !!file);
tests.assert_eq(true, file.isOpen());
tests.assert_eq(13, file.size());
tests.assert_eq("Hello, World!", file.read("ascii", 128));
tests.assert_eq(true, file.seekAbsolute(1));
tests.assert_eq(true, file.seekRelative(2));
tests.assert_eq(3, file.tell());
tests.assert_eq(false, file.eof());
tests.assert_eq("lo, World!", file.read("ascii", 128));
tests.assert_eq(true, file.eof());
tests.assert_eq(true, file.close());
tests.assert_eq(false, file.isOpen());
// byte-level copy
let src = storage.openFile(baseDir + "/helloworld", "r", "open_existing");
let dst = storage.openFile(baseDir + "/helloworld2", "rw", "create_always");
tests.assert_eq(true, !!src);
tests.assert_eq(true, src.isOpen());
tests.assert_eq(true, !!dst);
tests.assert_eq(true, dst.isOpen());
tests.assert_eq(true, src.copyTo(dst, 10));
tests.assert_eq(true, dst.seekAbsolute(0));
tests.assert_eq("Hello, Wor", dst.read("ascii", 128));
tests.assert_eq(true, src.copyTo(dst, 3));
tests.assert_eq(true, dst.seekAbsolute(0));
tests.assert_eq("Hello, World!", dst.read("ascii", 128));
tests.assert_eq(true, src.eof());
tests.assert_eq(true, src.close());
tests.assert_eq(false, src.isOpen());
tests.assert_eq(true, dst.eof());
tests.assert_eq(true, dst.close());
tests.assert_eq(false, dst.isOpen());
// truncate
tests.assert_eq(true, storage.copy(baseDir + "/helloworld", baseDir + "/helloworld2"));
file = storage.openFile(baseDir + "/helloworld2", "w", "open_existing");
tests.assert_eq(true, !!file);
tests.assert_eq(true, file.seekAbsolute(5));
tests.assert_eq(true, file.truncate());
tests.assert_eq(true, file.close());
file = storage.openFile(baseDir + "/helloworld2", "r", "open_existing");
tests.assert_eq(true, !!file);
tests.assert_eq("Hello", file.read("ascii", 128));
tests.assert_eq(true, file.close());
// existence
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld2"));
tests.assert_eq(false, storage.fileExists(baseDir + "/sus_amogus_123"));
tests.assert_eq(false, storage.directoryExists(baseDir + "/helloworld"));
tests.assert_eq(false, storage.fileExists(baseDir));
tests.assert_eq(true, storage.directoryExists(baseDir));
tests.assert_eq(true, storage.fileOrDirExists(baseDir));
tests.assert_eq(true, storage.remove(baseDir + "/helloworld2"));
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld2"));
// stat
let stat = storage.stat(baseDir + "/helloworld");
tests.assert_eq(true, !!stat);
tests.assert_eq(baseDir + "/helloworld", stat.path);
tests.assert_eq(false, stat.isDirectory);
tests.assert_eq(13, stat.size);
// rename
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123"));
tests.assert_eq(true, storage.rename(baseDir + "/helloworld", baseDir + "/helloworld123"));
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld123"));
tests.assert_eq(true, storage.rename(baseDir + "/helloworld123", baseDir + "/helloworld"));
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123"));
// copy
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123"));
tests.assert_eq(true, storage.copy(baseDir + "/helloworld", baseDir + "/helloworld123"));
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld"));
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld123"));
// next avail
tests.assert_eq("helloworld1", storage.nextAvailableFilename(baseDir, "helloworld", "", 20));
// fs info
let fsInfo = storage.fsInfo("/ext");
tests.assert_eq(true, !!fsInfo);
tests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace); // idk \(-_-)/
fsInfo = storage.fsInfo("/int");
tests.assert_eq(true, !!fsInfo);
tests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace);
// path operations
tests.assert_eq(true, storage.arePathsEqual("/ext/test", "/ext/Test"));
tests.assert_eq(false, storage.arePathsEqual("/ext/test", "/ext/Testttt"));
tests.assert_eq(true, storage.isSubpathOf("/ext/test", "/ext/test/sub"));
tests.assert_eq(false, storage.isSubpathOf("/ext/test/sub", "/ext/test"));
// dir
let entries = storage.readDirectory(baseDir);
tests.assert_eq(true, !!entries);
// FIXME: (-nofl) this test suite assumes that files are listed by
// `readDirectory` in the exact order that they were created, which is not
// something that is actually guaranteed.
// Possible solution: sort and compare the array.
tests.assert_eq("helloworld", entries[0].path);
tests.assert_eq("helloworld123", entries[1].path);
tests.assert_eq(true, storage.rmrf(baseDir));
tests.assert_eq(true, storage.makeDirectory(baseDir));

View file

@ -0,0 +1,51 @@
#include <furi.h>
#include <errno.h>
#include "../test.h" // IWYU pragma: keep
#define TAG "ErrnoTest"
#define THREAD_CNT 16
#define ITER_CNT 1000
static int32_t errno_fuzzer(void* context) {
int start_value = (int)context;
int32_t fails = 0;
for(int i = start_value; i < start_value + ITER_CNT; i++) {
errno = i;
furi_thread_yield();
if(errno != i) fails++;
}
for(int i = 0; i < ITER_CNT; i++) {
errno = 0;
furi_thread_yield();
UNUSED(strtol("123456", NULL, 10)); // -V530
furi_thread_yield();
if(errno != 0) fails++;
errno = 0;
furi_thread_yield();
UNUSED(strtol("123456123456123456123456123456123456123456123456", NULL, 10)); // -V530
furi_thread_yield();
if(errno != ERANGE) fails++;
}
return fails;
}
void test_errno_saving(void) {
FuriThread* threads[THREAD_CNT];
for(int i = 0; i < THREAD_CNT; i++) {
int start_value = i * ITER_CNT;
threads[i] = furi_thread_alloc_ex("ErrnoFuzzer", 1024, errno_fuzzer, (void*)start_value);
furi_thread_set_priority(threads[i], FuriThreadPriorityNormal);
furi_thread_start(threads[i]);
}
for(int i = 0; i < THREAD_CNT; i++) {
furi_thread_join(threads[i]);
mu_assert_int_eq(0, furi_thread_get_return_code(threads[i]));
furi_thread_free(threads[i]);
}
}

View file

@ -1,205 +0,0 @@
#include "../test.h"
#include <furi.h>
#include <furi_hal.h>
#include <FreeRTOS.h>
#include <task.h>
#define TAG "TestFuriEventLoop"
#define EVENT_LOOP_EVENT_COUNT (256u)
typedef struct {
FuriMessageQueue* mq;
FuriEventLoop* producer_event_loop;
uint32_t producer_counter;
FuriEventLoop* consumer_event_loop;
uint32_t consumer_counter;
} TestFuriData;
bool test_furi_event_loop_producer_mq_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriData* data = context;
furi_check(data->mq == object, "Invalid queue");
FURI_LOG_I(
TAG, "producer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
if(data->producer_counter == EVENT_LOOP_EVENT_COUNT / 2) {
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
furi_event_loop_subscribe_message_queue(
data->producer_event_loop,
data->mq,
FuriEventLoopEventOut,
test_furi_event_loop_producer_mq_callback,
data);
}
if(data->producer_counter == EVENT_LOOP_EVENT_COUNT) {
furi_event_loop_stop(data->producer_event_loop);
return false;
}
data->producer_counter++;
furi_check(
furi_message_queue_put(data->mq, &data->producer_counter, 0) == FuriStatusOk,
"furi_message_queue_put failed");
furi_delay_us(furi_hal_random_get() % 1000);
return true;
}
int32_t test_furi_event_loop_producer(void* p) {
furi_check(p);
TestFuriData* data = p;
FURI_LOG_I(TAG, "producer start 1st run");
data->producer_event_loop = furi_event_loop_alloc();
furi_event_loop_subscribe_message_queue(
data->producer_event_loop,
data->mq,
FuriEventLoopEventOut,
test_furi_event_loop_producer_mq_callback,
data);
furi_event_loop_run(data->producer_event_loop);
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
furi_event_loop_free(data->producer_event_loop);
FURI_LOG_I(TAG, "producer start 2nd run");
data->producer_counter = 0;
data->producer_event_loop = furi_event_loop_alloc();
furi_event_loop_subscribe_message_queue(
data->producer_event_loop,
data->mq,
FuriEventLoopEventOut,
test_furi_event_loop_producer_mq_callback,
data);
furi_event_loop_run(data->producer_event_loop);
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
furi_event_loop_free(data->producer_event_loop);
FURI_LOG_I(TAG, "producer end");
return 0;
}
bool test_furi_event_loop_consumer_mq_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriData* data = context;
furi_check(data->mq == object);
furi_delay_us(furi_hal_random_get() % 1000);
furi_check(furi_message_queue_get(data->mq, &data->consumer_counter, 0) == FuriStatusOk);
FURI_LOG_I(
TAG, "consumer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT / 2) {
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
furi_event_loop_subscribe_message_queue(
data->consumer_event_loop,
data->mq,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_mq_callback,
data);
}
if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT) {
furi_event_loop_stop(data->consumer_event_loop);
return false;
}
return true;
}
int32_t test_furi_event_loop_consumer(void* p) {
furi_check(p);
TestFuriData* data = p;
FURI_LOG_I(TAG, "consumer start 1st run");
data->consumer_event_loop = furi_event_loop_alloc();
furi_event_loop_subscribe_message_queue(
data->consumer_event_loop,
data->mq,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_mq_callback,
data);
furi_event_loop_run(data->consumer_event_loop);
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
furi_event_loop_free(data->consumer_event_loop);
FURI_LOG_I(TAG, "consumer start 2nd run");
data->consumer_counter = 0;
data->consumer_event_loop = furi_event_loop_alloc();
furi_event_loop_subscribe_message_queue(
data->consumer_event_loop,
data->mq,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_mq_callback,
data);
furi_event_loop_run(data->consumer_event_loop);
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
furi_event_loop_free(data->consumer_event_loop);
FURI_LOG_I(TAG, "consumer end");
return 0;
}
void test_furi_event_loop(void) {
TestFuriData data = {};
data.mq = furi_message_queue_alloc(16, sizeof(uint32_t));
FuriThread* producer_thread = furi_thread_alloc();
furi_thread_set_name(producer_thread, "producer_thread");
furi_thread_set_stack_size(producer_thread, 1 * 1024);
furi_thread_set_callback(producer_thread, test_furi_event_loop_producer);
furi_thread_set_context(producer_thread, &data);
furi_thread_start(producer_thread);
FuriThread* consumer_thread = furi_thread_alloc();
furi_thread_set_name(consumer_thread, "consumer_thread");
furi_thread_set_stack_size(consumer_thread, 1 * 1024);
furi_thread_set_callback(consumer_thread, test_furi_event_loop_consumer);
furi_thread_set_context(consumer_thread, &data);
furi_thread_start(consumer_thread);
// Wait for thread to complete their tasks
furi_thread_join(producer_thread);
furi_thread_join(consumer_thread);
// The test itself
mu_assert_int_eq(data.producer_counter, data.consumer_counter);
mu_assert_int_eq(data.producer_counter, EVENT_LOOP_EVENT_COUNT);
// Release memory
furi_thread_free(consumer_thread);
furi_thread_free(producer_thread);
furi_message_queue_free(data.mq);
}

View file

@ -0,0 +1,490 @@
#include "../test.h"
#include <furi.h>
#include <furi_hal.h>
#include <FreeRTOS.h>
#include <task.h>
#define TAG "TestFuriEventLoop"
#define MESSAGE_COUNT (256UL)
#define EVENT_FLAG_COUNT (23UL)
#define PRIMITIVE_COUNT (4UL)
#define RUN_COUNT (2UL)
typedef struct {
FuriEventLoop* event_loop;
uint32_t message_queue_count;
uint32_t stream_buffer_count;
uint32_t event_flag_count;
uint32_t semaphore_count;
uint32_t primitives_tested;
} TestFuriEventLoopThread;
typedef struct {
FuriMessageQueue* message_queue;
FuriStreamBuffer* stream_buffer;
FuriEventFlag* event_flag;
FuriSemaphore* semaphore;
TestFuriEventLoopThread producer;
TestFuriEventLoopThread consumer;
} TestFuriEventLoopData;
static void test_furi_event_loop_pending_callback(void* context) {
furi_check(context);
TestFuriEventLoopThread* test_thread = context;
furi_check(test_thread->primitives_tested < PRIMITIVE_COUNT);
test_thread->primitives_tested++;
FURI_LOG_I(TAG, "primitives tested: %lu", test_thread->primitives_tested);
if(test_thread->primitives_tested == PRIMITIVE_COUNT) {
furi_event_loop_stop(test_thread->event_loop);
}
}
static void test_furi_event_loop_thread_init(TestFuriEventLoopThread* test_thread) {
memset(test_thread, 0, sizeof(TestFuriEventLoopThread));
test_thread->event_loop = furi_event_loop_alloc();
}
static void test_furi_event_loop_thread_run_and_cleanup(TestFuriEventLoopThread* test_thread) {
furi_event_loop_run(test_thread->event_loop);
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
furi_event_loop_free(test_thread->event_loop);
}
static void test_furi_event_loop_producer_message_queue_callback(
FuriEventLoopObject* object,
void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->message_queue == object);
FURI_LOG_I(
TAG,
"producer MessageQueue: %lu %lu",
data->producer.message_queue_count,
data->consumer.message_queue_count);
if(data->producer.message_queue_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);
furi_event_loop_subscribe_message_queue(
data->producer.event_loop,
data->message_queue,
FuriEventLoopEventOut,
test_furi_event_loop_producer_message_queue_callback,
data);
} else if(data->producer.message_queue_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);
furi_event_loop_pend_callback(
data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);
return;
}
data->producer.message_queue_count++;
furi_check(
furi_message_queue_put(data->message_queue, &data->producer.message_queue_count, 0) ==
FuriStatusOk);
furi_delay_us(furi_hal_random_get() % 100);
}
static void test_furi_event_loop_producer_stream_buffer_callback(
FuriEventLoopObject* object,
void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->stream_buffer == object);
TestFuriEventLoopThread* producer = &data->producer;
TestFuriEventLoopThread* consumer = &data->consumer;
FURI_LOG_I(
TAG,
"producer StreamBuffer: %lu %lu",
producer->stream_buffer_count,
consumer->stream_buffer_count);
if(producer->stream_buffer_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);
furi_event_loop_subscribe_stream_buffer(
producer->event_loop,
data->stream_buffer,
FuriEventLoopEventOut,
test_furi_event_loop_producer_stream_buffer_callback,
data);
} else if(producer->stream_buffer_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);
furi_event_loop_pend_callback(
producer->event_loop, test_furi_event_loop_pending_callback, producer);
return;
}
producer->stream_buffer_count++;
furi_check(
furi_stream_buffer_send(
data->stream_buffer, &producer->stream_buffer_count, sizeof(uint32_t), 0) ==
sizeof(uint32_t));
furi_delay_us(furi_hal_random_get() % 100);
}
static void
test_furi_event_loop_producer_event_flag_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->event_flag == object);
const uint32_t producer_flags = (1UL << data->producer.event_flag_count);
const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);
FURI_LOG_I(TAG, "producer EventFlag: 0x%06lX 0x%06lX", producer_flags, consumer_flags);
furi_check(furi_event_flag_set(data->event_flag, producer_flags) & producer_flags);
if(data->producer.event_flag_count == EVENT_FLAG_COUNT / 2) {
furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);
furi_event_loop_subscribe_event_flag(
data->producer.event_loop,
data->event_flag,
FuriEventLoopEventOut,
test_furi_event_loop_producer_event_flag_callback,
data);
} else if(data->producer.event_flag_count == EVENT_FLAG_COUNT) {
furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);
furi_event_loop_pend_callback(
data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);
return;
}
data->producer.event_flag_count++;
furi_delay_us(furi_hal_random_get() % 100);
}
static void
test_furi_event_loop_producer_semaphore_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->semaphore == object);
TestFuriEventLoopThread* producer = &data->producer;
TestFuriEventLoopThread* consumer = &data->consumer;
FURI_LOG_I(
TAG, "producer Semaphore: %lu %lu", producer->semaphore_count, consumer->semaphore_count);
furi_check(furi_semaphore_release(data->semaphore) == FuriStatusOk);
if(producer->semaphore_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);
furi_event_loop_subscribe_semaphore(
producer->event_loop,
data->semaphore,
FuriEventLoopEventOut,
test_furi_event_loop_producer_semaphore_callback,
data);
} else if(producer->semaphore_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);
furi_event_loop_pend_callback(
producer->event_loop, test_furi_event_loop_pending_callback, producer);
return;
}
data->producer.semaphore_count++;
furi_delay_us(furi_hal_random_get() % 100);
}
static int32_t test_furi_event_loop_producer(void* p) {
furi_check(p);
TestFuriEventLoopData* data = p;
TestFuriEventLoopThread* producer = &data->producer;
for(uint32_t i = 0; i < RUN_COUNT; ++i) {
FURI_LOG_I(TAG, "producer start run %lu", i);
test_furi_event_loop_thread_init(producer);
furi_event_loop_subscribe_message_queue(
producer->event_loop,
data->message_queue,
FuriEventLoopEventOut,
test_furi_event_loop_producer_message_queue_callback,
data);
furi_event_loop_subscribe_stream_buffer(
producer->event_loop,
data->stream_buffer,
FuriEventLoopEventOut,
test_furi_event_loop_producer_stream_buffer_callback,
data);
furi_event_loop_subscribe_event_flag(
producer->event_loop,
data->event_flag,
FuriEventLoopEventOut,
test_furi_event_loop_producer_event_flag_callback,
data);
furi_event_loop_subscribe_semaphore(
producer->event_loop,
data->semaphore,
FuriEventLoopEventOut,
test_furi_event_loop_producer_semaphore_callback,
data);
test_furi_event_loop_thread_run_and_cleanup(producer);
}
FURI_LOG_I(TAG, "producer end");
return 0;
}
static void test_furi_event_loop_consumer_message_queue_callback(
FuriEventLoopObject* object,
void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->message_queue == object);
furi_delay_us(furi_hal_random_get() % 100);
furi_check(
furi_message_queue_get(data->message_queue, &data->consumer.message_queue_count, 0) ==
FuriStatusOk);
FURI_LOG_I(
TAG,
"consumer MessageQueue: %lu %lu",
data->producer.message_queue_count,
data->consumer.message_queue_count);
if(data->consumer.message_queue_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);
furi_event_loop_subscribe_message_queue(
data->consumer.event_loop,
data->message_queue,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_message_queue_callback,
data);
} else if(data->consumer.message_queue_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);
furi_event_loop_pend_callback(
data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);
}
}
static void test_furi_event_loop_consumer_stream_buffer_callback(
FuriEventLoopObject* object,
void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->stream_buffer == object);
TestFuriEventLoopThread* producer = &data->producer;
TestFuriEventLoopThread* consumer = &data->consumer;
furi_delay_us(furi_hal_random_get() % 100);
furi_check(
furi_stream_buffer_receive(
data->stream_buffer, &consumer->stream_buffer_count, sizeof(uint32_t), 0) ==
sizeof(uint32_t));
FURI_LOG_I(
TAG,
"consumer StreamBuffer: %lu %lu",
producer->stream_buffer_count,
consumer->stream_buffer_count);
if(consumer->stream_buffer_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(consumer->event_loop, data->stream_buffer);
furi_event_loop_subscribe_stream_buffer(
consumer->event_loop,
data->stream_buffer,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_stream_buffer_callback,
data);
} else if(consumer->stream_buffer_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(data->consumer.event_loop, data->stream_buffer);
furi_event_loop_pend_callback(
consumer->event_loop, test_furi_event_loop_pending_callback, consumer);
}
}
static void
test_furi_event_loop_consumer_event_flag_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->event_flag == object);
furi_delay_us(furi_hal_random_get() % 100);
const uint32_t producer_flags = (1UL << data->producer.event_flag_count);
const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);
furi_check(
furi_event_flag_wait(data->event_flag, consumer_flags, FuriFlagWaitAny, 0) &
consumer_flags);
FURI_LOG_I(TAG, "consumer EventFlag: 0x%06lX 0x%06lX", producer_flags, consumer_flags);
if(data->consumer.event_flag_count == EVENT_FLAG_COUNT / 2) {
furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);
furi_event_loop_subscribe_event_flag(
data->consumer.event_loop,
data->event_flag,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_event_flag_callback,
data);
} else if(data->consumer.event_flag_count == EVENT_FLAG_COUNT) {
furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);
furi_event_loop_pend_callback(
data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);
return;
}
data->consumer.event_flag_count++;
}
static void
test_furi_event_loop_consumer_semaphore_callback(FuriEventLoopObject* object, void* context) {
furi_check(context);
TestFuriEventLoopData* data = context;
furi_check(data->semaphore == object);
furi_delay_us(furi_hal_random_get() % 100);
TestFuriEventLoopThread* producer = &data->producer;
TestFuriEventLoopThread* consumer = &data->consumer;
furi_check(furi_semaphore_acquire(data->semaphore, 0) == FuriStatusOk);
FURI_LOG_I(
TAG, "consumer Semaphore: %lu %lu", producer->semaphore_count, consumer->semaphore_count);
if(consumer->semaphore_count == MESSAGE_COUNT / 2) {
furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);
furi_event_loop_subscribe_semaphore(
consumer->event_loop,
data->semaphore,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_semaphore_callback,
data);
} else if(consumer->semaphore_count == MESSAGE_COUNT) {
furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);
furi_event_loop_pend_callback(
consumer->event_loop, test_furi_event_loop_pending_callback, consumer);
return;
}
data->consumer.semaphore_count++;
}
static int32_t test_furi_event_loop_consumer(void* p) {
furi_check(p);
TestFuriEventLoopData* data = p;
TestFuriEventLoopThread* consumer = &data->consumer;
for(uint32_t i = 0; i < RUN_COUNT; ++i) {
FURI_LOG_I(TAG, "consumer start run %lu", i);
test_furi_event_loop_thread_init(consumer);
furi_event_loop_subscribe_message_queue(
consumer->event_loop,
data->message_queue,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_message_queue_callback,
data);
furi_event_loop_subscribe_stream_buffer(
consumer->event_loop,
data->stream_buffer,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_stream_buffer_callback,
data);
furi_event_loop_subscribe_event_flag(
consumer->event_loop,
data->event_flag,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_event_flag_callback,
data);
furi_event_loop_subscribe_semaphore(
consumer->event_loop,
data->semaphore,
FuriEventLoopEventIn,
test_furi_event_loop_consumer_semaphore_callback,
data);
test_furi_event_loop_thread_run_and_cleanup(consumer);
}
FURI_LOG_I(TAG, "consumer end");
return 0;
}
void test_furi_event_loop(void) {
TestFuriEventLoopData data = {};
data.message_queue = furi_message_queue_alloc(16, sizeof(uint32_t));
data.stream_buffer = furi_stream_buffer_alloc(16, sizeof(uint32_t));
data.event_flag = furi_event_flag_alloc();
data.semaphore = furi_semaphore_alloc(8, 0);
FuriThread* producer_thread =
furi_thread_alloc_ex("producer_thread", 1 * 1024, test_furi_event_loop_producer, &data);
furi_thread_start(producer_thread);
FuriThread* consumer_thread =
furi_thread_alloc_ex("consumer_thread", 1 * 1024, test_furi_event_loop_consumer, &data);
furi_thread_start(consumer_thread);
// Wait for thread to complete their tasks
furi_thread_join(producer_thread);
furi_thread_join(consumer_thread);
TestFuriEventLoopThread* producer = &data.producer;
TestFuriEventLoopThread* consumer = &data.consumer;
// The test itself
mu_assert_int_eq(producer->message_queue_count, consumer->message_queue_count);
mu_assert_int_eq(producer->message_queue_count, MESSAGE_COUNT);
mu_assert_int_eq(producer->stream_buffer_count, consumer->stream_buffer_count);
mu_assert_int_eq(producer->stream_buffer_count, MESSAGE_COUNT);
mu_assert_int_eq(producer->event_flag_count, consumer->event_flag_count);
mu_assert_int_eq(producer->event_flag_count, EVENT_FLAG_COUNT);
mu_assert_int_eq(producer->semaphore_count, consumer->semaphore_count);
mu_assert_int_eq(producer->semaphore_count, MESSAGE_COUNT);
// Release memory
furi_thread_free(consumer_thread);
furi_thread_free(producer_thread);
furi_message_queue_free(data.message_queue);
furi_stream_buffer_free(data.stream_buffer);
furi_event_flag_free(data.event_flag);
furi_semaphore_free(data.semaphore);
}

View file

@ -0,0 +1,103 @@
#include <furi.h>
#include "../test.h" // IWYU pragma: keep
#define MESSAGE_QUEUE_CAPACITY (16U)
#define MESSAGE_QUEUE_ELEMENT_SIZE (sizeof(uint32_t))
#define STREAM_BUFFER_SIZE (32U)
#define STREAM_BUFFER_TRG_LEVEL (STREAM_BUFFER_SIZE / 2U)
typedef struct {
FuriMessageQueue* message_queue;
FuriStreamBuffer* stream_buffer;
} TestFuriPrimitivesData;
static void test_furi_message_queue(TestFuriPrimitivesData* data) {
FuriMessageQueue* message_queue = data->message_queue;
mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_capacity(message_queue));
mu_assert_int_eq(
MESSAGE_QUEUE_ELEMENT_SIZE, furi_message_queue_get_message_size(message_queue));
for(uint32_t i = 0;; ++i) {
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_space(message_queue));
mu_assert_int_eq(i, furi_message_queue_get_count(message_queue));
if(furi_message_queue_put(message_queue, &i, 0) != FuriStatusOk) {
break;
}
}
mu_assert_int_eq(0, furi_message_queue_get_space(message_queue));
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_count(message_queue));
for(uint32_t i = 0;; ++i) {
mu_assert_int_eq(i, furi_message_queue_get_space(message_queue));
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_count(message_queue));
uint32_t value;
if(furi_message_queue_get(message_queue, &value, 0) != FuriStatusOk) {
break;
}
mu_assert_int_eq(i, value);
}
mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));
}
static void test_furi_stream_buffer(TestFuriPrimitivesData* data) {
FuriStreamBuffer* stream_buffer = data->stream_buffer;
mu_assert(furi_stream_buffer_is_empty(stream_buffer), "Must be empty");
mu_assert(!furi_stream_buffer_is_full(stream_buffer), "Must be not full");
mu_assert_int_eq(0, furi_stream_buffer_bytes_available(stream_buffer));
mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_spaces_available(stream_buffer));
for(uint8_t i = 0;; ++i) {
mu_assert_int_eq(i, furi_stream_buffer_bytes_available(stream_buffer));
mu_assert_int_eq(
STREAM_BUFFER_SIZE - i, furi_stream_buffer_spaces_available(stream_buffer));
if(furi_stream_buffer_send(stream_buffer, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
break;
}
}
mu_assert(!furi_stream_buffer_is_empty(stream_buffer), "Must be not empty");
mu_assert(furi_stream_buffer_is_full(stream_buffer), "Must be full");
mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_bytes_available(stream_buffer));
mu_assert_int_eq(0, furi_stream_buffer_spaces_available(stream_buffer));
for(uint8_t i = 0;; ++i) {
mu_assert_int_eq(
STREAM_BUFFER_SIZE - i, furi_stream_buffer_bytes_available(stream_buffer));
mu_assert_int_eq(i, furi_stream_buffer_spaces_available(stream_buffer));
uint8_t value;
if(furi_stream_buffer_receive(stream_buffer, &value, sizeof(uint8_t), 0) !=
sizeof(uint8_t)) {
break;
}
mu_assert_int_eq(i, value);
}
}
// This is a stub that needs expanding
void test_furi_primitives(void) {
TestFuriPrimitivesData data = {
.message_queue =
furi_message_queue_alloc(MESSAGE_QUEUE_CAPACITY, MESSAGE_QUEUE_ELEMENT_SIZE),
.stream_buffer = furi_stream_buffer_alloc(STREAM_BUFFER_SIZE, STREAM_BUFFER_TRG_LEVEL),
};
test_furi_message_queue(&data);
test_furi_stream_buffer(&data);
furi_message_queue_free(data.message_queue);
furi_stream_buffer_free(data.stream_buffer);
}

View file

@ -8,6 +8,8 @@ void test_furi_concurrent_access(void);
void test_furi_pubsub(void);
void test_furi_memmgr(void);
void test_furi_event_loop(void);
void test_errno_saving(void);
void test_furi_primitives(void);
static int foo = 0;
@ -42,6 +44,14 @@ MU_TEST(mu_test_furi_event_loop) {
test_furi_event_loop();
}
MU_TEST(mu_test_errno_saving) {
test_errno_saving();
}
MU_TEST(mu_test_furi_primitives) {
test_furi_primitives();
}
MU_TEST_SUITE(test_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check);
@ -51,6 +61,8 @@ MU_TEST_SUITE(test_suite) {
MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr);
MU_RUN_TEST(mu_test_furi_event_loop);
MU_RUN_TEST(mu_test_errno_saving);
MU_RUN_TEST(mu_test_furi_primitives);
}
int run_minunit_test_furi(void) {

View file

@ -0,0 +1,88 @@
#include "../test.h" // IWYU pragma: keep
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_random.h>
#include <storage/storage.h>
#include <applications/system/js_app/js_thread.h>
#include <stdint.h>
#define JS_SCRIPT_PATH(name) EXT_PATH("unit_tests/js/" name ".js")
typedef enum {
JsTestsFinished = 1,
JsTestsError = 2,
} JsTestFlag;
typedef struct {
FuriEventFlag* event_flags;
FuriString* error_string;
} JsTestCallbackContext;
static void js_test_callback(JsThreadEvent event, const char* msg, void* param) {
JsTestCallbackContext* context = param;
if(event == JsThreadEventPrint) {
FURI_LOG_I("js_test", "%s", msg);
} else if(event == JsThreadEventError || event == JsThreadEventErrorTrace) {
context->error_string = furi_string_alloc_set_str(msg);
furi_event_flag_set(context->event_flags, JsTestsFinished | JsTestsError);
} else if(event == JsThreadEventDone) {
furi_event_flag_set(context->event_flags, JsTestsFinished);
}
}
static void js_test_run(const char* script_path) {
JsTestCallbackContext* context = malloc(sizeof(JsTestCallbackContext));
context->event_flags = furi_event_flag_alloc();
JsThread* thread = js_thread_run(script_path, js_test_callback, context);
uint32_t flags = furi_event_flag_wait(
context->event_flags, JsTestsFinished, FuriFlagWaitAny, FuriWaitForever);
if(flags & FuriFlagError) {
// getting the flags themselves should not fail
furi_crash();
}
FuriString* error_string = context->error_string;
js_thread_stop(thread);
furi_event_flag_free(context->event_flags);
free(context);
if(flags & JsTestsError) {
// memory leak: not freeing the FuriString if the tests fail,
// because mu_fail executes a return
//
// who cares tho?
mu_fail(furi_string_get_cstr(error_string));
}
}
MU_TEST(js_test_basic) {
js_test_run(JS_SCRIPT_PATH("basic"));
}
MU_TEST(js_test_math) {
js_test_run(JS_SCRIPT_PATH("math"));
}
MU_TEST(js_test_event_loop) {
js_test_run(JS_SCRIPT_PATH("event_loop"));
}
MU_TEST(js_test_storage) {
js_test_run(JS_SCRIPT_PATH("storage"));
}
MU_TEST_SUITE(test_js) {
MU_RUN_TEST(js_test_basic);
MU_RUN_TEST(js_test_math);
MU_RUN_TEST(js_test_event_loop);
MU_RUN_TEST(js_test_storage);
}
int run_minunit_test_js(void) {
MU_RUN_SUITE(test_js);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_js)

View file

@ -31,7 +31,7 @@ extern "C" {
#include <Windows.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#define __func__ __FUNCTION__
#define __func__ __FUNCTION__ //-V1059
#endif
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
@ -56,7 +56,7 @@ extern "C" {
#endif
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)
#define __func__ __extension__ __FUNCTION__
#define __func__ __extension__ __FUNCTION__ //-V1059
#endif
#else
@ -102,6 +102,7 @@ void minunit_printf_warning(const char* format, ...);
MU__SAFE_BLOCK(minunit_setup = setup_fun; minunit_teardown = teardown_fun;)
/* Test runner */
//-V:MU_RUN_TEST:550
#define MU_RUN_TEST(test) \
MU__SAFE_BLOCK( \
if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \

View file

@ -496,7 +496,7 @@ NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void*
MfClassicKey key = {
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
};
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL, false);
frame_test->state = (error == MfClassicErrorNone) ?
NfcTestMfClassicSendFrameTestStateReadBlock :
NfcTestMfClassicSendFrameTestStateFail;

View file

@ -6,9 +6,10 @@
// 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 UNIT_TESTS_RESOURCES_PATH(path) EXT_PATH("unit_tests/" path)
#define UNIT_TESTS_PATH(path) EXT_PATH(".tmp/unit_tests/" path)
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
#define STORAGE_LOCKED_FILE UNIT_TESTS_PATH("locked_file.test")
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir")
@ -369,33 +370,78 @@ MU_TEST(storage_file_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
mu_check(write_file_13DA(storage, EXT_PATH("file.old")));
mu_check(check_file_13DA(storage, EXT_PATH("file.old")));
mu_check(write_file_13DA(storage, UNIT_TESTS_PATH("file.old")));
mu_check(check_file_13DA(storage, UNIT_TESTS_PATH("file.old")));
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL));
mu_check(check_file_13DA(storage, EXT_PATH("file.new")));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new")));
FSE_OK,
storage_common_rename(storage, UNIT_TESTS_PATH("file.old"), UNIT_TESTS_PATH("file.new")));
mu_assert_int_eq(
FSE_NOT_EXIST, storage_common_stat(storage, UNIT_TESTS_PATH("file.old"), NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, UNIT_TESTS_PATH("file.new"), NULL));
mu_check(check_file_13DA(storage, UNIT_TESTS_PATH("file.new")));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, UNIT_TESTS_PATH("file.new")));
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static const char* dir_rename_tests[][2] = {
{UNIT_TESTS_PATH("dir.old"), UNIT_TESTS_PATH("dir.new")},
{UNIT_TESTS_PATH("test_dir"), UNIT_TESTS_PATH("test_dir-new")},
{UNIT_TESTS_PATH("test"), UNIT_TESTS_PATH("test-test")},
};
MU_TEST(storage_dir_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_create(storage, EXT_PATH("dir.old"));
for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {
const char* old_path = dir_rename_tests[i][0];
const char* new_path = dir_rename_tests[i][1];
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old")));
storage_dir_create(storage, old_path);
mu_check(storage_dir_rename_check(storage, old_path));
mu_assert_int_eq(FSE_OK, storage_common_rename(storage, old_path, new_path));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, old_path, NULL));
mu_check(storage_dir_rename_check(storage, new_path));
storage_dir_remove(storage, new_path);
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, new_path, NULL));
}
furi_record_close(RECORD_STORAGE);
}
MU_TEST(storage_equiv_and_subdir) {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL));
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new")));
true,
storage_common_equivalent_path(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
true,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah/"), UNIT_TESTS_PATH("blah/")));
mu_assert_int_eq(
false,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah-blah")));
mu_assert_int_eq(
false,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah/"), UNIT_TESTS_PATH("blah-blah/")));
storage_dir_remove(storage, EXT_PATH("dir.new"));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL));
mu_assert_int_eq(
true, storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
true,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah/blah")));
mu_assert_int_eq(
false,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah/blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
false,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah-blah")));
furi_record_close(RECORD_STORAGE);
}
@ -403,10 +449,13 @@ MU_TEST(storage_dir_rename) {
MU_TEST_SUITE(storage_rename) {
MU_RUN_TEST(storage_file_rename);
MU_RUN_TEST(storage_dir_rename);
MU_RUN_TEST(storage_equiv_and_subdir);
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_remove(storage, EXT_PATH("dir.old"));
storage_dir_remove(storage, EXT_PATH("dir.new"));
for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {
storage_dir_remove(storage, dir_rename_tests[i][0]);
storage_dir_remove(storage, dir_rename_tests[i][1]);
}
furi_record_close(RECORD_STORAGE);
}
@ -653,7 +702,7 @@ MU_TEST(test_md5_calc) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
const char* path = UNIT_TESTS_PATH("storage/md5.txt");
const char* path = UNIT_TESTS_RESOURCES_PATH("storage/md5.txt");
const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2";
const uint8_t md5[MD5_HASH_SIZE] = {
0x2a,

View file

@ -7,7 +7,7 @@
#include <rpc/rpc_i.h>
#include <flipper.pb.h>
#include <core/event_loop.h>
#include <applications/system/js_app/js_thread.h>
static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)),
@ -33,13 +33,9 @@ static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
xQueueGenericSend,
BaseType_t,
(QueueHandle_t, const void* const, TickType_t, const BaseType_t)),
API_METHOD(furi_event_loop_alloc, FuriEventLoop*, (void)),
API_METHOD(furi_event_loop_free, void, (FuriEventLoop*)),
API_METHOD(
furi_event_loop_subscribe_message_queue,
void,
(FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*)),
API_METHOD(furi_event_loop_unsubscribe, void, (FuriEventLoop*, FuriEventLoopObject*)),
API_METHOD(furi_event_loop_run, void, (FuriEventLoop*)),
API_METHOD(furi_event_loop_stop, void, (FuriEventLoop*)),
js_thread_run,
JsThread*,
(const char* script_path, JsThreadCallback callback, void* context)),
API_METHOD(js_thread_stop, void, (JsThread * worker)),
API_VARIABLE(PB_Main_msg, PB_Main_msg_t)));

View file

@ -92,7 +92,7 @@ typedef struct {
const GpioPin* g0_pin;
SubGhzDeviceCC1101ExtAsyncTx async_tx;
SubGhzDeviceCC1101ExtAsyncRx async_rx;
bool power_amp;
bool amp_and_leds;
bool extended_range;
} SubGhzDeviceCC1101Ext;
@ -219,11 +219,11 @@ bool subghz_device_cc1101_ext_alloc(SubGhzDeviceConf* conf) {
subghz_device_cc1101_ext->async_mirror_pin = NULL;
subghz_device_cc1101_ext->spi_bus_handle = &furi_hal_spi_bus_handle_external;
subghz_device_cc1101_ext->g0_pin = SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO;
subghz_device_cc1101_ext->power_amp = false;
subghz_device_cc1101_ext->amp_and_leds = false;
subghz_device_cc1101_ext->extended_range = false;
if(conf) {
if(conf->ver == SUBGHZ_DEVICE_CC1101_CONFIG_VER) {
subghz_device_cc1101_ext->power_amp = conf->power_amp;
subghz_device_cc1101_ext->amp_and_leds = conf->amp_and_leds;
subghz_device_cc1101_ext->extended_range = conf->extended_range;
} else {
FURI_LOG_E(TAG, "Config version mismatch");
@ -233,7 +233,7 @@ bool subghz_device_cc1101_ext_alloc(SubGhzDeviceConf* conf) {
subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0;
furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle);
if(subghz_device_cc1101_ext->power_amp) {
if(subghz_device_cc1101_ext->amp_and_leds) {
furi_hal_gpio_init_simple(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, GpioModeOutputPushPull);
}
@ -244,7 +244,7 @@ void subghz_device_cc1101_ext_free(void) {
furi_assert(subghz_device_cc1101_ext != NULL);
furi_hal_spi_bus_handle_deinit(subghz_device_cc1101_ext->spi_bus_handle);
if(subghz_device_cc1101_ext->power_amp) {
if(subghz_device_cc1101_ext->amp_and_leds) {
furi_hal_gpio_init_simple(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, GpioModeAnalog);
}
@ -421,6 +421,7 @@ void subghz_device_cc1101_ext_reset(void) {
// Reset GDO2 (!TX/RX) to floating state
cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
}
@ -430,13 +431,13 @@ void subghz_device_cc1101_ext_idle(void) {
//waiting for the chip to switch to IDLE mode
furi_check(cc1101_wait_status_state(
subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000));
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0);
// Reset GDO2 (!TX/RX) to floating state
cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
if(subghz_device_cc1101_ext->power_amp) {
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0);
}
}
void subghz_device_cc1101_ext_rx(void) {
@ -445,14 +446,17 @@ void subghz_device_cc1101_ext_rx(void) {
//waiting for the chip to switch to Rx mode
furi_check(
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000));
// Go GDO2 (!TX/RX) to high (RX state)
cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);
if(subghz_device_cc1101_ext->amp_and_leds) {
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0);
// Go GDO2 (!TX/RX) to high (RX state)
cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle,
CC1101_IOCFG2,
CC1101IocfgHW | CC1101_IOCFG_INV);
}
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
if(subghz_device_cc1101_ext->power_amp) {
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0);
}
}
bool subghz_device_cc1101_ext_tx(void) {
@ -462,12 +466,14 @@ bool subghz_device_cc1101_ext_tx(void) {
//waiting for the chip to switch to Tx mode
furi_check(
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000));
// Go GDO2 (!TX/RX) to low (TX state)
cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
if(subghz_device_cc1101_ext->power_amp) {
if(subghz_device_cc1101_ext->amp_and_leds) {
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 1);
// Go GDO2 (!TX/RX) to low (TX state)
cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW);
}
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
return true;
}

View file

@ -1,3 +1,12 @@
App(
appid="example_event_loop_event_flags",
name="Example: Event Loop Event Flags",
apptype=FlipperAppType.EXTERNAL,
sources=["example_event_loop_event_flags.c"],
entry_point="example_event_loop_event_flags_app",
fap_category="Examples",
)
App(
appid="example_event_loop_timer",
name="Example: Event Loop Timer",

View file

@ -0,0 +1,173 @@
/**
* @file example_event_loop_event_flags.c
* @brief Example application demonstrating the use of the FuriEventFlag primitive in FuriEventLoop instances.
*
* This application receives keystrokes from the input service and sets the appropriate flags,
* which are subsequently processed in the event loop
*/
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_port.h>
#include <furi_hal_random.h>
#define TAG "ExampleEventLoopEventFlags"
typedef struct {
Gui* gui;
ViewPort* view_port;
FuriEventLoop* event_loop;
FuriEventFlag* event_flag;
} EventLoopEventFlagsApp;
typedef enum {
EventLoopEventFlagsOk = (1 << 0),
EventLoopEventFlagsUp = (1 << 1),
EventLoopEventFlagsDown = (1 << 2),
EventLoopEventFlagsLeft = (1 << 3),
EventLoopEventFlagsRight = (1 << 4),
EventLoopEventFlagsBack = (1 << 5),
EventLoopEventFlagsExit = (1 << 6),
} EventLoopEventFlags;
#define EVENT_LOOP_EVENT_FLAGS_MASK \
(EventLoopEventFlagsOk | EventLoopEventFlagsUp | EventLoopEventFlagsDown | \
EventLoopEventFlagsLeft | EventLoopEventFlagsRight | EventLoopEventFlagsBack | \
EventLoopEventFlagsExit)
// This function is executed in the GUI context each time an input event occurs (e.g. the user pressed a key)
static void event_loop_event_flags_app_input_callback(InputEvent* event, void* context) {
furi_assert(context);
EventLoopEventFlagsApp* app = context;
UNUSED(app);
if(event->type == InputTypePress) {
if(event->key == InputKeyOk) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsOk);
} else if(event->key == InputKeyUp) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsUp);
} else if(event->key == InputKeyDown) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsDown);
} else if(event->key == InputKeyLeft) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsLeft);
} else if(event->key == InputKeyRight) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsRight);
} else if(event->key == InputKeyBack) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsBack);
}
} else if(event->type == InputTypeLong) {
if(event->key == InputKeyBack) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsExit);
}
}
}
// This function is executed each time a new event flag is inserted in the input event flag.
static void
event_loop_event_flags_app_event_flags_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopEventFlagsApp* app = context;
furi_assert(object == app->event_flag);
EventLoopEventFlags events =
furi_event_flag_wait(app->event_flag, EVENT_LOOP_EVENT_FLAGS_MASK, FuriFlagWaitAny, 0);
furi_check((events) != 0);
if(events & EventLoopEventFlagsOk) {
FURI_LOG_I(TAG, "Press \"Ok\"");
}
if(events & EventLoopEventFlagsUp) {
FURI_LOG_I(TAG, "Press \"Up\"");
}
if(events & EventLoopEventFlagsDown) {
FURI_LOG_I(TAG, "Press \"Down\"");
}
if(events & EventLoopEventFlagsLeft) {
FURI_LOG_I(TAG, "Press \"Left\"");
}
if(events & EventLoopEventFlagsRight) {
FURI_LOG_I(TAG, "Press \"Right\"");
}
if(events & EventLoopEventFlagsBack) {
FURI_LOG_I(TAG, "Press \"Back\"");
}
if(events & EventLoopEventFlagsExit) {
FURI_LOG_I(TAG, "Exit App");
furi_event_loop_stop(app->event_loop);
}
}
static EventLoopEventFlagsApp* event_loop_event_flags_app_alloc(void) {
EventLoopEventFlagsApp* app = malloc(sizeof(EventLoopEventFlagsApp));
// Create event loop instances.
app->event_loop = furi_event_loop_alloc();
// Create event flag instances.
app->event_flag = furi_event_flag_alloc();
// Create GUI instance.
app->gui = furi_record_open(RECORD_GUI);
app->view_port = view_port_alloc();
// Gain exclusive access to the input events
view_port_input_callback_set(app->view_port, event_loop_event_flags_app_input_callback, app);
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
// Notify the event loop about incoming messages in the event flag
furi_event_loop_subscribe_event_flag(
app->event_loop,
app->event_flag,
FuriEventLoopEventIn | FuriEventLoopEventFlagEdge,
event_loop_event_flags_app_event_flags_callback,
app);
return app;
}
static void event_loop_event_flags_app_free(EventLoopEventFlagsApp* app) {
gui_remove_view_port(app->gui, app->view_port);
furi_record_close(RECORD_GUI);
app->gui = NULL;
// Delete all instances
view_port_free(app->view_port);
app->view_port = NULL;
// IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.
// Failure to do so will result in a crash.
furi_event_loop_unsubscribe(app->event_loop, app->event_flag);
furi_event_flag_free(app->event_flag);
app->event_flag = NULL;
furi_event_loop_free(app->event_loop);
app->event_loop = NULL;
free(app);
}
static void event_loop_event_flags_app_run(EventLoopEventFlagsApp* app) {
FURI_LOG_I(TAG, "Press keys to see them printed here.");
FURI_LOG_I(TAG, "Quickly press different keys to generate events.");
FURI_LOG_I(TAG, "Long press \"Back\" to exit app.");
// Run the application event loop. This call will block until the application is about to exit.
furi_event_loop_run(app->event_loop);
}
/*******************************************************************
* vvv START HERE vvv
*
* The application's entry point - referenced in application.fam
*******************************************************************/
int32_t example_event_loop_event_flags_app(void* arg) {
UNUSED(arg);
EventLoopEventFlagsApp* app = event_loop_event_flags_app_alloc();
event_loop_event_flags_app_run(app);
event_loop_event_flags_app_free(app);
return 0;
}

View file

@ -52,7 +52,7 @@ typedef struct {
*/
// This function is executed each time the data is taken out of the stream buffer. It is used to restart the worker timer.
static bool
static void
event_loop_multi_app_stream_buffer_worker_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiAppWorker* worker = context;
@ -62,8 +62,6 @@ static bool
FURI_LOG_I(TAG, "Data was removed from buffer");
// Restart the timer to generate another block of random data.
furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);
return true;
}
// This function is executed when the worker timer expires. The timer will NOT restart automatically
@ -152,7 +150,7 @@ static void event_loop_multi_app_input_callback(InputEvent* event, void* context
}
// This function is executed each time new data is available in the stream buffer.
static bool
static void
event_loop_multi_app_stream_buffer_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiApp* app = context;
@ -172,12 +170,10 @@ static bool
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
furi_string_free(tmp_str);
return true;
}
// This function is executed each time a new message is inserted in the input queue.
static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
static void event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiApp* app = context;
@ -222,8 +218,6 @@ static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* objec
// Not a long press, just print the key's name.
FURI_LOG_I(TAG, "Short press: %s", input_get_key_name(event.key));
}
return true;
}
// This function is executed each time the countdown timer expires.

View file

@ -59,7 +59,7 @@ static int32_t event_loop_mutex_app_worker_thread(void* context) {
}
// This function is being run each time when the mutex gets released
static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
static void event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMutexApp* app = context;
@ -82,8 +82,6 @@ static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, voi
MUTEX_EVENT_AND_FLAGS,
event_loop_mutex_app_event_callback,
app);
return true;
}
static EventLoopMutexApp* event_loop_mutex_app_alloc(void) {

View file

@ -54,7 +54,7 @@ static int32_t event_loop_stream_buffer_app_worker_thread(void* context) {
}
// This function is being run each time when the number of bytes in the buffer is above its trigger level.
static bool
static void
event_loop_stream_buffer_app_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopStreamBufferApp* app = context;
@ -76,8 +76,6 @@ static bool
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
furi_string_free(tmp_str);
return true;
}
static EventLoopStreamBufferApp* event_loop_stream_buffer_app_alloc(void) {

View file

@ -1,7 +1,13 @@
# Number Input
# Number Input {#example_number_input}
Simple keyboard that limits user inputs to a full number (integer). Useful to enforce correct entries without the need of intense validations after a user input.
Simple keyboard that limits user inputs to a full number (integer). Useful to enforce correct entries without the need for intense validations after a user input.
Definition of min/max values is required. Numbers are of type int32_t. If negative numbers are allowed withing min - max, an additional button is displayed to switch the sign between + and -.
## Source code
It is also possible to define a header text, shown in this example app with the 3 different input options.
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_number_input).
## General principle
Definition of min/max values is required. Numbers are of type int32_t. If negative numbers are allowed within min - max, an additional button is displayed to switch the sign between + and -.
It is also possible to define a header text, as shown in this example app with the 3 different input options.

View file

@ -94,6 +94,11 @@ static void bad_usb_save_settings(BadUsbApp* app) {
furi_record_close(RECORD_STORAGE);
}
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface) {
app->interface = interface;
bad_usb_view_set_interface(app->bad_usb_view, interface);
}
BadUsbApp* bad_usb_app_alloc(char* arg) {
BadUsbApp* app = malloc(sizeof(BadUsbApp));
@ -125,7 +130,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
app->view_dispatcher, BadUsbAppViewWidget, widget_get_view(app->widget));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, BadUsbAppViewPopup, popup_get_view(app->popup));
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
@ -171,9 +180,13 @@ void bad_usb_app_free(BadUsbApp* app) {
bad_usb_view_free(app->bad_usb_view);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWidget);
widget_free(app->widget);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewPopup);
popup_free(app->popup);
// Config menu
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
variable_item_list_free(app->var_item_list);

View file

@ -13,6 +13,7 @@
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/popup.h>
#include "views/bad_usb_view.h"
#include <furi_hal_usb.h>
@ -33,6 +34,7 @@ struct BadUsbApp {
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
Popup* popup;
VariableItemList* var_item_list;
BadUsbAppError error;
@ -46,7 +48,10 @@ struct BadUsbApp {
};
typedef enum {
BadUsbAppViewError,
BadUsbAppViewWidget,
BadUsbAppViewPopup,
BadUsbAppViewWork,
BadUsbAppViewConfig,
} BadUsbAppView;
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);

View file

@ -1,12 +1,17 @@
REM This is BadUSB demo script for ChromeOS by kowalski7cc
REM This is BadUSB demo script for Chrome and ChromeOS by kowalski7cc
REM Exit from Overview
ESC
REM Open a new tab
CTRL t
REM wait for some slower chromebooks
DELAY 1000
REM Make sure we have omnibox focus
CTRL l
DELAY 200
REM Open an empty editable page
DEFAULT_DELAY 50
STRING data:text/html, <html contenteditable autofocus><style>body{font-family:monospace;}
STRING data:text/html, <html contenteditable autofocus><title>Flipper Zero BadUSB Demo</title><style>body{font-family:monospace;}
ENTER
DELAY 500

View file

@ -5,14 +5,22 @@ REM Check the `lsusb` command to know your own devices IDs
REM This is BadUSB demo script for Linux/Gnome
REM Exit from Overview
ESC
DELAY 200
REM Open terminal window
DELAY 1000
ALT F2
DELAY 500
STRING gnome-terminal --maximize
DELAY 500
DELAY 1000
REM Let's guess user terminal, based on (almost) glib order with ptyxis now default in Fedora 41
STRING sh -c "xdg-terminal-exec||kgx||ptyxis||gnome-terminal||mate-terminal||xfce4-terminal||tilix||konsole||xterm"
DELAY 300
ENTER
REM It can take a bit to open the correct terminal
DELAY 1500
REM Make sure we are running in a POSIX-compliant shell
STRING env sh
ENTER
DELAY 750
REM Clear the screen in case some banner was displayed
STRING clear

View file

@ -2,31 +2,13 @@
enum SubmenuIndex {
ConfigIndexKeyboardLayout,
ConfigIndexInterface,
ConfigIndexBleUnpair,
};
const char* const interface_mode_text[2] = {
"USB",
"BLE",
};
void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
BadUsbApp* bad_usb = context;
if(index != ConfigIndexInterface) {
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
}
}
void bad_usb_scene_config_interface_callback(VariableItem* item) {
BadUsbApp* bad_usb = variable_item_get_context(item);
furi_assert(bad_usb);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, interface_mode_text[index]);
bad_usb->interface = index;
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ConfigIndexInterface);
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
}
static void draw_menu(BadUsbApp* bad_usb) {
@ -36,16 +18,7 @@ static void draw_menu(BadUsbApp* bad_usb) {
variable_item_list_add(var_item_list, "Keyboard Layout (global)", 0, NULL, NULL);
VariableItem* item = variable_item_list_add(
var_item_list, "Interface", 2, bad_usb_scene_config_interface_callback, bad_usb);
if(bad_usb->interface == BadUsbHidInterfaceUsb) {
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, interface_mode_text[0]);
} else {
variable_item_set_current_value_index(item, 1);
variable_item_set_current_value_text(item, interface_mode_text[1]);
variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL);
}
variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL);
}
void bad_usb_scene_config_on_enter(void* context) {
@ -68,10 +41,8 @@ bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
consumed = true;
if(event.event == ConfigIndexKeyboardLayout) {
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
} else if(event.event == ConfigIndexInterface) {
draw_menu(bad_usb);
} else if(event.event == ConfigIndexBleUnpair) {
bad_usb_hid_ble_remove_pairing();
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);
} else {
furi_crash("Unknown key type");
}

View file

@ -3,3 +3,5 @@ ADD_SCENE(bad_usb, work, Work)
ADD_SCENE(bad_usb, error, Error)
ADD_SCENE(bad_usb, config, Config)
ADD_SCENE(bad_usb, config_layout, ConfigLayout)
ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)
ADD_SCENE(bad_usb, unpair_done, UnpairDone)

View file

@ -0,0 +1,53 @@
#include "../bad_usb_app_i.h"
void bad_usb_scene_confirm_unpair_widget_callback(
GuiButtonType type,
InputType input_type,
void* context) {
UNUSED(input_type);
SceneManagerEvent event = {.type = SceneManagerEventTypeCustom, .event = type};
bad_usb_scene_confirm_unpair_on_event(context, event);
}
void bad_usb_scene_confirm_unpair_on_enter(void* context) {
BadUsbApp* bad_usb = context;
Widget* widget = bad_usb->widget;
widget_add_button_element(
widget, GuiButtonTypeLeft, "Cancel", bad_usb_scene_confirm_unpair_widget_callback, context);
widget_add_button_element(
widget,
GuiButtonTypeRight,
"Unpair",
bad_usb_scene_confirm_unpair_widget_callback,
context);
widget_add_text_box_element(
widget, 0, 0, 128, 64, AlignCenter, AlignTop, "\e#Unpair the Device?\e#\n", false);
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewWidget);
}
bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) {
BadUsbApp* bad_usb = context;
SceneManager* scene_manager = bad_usb->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone);
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(scene_manager);
}
}
return consumed;
}
void bad_usb_scene_confirm_unpair_on_exit(void* context) {
BadUsbApp* bad_usb = context;
Widget* widget = bad_usb->widget;
widget_reset(widget);
}

View file

@ -43,7 +43,7 @@ void bad_usb_scene_error_on_enter(void* context) {
"Disconnect from\nPC or phone to\nuse this function.");
}
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError);
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWidget);
}
bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {

View file

@ -0,0 +1,39 @@
#include "../bad_usb_app_i.h"
static void bad_usb_scene_unpair_done_popup_callback(void* context) {
BadUsbApp* bad_usb = context;
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);
}
void bad_usb_scene_unpair_done_on_enter(void* context) {
BadUsbApp* bad_usb = context;
Popup* popup = bad_usb->popup;
bad_usb_hid_ble_remove_pairing();
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback);
popup_set_context(popup, bad_usb);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);
}
bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {
BadUsbApp* bad_usb = context;
UNUSED(bad_usb);
UNUSED(event);
bool consumed = false;
return consumed;
}
void bad_usb_scene_unpair_done_on_exit(void* context) {
BadUsbApp* bad_usb = context;
Popup* popup = bad_usb->popup;
UNUSED(popup);
popup_reset(popup);
}

View file

@ -20,14 +20,27 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
bad_usb_script_close(app->bad_usb_script);
app->bad_usb_script = NULL;
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
if(app->interface == BadUsbHidInterfaceBle) {
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
} else {
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
}
}
consumed = true;
} else if(event.event == InputKeyOk) {
bad_usb_script_start_stop(app->bad_usb_script);
consumed = true;
} else if(event.event == InputKeyRight) {
bad_usb_script_pause_resume(app->bad_usb_script);
if(bad_usb_view_is_idle_state(app->bad_usb_view)) {
bad_usb_set_interface(
app,
app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
BadUsbHidInterfaceBle);
bad_usb_script_close(app->bad_usb_script);
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
} else {
bad_usb_script_pause_resume(app->bad_usb_script);
}
consumed = true;
}
} else if(event.type == SceneManagerEventTypeTick) {
@ -39,6 +52,8 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
void bad_usb_scene_work_on_enter(void* context) {
BadUsbApp* app = context;
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);

View file

@ -18,6 +18,7 @@ typedef struct {
BadUsbState state;
bool pause_wait;
uint8_t anim_frame;
BadUsbHidInterface interface;
} BadUsbModel;
static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
@ -40,14 +41,24 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
if(model->interface == BadUsbHidInterfaceBle) {
canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22);
} else {
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
}
BadUsbWorkerState state = model->state.state;
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
(state == BadUsbStateNotConnected)) {
elements_button_center(canvas, "Run");
elements_button_left(canvas, "Config");
if(model->interface == BadUsbHidInterfaceBle) {
elements_button_right(canvas, "USB");
elements_button_left(canvas, "Config");
} else {
elements_button_right(canvas, "BLE");
elements_button_left(canvas, "Layout");
}
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
elements_button_center(canvas, "Stop");
if(!model->pause_wait) {
@ -266,6 +277,10 @@ void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st) {
true);
}
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface) {
with_view_model(bad_usb->view, BadUsbModel * model, { model->interface = interface; }, true);
}
bool bad_usb_view_is_idle_state(BadUsb* bad_usb) {
bool is_idle = false;
with_view_model(

View file

@ -23,4 +23,6 @@ void bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout);
void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st);
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface);
bool bad_usb_view_is_idle_state(BadUsb* bad_usb);

View file

@ -10,5 +10,4 @@ typedef enum {
GpioUsbUartEventConfig,
GpioUsbUartEventConfigSet,
GpioUsbUartEventStop,
} GpioCustomEvent;

View file

@ -3,4 +3,3 @@ ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, usb_uart, UsbUart)
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)
ADD_SCENE(gpio, exit_confirm, ExitConfirm)

View file

@ -1,43 +0,0 @@
#include "gpio_app_i.h"
void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) {
GpioApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void gpio_scene_exit_confirm_on_enter(void* context) {
GpioApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_context(dialog, app);
dialog_ex_set_left_button_text(dialog, "Exit");
dialog_ex_set_right_button_text(dialog, "Stay");
dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop);
dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);
}
bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = scene_manager_previous_scene(app->scene_manager);
if(consumed && event.event == DialogExResultLeft) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventStop);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void gpio_scene_exit_confirm_on_exit(void* context) {
GpioApp* app = context;
// Clean view
dialog_ex_reset(app->dialog);
}

View file

@ -6,12 +6,7 @@ typedef struct {
UsbUartState state;
} SceneUsbUartBridge;
static SceneUsbUartBridge* scene_usb_uart;
typedef enum {
UsbUartSceneStateInitialize,
UsbUartSceneStateKeep,
} UsbUartSceneState;
static SceneUsbUartBridge* scene_usb_uart = NULL;
void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
furi_assert(context);
@ -19,11 +14,21 @@ void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void gpio_scene_usb_uart_dialog_callback(DialogExResult result, void* context) {
GpioApp* app = context;
if(result == DialogExResultLeft) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
scene_usb_uart = NULL;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
} else {
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
}
}
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
UsbUartSceneState state =
scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(state == UsbUartSceneStateInitialize) {
if(!scene_usb_uart) {
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
scene_usb_uart->cfg.vcp_ch = 0;
scene_usb_uart->cfg.uart_ch = 0;
@ -37,7 +42,6 @@ void gpio_scene_usb_uart_on_enter(void* context) {
usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
}
@ -45,19 +49,16 @@ void gpio_scene_usb_uart_on_enter(void* context) {
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioUsbUartEventConfig) {
scene_manager_set_scene_state(
app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateKeep);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
} else if(event.event == GpioUsbUartEventStop) {
scene_manager_set_scene_state(
app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateInitialize);
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
}
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
return true;
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateKeep);
scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm);
DialogEx* dialog = app->dialog;
dialog_ex_set_context(dialog, app);
dialog_ex_set_left_button_text(dialog, "Exit");
dialog_ex_set_right_button_text(dialog, "Stay");
dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop);
dialog_ex_set_result_callback(dialog, gpio_scene_usb_uart_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);
return true;
} else if(event.type == SceneManagerEventTypeTick) {
uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;
@ -75,10 +76,5 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
uint32_t state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart);
if(state == UsbUartSceneStateInitialize) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
}
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
}

View file

@ -991,31 +991,31 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3302 1639 405 423 407 420 410 1234 405 421 409 1210 440 387 432 420 410 417 413 1231 408 1238 412 388 431 422 408 392 438 1233 406 1238 412 389 430 396 434 419 411 390 440 413 406 394 436 391 439 388 431 421 409 417 413 414 405 421 409 392 438 1233 406 421 409 417 413 414 405 395 435 418 412 388 432 421 409 1236 414 387 432 420 410 390 440 388 431 1213 437 1208 431 1239 411 1234 405 1213 437 1208 431 1214 436 1235 415 386 433 420 410 1234 405 422 408 418 412 389 431 396 434 1211 439 388 432 422 408 418 412 1233 406 1238 412 415 404 422 408 1237 413 388 432 422 408 1236 414 1205 434 1210 440 1205 434 392 438 390 440 1231 408 392 438 415 404 396 434 392 438 415 404 422 408 419 411 416 414 412 407 394 436 417 413 414 405 421 409 417 413 1232 407 419 411 390 440 1204 435 418 412 415 415 412 407 419 411 1208 431 421 409 418 412 388 432 421 409 392 438 415 404 395 435 1211 439 388 432 1213 437 416 414 386 433 1212 438 415 404 396 434 392 438 416 414 413 406 420 410 417 413 1231 409 418 412 389 430 1240 410 391 439 1205 434 420 410 390 440 387 432 420 410 417 413
#
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3293 1649 405 396 434 419 411 1207 432 395 435 1236 414 413 406 394 436 417 413 1232 407 1212 438 415 404 396 434 419 411 1234 405 1239 411 417 413 414 405 421 409 419 411 389 430 423 407 393 437 416 414 387 432 394 436 417 413 388 431 421 409 1236 414 387 432 421 409 418 412 414 405 422 408 418 412 415 404 1240 410 391 439 414 405 421 409 419 411 1234 405 1239 411 1208 431 1239 411 1235 404 1214 436 1210 440 1205 434 419 411 416 414 1204 435 418 412 415 404 396 434 393 437 1235 404 396 434 420 410 416 414 1231 408 1210 440 387 432 421 409 1235 415 414 405 395 435 418 412 1232 407 420 410 1208 431 422 408 1237 413 415 404 396 434 419 411 416 414 413 406 420 410 417 413 414 405 421 409 418 412 415 404 422 408 419 411 416 414 413 406 1238 412 415 404 422 408 1237 413 414 405 421 409 418 412 416 414 1231 408 418 412 415 404 422 408 419 411 416 414 413 406 420 410 1235 404 423 407 420 410 1234 405 422 408 1210 440 414 405 421 409 392 438 415 404 422 408 420 410 417 413 1231 408 419 411 416 414 413 406 1237 413 415 404 1239 411 417 413 1232 407 420 410 417 413
#
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3302 1614 440 414 405 421 409 1235 414 413 406 1238 411 415 404 422 408 419 411 1234 405 1240 409 418 412 415 404 422 408 1237 412 1232 407 420 410 417 413 414 405 422 408 419 411 416 414 413 406 420 410 417 413 414 405 421 409 418 412 415 404 1239 410 418 412 415 404 422 408 419 411 416 414 413 406 420 410 1234 405 422 408 419 411 416 414 414 405 1239 411 1207 432 1239 410 1235 414 1230 409 1236 413 1232 407 1237 412 415 415 412 407 1237 412 414 405 422 408 419 411 416 414 1231 408 419 411 416 414 413 406 1238 411 1206 433 421 409 418 412 1206 433 421 409 418 412 1206 433 1238 412 1207 432 1213 436 390 440 1232 407 420 410 417 413 414 405 421 409 418 412 415 404 422 408 419 411 416 414 413 406 421 409 418 412 415 404 422 408 419 411 1233 406 421 409 418 412 1232 407 420 410 418 412 389 430 422 408 1210 440 414 405 395 435 418 412 415 404 423 407 420 410 416 414 414 405 422 408 418 412 415 404 1240 409 1235 414 413 406 420 410 417 413 414 405 422 408 419 411 416 414 1231 408 418 412 415 404 1240 409 1209 440 387 432 1212 437 1234 405 1214 435 1209 440 1204 435
#
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3301 1615 439 415 404 422 408 1210 439 414 405 1239 410 416 414 414 405 395 435 1209 440 1205 434 419 411 417 413 414 405 1212 437 1207 432 422 408 419 411 416 414 414 405 421 409 418 412 415 404 422 408 393 437 416 414 413 406 420 410 417 413 1205 434 420 410 417 413 414 405 421 409 418 412 415 404 422 408 1210 439 414 405 422 408 419 411 417 413 1205 434 1236 413 1206 433 1211 438 1207 432 1212 437 1209 440 1204 435 418 412 415 415 1204 435 418 412 415 404 422 408 419 411 1208 441 412 407 420 410 417 413 1205 434 1237 412 414 405 422 408 1210 439 415 404 396 434 419 411 1207 432 1213 436 417 413 1205 434 420 410 417 413 1231 408 419 411 416 414 413 406 421 409 418 412 414 405 422 408 419 411 415 404 424 406 421 409 418 412 415 404 1239 410 417 413 414 405 1239 410 416 414 413 406 422 408 419 411 1233 406 421 409 418 412 415 404 423 407 419 411 416 414 413 406 1238 411 416 414 413 406 421 409 1235 414 1230 409 418 412 415 415 412 407 420 410 417 413 414 405 422 408 1236 413 413 406 421 409 1235 414 1204 435 1210 439 1206 433 1212 437 1207 432 395 435 1236 413
#
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3295 1646 408 420 410 390 440 1205 434 393 437 1234 405 421 409 419 411 415 415 1230 409 1210 440 414 405 421 409 418 412 1233 406 1212 437 416 414 413 406 394 436 418 412 415 404 422 408 393 437 416 414 413 406 420 410 417 413 414 405 421 409 1236 413 414 405 421 409 418 412 415 404 423 407 419 411 416 414 1231 408 418 412 415 415 412 407 421 409 1235 414 1204 435 1209 441 1205 434 1210 440 1205 434 1212 437 1207 432 395 435 419 411 1233 406 421 409 418 412 415 404 422 408 1238 411 415 404 396 434 419 411 1234 405 1239 410 417 413 414 405 1213 436 417 413 414 405 1213 436 1235 404 1214 435 1209 441 413 406 421 409 418 412 1206 433 394 436 418 412 388 431 422 408 419 411 415 415 386 433 420 410 418 412 414 405 422 408 419 411 389 430 1241 408 418 412 415 404 1240 409 417 413 414 405 395 435 419 411 1233 406 395 435 418 412 415 404 422 408 419 411 416 414 413 406 421 409 1236 413 413 406 394 436 1235 414 1230 409 418 412 415 404 422 408 420 410 417 413 414 405 421 409 1236 413 413 406 420 410 417 413 1232 407 1237 412 416 414 1230 409 1236 413 1205 434 1210 439
#
#
name: Off
type: raw
frequency: 38000
@ -1029,25 +1029,25 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4349 4437 549 1615 551 1614 551 1614 551 1617 549 531 550 530 551 1615 550 531 550 532 549 530 551 531 550 530 551 1615 550 1614 551 531 550 1615 551 529 552 531 550 530 551 533 548 530 551 530 551 1616 549 1615 550 1616 550 1615 550 1614 551 1616 550 1615 551 1615 550 531 550 531 550 530 551 529 552 530 551 530 551 529 552 530 551 531 550 1615 550 532 549 1615 550 1616 550 531 550 531 550 530 551 530 551 529 552 532 549 530 551 530 551 531 550 529 552 531 550 1615 551 530 551 530 551 530 551 531 550 530 551 531 550 530 551 531 550 531 550 531 550 1616 550 1618 547 532 549 529 552 530 551 1615 551 1615 550 5379 4350 4436 550 1616 549 1615 551 1614 552 1615 550 529 552 530 551 1614 552 530 551 529 552 531 550 531 550 531 550 1614 552 1614 551 530 551 1615 550 530 551 530 551 530 551 530 551 531 550 532 549 1616 549 1615 551 1614 552 1615 550 1614 551 1616 550 1614 552 1615 550 529 552 530 551 530 551 530 551 531 550 531 550 530 551 530 551 531 550 1615 550 530 551 1615 550 1615 551 530 551 530 551 530 551 530 551 530 551 530 551 529 552 530 551 531 550 532 549 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 531 550 531 550 531 550 530 551 531 550 1615 551 1615 551 532 549 531 550 531 550 1616 549 1614 552
#
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4350 4438 549 1615 551 1614 552 1616 549 1616 550 530 551 531 550 1615 551 530 551 529 552 531 550 530 551 531 550 1614 551 1616 550 531 550 1616 549 530 551 531 550 530 551 529 552 530 551 531 550 1616 549 1616 550 1616 549 1616 550 1615 551 1614 551 1614 552 1615 551 530 551 531 550 530 551 531 550 531 550 529 552 532 549 531 550 530 551 1613 552 530 551 531 550 529 552 532 549 530 551 530 551 531 550 531 550 530 551 530 551 530 551 531 550 530 551 531 550 531 550 1615 551 529 552 530 551 530 551 530 551 530 551 530 551 530 551 530 551 532 549 531 550 531 550 532 549 531 550 531 550 530 551 530 551 5132 4351 4435 552 1616 550 1615 550 1615 551 1613 553 531 550 530 551 1615 550 530 551 531 550 531 550 530 551 532 549 1616 550 1616 549 530 551 1615 551 530 551 531 550 530 551 530 551 530 551 531 550 1615 551 1615 551 1614 551 1615 550 1615 551 1615 550 1615 550 1616 550 530 551 530 551 531 550 532 549 530 551 530 551 531 550 531 550 531 550 1615 550 530 551 530 551 530 551 529 552 531 550 530 551 531 550 531 550 530 551 530 551 531 550 530 551 530 551 530 551 531 550 1616 550 530 551 529 552 530 551 531 550 532 549 530 551 530 551 529 552 531 550 529 552 530 551 530 551 531 550 531 550 529 552 531 550
#
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4350 4436 550 1617 549 1615 550 1615 550 1617 548 530 551 531 550 1615 551 531 550 531 550 530 551 530 551 531 550 1614 552 1615 550 530 551 1614 551 531 550 531 550 531 550 529 552 532 549 530 551 1617 549 1616 549 1615 551 1619 547 1615 550 1615 550 1616 549 1616 550 530 551 531 550 530 551 530 551 531 550 530 551 529 552 529 552 530 551 1617 548 533 548 1615 551 1613 552 530 551 531 550 531 550 530 551 530 551 532 549 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 531 550 531 550 532 549 531 550 530 551 531 550 533 548 531 550 530 551 1617 548 1616 549 530 551 531 550 532 549 532 549 532 549 5200 4349 4436 550 1615 551 1615 551 1615 550 1616 550 531 550 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 1616 549 1615 551 530 551 1615 551 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 1616 550 1616 550 1615 550 1617 548 1616 549 1616 550 1615 550 531 550 530 551 531 550 531 550 532 549 530 551 531 550 531 550 532 549 1616 550 531 550 1616 550 1615 550 531 550 530 551 531 550 531 550 531 550 531 550 531 550 532 549 532 549 531 550 532 549 531 550 1616 550 531 550 530 551 532 549 532 549 530 551 532 549 531 550 532 549 531 550 1616 549 1617 549 531 550 530 551 531 550 532 549 532 549
#
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4350 4437 547 1618 548 1620 546 1620 546 1619 547 534 547 535 546 1619 547 534 547 536 545 536 545 535 546 535 546 1619 547 1620 545 534 523 1644 546 535 522 559 546 535 546 534 547 535 546 535 545 1620 546 1620 546 1620 546 1619 547 1619 546 1619 547 1620 545 1620 546 535 546 534 547 537 520 558 523 558 547 534 547 536 521 559 522 559 522 1644 546 535 546 535 522 560 545 536 521 559 522 559 522 558 523 559 522 560 521 559 522 559 522 560 521 559 522 561 520 1644 521 1645 520 559 522 559 522 559 522 559 522 559 522 560 521 560 521 560 521 561 520 559 522 560 521 559 522 559 522 559 522 1644 522 559 522 5341 4349 4439 520 1645 521 1645 521 1646 519 1645 521 560 521 561 520 1645 521 560 521 560 521 559 522 560 521 561 520 1646 520 1645 521 561 520 1645 521 561 520 560 521 560 521 560 521 560 521 561 520 1644 522 1644 522 1645 520 1645 521 1645 521 1645 520 1646 520 1644 522 561 520 560 521 560 521 561 520 560 521 561 520 561 520 561 520 560 521 1646 520 562 519 561 520 561 520 562 519 560 521 560 521 561 520 561 520 560 521 560 521 561 520 560 521 560 521 562 519 1646 520 1645 521 561 520 561 520 561 520 560 521 560 521 561 520 560 521 559 522 560 521 561 520 561 520 560 521 562 519 559 522 1645 521 561 520
#
#
name: Heat_lo
type: raw
frequency: 38000
@ -1061,33 +1061,79 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4387 4398 547 1609 547 530 547 1610 547 1611 545 530 547 530 547 1608 547 530 548 530 547 1611 545 532 546 532 547 1609 547 1610 547 531 547 1608 548 530 547 530 548 530 547 1610 546 1609 547 1610 547 1609 547 1609 547 1611 545 1609 548 1610 546 530 547 530 548 529 549 531 547 531 546 531 547 1608 548 1610 547 1608 548 533 545 1608 548 532 546 532 546 1611 545 532 547 532 545 530 548 1608 547 530 549 1608 547 1609 548 5203 4386 4398 547 1609 546 530 547 1609 546 1607 548 531 547 531 547 1609 547 530 548 531 547 1609 547 531 547 531 547 1608 547 1613 544 531 546 1609 547 531 547 531 547 532 546 1609 547 1609 546 1609 547 1609 547 1608 547 1608 548 1608 548 1609 547 530 547 530 547 530 547 532 546 530 547 530 548 1610 546 1608 547 1609 547 530 547 1609 547 530 547 530 548 1609 546 530 548 530 547 532 546 1610 546 531 546 1608 548 1608 548
#
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4388 4398 547 1608 548 531 546 1610 546 1609 547 530 547 529 548 1608 548 532 547 530 548 1612 544 529 549 530 548 1608 547 1609 547 531 546 1608 548 1607 549 529 549 1608 549 1609 548 1608 548 1608 548 1611 545 1608 548 530 548 1609 547 531 547 530 548 530 548 531 547 529 549 530 548 530 547 531 547 530 548 530 547 529 549 530 548 532 547 530 548 1609 547 1610 547 1608 548 1609 547 1608 548 1608 548 1608 548 1608 548 5203 4388 4396 549 1609 547 529 549 1610 546 1608 548 529 549 530 547 1609 547 530 548 529 549 1608 548 531 547 532 546 1609 547 1609 547 530 548 1609 548 1609 548 529 548 1608 548 1609 548 1609 547 1609 547 1608 548 1609 547 532 546 1608 548 531 548 531 548 530 548 530 548 531 547 530 548 531 548 531 547 530 548 530 548 530 548 531 547 529 549 529 549 1609 548 1608 548 1609 547 1608 548 1608 548 1608 548 1607 549 1607 549
#
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4384 4400 572 1585 571 505 572 1583 573 1584 572 508 570 503 575 1584 572 505 572 506 572 1583 573 504 573 506 571 1586 570 1585 572 532 546 1586 570 1585 571 506 571 1585 571 1583 573 1586 570 1583 573 1584 572 1589 569 505 572 1585 571 506 571 506 573 506 572 505 573 532 545 504 574 509 570 1611 545 506 572 1582 574 506 572 507 571 507 571 507 570 1584 572 507 571 1587 569 506 572 1584 572 1585 571 1583 573 1612 544 5179 4386 4400 570 1584 572 507 571 1583 572 1585 571 506 572 506 572 1584 572 505 572 504 574 1584 572 507 571 504 574 1583 573 1585 572 507 571 1584 572 1610 545 508 571 1587 569 1583 573 1583 573 1585 571 1585 572 1585 572 505 572 1584 572 505 573 507 572 506 571 504 574 505 573 505 574 508 571 1585 571 507 571 1585 571 506 571 506 572 504 574 505 572 1586 570 507 571 1586 570 505 573 1584 572 1585 571 1587 569 1584 573
#
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4386 4398 575 1582 574 503 575 1583 573 1582 574 505 573 504 574 1582 574 506 572 508 570 1583 573 504 574 505 573 1583 573 1584 573 505 573 1582 575 1583 574 504 574 1582 574 1583 573 1583 573 1583 573 1585 571 1586 572 504 573 1584 572 504 573 505 573 505 573 505 573 504 573 506 571 1583 574 505 573 1583 573 1583 573 1584 572 1583 573 505 572 505 573 504 574 1583 574 505 573 505 573 504 574 505 572 1584 572 1584 573 5178 4387 4400 571 1583 573 504 574 1584 572 1584 572 507 572 504 574 1582 574 505 572 505 573 1583 573 504 574 504 574 1582 574 1584 573 503 574 1583 573 1582 574 505 573 1583 573 1582 575 1583 573 1610 546 1584 572 1583 573 505 573 1610 546 506 572 505 573 504 574 504 574 505 573 505 573 1584 573 505 573 1582 574 1584 572 1583 573 1583 573 504 574 503 575 504 574 1585 571 507 571 504 573 506 572 505 572 1584 572 1585 571
#
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4388 4399 547 1608 548 530 548 1610 547 1610 547 529 549 529 548 1608 548 530 548 530 548 1607 549 533 545 531 548 1608 548 1610 546 531 547 1609 547 1608 548 529 549 1609 547 1609 547 1609 547 1609 547 1609 547 1608 548 529 548 1638 519 530 548 530 548 530 548 529 550 528 549 530 548 530 548 1608 548 530 548 1609 548 1610 547 1609 547 531 546 529 549 1608 548 530 548 1609 548 530 548 529 548 530 548 1609 548 1609 548 5205 4387 4398 547 1609 548 531 546 1609 547 1609 547 530 548 531 546 1609 547 531 548 530 573 1583 573 507 571 506 572 1583 573 1582 574 504 574 1581 575 1582 574 506 572 1583 574 1583 573 1583 573 1585 571 1584 572 1585 570 507 571 1582 574 505 574 532 545 505 573 505 572 506 571 505 573 505 573 1584 572 506 572 1583 573 1584 572 1583 573 505 572 504 573 1583 573 505 573 1586 571 506 572 505 573 507 572 1583 573 1584 572
#
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4388 4399 572 1583 573 532 546 1585 571 1583 574 503 575 505 573 1584 572 504 574 505 573 1584 573 506 572 504 573 1584 573 1584 572 505 574 1611 545 506 573 1583 573 1585 571 1586 545 1609 547 531 547 1611 545 1608 548 1607 549 530 548 529 548 531 548 532 546 1610 546 533 545 530 547 1609 547 1610 547 1609 547 533 545 529 548 530 548 530 547 531 546 530 547 530 548 533 544 1608 548 1608 548 1610 546 1606 550 1609 547 5203 4388 4397 548 1609 547 531 547 1608 548 1608 548 530 548 530 548 1608 548 531 547 531 547 1610 546 531 547 530 548 1609 547 1611 546 532 547 1609 547 531 547 1608 548 1610 546 1609 547 1608 548 530 547 1609 547 1608 548 1609 547 531 546 530 548 530 547 530 547 1608 548 532 547 534 545 1608 548 1608 548 1609 547 530 548 531 547 531 547 532 546 531 546 531 547 532 546 530 548 1608 547 1608 548 1610 546 1608 548 1608 548
data: 4388 4399 572 1583 573 532 546 1585 571 1583 574 503 575 505 573 1584 572 504 574 505 573 1584 573 506 572 504 573 1584 573 1584 572 505 574 1611 545 506 573 1583 573 1585 571 1586 545 1609 547 531 547 1611 545 1608 548 1607 549 530 548 529 548 531 548 532 546 1610 546 533 545 530 547 1609 547 1610 547 1609 547 533 545 529 548 530 548 530 547 531 546 530 547 530 548 533 544 1608 548 1608 548 1610 546 1606 550 1609 547 5203 4388 4397 548 1609 547 531 547 1608 548 1608 548 530 548 530 548 1608 548 531 547 531 547 1610 546 531 547 530 548 1609 547 1611 546 532 547 1609 547 531 547 1608 548 1610 546 1609 547 1608 548 530 547 1609 547 1608 548 1609 547 531 546 530 548 530 547 530 547 1608 548 532 547 534 545 1608 548 1608 548 1609 547 530 548 531 547 531 547 532 546 531 546 531 547 532 546 530 548 1608 547 1608 548 1610 546 1608 548 1608 548
#
# Model: Panasonic CS-E9HKR
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3466 1748 445 367 502 1292 444 422 444 420 445 422 443 420 445 421 445 420 445 363 503 420 445 422 444 420 445 424 445 1292 445 421 445 451 414 421 444 421 444 421 445 422 443 423 446 1292 443 1292 445 1292 445 421 497 372 444 1293 445 420 445 421 444 421 444 421 445 421 444 421 444 420 445 422 444 420 445 421 444 421 444 421 445 422 443 356 509 420 445 421 445 429 436 421 444 421 444 414 452 420 445 421 444 421 444 423 443 421 444 421 444 421 444 423 443 420 445 424 445 1291 444 1294 444 421 444 421 444 421 444 422 444 423 445 9989 3463 1752 444 424 445 1293 443 422 444 392 473 421 444 416 449 423 443 421 444 421 444 421 444 423 443 422 443 425 444 1292 444 423 443 422 443 419 446 421 444 423 443 421 444 426 443 1292 444 1294 443 1294 443 421 444 425 444 1294 444 422 443 421 444 422 443 424 442 421 444 421 444 421 444 423 444 421 444 422 443 421 444 423 443 425 444 1293 444 421 444 421 444 1293 444 423 446 1292 445 321 546 421 444 421 444 385 480 423 443 425 444 1291 445 1293 443 422 445 422 443 421 444 421 496 370 444 422 443 421 496 370 443 421 444 1291 497 1239 444 1284 504 1237 447 1293 444 425 444 1293 496 371 494 1241 495 370 496 369 497 297 517 421 496 370 495 369 496 370 496 370 495 370 495 369 496 372 494 370 495 369 496 370 495 370 496 370 495 373 495 1240 495 1241 495 1240 497 369 496 371 494 371 443 422 496 369 496 368 497 370 496 369 496 372 497 1239 496 1241 496 1241 496 370 495 310 555 371 495 371 494 368 497 370 495 370 496 361 504 370 495 369 496 372 494 372 494 369 496 371 494 371 495 373 495 1240 497 370 495 371 495 370 495 369 496 370 495 370 495 1242 495 373 492 370 495 366 500 369 496 371 495 370 495 371 495 369 496 371 494 370 495 371 495 371 494 371 494 370 495 372 494 380 488 1241 496 374 495 1241 495 1240 495 1240 495 1241 495 1242 494 1244 494
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3467 1751 442 425 444 1294 443 424 442 422 443 423 442 422 443 422 444 421 444 421 444 424 441 423 443 421 444 425 444 1294 443 424 442 423 442 422 443 425 440 423 443 423 442 427 442 1292 443 1322 415 1293 444 422 443 425 443 1295 443 424 441 421 444 423 442 424 442 425 440 423 442 423 442 424 442 292 573 421 444 423 442 422 444 422 443 422 443 423 442 425 441 423 442 425 440 421 444 424 442 422 443 422 443 422 443 424 442 422 443 422 443 422 443 424 442 421 444 426 443 1292 443 1294 444 423 442 422 443 423 442 424 442 426 442 9989 3465 1753 442 425 444 1292 445 424 442 424 441 421 444 422 443 422 444 423 442 423 442 352 513 423 443 423 442 425 443 1296 441 423 443 422 443 422 443 424 441 423 443 422 443 427 442 1293 442 1322 415 1293 444 421 444 425 443 1294 444 422 443 422 443 421 444 425 441 423 442 422 443 421 444 423 443 421 444 422 443 422 443 424 442 425 444 1261 475 422 443 422 443 1292 444 1293 442 1295 441 424 442 422 443 422 443 427 442 1322 415 1292 444 1292 444 1294 442 423 443 422 443 421 444 422 443 423 443 421 444 421 444 423 442 421 444 1291 444 1293 443 1294 442 1195 541 1293 444 425 443 1293 443 422 444 1322 414 421 444 422 443 423 443 422 443 423 442 422 443 425 441 422 443 422 443 423 442 423 443 422 443 422 443 421 444 423 444 421 444 424 444 1292 444 1293 444 1294 442 422 443 421 444 423 443 422 443 421 444 422 443 424 442 421 444 426 443 1291 444 1293 443 1293 444 422 443 421 444 422 444 421 444 422 443 421 444 423 443 423 442 421 444 422 443 423 444 421 444 421 444 421 444 422 444 425 444 1294 443 422 443 423 444 424 441 422 443 422 443 422 443 1294 443 422 443 421 444 424 442 421 444 413 452 421 444 423 443 421 444 422 443 422 443 422 444 421 444 422 443 422 443 423 443 426 443 1294 442 421 444 423 442 1293 443 1293 443 421 444 422 444 362 505
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3467 1751 444 424 444 1291 446 422 444 420 445 420 445 419 446 421 445 378 487 421 444 420 445 420 446 421 444 423 446 1321 415 451 415 420 445 420 445 419 446 421 445 419 446 422 447 1290 445 1291 445 1291 446 420 445 424 445 1293 445 420 445 420 445 420 445 421 445 420 445 420 445 419 446 421 445 420 445 421 444 420 445 421 445 420 445 420 445 420 445 421 445 419 446 420 445 420 445 420 446 420 445 421 444 419 446 422 444 420 445 420 445 421 444 421 445 421 444 424 445 1290 445 1292 446 420 445 421 444 420 445 423 443 424 444 9988 3465 1751 446 423 445 1291 446 421 445 420 445 420 445 420 445 422 444 420 445 420 445 421 444 421 446 421 444 424 445 1291 446 421 445 421 444 420 445 420 445 420 446 421 444 423 446 1291 444 1293 444 1292 445 420 445 423 446 1294 444 419 446 420 445 421 444 422 444 420 445 421 444 420 445 421 445 421 444 420 445 420 445 422 444 421 444 420 445 420 445 421 444 1320 415 1291 445 1292 444 422 444 420 445 419 446 420 445 421 445 421 444 424 445 1291 446 421 445 420 445 420 445 421 444 421 445 421 444 421 444 420 445 420 445 1291 445 1292 443 1290 445 1291 446 1292 445 424 444 1293 444 421 444 1292 445 422 443 421 444 381 485 420 445 420 445 420 445 422 444 421 444 420 445 421 444 421 445 421 444 421 444 420 445 421 445 421 444 424 445 1291 444 1292 445 1291 446 420 445 419 446 422 444 421 444 420 445 420 445 421 445 420 445 423 446 1291 444 1291 446 1292 445 420 445 420 445 422 444 420 445 420 445 420 445 422 444 421 444 420 445 419 446 420 446 420 445 419 446 419 446 421 445 423 445 1293 444 419 446 421 445 420 445 419 446 420 445 420 445 1292 445 420 445 420 445 421 445 420 445 419 446 419 446 420 446 419 446 420 445 420 445 420 446 420 445 420 445 420 445 421 445 419 446 420 445 423 446 1292 445 1290 445 1290 445 1292 444 1291 445 1293 445
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3463 1751 443 424 445 1293 443 422 444 420 445 421 444 421 444 422 444 421 444 421 444 421 444 353 513 422 443 425 444 1293 444 422 444 421 444 420 445 421 444 422 444 421 444 425 444 1291 444 1294 443 1293 444 421 444 424 445 1294 444 420 445 421 444 416 449 422 444 421 444 421 444 421 444 422 444 420 445 421 444 422 443 422 444 421 444 421 444 420 445 373 493 420 445 421 444 420 445 422 444 420 445 422 443 421 444 422 444 421 444 421 444 422 443 421 445 421 444 424 445 1291 444 1294 444 421 444 420 445 420 445 422 444 424 444 9991 3463 1751 445 425 444 1293 444 422 444 421 444 420 445 421 444 423 443 421 444 421 444 421 444 422 444 421 444 424 445 1293 444 422 444 420 445 422 443 421 444 424 442 421 444 425 444 1292 443 1292 445 1292 445 421 444 424 445 1293 445 421 444 421 444 421 444 422 444 421 444 421 444 422 443 422 444 423 442 421 444 423 442 423 443 425 443 1293 444 423 442 421 444 1292 444 422 443 425 443 1295 443 421 444 368 549 373 443 1293 496 1240 495 1240 495 1241 496 371 495 370 495 370 495 370 495 371 495 370 495 369 496 371 494 371 494 1240 442 1293 495 1240 496 1241 496 1240 497 373 496 1241 496 371 494 1184 553 370 495 370 495 370 496 370 443 423 494 370 443 424 495 370 442 423 442 422 443 424 442 423 442 423 442 425 440 424 442 424 441 427 442 1294 441 1294 443 1294 443 424 441 423 442 425 442 421 444 423 442 424 441 425 441 424 441 426 443 1293 442 1295 442 1295 441 424 441 394 471 425 441 423 442 423 442 424 441 424 442 424 441 423 442 423 442 424 442 423 442 424 441 423 442 424 442 428 440 1295 442 424 441 425 441 424 441 424 441 423 442 424 441 1294 443 424 441 424 441 424 442 423 442 423 442 423 442 425 441 424 441 424 441 424 441 424 442 424 441 449 416 423 442 424 442 427 442 1295 442 424 441 424 441 1295 442 427 442 1295 441 425 441 426 441
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3461 1753 439 429 440 1298 439 427 439 426 439 426 439 426 439 427 439 426 439 426 439 426 439 427 439 426 439 430 438 1298 438 428 438 426 439 427 438 427 438 427 439 427 438 430 414 1321 439 1299 413 1323 414 451 414 455 414 1324 414 452 413 451 414 453 413 436 430 452 413 452 413 451 414 453 413 452 413 451 414 452 413 454 412 452 413 452 413 452 413 453 413 452 413 452 413 452 413 454 413 453 412 452 413 452 413 453 413 452 413 452 413 452 413 453 413 452 413 456 412 1323 412 1325 413 452 413 453 412 453 412 454 412 455 390 10044 3438 1779 412 457 412 1324 413 454 412 453 412 453 412 453 412 454 412 453 412 453 412 454 411 455 411 453 412 457 389 1347 390 477 389 476 389 476 389 476 389 402 465 475 390 479 390 1346 389 1348 389 1347 390 476 389 479 390 1348 390 476 389 476 389 476 389 477 389 475 390 476 389 476 389 477 389 476 389 476 389 476 389 478 388 480 389 1347 390 475 390 476 389 1347 390 476 389 479 389 1349 389 476 389 476 389 476 389 477 389 476 389 479 390 1348 389 477 389 476 389 476 389 476 389 476 390 476 389 476 389 476 389 476 389 1346 389 1347 389 1346 389 1347 390 1346 390 479 390 1347 390 476 389 1374 363 474 415 454 388 480 386 475 414 454 387 474 391 476 414 455 387 473 392 476 414 453 389 476 389 475 390 475 390 476 390 476 390 479 389 1346 389 1347 390 1348 389 476 389 476 389 477 389 476 389 476 389 475 390 477 389 440 425 479 389 1345 390 1347 390 1347 390 476 389 475 390 477 389 476 389 476 389 476 389 477 389 476 389 476 389 475 390 477 389 476 389 476 389 476 389 477 389 479 390 1347 390 476 389 477 389 476 389 476 389 476 389 476 389 1347 390 476 389 476 389 477 389 503 362 476 389 476 389 477 389 475 390 476 389 476 389 477 389 476 389 476 389 476 389 477 389 479 389 1347 390 479 390 1347 390 1348 389 476 389 476 389 477 389 477 390
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3489 1725 493 375 493 1167 569 375 491 373 492 372 493 374 492 373 493 372 493 373 442 423 492 375 493 372 492 378 490 1245 443 423 493 374 491 375 490 374 442 424 492 374 491 379 440 1293 492 1245 443 1294 442 423 491 379 441 1296 491 375 441 423 442 423 442 425 441 423 442 425 440 424 441 425 441 424 441 421 444 424 441 425 441 423 442 423 442 423 442 453 413 424 442 451 414 424 441 426 440 424 441 424 441 424 441 425 441 424 441 424 441 424 441 426 440 425 440 427 442 1322 413 1296 442 424 441 423 442 424 441 424 442 427 441 9994 3463 1755 440 428 441 1296 441 426 440 425 440 426 439 425 440 426 440 423 442 425 440 425 440 372 494 425 440 428 440 1297 440 426 440 425 440 426 439 426 439 427 439 425 440 429 439 1297 439 1297 439 1297 440 426 439 429 440 1299 439 426 439 426 439 428 437 427 439 426 439 426 439 427 438 427 439 427 438 427 438 427 438 429 437 427 414 451 414 451 414 418 447 1323 414 452 413 455 414 1325 413 452 413 452 413 452 413 453 413 452 413 456 413 1324 413 453 413 452 413 453 412 452 413 454 412 453 412 453 412 453 412 452 413 1324 412 1323 412 1324 412 1324 412 1325 412 456 413 1324 412 404 461 1325 412 453 412 453 412 454 412 453 412 453 412 454 411 455 411 453 412 453 412 453 412 454 412 453 412 453 412 453 412 454 413 453 412 457 412 1323 413 1324 413 1324 412 453 412 453 412 454 413 453 412 453 412 453 412 454 412 452 413 456 412 1323 413 1324 413 1323 414 453 412 452 413 453 413 452 413 452 413 452 413 453 413 450 415 452 413 452 413 453 413 452 413 451 439 427 438 428 439 430 439 1298 439 426 439 428 438 426 439 428 437 426 439 427 438 1298 439 427 438 426 439 427 439 426 439 426 439 425 440 427 440 426 439 426 439 426 439 425 441 426 439 425 440 425 440 427 439 426 439 425 440 429 439 1297 440 1297 440 425 440 425 440 426 441 427 440
#
# Model: Maytag M6X06F2A
#
name: Off
type: parsed
protocol: NEC
address: 20 00 00 00
command: 02 00 00 00

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
#Last Updated 18th Feb, 2024
#Last Checked 18th Feb, 2024
# Last Updated 5th Oct, 2024
# Last Checked 5th Oct, 2024
#
name: Power
type: raw
@ -1743,7 +1743,7 @@ type: parsed
protocol: NEC
address: 30 00 00 00
command: 86 00 00 00
#
#
name: Mode
type: parsed
protocol: NEC
@ -1989,115 +1989,115 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8993 4485 589 1651 589 529 590 529 591 530 590 530 589 530 590 531 589 530 589 531 589 1649 590 1650 589 1649 590 1650 589 1651 588 1649 590 1650 589 1654 585 1650 589 1651 588 1650 590 533 586 531 588 532 588 530 590 530 590 532 587 531 588 530 589 1650 589 1650 589 1652 587 1651 588 1651 588 1650 589 1650 589 1652 587 530 590 530 589 530 589 531 588 531 588 531 588 530 589 531 588 1651 588 1650 590 1651 589 1651 589
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2280 776 785 1565 783 796 782 790 783 1549 783 810 752 805 752 800 752 858 752 830 752 826 776 797 775 793 774 789 773 810 747 805 747 102605 2223 832 752 1595 753 825 752 820 752 1581 752 811 751 806 751 802 750 860 750 833 775 804 773 799 773 795 773 790 773 785 772 780 773
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4612 4435 543 1461 544 1460 545 1461 544 1460 543 1462 518 1487 518 2441 516 1513 491 1513 492 2465 492 1513 516 2441 516 1488 516 1489 515 2443 514 1492 514 1485 4580 4467 513 1492 513 1492 513 1492 513 1491 514 1492 513 1492 513 2445 513 1492 513 1492 513 2445 513 1492 513 2445 513 1492 513 1492 513 2445 513 1493 513 14064 9205 2275 513
#
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4611 4435 544 1460 545 1460 546 1459 546 1460 519 1487 517 1487 518 2440 518 1488 517 1488 516 1512 517 2440 517 1488 516 1488 516 1489 515 2444 513 2445 514 1485 4581 4467 513 1492 513 1492 513 1492 513 1492 513 1492 513 1492 513 2444 514 1492 513 1491 514 1492 513 2445 513 1492 513 1492 513 1492 513 2444 513 2446 513 14066 9203 2275 513
#
#
name: Speed_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4611 4434 545 1460 545 1459 546 1457 548 1458 521 1485 520 1485 545 2413 544 1462 542 2439 518 1487 517 1489 515 2444 512 1493 512 1493 512 1493 512 2446 512 1486 4581 4470 512 1493 511 1493 512 1493 512 1493 512 1493 512 1493 512 2446 511 1493 511 2446 512 1493 512 1493 512 2446 512 1493 512 1494 511 1493 512 2447 512 14071 9203 2279 511
#
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4608 4436 545 1458 547 1458 547 1458 547 1458 521 1484 521 1485 520 2439 544 1461 544 2440 518 1487 517 2441 516 1489 515 2444 513 1493 512 1492 513 1494 512 1486 4577 4471 512 1493 512 1493 512 1493 512 1492 513 1493 512 1493 512 2446 512 1493 512 2446 512 1493 512 2446 512 1493 512 2446 512 1493 512 1493 512 1494 512 14051 9198 2279 512
#
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4605 4437 544 1460 545 1459 546 1458 546 1459 520 1485 520 1486 545 2414 543 1487 517 1488 515 1489 515 1491 513 2446 512 2446 511 2446 512 1493 512 1494 511 1487 4573 4472 511 1493 511 1493 511 1493 511 1493 512 1494 510 1494 511 2446 511 1493 511 1494 511 1494 510 1493 511 2447 511 2446 511 2446 511 1494 511 1495 511 14064 9191 2281 510
#
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4617 4436 544 1459 545 1458 545 1458 546 1458 520 1485 519 1485 545 2413 544 1462 542 1486 517 2440 515 2442 513 1492 512 1492 513 2444 512 1492 512 1493 513 1485 4582 4469 512 1493 512 1492 512 1492 512 1493 511 1493 512 1492 512 2445 511 1493 511 1493 511 2445 512 2446 511 1493 511 1493 511 2445 511 1493 511 1494 511 14048 9216 2280 512
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9220 4459 632 565 628 539 628 539 627 540 626 542 624 545 622 548 619 571 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 1652 596 571 596 571 596 572 595 1652 596 571 596 571 596 571 596 572 595 1652 596 1652 596 1653 595 571 596 1652 596 39508 9197 2229 595 96159 9231 2229 596
#
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9244 4432 633 564 629 538 630 538 629 539 627 540 626 542 624 570 598 547 620 1651 597 1651 597 1651 597 1651 597 1651 597 1651 597 1651 598 1651 597 1651 597 1651 597 1651 597 570 597 570 597 570 597 570 597 570 597 570 597 570 597 570 598 1651 597 1651 597 1651 597 1651 597 1652 596 39509 9207 2227 597
#
#
name: Speed_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9223 4461 632 565 628 540 627 540 626 541 625 542 624 545 622 547 621 571 597 1629 620 1652 597 1653 595 1653 596 1653 596 1653 596 1653 596 1653 596 572 596 572 595 1653 596 1653 595 572 595 572 596 572 595 572 595 1653 596 1653 596 572 595 572 595 1653 596 1653 595 1653 596 1653 595 39519 9204 2229 596
#
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9227 4459 634 564 629 539 629 538 629 539 627 541 625 543 624 570 598 571 597 1652 597 1652 597 1652 597 1652 597 1652 597 1652 597 1652 597 1652 597 1652 597 571 597 571 596 1652 597 571 596 571 597 571 596 571 597 571 596 1652 597 1652 597 571 597 1652 597 1652 597 1653 596 1653 596 39523 9207 2229 596
#
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9223 4459 633 564 629 538 629 538 628 539 627 541 624 544 622 570 597 570 597 1651 597 1651 597 1651 597 1651 598 1651 597 1651 597 1651 597 1651 597 570 597 1651 597 1651 597 1651 597 1651 597 570 598 1651 597 570 597 1651 597 570 597 570 597 570 597 570 597 1652 596 570 597 1652 596 39519 9202 2226 597
#
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9241 4434 633 565 602 565 628 539 628 539 627 540 626 542 624 545 622 547 620 1628 620 1629 619 1629 619 1652 596 1653 595 1629 619 1652 596 1653 595 571 596 1653 595 571 596 571 596 572 595 571 596 1653 595 572 595 1653 595 571 596 1653 595 1653 595 1653 595 1653 596 572 595 1653 595 39519 9226 2229 597
#
#
name: Power
type: raw
frequency: 40000
frequency: 38000
duty_cycle: 0.4
data: 20592 6864 2288 9152 11440 2288 2288 6864 2288 2288 2288 13728
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1328 375 1304 375 460 1196 1327 379 1299 379 430 1221 459 1221 458 1222 483 1197 483 1198 481 1222 1299 7068 1297 382 1297 382 453 1227 1296 382 1297 382 453 1227 453 1228 452 1228 452 1227 453 1228 452 1228 1296 7322 1295 383 1296 383 452 1228 1296 383 1296 383 452 1228 452 1228 452 1228 452 1228 452 1228 452 1228 1296 7070 1295 383 1296 384 451 1228 1296 383 1296 383 452 1228 452 1229 451 1228 451 1229 451 1228 451 1229 1295
#
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1327 373 1306 377 459 1194 1329 373 1305 377 1276 402 433 1219 486 1194 486 1195 484 1219 1302 379 1299 7067 1299 380 1299 381 455 1224 1299 381 1298 381 1298 381 455 1225 455 1225 455 1225 455 1225 1298 381 1298 7344 1298 381 1299 381 455 1225 1298 381 1298 381 1298 381 455 1225 455 1225 455 1225 455 1226 1298 381 1298 7069 1298 382 1298 382 454 1225 1298 382 1298 382 1298 382 454 1226 454 1226 454 1226 454 1226 1297 382 1297
#
#
name: Speed_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1330 377 1302 377 459 1193 1330 376 1303 376 1277 401 435 1218 487 1194 486 1194 1329 377 1299 380 456 7911 1299 380 1299 380 456 1225 1299 380 1299 380 1299 380 456 1224 456 1225 455 1225 1298 381 1299 380 456 8149 1299 381 1298 381 455 1225 1299 380 1299 381 1299 381 455 1225 455 1225 455 1226 1298 381 1299 381 455 7913 1298 381 1299 381 455 1226 1298 381 1299 381 1298 382 454 1225 455 1226 454 1226 1298 382 1298 381 455
#
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1329 373 1306 377 459 1193 1330 377 1302 377 1277 402 434 1219 485 1195 1329 377 1300 379 456 1224 455 7911 1299 381 1298 381 455 1224 1299 380 1299 381 1298 381 455 1225 455 1225 1299 380 1299 381 455 1225 455 8165 1298 381 1299 381 455 1225 1299 381 1299 381 1298 381 455 1225 455 1226 1298 381 1299 381 455 1225 455 7913 1298 381 1298 381 455 1226 1297 381 1298 382 1297 382 454 1226 454 1226 1297 382 1298 382 454 1226 454
#
#
name: Timer
type: raw
frequency: 38000
@ -2139,69 +2139,393 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2221 844 752 1600 758 1591 757 820 756 816 749 817 748 813 752 804 751 1632 758 1596 752 1597 751 1594 754 1585 752 1582 756 806 749 808 757 102464 2224 841 756 1597 751 1599 749 828 748 824 752 815 750 811 754 802 753 1629 750 1604 754 1595 753 1591 757 1583 755 1579 759 804 751 806 749
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1261 425 1261 425 444 1241 1261 425 1261 454 414 1241 445 1240 445 1241 444 1241 444 1242 443 1239 1263 7168 1261 424 1262 425 444 1241 1261 424 1261 425 444 1242 444 1240 445 1241 444 1240 445 1240 445 1241 1261
#
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1261 424 1261 424 445 1241 1261 425 1260 424 445 1242 444 1240 445 1240 445 1239 446 1241 1261 424 445 7984 1261 424 1261 424 445 1241 1261 424 1261 424 445 1240 446 1240 445 1240 445 1241 444 1240 1262 425 444 7983 1262 424 1261 424 445 1239 1263 424 1261 424 445 1241 445 1240 445 1240 445 1239 446 1240 1262 425 444 7983 1262 424 1261 425 444 1239 1263 425 1260 425 443 1241 445 1241 444 1241 444 1241 445 1240 1262 424 445 7982 1263 425 1260 424 445 1240 1262 424 1261 424 445 1240 446 1241 444 1241 444 1239 446 1239 1263 425 444 7984 1261 424 1261 424 444 1240 1262 425 1260 424 445 1240 445 1241 444 1240 445 1240 445 1241 1261 424 444 7984 1261 425 1261 424 444 1241 1261 424 1261 425 444 1240 445 1240 445 1240 445 1240 445 1240 1262 424 445 7984 1261 425 1260 425 444 1240 1262 425 1260 425 444 1241 445 1240 445 1240 445 1240 445 1240 1262 425 444 7984 1261 425 1260 426 443 1241 1261 425 1260 426 442 1240 446 1241 444 1241 444 1242 443 1240 1262 426 443 7985 1260 426 1259 426 442 1243 1259 426 1259 426 442 1241 445 1241 444 1240 445 1241 444 1241 1261 425 443 7984 1261 425 1260 425 443 1242 1261 426 1259 426 442 1242 444 1241 444 1241 444 1241 444 1241 1261 425 443 7986 1260 425 1260 425 444 1241 1261 426 1259 427 442 1242 444 1240 445 1241 444 1240 445 1240 1262 425 444
#
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1261 425 1260 424 445 1241 1261 426 1260 424 445 1241 445 1240 1262 426 443 1240 446 1240 445 1241 444 7985 1260 424 1261 426 443 1241 1261 425 1260 426 443 1240 446 1240 1261 425 444 1241 445 1241 444 1241 444
#
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1261 425 1260 426 442 1241 1262 426 1259 425 444 1241 445 1241 444 1240 1262 425 443 1241 445 1241 444 7983 1262 425 1260 425 444 1241 1261 425 1260 425 444 1241 445 1240 445 1240 1262 425 444 1240 446 1240 445 7985 1260 424 1261 425 444 1241 1261 425 1260 425 444 1239 447 1240 445 1240 1262 425 444 1241 444 1241 444 7983 1262 425 1260 424 445 1241 1261 424 1261 424 445 1241 445 1240 445 1240 1262 424 445 1240 446 1241 444 7983 1262 425 1260 425 444 1240 1262 425 1260 424 445 1240 446 1240 445 1241 1261 425 444 1240 446 1240 445 7985 1260 424 1261 425 444 1241 1261 424 1261 425 444 1240 446 1239 446 1240 1262 426 443 1240 446 1241 444 7984 1261 424 1261 424 445 1241 1261 425 1260 425 444 1241 445 1240 445 1239 1263 424 445 1240 446 1240 445 7984 1261 424 1261 425 444 1240 1262 424 1261 425 444 1241 444 1241 444 1240 1262 425 444 1242 444 1239 446 7983 1262 424 1261 426 443 1240 1262 426 1259 425 444 1242 444 1241 444 1239 1263 425 444 1241 444 1241 445 7983 1262 424 1261 424 445 1240 1262 425 1260 425 444 1241 445 1240 445 1241 1261 425 444 1241 445 1239 446 7983 1262 425 1260 425 444 1240 1262 425 1260 425 444 1242 443 1239 446 1241 1261 425 444 1241 444 1241 444 7983 1262 425 1260 426 443 1241 1261 426 1259 425 443 1241 445 1241 444 1242 1260 426 442 1241 445 1241 444 7984 1261 425 1260 426 494 1190 1260 426 1259 425 496 1189 497 1188 497 1188 1262 427 494 1190 496 1189 496 7932 1261 426 1259 426 494 1190 1260 426 1259 425 495 1190 496 1189 496 1190 1260 428 492 1190 496 1189 496
#
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1259 425 1260 454 415 1244 1259 426 1259 425 444 1245 441 1241 444 1241 444 1241 1261 454 415 1242 444 7984 1261 425 1261 426 443 1243 1259 426 1260 427 442 1241 445 1240 445 1242 443 1242 1260 427 442 1242 443
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1292 432 1293 433 444 1244 1296 458 1266 459 418 1245 449 1244 449 1245 448 1244 448 1243 449 1245 1293 7239 1292 458 1263 458 418 1245 1292 432 1289 433 443 1244 448 1245 447 1244 448 1245 446 1244 447 1245 1290 8319 1290 432 1287 432 443 1244 1291 458 1261 458 417 1244 448 1245 446 1244 447 1244 447 1244 447 1245 1289 7239 1289 458 1260 458 417 1245 1289 458 1260 458 417 1245 447 1244 447 1244 446 1244 446 1244 446 1245 1287 8320 1287 458 1259 432 443 1245 1288 431 1286 432 443 1246 445 1244 446 1244 446 1243 447 1245 445 1244 1287 7237 1287 458 1258 458 416 1245 1287 431 1285 458 416 1244 447 1244 446 1244 446 1244 446 1244 446 1244 1287 8318 1286 458 1258 433 441 1245 1287 431 1285 458 416 1244 446 1243 447 1245 445 1243 446 1244 445 1244 1286 7237 1286 432 1284 458 416 1244 1287 432 1284 431 443 1245 445 1243 446 1244 445 1243 446 1244 445 1244 1286 8317 1285 431 1284 432 441 1244 1287 431 1285 431 443 1245 445 1244 445 1243 446 1243 446 1244 445 1244 1286 7236 1286 458 1257 431 443 1244 1287 431 1285 432 442 1245 445 1244 445 1244 446 1244 445 1244 446 1244 1287 8316 1287 458 1259 432 442 1245 1288 432 1285 432 442 1245 446 1245 445 1244 446 1244 446 1244 446 1243 1289 7236 1287 431 1286 432 442 1244 1289 431 1286 433 441 1245 446 1244 446 1244 446 1244 446 1244 446 1245 1286 8317 1286 432 1284 432 442 1245 1287 458 1259 432 442 1245 446 1244 446 1244 446 1244 446 1244 446 1244 1287 7237 1286 431 1285 431 443 1245 1287 433 1284 458 416 1245 445 1245 445 1243 446 1244 446 1244 445 1244 1287 8317 1286 432 1284 458 416 1245 1287 431 1285 432 442 1245 445 1244 445 1244 445 1243 446 1244 445 1244 1286 7235 1286 433 1282 431 442 1244 1287 431 1284 432 441 1245 445 1244 445 1244 445 1244 445 1244 445 1244 1286 8316 1285 431 1284 431 442 1244 1287 431 1284 433 440 1244 446 1244 445 1243 446 1244 445 1244 445 1243 1286 7236 1284 431 1283 431 442 1244 1286 431 1284 431 442 1245 445 1244 445 1244 445 1244 445 1244 445 1244 1286 8315 1285 431 1283 431 442 1245 1285 431 1284 432 441 1244 446 1244 445 1244 445 1244 445 1244 445 1244 1285 7235 1285 431 1283 430 443 1244 1286 431 1284 430 443 1245 445 1244 445 1244 445 1244 445 1244 445 1243 1286 8316 1284 457 1257 430 443 1244 1286 432 1283 431 442 1245 445 1244 445 1243 446 1243 446 1244 445 1243 1286 7235 1285 431 1283 432 441 1244 1286 431 1284 431 442 1245 445 1244 445 1244 445 1244 445 1244 445 1244 1285 8316 1284 431 1283 431 442 1245 1285 430 1285 430 443 1245 445 1243 446 1244 445 1244 445 1244 445 1244 1285 7235 1285 432 1282 431 442 1245 1285 432 1282 431 442 1245 445 1244 445 1243 446 1244 445 1244 445 1244 1285 8314 1285 431 1283 431 442 1245 1285 430 1284 431 442 1244 446 1244 445 1244 445 1244 445 1244 445 1244 1285 7235 1284 431 1283 431 442 1245 1285 431 1283 431 442 1244 446 1244 445 1244 445 1244 445 1244 445 1244 1285 8316 1284 431 1283 431 442 1245 1285 431 1283 430 443 1245 445 1244 445 1244 445 1244 445 1244 445 1243 1286 7236 1285 430 1285 431 442 1245 1287 431 1285 431 443 1245 446 1244 446 1244 446 1244 445 1244 446 1244 1285 8319 1292 432 1291 432 444 1246 1291 432 1292 432 444 1246 447 1245 447 1245 447 1245 447 1245 447 1245 1296 7239 1288 430 1287 431 443 1246 1288 431 1287 431 443 1246 446 1244 447 1245 446 1244 447 1245 445 1245 1288 8319 1287 431 1285 431 443 1245 1288 431 1285 431 442 1245 446 1244 446 1245 445 1245 445 1245 445 1245 1287 7237 1288 431 1286 431 443 1245 1291 431 1290 431 444 1246 446 1244 447 1245 446 1244 447 1244 446 1244 1288
#
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1290 458 1263 432 444 1245 1292 458 1263 433 443 1245 472 1221 448 1244 448 1245 447 1244 1292 433 443 8060 1289 458 1261 458 417 1245 1291 458 1261 458 418 1245 448 1244 472 1220 471 1220 448 1244 1290 431 444 9141 1290 458 1261 458 417 1245 1290 458 1261 432 444 1245 470 1220 472 1220 447 1244 447 1243 1290 457 418 8059 1288 457 1260 458 417 1245 1289 458 1260 431 444 1245 471 1220 471 1219 471 1220 448 1244 1288 457 417 9140 1287 432 1285 458 416 1244 1289 457 1260 431 444 1243 448 1244 471 1220 446 1244 471 1220 1288 430 445 8057 1289 431 1285 457 417 1244 1289 457 1259 458 416 1245 470 1220 471 1220 446 1244 447 1243 1288 432 442 9139 1287 457 1259 431 443 1244 1288 457 1259 432 443 1244 447 1243 447 1244 446 1243 472 1219 1288 457 417 8058 1287 457 1259 430 444 1245 1287 431 1285 457 417 1245 446 1244 447 1244 446 1244 446 1244 1287 457 417 9139 1287 457 1258 457 417 1244 1287 457 1259 432 442 1245 446 1243 447 1244 447 1244 446 1243 1288 431 443 8057 1287 431 1284 431 443 1245 1287 431 1285 431 443 1244 447 1243 447 1243 447 1244 446 1243 1287 431 443 9139 1286 431 1284 457 417 1245 1287 430 1286 457 417 1245 446 1243 447 1243 447 1243 447 1244 1287 431 443 8057 1287 430 1285 457 417 1243 1289 431 1284 457 417 1244 447 1244 446 1243 447 1243 447 1243 1287 431 443 9136 1286 431 1284 457 417 1244 1287 430 1285 457 417 1244 447 1243 447 1244 446 1243 447 1243 1287 430 444 8054 1286 430 1284 431 443 1244 1286 430 1285 431 443 1244 447 1243 447 1243 447 1243 447 1243 1287 431 443
#
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1304 432 1273 432 443 1214 1304 433 1273 432 443 1213 475 1214 1304 432 443 1212 476 1215 472 1212 475 8025 1304 432 1273 432 443 1212 1306 432 1274 432 443 1215 473 1213 1304 432 443 1213 474 1212 475 1212 475 9100 1305 432 1273 432 443 1213 1305 432 1274 433 442 1214 474 1212 1305 432 443 1212 476 1213 474 1214 473 8026 1302 432 1273 433 442 1213 1305 432 1274 432 443 1213 475 1213 1304 432 443 1213 474 1213 474 1212 475
#
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1304 432 1276 404 472 1213 1307 404 1304 405 470 1214 473 1215 472 1212 1333 378 471 1214 526 1160 527 7968 1330 378 1329 378 523 1163 1329 379 1328 379 522 1163 524 1164 523 1163 1304 404 521 1165 522 1163 523 9058 1303 404 1302 405 519 1165 1304 405 1302 405 519 1166 521 1164 523 1165 1303 406 518 1166 521 1165 522 7973 1302 404 1302 405 519 1165 1304 404 1303 403 522 1165 522 1164 522 1164 1304 404 521 1164 523 1165 521 9062 1301 404 1302 405 468 1215 1304 404 1302 406 467 1216 471 1216 470 1216 1303 406 467 1218 469 1217 469 8025 1278 430 1276 430 440 1244 1278 429 1277 456 414 1245 442 1244 442 1244 1278 430 439 1244 443 1243 443
#
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1307 432 1277 432 444 1213 1309 432 1278 433 443 1213 476 1213 475 1212 476 1213 1308 432 444 1213 476 8021 1309 432 1277 432 444 1213 1309 432 1278 433 443 1212 476 1212 476 1212 476 1212 1309 432 444 1213 475 9107 1307 432 1277 432 444 1213 1308 432 1277 433 443 1212 476 1211 477 1212 476 1211 1310 432 444 1213 476 8022 1308 432 1277 432 444 1212 1309 432 1278 432 444 1212 476 1211 477 1212 476 1212 1308 432 444 1212 476 9107 1307 431 1305 405 497 1162 1333 379 1331 405 497 1163 525 1162 525 1164 523 1163 1308 432 442 1217 471 8026 1283 430 1279 430 442 1244 1281 431 1279 431 440 1244 445 1244 444 1243 444 1243 1282 430 441 1245 444
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1793 719 954 1558 955 720 955 1558 954 720 955 720 1769 743 930 2420 6794 2446 926 5775 926 1586 5955 746 926 1587 925 4938 900 2450 5117 747 1764 1586 925 4100 926 3263 4252 773 2602 1586 2602 1586 1764 3262 7629 1585 4252 774 923 751 924 1589 923 751 899 3290 1762 1588 923 8291 923 1589 2601 750 1762 1588 923 752 2575 1613 898
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4482 4502 457 1536 457 1538 456 1538 456 1538 456 1536 458 1537 458 2486 457 1536 458 1537 457 2485 458 1537 457 2485 458 1536 458 1538 456 2486 457 1537 458 1536 4483 4502 457 1536 457 1538 457 1537 458 1538 457 1538 456 1537 457 2486 457 1537 457 1538 456 2486 457 1538 456 2486 457 1538 456 1537 457 2486 457 1537 457 13998 9008 2315 456 49985 9008 2315 506 49937 9031 2292 507 49938 9009 2316 506
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4504 4479 509 1483 510 1485 509 1484 510 1485 510 1485 509 1484 511 2433 510 1485 509 2435 509 1484 511 2433 510 1485 510 2434 509 1486 509 1484 511 1486 508 1483 4507 4480 508 1484 509 1484 510 1485 509 1485 509 1485 510 1485 509 2433 511 1486 508 2435 508 1485 509 2433 510 1485 509 2433 510 1486 508 1485 509 1485 509 13943 9034 2291 509 49934 9031 2292 509 49936 9030 2291 509
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4505 4479 509 1483 511 1484 511 1485 510 1485 509 1485 510 1485 509 2433 510 1484 510 1486 508 1485 509 1485 509 2434 509 2433 510 2433 510 1485 510 1486 508 1483 4507 4479 509 1483 510 1486 509 1485 509 1484 511 1484 511 1484 510 2435 509 1484 510 1485 510 1486 509 1484 511 2435 508 2434 510 2434 510 1485 510 1484 511 13947 9033 2291 510 49944 9033 2292 510
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1268 414 1273 415 430 1244 1271 416 1270 415 429 1248 425 1245 427 1242 430 1244 428 1244 428 1246 1268 7164 1270 414 1272 414 430 1244 1272 414 1272 415 429 1245 428 1247 425 1248 424 1243 429 1245 427 1246 1269 8269 1241 414 1271 414 429 1248 1267 415 1271 415 428 1244 429 1246 426 1245 427 1245 427 1245 427 1244 1270 7166 1267 414 1272 414 429 1245 1271 414 1272 416 427 1246 427 1244 428 1245 427 1246 426 1247 425 1246 1269 8242 1268 414 1271 416 427 1246 1270 415 1271 414 430 1246 427 1244 428 1247 425 1244 428 1245 427 1245 1269 7164 1269 414 1270 416 428 1246 1269 415 1271 416 427 1245 428 1246 426 1244 428 1246 426 1243 429 1246 1244 8266 1268 414 1272 414 430 1245 1271 416 1270 415 429 1246 427 1244 428 1244 428 1245 427 1244 428 1245 1245 7189 1268 415 1271 415 428 1247 1268 415 1247 441 426 1248 425 1245 427 1244 428 1243 429 1245 427 1244 1270
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1244 437 1274 415 430 1246 1247 439 1272 415 430 1245 429 1243 430 1243 429 1243 1247 439 429 1246 428 7992 1246 438 1248 438 430 1246 1269 415 1272 416 429 1246 428 1243 430 1245 427 1243 1270 416 429 1244 430 9049 1246 438 1271 416 429 1244 1271 414 1273 415 430 1245 429 1244 429 1243 429 1245 1268 416 429 1244 430 7993 1247 439 1271 415 430 1245 1247 438 1272 415 430 1246 428 1246 427 1243 429 1246 1244 438 430 1245 429
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1245 438 1248 438 430 1245 1247 439 1271 417 427 1246 428 1245 427 1245 1245 438 429 1248 425 1246 426 7994 1242 437 1249 438 429 1249 1242 439 1266 419 429 1246 427 1244 428 1245 1245 438 429 1245 428 1245 427 9305 1245 438 1247 439 429 1245 1246 438 1271 416 428 1246 427 1244 428 1243 1246 439 428 1245 428 1243 429 7989 1246 439 1246 439 429 1247 1243 439 1246 440 428 1246 427 1247 425 1248 1241 439 428 1246 427 1244 428 9305 1243 439 1246 439 429 1244 1247 440 1246 439 429 1245 428 1244 428 1243 1247 440 427 1246 427 1246 426 7990 1246 438 1270 416 429 1243 1248 438 1271 416 429 1244 429 1244 428 1243 1246 438 429 1245 428 1244 428
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1311 373 1313 373 468 1179 1340 372 1314 372 469 1180 495 1179 495 1179 495 1178 496 1177 497 1178 1341 7109 1340 372 1314 366 476 1206 1313 372 1314 373 468 1178 497 1181 493 1205 469 1178 496 1179 495 1181 1338 8215 1314 373 1313 372 469 1206 1313 373 1313 372 469 1206 469 1206 468 1204 470 1179 496 1180 494 1205 1313 7136 1314 373 1313 372 469 1181 1338 372 1314 372 469 1205 469 1206 468 1206 468 1206 468 1206 468 1205 1314 8215 1424 364 1322 363 479 1094 1424 364 1322 364 477 1095 580 1069 605 1095 524 1151 523 1150 524 1126 1393 7082 1419 364 1270 365 503 1124 1395 365 1320 365 397 1205 470 1205 469 1204 470 1204 470 1204 470 1204 1315 8215 1313 371 1315 371 495 1180 1315 371 1315 371 470 1204 495 1180 470 1205 494 1180 494 1180 494 1180 1338 7112 1338 366 1320 366 476 1181 1338 366 1320 366 476 1181 493 1181 494 1181 493 1181 494 1181 493 1181 1338 8194 1337 366 1320 366 476 1182 1337 366 1320 366 476 1182 493 1181 493 1181 494 1182 492 1182 493 1181 1337 7114 1337 366 1320 366 476 1182 1337 366 1320 366 476 1182 493 1182 492 1182 493 1182 492 1182 492 1182 1337 8194 1336 366 1320 366 476 1182 1337 366 1320 366 476 1182 493 1182 492 1182 492 1182 493 1182 492 1182 1336 7115 1336 366 1320 366 476 1183 1336 366 1320 366 476 1182 493 1182 493 1182 492 1182 492 1182 493 1182 1336
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1397 363 1322 364 476 1149 1369 364 1321 364 422 1204 470 1177 497 1177 497 1176 1342 371 469 1177 497 7967 1314 371 1314 371 469 1178 1341 373 1312 372 468 1205 468 1205 469 1206 468 1205 1314 372 468 1205 524 8943 1397 364 1321 363 478 1121 1397 364 1321 364 477 1122 552 1149 525 1123 551 1121 1397 364 476 1150 524 7913 1368 364 1266 373 523 1150 1313 372 1313 373 468 1177 497 1205 469 1205 469 1179 1340 372 468 1205 469 9029 1314 372 1314 372 468 1179 1340 372 1313 371 470 1205 469 1205 469 1205 469 1204 1315 371 469 1205 469 7969 1313 371 1314 370 470 1178 1396 364 1322 364 476 1125 550 1123 551 1149 525 1123 1395 363 478 1149 525
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1396 364 1322 364 477 1123 1396 364 1322 364 477 1125 549 1124 1395 364 477 1126 548 1125 549 1127 547 7892 1391 364 1322 364 477 1129 1390 364 1272 371 470 1179 546 1128 1342 370 471 1205 470 1179 496 1204 470 9004 1391 365 1321 364 477 1127 1392 364 1272 371 520 1128 547 1128 1341 370 471 1204 521 1130 544 1154 520 7894 1391 364 1272 371 470 1205 1315 370 1316 370 471 1179 496 1204 1316 370 471 1179 496 1204 470 1204 470 9029 1315 370 1316 370 471 1181 1339 370 1315 370 471 1204 470 1204 1315 370 471 1204 470 1204 470 1204 470 7968 1315 370 1316 370 470 1204 1315 370 1316 370 470 1203 471 1203 1316 369 472 1203 471 1203 471 1203 471 9026 1316 369 1316 368 472 1202 1317 369 1316 369 471 1203 471 1202 1316 369 471 1202 471 1203 471 1203 496 7942 1341 364 1321 364 476 1178 1341 364 1322 364 476 1177 497 1178 1341 364 476 1178 496 1178 496 1177 497
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1338 372 1314 372 469 1179 1339 371 1314 372 469 1178 496 1178 496 1179 1339 371 470 1204 470 1178 496 7942 1339 372 1367 364 479 1124 1451 362 1323 362 480 1092 582 1093 581 1093 1424 363 478 1094 524 1149 525 9208 1391 364 1321 364 478 1150 1368 364 1322 364 532 1095 576 1098 524 1150 1367 364 505 1123 550 1123 550 7888 1314 371 1315 371 470 1204 1315 371 1314 371 470 1204 470 1204 470 1204 1315 371 470 1204 470 1204 470
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2228 672 788 676 787 675 788 676 816 676 787 1409 759 1437 759 704 785 678 785 1412 784 680 783 681 782 683 781 683 780 684 779 684 780 684 779 1417 780 684 779 1417 780 1417 779 1417 779 49573 2249 689 780 1417 779 49573 2250 690 780 1417 780
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2278 652 808 655 784 679 785 681 782 704 759 1437 759 1437 759 705 784 679 785 1412 783 680 782 682 781 1415 781 1416 780 683 780 683 781 1416 780 683 780 1416 780 683 781 683 781 683 781 49575 2250 689 781 1416 780
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2259 672 788 674 789 674 789 704 787 677 787 1408 758 1437 759 705 758 705 758 1437 784 679 784 680 783 1414 782 1415 781 1416 780 684 779 684 779 1417 779 684 779 684 779 684 779 1417 779 49567 2253 687 782 1415 781
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1335 509 1337 510 448 1390 1336 509 1337 511 447 1391 448 1390 449 1391 1335 511 447 1394 445 1391 448 9180 1334 510 1335 511 447 1393 1333 510 1335 511 448 1392 447 1391 448 1390 1335 511 447 1392 447 1391 448 9180 1334 510 1335 511 447 1391 1335 510 1336 511 448 1391 449 1391 448 1391 1335 511 447 1392 447 1391 448 9180 1335 510 1335 511 447 1391 1335 509 1337 510 448 1392 448 1391 448 1391 1335 511 448 1391 448 1391 448 9181 1335 510 1335 511 447 1391 1336 509 1337 511 448 1392 448 1392 447 1391 1335 511 447 1392 448 1393 446 9182 1334 510 1335 511 447 1391 1335 510 1336 511 448 1392 448 1392 447 1391 1335 511 447 1392 448 1392 447 9182 1334 510 1335 511 447 1391 1335 510 1335 511 448 1393 447 1392 447 1390 1336 511 447 1393 446 1393 446 9182 1334 511 1356 490 473 1366 1357 487 1335 511 474 1366 474 1365 474 1364 1334 511 475 1366 473 1366 473 9155 1334 510 1345 501 474 1365 1358 487 1335 512 474 1366 473 1365 474 1364 1334 511 475 1367 472 1366 473
#
name: Mode
type: parsed
protocol: NEC
address: 00 00 00 00
command: 43 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1286 403 1287 401 447 1245 1289 405 1289 402 446 1244 447 1245 446 1246 445 1247 444 1248 443 1245 1288 7139 1286 403 1287 402 446 1246 1287 406 1288 402 446 1246 445 1244 447 1247 444 1244 446 1244 446 1246 1287 7138 1286 402 1288 402 446 1246 1288 405 1289 402 445 1245 446 1247 444 1245 446 1246 445 1273 418 1247 1287 7137 1288 402 1288 402 445 1247 1286 406 1288 402 445 1247 444 1245 446 1245 446 1244 447 1246 445 1245 1288 7137 1288 402 1288 402 446 1246 1287 405 1288 402 445 1247 444 1246 444 1247 444 1246 445 1246 445 1246 1287 7138 1287 403 1287 402 445 1246 1288 406 1288 402 446 1246 445 1246 445 1245 446 1245 446 1246 445 1246 1288 7139 1286 402 1288 401 446 1246 1287 406 1287 402 446 1247 444 1245 446 1247 444 1245 446 1247 444 1246 1287 7138 1288 401 1289 401 447 1248 1285 405 1289 402 446 1245 446 1246 445 1246 445 1245 446 1245 445 1247 1286 7138 1288 402 1288 401 447 1247 1286 406 1288 402 446 1246 445 1245 446 1246 445 1245 446 1245 446 1246 1287 7138 1287 402 1288 401 447 1245 1288 406 1288 401 447 1245 446 1245 446 1246 445 1244 447 1245 446 1247 1286
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1287 402 1289 402 446 1249 1285 406 1288 402 446 1245 446 1246 445 1246 445 1247 444 1247 1286 402 446 7981 1286 404 1287 402 445 1247 1287 406 1288 402 446 1245 446 1246 445 1246 445 1246 445 1247 1287 402 446 7978 1288 403 1288 402 446 1246 1288 406 1288 402 446 1248 443 1248 443 1274 417 1247 445 1248 1286 402 446 7980 1289 402 1289 402 446 1246 1288 406 1288 402 446 1247 444 1246 445 1247 445 1246 445 1249 1285 403 445 7986 1234 454 1288 403 445 1275 1208 458 1237 454 445 1247 444 1247 445 1247 444 1247 445 1247 1236 454 445 7988 1233 454 1237 454 445 1247 1287 408 1236 454 445 1247 445 1248 444 1247 445 1248 443 1248 1286 403 445 7986 1286 403 1288 403 445 1249 1285 406 1288 403 445 1249 442 1246 445 1249 442 1248 443 1248 1286 403 445 7985 1286 403 1288 403 445 1247 1287 406 1288 403 445 1249 442 1247 444 1247 444 1246 445 1247 1287 403 445 7984 1286 404 1286 402 446 1248 1285 407 1287 402 446 1247 444 1246 445 1246 445 1247 444 1247 1287 402 446 7985 1284 403 1287 403 445 1248 1285 407 1287 403 445 1247 444 1247 444 1247 444 1246 445 1247 1286 403 445 7982 1287 403 1287 402 446 1247 1287 407 1287 402 446 1246 445 1246 445 1246 445 1248 443 1247 1287 402 446
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1287 402 1289 401 447 1247 1287 405 1289 401 447 1247 444 1245 1289 401 447 1247 444 1245 447 1247 445 7983 1287 403 1288 402 446 1245 1290 406 1289 401 447 1245 447 1248 1286 402 446 1246 446 1246 446 1246 446 7981 1289 402 1289 402 446 1247 1287 406 1288 403 445 1246 445 1248 1286 401 447 1246 445 1245 446 1248 443 7984 1288 403 1288 402 446 1247 1287 406 1288 401 447 1245 446 1246 1288 401 447 1246 445 1245 446 1248 443 7984 1286 403 1288 401 447 1249 1285 405 1289 402 446 1246 445 1246 1288 402 446 1246 445 1247 444 1246 445 7982 1288 402 1289 401 447 1247 1287 405 1289 401 447 1245 446 1249 1285 401 447 1250 441 1245 446 1245 447 7984 1286 402 1288 401 447 1244 1290 405 1289 401 447 1245 446 1244 1290 401 447 1246 445 1246 445 1247 444 7981 1288 402 1288 402 446 1246 1287 405 1289 401 447 1244 447 1246 1288 401 447 1245 446 1246 445 1246 445 7983 1287 403 1288 402 446 1245 1289 406 1288 402 446 1246 445 1247 1287 402 446 1247 444 1246 445 1246 445 7985 1286 403 1288 402 446 1246 1288 406 1288 402 446 1245 446 1247 1287 402 446 1248 443 1246 445 1246 445
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1235 454 1236 454 444 1248 1235 459 1234 454 443 1249 441 1249 442 1249 1234 456 441 1248 443 1250 440 7985 1234 456 1234 454 497 1195 1234 458 1235 455 490 1200 496 1194 491 1199 1237 454 491 1200 497 1194 491 7937 1234 455 1235 455 490 1201 1234 459 1235 455 490 1200 497 1195 496 1196 1234 455 416 1274 497 1193 492 7937 1233 455 1235 454 417 1275 1234 459 1234 455 441 1250 441 1248 442 1249 1236 455 441 1249 442 1250 441 7985 1234 455 1235 454 443 1249 1235 458 1236 455 441 1249 442 1248 442 1250 1233 454 443 1247 443 1247 443 7983 1235 455 1235 454 443 1247 1236 458 1236 454 444 1250 441 1249 442 1248 1235 454 443 1248 443 1249 442 7984 1235 455 1235 455 442 1249 1235 459 1234 455 416 1275 415 1274 491 1201 1235 454 416 1276 415 1274 417 8011 1233 455 1235 456 414 1276 1234 457 1236 453 417 1275 416 1276 414 1274 1236 454 416 1276 415 1275 416 8011 1234 455 1235 453 394 1299 1235 458 1236 454 392 1300 391 1298 393 1298 1236 455 392 1298 393 1300 391
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1233 456 1234 454 416 1276 1235 458 1236 455 415 1275 416 1274 417 1274 417 1274 1237 454 416 1274 417 8009 1234 456 1234 456 414 1275 1236 458 1236 454 416 1276 415 1276 392 1298 393 1299 1235 453 394 1298 393 8030 1235 455 1235 453 393 1299 1235 459 1235 455 391 1299 392 1299 392 1298 392 1299 1235 454 392 1300 391 8032 1235 454 1236 456 390 1300 1235 458 1236 454 392 1298 393 1299 392 1300 391 1298 1237 453 393 1298 393 8035 1235 455 1235 453 393 1299 1236 458 1236 454 393 1297 394 1299 392 1297 394 1298 1236 456 391 1297 394 8035 1234 453 1237 453 393 1299 1235 461 1232 452 394 1299 392 1297 393 1300 391 1300 1234 453 393 1298 393 8034 1236 453 1237 453 393 1298 1236 457 1237 453 393 1298 393 1299 392 1299 392 1299 1235 454 393 1298 393
#
name: Power
type: parsed
protocol: NEC
address: 02 00 00 00
command: 11 00 00 00
#
name: Speed_up
type: parsed
protocol: NEC
address: 02 00 00 00
command: 0D 00 00 00
#
name: Timer
type: parsed
protocol: NEC
address: 02 00 00 00
command: 09 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 654 1906 626 1880 1921 610 1922 637 626 1906 626 1880 652 1880 1920 612 652 1881 1920 637 1895 638 625 1881 651 1881 651 1906 1895 610 654 1906 626 1907 1894 638 1894 610 654 1906 626 1882 650 1878 1923 609 654 22169 625 1881 651 1880 1921 637 1895 610 653 1878 654 1906 626 1880 1921 637 626 1906 1895 612 1920 637 626 1906 626 1880 653 1906 1895 610 653 1879 653 1907 1894 609 1923 637 626 1906 626 1906 626 1879 1922 637 626 22140 654 1881 651 1906 1895 609 1923 637 626 1906 626 1877 655 1906 1895 612 651 1880 1921 612 1920 638 625 1906 626 1881 651 1906 1895 638 625 1880 652 1879 1922 638 1894 611 652 1879 653 1906 626 1906 1895 610 653 22169 625 1880 652 1879 1922 610 1922 613 650 1879 653 1880 652 1906 1895 612 651 1906 1895 610 1922 637 626 1882 650 1907 625 1878 1923 612 652 1906 626 1906 1895 638 1894 611 653 1879 653 1906 626 1880 1921 610 653 22141 653 1907 625 1880 1921 612 1920 637 626 1879 653 1906 626 1879 1922 637 626 1880 1921 609 1923 638 625 1907 625 1884 648 1879 1922 637 626 1906 626 1906 1895 612 1920 637 626 1879 653 1878 654 1906 1895 612 651 22169 625 1906 626 1907 1894 610 1922 637 626 1881 651 1906 626 1879 1922 638 626 1906 1895 613 1919 638 625 1907 625 1880 652 1906 1895 638 625 1907 625 1879 1922 611 1921 638 625 1906 626 1907 625 1907 1894 611 652 22143 651 1881 651 1906 1895 611 1921 637 626 1906 626 1880 652 1907 1894 611 652 1879 1922 612 1920 611 652 1907 625 1906 626 1881 1920 637 627 1907 625 1906 1895 638 1894 638 625 1880 652 1877 655 1906 1895 638 625 22143 651 1907 625 1906 1895 609 1923 608 655 1906 626 1878 654 1906 1894 637 627 1878 1923 637 1895 637 626 1879 653 1907 625 1907 1894 611 653 1906 626 1880 1921 637 1895 612 652 1907 625 1907 625 1881 1920 611 653 22168 626 1879 653 1906 1895 638 1894 609 654 1907 625 1906 626 1879 1922 638 625 1880 1921 638 1894 608 655 1878 654 1906 626 1907 1894 637 626 1906 626 1906 1895 638 1894 608 656 1906 626 1907 625 1880 1921 639 625 22168 626 1906 626 1880 1921 610 1922 638 625 1906 626 1906 626 1881 1920 638 626 1906 1895 637 1895 612 652 1906 626 1878 654 1906 1895 637 626 1877 655 1907 1894 638 1894 610 654 1906 626 1906 626 1907 1894 609 654 22145 649 1879 653 1880 1921 637 1895 611 653 1906 626 1907 625 1878 1923 638 625 1906 1895 610 1922 637 626 1878 654 1881 651 1881 1920 610 654 1907 625 1906 1895 637 1895 637 626 1906 626 1880 652 1906 1895 610 654 22140 654 1882 650 1881 1920 609 1923 611 653 1879 653 1879 653 1879 1922 637 626 1906 1895 613 1919 613 651 1906 626 1880 652 1881 1920 637 626 1883 649 1907 1894 638 1894 609 655 1907 625 1879 653 1906 1895 638 626 22146 649 1906 626 1906 1895 637 1895 638 625 1879 653 1906 626 1906 1895 610 653 1907 1893 638 1894 610 654 1906 626 1906 626 1882 1919 611 653 1907 625 1881 1920 611 1921 610 654 1880 652 1906 626 1907 1894 610 654 22169 625 1906 626 1906 1895 611 1921 610 654 1906 626 1906 626 1881 1920 610 653 1906 1895 637 1895 638 625 1906 626 1880 652 1906 1895 613 651 1907 625 1906 1895 611 1921 638 625 1906 626 1880 652 1881 1920 615 649 22169 625 1878 654 1881 1920 637 1895 612 651 1907 625 1907 625 1906 1895 612 651 1906 1895 612 1920 638 625 1879 653 1906 626 1906 1895 614 650 1907 625 1906 1895 611 1921 638 625 1882 650 1879 653 1878 1923 610 653 22142 653 1878 654 1880 1921 612 1920 613 650 1906 626 1906 626 1881 1920 611 653 1879 1922 612 1920 610 653 1880 652 1906 626 1906 1895 613 650 1907 625 1881 1920 638 1894 614 649 1881 651 1907 625 1881 1920 611 652 22169 625 1906 626 1880 1921 612 1920 608 656 1906 626 1881 651 1879 1922 609 654 1881 1920 612 1920 614 649 1880 652 1906 626 1907 1894 637 627 1906 626 1907 1894 613 1919 611 652 1881 651 1879 653 1906 1895 610 654 22169 625 1880 652 1906 1895 637 1895 609 654 1881 651 1879 653 1905 1896 609 654 1907 1894 614 1918 637 626 1906 626 1880 652 1906 1895 614 649 1906 626 1879 1922 609 1923 638 626 1880 652 1906 626 1906 1895 637 626 22145 649 1879 653 1880 1921 612 1920 608 655 1882 650 1906 626 1906 1895 638 625 1879 1922 637 1895 638 626 1879 653 1880 652 1907 1894 615 649 1906 626 1882 1919 612 1920 612 651 1906 626 1879 653 1881 1920 638 625 22142 652 1879 653 1878 1923 610 1922 612 652 1879 653 1879 653 1879 1922 611 653 1878 1923 609 1923 610 654 1880 652 1906 626 1882 1919 609 655 1906 626 1906 1895 612 1920 611 652 1880 652 1906 626 1880 1921 609 655
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 627 1905 627 1906 1895 610 1923 611 653 1906 627 1878 655 1906 1895 609 1923 636 1896 636 628 1906 1895 611 653 1878 654 1879 1922 610 654 1877 1924 637 1895 637 627 1906 1895 637 627 1882 650 1906 1895 637 627 22143 654 1878 654 1906 1895 612 1921 611 653 1879 654 1880 653 1880 1921 637 1895 610 1923 637 627 1906 1895 609 655 1906 627 1877 1924 609 654 1906 1895 636 1896 611 653 1879 1922 637 626 1906 626 1906 1895 609 655 22170 627 1905 627 1906 1895 636 1896 611 653 1879 654 1880 652 1878 1923 637 1895 611 1922 608 655 1879 1922 637 627 1880 653 1879 1922 610 654 1879 1922 611 1921 607 657 1878 1923 610 653 1905 627 1906 1895 609 655
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 654 1906 599 1906 1895 665 1867 638 626 1908 625 1909 651 1906 1867 638 1895 638 1894 639 653 1881 652 1879 653 1906 1895 610 654 1906 1868 637 1895 664 1896 609 655 1878 655 1905 627 1878 1923 637 627 1879 1922 20875 653 1878 655 1880 1921 612 1921 612 652 1880 652 1880 653 1905 1896 611 1921 613 1920 609 655 1878 655 1905 627 1880 1921 637 627 1880 1921 610 1923 610 1922 611 653 1905 627 1878 655 1882 1919 637 627 1879 1922 20875 653 1906 627 1883 1918 636 1896 610 654 1881 652 1879 653 1906 1895 637 1895 611 1921 610 654 1879 654 1879 654 1881 1920 609 655 1878 1923 612 1920 608 1925 610 654 1882 651 1906 626 1880 1921 610 654 1879 1922
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 574 1958 574 1934 1868 689 1843 663 601 1957 575 1931 602 1931 1871 689 574 1932 1870 661 602 1932 1870 662 1871 661 602 1930 603 1931 601 1930 603 1929 1873 689 574 1931 1871 689 1843 689 574 1958 574 1930 603 22195 602 1958 574 1932 1870 660 1873 661 602 1932 601 1957 575 1958 1844 689 574 1932 1870 661 602 1931 1871 689 1844 661 602 1932 601 1929 603 1958 574 1958 1844 662 601 1930 1872 688 1844 663 600 1933 599 1933 600 22222 575 1932 600 1932 1870 661 1871 662 601 1958 574 1931 602 1930 1871 660 603 1958 1844 688 575 1931 1871 661 1871 688 575 1931 602 1958 574 1931 602 1931 1871 661 602 1932 1870 661 1872 689 574 1930 602 1932 601 22198 599 1932 601 1958 1843 663 1870 661 602 1931 601 1931 602 1958 1844 660 603 1931 1871 662 601 1930 1871 663 1870 661 602 1931 602 1957 575 1930 603 1931 1871 662 601 1929 1873 664 1868 661 603 1931 602 1932 601
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1269 409 1272 433 402 1253 1264 417 1264 451 405 1259 432 1256 1271 418 438 1253 428 1263 428 1262 429 8016 1267 411 1270 409 436 1245 1272 409 1272 443 402 1261 430 1258 1269 420 436 1255 436 1254 437 1253 438 8046 1268 410 1271 407 428 1253 1264 417 1264 425 431 1259 432 1256 1271 417 428 1263 428 1262 429 1261 430 8026 1267 410 1271 409 436 1244 1262 418 1263 425 431 1259 432 1256 1271 418 438 1252 429 1261 430 1260 431 8027 1267 411 1270 435 410 1244 1262 418 1263 452 404 1259 432 1256 1271 418 438 1252 429 1261 430 1260 431 8014 1269 408 1273 432 403 1251 1266 414 1267 449 407 1255 436 1252 1265 425 431 1259 432 1258 433 1257 434 8050 1264 413 1268 437 408 1246 1271 409 1272 443 402 1260 431 1257 1270 418 438 1253 428 1262 429 1260 431 8025 1268 408 1273 406 429 1251 1266 415 1266 448 408 1256 435 1252 1265 424 432 1258 433 1256 435 1255 436 8017 1266 410 1271 435 410 1243 1263 416 1265 451 405 1257 434 1254 1273 443 402 1260 431 1259 432 1258 433
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1270 407 1264 441 404 1248 1269 413 1268 445 411 1251 430 1260 431 1259 432 1255 1272 444 401 1260 431 7984 1268 408 1263 441 404 1248 1269 439 1242 418 427 1260 431 1259 432 1257 434 1254 1263 426 430 1257 434 8018 1266 411 1270 434 401 1251 1266 442 1239 420 436 1252 429 1261 430 1259 432 1255 1272 417 428 1259 432 7992 1271 405 1266 438 407 1244 1262 444 1237 423 433 1255 436 1253 428 1261 430 1257 1270 446 410 1251 430 7998 1265 411 1270 434 401 1251 1266 442 1239 420 436 1252 429 1261 430 1259 432 1255 1272 444 401 1259 432 7982 1270 405 1266 439 406 1245 1272 435 1236 424 432 1256 435 1254 437 1252 429 1259 1268 447 409 1252 429 8022 1272 404 1267 438 407 1244 1262 444 1237 422 434 1254 437 1252 429 1260 431 1256 1271 444 401 1259 432
#
name: Power
type: parsed
protocol: NEC
address: 80 00 00 00
command: 12 00 00 00
#
name: Timer
type: parsed
protocol: NEC
address: 80 00 00 00
command: 1F 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8997 4462 577 1659 577 541 576 540 578 540 577 541 577 540 578 539 578 540 578 540 578 1658 578 1658 578 1657 578 1658 578 1657 579 1658 578 1658 578 1659 577 1658 578 540 578 539 579 540 578 540 578 540 578 540 578 540 578 539 578 1658 578 1659 577 1658 578 1658 578 1658 578 1658 578 1659 577 1658 578 541 577 540 577 540 578 540 578 539 579 540 578 540 578 539 579 1658 578 1658 578 1658 578 1658 578 1658 578 1658 578
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8998 4462 575 1659 576 540 577 540 578 540 578 540 577 540 577 541 576 540 577 540 577 1658 577 1656 579 1658 577 1657 578 1658 577 1657 578 1657 578 1657 578 1658 577 1658 578 540 578 539 579 539 579 541 577 540 578 539 578 540 578 539 578 1657 578 1657 579 1658 578 1657 579 1658 578 1657 578 1657 579 1657 578 539 579 539 578 540 577 540 577 540 577 539 578 540 577 539 578 1659 576 1658 578 1658 577 1657 578 1657 604
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8998 4462 577 1660 577 538 580 541 577 541 577 540 578 541 577 540 578 540 578 540 578 1659 578 1658 579 1659 578 1658 578 1660 577 1659 578 1659 578 1658 579 1659 578 541 577 540 578 1658 579 539 579 540 578 541 577 541 577 541 577 1659 577 1660 577 540 578 1658 579 1659 578 1660 577 1659 577 1658 578 540 578 541 577 1659 577 540 578 541 577 540 578 541 577 540 578 1659 577 1659 577 540 578 1658 578 1660 576 1658 578
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9001 4465 577 1659 579 540 578 539 579 540 578 541 577 540 578 540 578 541 577 540 578 1658 579 1659 578 1659 578 1660 577 1659 578 1659 578 1658 579 1658 579 1658 579 540 578 1658 579 540 578 540 578 540 578 540 578 541 577 539 579 1660 577 539 579 1658 578 1659 578 1659 578 1658 578 1658 578 1658 578 540 578 1659 577 539 579 540 578 540 578 540 578 540 578 540 578 1659 577 540 578 1658 578 1659 577 1658 578 1658 578
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8997 4461 578 1658 579 539 579 541 577 539 579 540 578 540 578 539 579 540 578 540 578 1658 578 1658 578 1658 578 1657 579 1658 578 1658 578 1658 578 1658 578 1658 578 1659 578 1659 578 539 579 541 577 540 578 540 578 540 578 541 577 540 578 540 578 1658 579 1659 578 1658 579 1659 578 1659 578 1658 578 1657 579 1657 579 539 579 540 578 541 576 539 579 540 578 540 578 540 578 540 577 1658 578 1658 578 1658 578 1658 578
#
name: Rotate
type: parsed
protocol: NEC
address: 30 00 00 00
command: 83 00 00 00
#
name: Rotate
type: parsed
protocol: NEC
address: 30 00 00 00
command: 9C 00 00 00
#
name: Timer
type: parsed
protocol: NEC
address: 30 00 00 00
command: 87 00 00 00
#
name: Mode
type: parsed
protocol: NEC
address: 30 00 00 00
command: 86 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1239 476 1211 476 390 1268 420 1268 418 1267 420 1267 1240 476 389 1268 419 1267 420 1266 421 1266 421 8016 1240 476 1211 476 390 1267 421 1267 419 1267 420 1266 1241 476 390 1268 419 1267 420 1266 448 1240 446 1238 102 6648 1241 476 1211 476 390 1268 420 1266 420 1268 419 1266 1241 476 390 1268 420 1267 420 1267 447 1240 446 7990 1239 476 1211 476 390 1269 419 1266 420 1267 420 1266 1241 476 390 1268 420 1266 421 1266 448 1240 447 7990 1238 476 1211 476 390 1267 421 1267 419 1268 419 1267 1240 476 390 1269 419 1267 420 1266 448 1240 446 1236 104 6649 1239 476 1211 476 390 1268 420 1267 419 1267 420 1265 1242 476 390 1268 420 1267 420 1266 447 1239 422
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1202 481 1202 481 437 1248 1200 483 1201 481 437 1247 436 1247 436 1247 436 1247 436 1248 435 1247 1201 7032 1202 482 1201 481 437 1247 1178 506 1177 505 436 1248 435 1248 435 1248 435 1246 437 1248 435 1247 1179 7056 1178 504 1179 506 434 1248 1178 505 1178 505 436 1247 436 1247 436 1247 436 1248 435 1247 436 1247 1179 7056 1178 505 1178 505 435 1247 1179 505 1178 504 436 1247 436 1248 434 1249 435 1247 436 1246 437 1248 1178
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1309 373 1310 373 493 1192 1308 374 1309 372 495 1191 492 1189 1311 375 491 1190 494 1189 494 1191 492 7740 1310 373 1310 373 494 1192 1308 376 1307 374 493 1191 492 1190 1310 376 491 1190 493 1219 464 1190 493 7743 1308 374 1309 376 491 1190 1309 375 1308 377 491 1190 493 1192 1257 426 491 1219 464 1192 491 1190 493 7742 1258 427 1256 426 491 1219 1230 425 1258 424 493 1190 493 1192 1258 453 464 1192 491 1193 490 1220 463
#
name: Power
type: parsed
protocol: NEC
address: 00 00 00 00
command: 55 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1226 441 1228 442 441 1228 1227 472 1198 441 443 1231 439 1228 442 1227 443 1227 442 1226 443 1227 1228 7154
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1197 472 1198 442 414 1260 1223 472 1198 444 413 1285 385 1256 1227 472 385 1285 385 1285 385 1256 414 7968
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9358 4535 573 574 575 572 577 570 579 568 571 576 573 574 575 572 577 571 578 596 553 565 574 1695 574 1696 573 1697 572 1697 572 1698 571 1700 579 567 572 1697 572 1699 580 568 571 576 573 574 575 571 578 1718 551 1694 575 572 577 569 580 1689 580 1690 579 1690 579 1693 576 558 581 41436 9369 2240 578 95786 9363 2236 572
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9302 4546 571 576 573 571 578 567 572 573 576 569 570 575 574 571 578 567 572 574 575 568 571 1699 569 1700 568 1702 576 1693 575 1694 574 1698 570 573 576 1693 575 1695 573 1699 569 576 573 572 577 566 573 1697 571 1701 577 567 572 573 576 567 572 1699 569 1700 578 1694 574 557 572 41653 9292 2245 573 96004 9293 2241 577
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9413 4549 577 572 577 573 576 574 575 575 574 576 573 577 572 577 582 568 581 569 580 567 582 1693 575 1699 579 1695 573 1701 577 1697 581 1693 575 1701 577 573 576 571 578 1699 579 570 579 571 578 570 579 1724 554 566 573 1702 576 1700 578 569 580 1695 573 1701 577 1699 579 583 546 41516 9407 2244 573 95961 9506 2241 586
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1329 376 1303 374 462 1193 1331 377 1302 376 460 1192 462 1218 462 1218 462 1218 487 1193 1330 376 459 7907 1300 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 456 1224 456 1224 456 1225 1298 381 455 8172 1298 380 1299 380 456 1224 1299 380 1299 381 455 1225 455 1225 455 1225 455 1225 455 1225 1298 381 455 7912 1298 381 1298 381 455 1225 1298 381 1298 381 455 1225 454 1225 455 1225 455 1225 455 1225 1298 381 455
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1329 378 1301 378 458 1195 1329 375 1304 378 456 1196 460 1220 1329 379 457 1196 484 1221 458 1222 457 7910 1298 382 1297 382 454 1226 1297 382 1297 382 454 1226 454 1226 1297 382 454 1226 454 1226 454 1226 454 8158 1297 382 1297 382 454 1226 1297 382 1297 382 454 1226 454 1226 1297 382 453 1227 453 1227 453 1227 453 7913 1296 382 1297 383 453 1227 1296 383 1296 383 453 1227 453 1227 1296 383 453 1227 453 1227 453 1228 452
#
name: Mode
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1245 415 1250 436 386 1274 1224 410 1245 441 381 1278 387 1272 383 1250 405 1255 1254 407 405 1281 384 8184 1248 413 1252 434 388 1272 1216 418 1247 439 383 1250 405 1280 385 1248 407 1279 1219 415 407 1278 387 8179 1253 408 1247 439 383 1250 1248 412 1243 444 378 1255 410 1275 380 1280 385 1275 1223 411 411 1249 406
#
name: Speed_up
type: parsed
protocol: NEC
address: 80 00 00 00
command: 11 00 00 00
#
name: Rotate
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4608 4604 546 1509 540 1516 544 1512 548 1509 540 1517 543 1513 547 2555 545 1511 539 2564 546 1510 540 2562 538 1518 542 2559 541 1516 544 1512 548 1509 541 1514 4605 4605 545 1510 540 1517 543 1514 546 1510 540 1517 543 1514 546 2556 544 1512 548 2555 545 1510 540 2563 547 1508 542 2587 513 1517 543 1513 547 1510 540 13755 9237 2298 545

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
Filetype: IR library file
Version: 1
# Last Updated 18th Feb, 2024
# Last Checked 18th Feb, 2024
#
# Last Updated 5th Oct, 2024
# Last Checked 5th Oct, 2024
#
# TEMP FIX FOR POWER
#
# ON
@ -71,7 +71,7 @@ type: parsed
protocol: NECext
address: 00 30 00 00
command: 83 7C 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
@ -107,13 +107,13 @@ type: parsed
protocol: NECext
address: 87 4E 00 00
command: 29 D6 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 87 4E 00 00
command: 08 F7 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
@ -149,7 +149,6 @@ type: parsed
protocol: NEC
address: 02 00 00 00
command: 1D 00 00 00
#
# ON
name: Power
type: raw
@ -162,7 +161,6 @@ type: parsed
protocol: NEC
address: 02 00 00 00
command: 1D 00 00 00
#
# ON
name: Power
type: raw
@ -835,25 +833,25 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591
#
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 18 E9 00 00
command: 49 B6 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 02 00 00 00
command: 14 00 00 00
#
#
name: Power
type: parsed
protocol: NEC
@ -865,13 +863,13 @@ type: parsed
protocol: NEC
address: 02 00 00 00
command: 48 00 00 00
#
#
name: Vol_dn
type: parsed
protocol: NEC
address: 02 00 00 00
command: 40 00 00 00
#
#
name: Mute
type: parsed
protocol: NEC
@ -901,7 +899,7 @@ type: parsed
protocol: NECext
address: B8 57 00 00
command: 1E E1 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
@ -925,13 +923,13 @@ type: parsed
protocol: NEC
address: 32 00 00 00
command: 8F 00 00 00
#
#
name: Vol_up
type: parsed
protocol: NEC
address: 32 00 00 00
command: 8C 00 00 00
#
#
name: Mute
type: raw
frequency: 38000
@ -949,19 +947,19 @@ type: parsed
protocol: NEC
address: 00 00 00 00
command: A8 00 00 00
#
#
name: Mute
type: parsed
protocol: NEC
address: 00 00 00 00
command: 88 00 00 00
#
#
name: Vol_dn
type: parsed
protocol: NEC
address: 00 00 00 00
command: 9C 00 00 00
#
#
name: Vol_up
type: parsed
protocol: NEC
@ -979,19 +977,19 @@ type: parsed
protocol: NECext
address: 87 45 00 00
command: 17 E8 00 00
#
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 87 45 00 00
command: 50 AF 00 00
#
#
name: Mute
type: raw
frequency: 38000
@ -1009,13 +1007,13 @@ type: parsed
protocol: NECext
address: FF FF 00 00
command: E8 17 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: FF FF 00 00
command: BD 42 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
@ -1033,13 +1031,13 @@ type: parsed
protocol: Kaseikyo
address: 41 54 32 00
command: 05 00 00 00
#
#
name: Vol_up
type: parsed
protocol: Kaseikyo
address: 41 54 32 00
command: 70 01 00 00
#
#
name: Vol_dn
type: parsed
protocol: Kaseikyo
@ -1099,61 +1097,61 @@ type: parsed
protocol: NECext
address: 4F 50 00 00
command: 02 FD 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 4F 50 00 00
command: 08 F7 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 4F 50 00 00
command: 0B F4 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 81 03 00 00
command: F0 0F 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 87 45 00 00
command: 51 AE 00 00
#
#
name: Mute
type: parsed
protocol: NECext
address: 87 45 00 00
command: 52 AD 00 00
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500
#
#
name: Vol_up
type: parsed
protocol: NEC
address: 01 00 00 00
command: 06 00 00 00
#
#
name: Vol_dn
type: parsed
protocol: NEC
address: 01 00 00 00
command: 09 00 00 00
#
#
name: Mute
type: parsed
protocol: NEC
@ -1171,253 +1169,253 @@ type: parsed
protocol: NEC
address: 01 00 00 00
command: 00 00 00 00
#
#
name: Mute
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9035 4437 563 548 563 548 563 522 594 1645 591 1639 592 518 593 548 563 552 563 1640 592 548 563 553 562 1668 564 524 592 1642 594 1674 562 1673 563 1639 593 548 563 552 564 1669 562 548 563 520 615 529 586 1645 587 529 587 1650 586 1646 586 529 586 1650 586 1649 587 1646 586 524 587 524 587 524 587 524 587 525 643 467 644 440 671 467 644 472 643 1592 644 1593 643 1593 642 1594 641 1594 587 1649 585 1651 563 1682 562 14430 9008 2205 562
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 84 F4 00 00
command: 2C D3 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 84 F4 00 00
command: 2F D0 00 00
#
#
name: Mute
type: parsed
protocol: NECext
address: 4F 50 00 00
command: 0F F0 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 02 00 00 00
command: 12 00 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 02 00 00 00
command: 12 00 00 00
#
#
name: Vol_dn
type: parsed
protocol: NEC
address: 02 00 00 00
command: 06 00 00 00
#
#
name: Vol_up
type: parsed
protocol: NEC
address: 02 00 00 00
command: 00 00 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 04 B1 00 00
command: 58 A7 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 04 B1 00 00
command: 58 A7 00 00
#
#
name: Mute
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9107 4376 681 1573 681 472 655 472 654 474 652 475 652 476 651 476 652 476 651 476 651 1604 651 1604 651 1604 651 1603 652 1604 651 1604 651 1604 651 1604 650 476 651 477 650 477 650 476 651 477 651 1604 650 476 651 477 650 1604 651 1604 650 1604 651 1604 650 1604 651 477 650 1604 650 39498 9079 2178 651
#
#
name: Power
type: parsed
protocol: NECext
address: 8B CA 00 00
command: 12 ED 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 8B CA 00 00
command: 12 ED 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 8B CA 00 00
command: 44 BB 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 8B CA 00 00
command: 46 B9 00 00
#
#
name: Mute
type: parsed
protocol: NECext
address: 8B CA 00 00
command: 11 EE 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 81 03 00 00
command: F0 0F 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 01 00 00 00
command: 40 00 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 00 BD 00 00
command: 01 FE 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 01 00 00 00
command: 40 00 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 00 BD 00 00
command: 01 FE 00 00
#
#
name: Vol_dn
type: parsed
protocol: NECext
address: 00 BD 00 00
command: 10 EF 00 00
#
#
name: Vol_up
type: parsed
protocol: NECext
address: 00 BD 00 00
command: 0C F3 00 00
#
#
name: Mute
type: parsed
protocol: NECext
address: 00 BD 00 00
command: 6A 95 00 00
#
#
name: Vol_up
type: parsed
protocol: NEC
address: 02 00 00 00
command: 11 00 00 00
#
#
name: Mute
type: parsed
protocol: NECext
address: 87 4E 00 00
command: 51 AE 00 00
#
#
name: Pause
type: parsed
protocol: NECext
address: 00 30 00 00
command: A8 57 00 00
#
#
name: Play
type: parsed
protocol: NECext
address: 00 30 00 00
command: A9 56 00 00
#
#
name: Pause
type: parsed
protocol: NECext
address: 00 30 00 00
command: A9 56 00 00
#
#
name: Play
type: parsed
protocol: NECext
address: 83 55 00 00
command: 5E A1 00 00
#
#
name: Pause
type: parsed
protocol: NECext
address: 83 55 00 00
command: 5B A4 00 00
#
#
name: Play
type: parsed
protocol: NEC
address: 00 00 00 00
command: 93 00 00 00
#
#
name: Pause
type: parsed
protocol: NEC
address: 00 00 00 00
command: 93 00 00 00
#
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: 15 00 00 00
#
#
name: Pause
type: parsed
protocol: NEC
address: 01 00 00 00
command: 15 00 00 00
#
#
name: Play
type: parsed
protocol: NECext
address: B8 57 00 00
command: 18 E7 00 00
#
#
name: Play
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8981 4411 532 1616 557 1592 557 465 556 465 556 466 555 467 554 468 554 1595 554 467 554 468 554 1595 554 467 580 1569 581 1568 581 1568 581 1569 580 441 581 1569 580 442 580 1570 579 1594 555 467 555 443 579 442 580 1594 554 467 555 1594 555 467 555 468 554 1595 554 1595 528 1620 529 42169 9008 2106 531
#
#
name: Pause
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8981 4411 532 1616 557 1592 557 465 556 465 556 466 555 467 554 468 554 1595 554 467 554 468 554 1595 554 467 580 1569 581 1568 581 1568 581 1569 580 441 581 1569 580 442 580 1570 579 1594 555 467 555 443 579 442 580 1594 554 467 555 1594 555 467 555 468 554 1595 554 1595 528 1620 529 42169 9008 2106 531
#
#
name: Play
type: parsed
protocol: NEC
address: 31 00 00 00
command: 41 00 00 00
#
#
name: Pause
type: parsed
protocol: NEC
address: 31 00 00 00
command: 41 00 00 00
#
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: 03 00 00 00
#
#
name: Pause
type: parsed
protocol: NEC
@ -1483,75 +1481,339 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 353 1742 355 693 355 1742 355 1742 355 693 355 1744 353 694 354 1743 354 693 355 1743 354 694 354 693 355 694 354 1742 355 695 353 43687 354 1743 354 696 352 1744 353 1744 353 694 354 695 353 1742 355 694 354 1743 354 694 354 1743 354 1742 355 1742 355 694 354 1744 353 41606 351 1745 352 696 352 1746 351 1746 351 697 351 1747 350 696 352 1745 352 698 350 1746 351 698 350 696 352 698 350 1746 351 699 349
#
#
name: Power
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 02 00 00 00
#
#
name: Power
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 02 00 00 00
#
#
name: Mute
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: D1 00 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 00 00 00 00
command: 8A 00 00 00
#
#
name: Power
type: parsed
protocol: NEC
address: 00 00 00 00
command: 8A 00 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 81 03 00 00
command: F0 0F 00 00
#
#
name: Power
type: parsed
protocol: NECext
address: 81 03 00 00
command: F0 0F 00 00
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9033 4255 562 543 564 1701 562 1674 563 567 565 541 564 567 565 567 537 1700 564 1700 537 568 564 1699 564 541 565 1699 565 540 565 567 565 570 535 567 565 567 565 1673 564 567 565 1673 564 567 565 540 618 514 565 1700 563 1674 564 567 564 1674 563 569 563 1672 565 1700 589 1648 564
#
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9033 4255 562 543 564 1701 562 1674 563 567 565 541 564 567 565 567 537 1700 564 1700 537 568 564 1699 564 541 565 1699 565 540 565 567 565 570 535 567 565 567 565 1673 564 567 565 1673 564 567 565 540 618 514 565 1700 563 1674 564 567 564 1674 563 569 563 1672 565 1700 589 1648 564
#
#
name: Mute
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9034 4255 564 567 564 1671 566 1699 564 540 565 567 565 540 564 566 566 1699 563 1672 567 565 566 1673 565 567 565 1672 565 566 566 539 565 567 565 1698 539 566 566 1698 564 541 565 565 567 1672 565 566 566 1672 565 567 565 1699 538 566 566 1698 564 1673 566 566 565 1672 566 566 566
#
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9035 4254 565 566 565 1672 566 1699 564 539 567 566 566 540 564 567 565 1698 539 1698 566 566 564 1673 565 566 565 1672 565 566 566 539 566 566 566 567 563 541 565 566 566 538 566 566 566 1671 566 566 566 1697 565 1675 563 1699 563 1674 565 1699 538 1700 564 565 565 1674 564 567 565
#
#
name: Vol_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9010 4253 564 566 566 1671 566 1699 565 565 539 568 564 566 565 539 566 1699 565 1672 565 566 566 1672 564 567 565 1672 565 567 565 567 564 541 564 1698 566 539 565 567 565 567 562 542 565 1699 564 539 567 1699 565 540 564 1698 566 1672 565 1698 566 1672 565 567 565 1671 565 566 566
#
name: Vol_up
type: parsed
protocol: NECext
address: 00 30 00 00
command: 97 68 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 00 30 00 00
command: 98 67 00 00
#
name: Power
type: parsed
protocol: NECext
address: 48 50 00 00
command: 02 FD 00 00
#
name: Power
type: parsed
protocol: NECext
address: 48 50 00 00
command: 02 FD 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 48 50 00 00
command: 27 D8 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2288 611 571 587 1153 587 572 587 572 1167 572 1168 571 587 573 587 572 587 572 589 570 587 572 587 572 587 572 588 571 77346 2287 611 571 588 1152 588 571 588 571 1168 571 1168 571 588 571 588 571 588 571 589 570 587 572 588 572 587 572 587 572
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2288 611 571 587 1153 587 572 587 572 1167 572 1168 571 587 573 587 572 587 572 589 570 587 572 587 572 587 572 588 571 77346 2287 611 571 588 1152 588 571 588 571 1168 571 1168 571 588 571 588 571 588 571 589 570 587 572 588 572 587 572 587 572
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2288 610 572 588 1151 587 573 588 571 1169 570 586 573 587 572 587 1152 587 573 586 1153 588 572 587 573 586 573 587 572 76771 2289 610 572 588 1151 587 573 587 572 1166 573 587 572 587 572 587 1152 588 571 588 1151 587 573 587 572 587 573 588 571
#
name: Vol_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2310 588 571 589 1151 588 571 589 570 1168 572 1169 1150 588 1151 588 1151 590 570 588 1152 588 572 589 570 588 572 588 572 75033 2288 609 573 587 1152 587 572 586 574 1168 571 1167 1152 587 1152 586 1153 587 572 587 1152 587 572 588 571 587 572 587 572
#
name: Mute
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2288 610 572 586 1153 587 573 587 572 1166 574 1168 571 1167 1152 588 572 587 1152 588 572 587 572 587 573 587 572 588 571 75611 2288 610 572 587 1152 588 572 588 572 1167 572 1166 573 1167 1152 587 573 587 1152 588 572 588 571 586 573 586 573 585 574
#
name: Mute
type: parsed
protocol: NECext
address: 83 55 00 00
command: AD 52 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 83 55 00 00
command: B1 4E 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 83 55 00 00
command: B3 4C 00 00
#
name: Power
type: parsed
protocol: NEC
address: 30 00 00 00
command: 0B 00 00 00
#
name: Power
type: parsed
protocol: NEC
address: 30 00 00 00
command: 0B 00 00 00
#
name: Power
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 0A F5 00 00
#
name: Power
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 0A F5 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 4A B5 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 48 B7 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 0E F1 00 00
#
name: Power
type: parsed
protocol: NECext
address: 00 6A 00 00
command: 40 BF 00 00
#
name: Power
type: parsed
protocol: NECext
address: 00 6A 00 00
command: 40 BF 00 00
#
name: Power
type: parsed
protocol: NECext
address: 04 0F 00 00
command: AD 52 00 00
#
name: Power
type: parsed
protocol: NECext
address: 04 0F 00 00
command: AD 52 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 04 0F 00 00
command: 02 FD 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 04 0F 00 00
command: 03 FC 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 04 0F 00 00
command: 09 F6 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 32 00 00 00
command: 03 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 32 00 00 00
command: 09 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 32 00 00 00
command: 0C 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9024 4506 570 582 542 582 518 606 518 606 518 606 518 606 518 606 518 610 518 1698 548 1698 546 1700 546 1700 546 1698 546 1700 546 1696 548 1702 570 1674 546 610 516 1698 546 606 542 582 542 582 544 1674 546 610 520 606 542 1674 570 582 542 1676 546 1698 570 1674 572 582 520 1698 546
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9024 4506 570 582 542 582 518 606 518 606 518 606 518 606 518 606 518 610 518 1698 548 1698 546 1700 546 1700 546 1698 546 1700 546 1696 548 1702 570 1674 546 610 516 1698 546 606 542 582 542 582 544 1674 546 610 520 606 542 1674 570 582 542 1676 546 1698 570 1674 572 582 520 1698 546
#
name: Mute
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9044 4484 572 580 544 580 544 580 544 580 542 582 544 580 520 604 542 586 544 1674 570 1674 570 1676 570 1674 572 1672 570 1674 546 1700 568 1678 570 582 542 1672 572 580 542 580 542 1674 566 582 542 1672 570 584 538 1674 564 580 534 1674 556 1674 556 580 530 1672 558 582 524 1676 552
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1221 1171 433 566 433 881 433 2381 433 1486 434 565 434 1486 433 1776 433 2380 433 565 434 2381 433 1170 434 87358 1220 1171 433 566 433 883 431 2381 433 1486 433 567 432 1487 432 1775 434 2381 432 566 433 2380 434 1171 433 86252 1221 1172 432 565 434 880 435 2381 433 1486 433 565 434 1487 432 1776 433 2380 434 566 433 2379 434 1170 434
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1221 1171 433 566 433 881 433 2381 433 1486 434 565 434 1486 433 1776 433 2380 433 565 434 2381 433 1170 434 87358 1220 1171 433 566 433 883 431 2381 433 1486 433 567 432 1487 432 1775 434 2381 432 566 433 2380 434 1171 433 86252 1221 1172 432 565 434 880 435 2381 433 1486 433 565 434 1487 432 1776 433 2380 434 566 433 2379 434 1170 434
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1221 1173 431 566 433 882 432 2382 432 1487 432 566 433 565 434 566 433 2671 432 2670 433 2381 433 566 433 87411 324 937 325 358 325 647 326
#
name: Vol_dn
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1221 1172 432 566 433 882 433 2381 432 1486 434 566 433 565 434 882 433 1486 434 2669 434 2065 433 566 433 87779 324 936 326 358 325 647 326
#
name: Vol_up
type: parsed
protocol: SIRC15
address: 54 00 00 00
command: 12 00 00 00
#
name: Vol_dn
type: parsed
protocol: SIRC15
address: 54 00 00 00
command: 13 00 00 00
#
name: Play
type: parsed
protocol: NECext
address: 00 30 00 00
command: 21 DE 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 00 30 00 00
command: 21 DE 00 00
#
name: Play
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 09 F6 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 86 6B 00 00
command: 09 F6 00 00
#
name: Vol_down
type: parsed
protocol: NECext
address: 4F 50 00 00
command: 0B F4 00 00

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ ADD_SCENE(infrared, universal_ac, UniversalAC)
ADD_SCENE(infrared, universal_fan, UniversalFan)
ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, universal_leds, UniversalLEDs)
ADD_SCENE(infrared, gpio_settings, GpioSettings)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)

View file

@ -4,6 +4,7 @@ typedef enum {
SubmenuIndexUniversalTV,
SubmenuIndexUniversalAudio,
SubmenuIndexUniversalProjector,
SubmenuIndexUniversalLEDs,
SubmenuIndexUniversalFan,
SubmenuIndexUniversalAirConditioner,
} SubmenuIndex;
@ -38,6 +39,13 @@ void infrared_scene_universal_on_enter(void* context) {
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"LEDs",
SubmenuIndexUniversalLEDs,
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"Fans",
@ -73,6 +81,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexUniversalProjector) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);
consumed = true;
} else if(event.event == SubmenuIndexUniversalLEDs) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalLEDs);
consumed = true;
} else if(event.event == SubmenuIndexUniversalFan) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalFan);
consumed = true;

View file

@ -0,0 +1,133 @@
#include "../infrared_app_i.h"
#include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_leds_on_enter(void* context) {
InfraredApp* infrared = context;
ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force;
// Button codes
// Power_off, Power_on, Brightness_up, Brightness_dn, Red, Blue, Green, White
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/leds.ir"));
button_panel_reserve(button_panel, 2, 4);
uint32_t i = 0;
button_panel_add_item(
button_panel,
i,
0,
0,
10,
12,
&I_power_19x20,
&I_power_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
button_panel_add_icon(button_panel, 15, 34, &I_on_text_9x5);
infrared_brute_force_add_record(brute_force, i++, "Power_on");
button_panel_add_item(
button_panel,
i,
1,
0,
35,
12,
&I_off_19x20,
&I_off_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
button_panel_add_icon(button_panel, 38, 34, &I_off_text_12x5);
infrared_brute_force_add_record(brute_force, i++, "Power_off");
button_panel_add_item(
button_panel,
i,
0,
1,
10,
42,
&I_plus_19x20,
&I_plus_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "Brightness_up");
button_panel_add_item(
button_panel,
i,
1,
1,
35,
42,
&I_minus_19x20,
&I_minus_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
button_panel_add_icon(button_panel, 12, 64, &I_brightness_text_40x5);
infrared_brute_force_add_record(brute_force, i++, "Brightness_dn");
button_panel_add_item(
button_panel,
i,
0,
2,
10,
74,
&I_red_19x20,
&I_red_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "Red");
button_panel_add_item(
button_panel,
i,
1,
2,
35,
74,
&I_green_19x20,
&I_green_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "Green");
button_panel_add_item(
button_panel,
i,
0,
3,
10,
99,
&I_blue_19x20,
&I_blue_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "Blue");
button_panel_add_item(
button_panel,
i,
1,
3,
35,
99,
&I_white_19x20,
&I_white_hover_19x20,
infrared_scene_universal_common_item_callback,
context);
button_panel_add_icon(button_panel, 19, 121, &I_color_text_24x5);
infrared_brute_force_add_record(brute_force, i++, "White");
button_panel_add_label(button_panel, 20, 9, FontPrimary, "LEDs");
infrared_scene_universal_common_on_enter(context);
}
bool infrared_scene_universal_leds_on_event(void* context, SceneManagerEvent event) {
return infrared_scene_universal_common_on_event(context, event);
}
void infrared_scene_universal_leds_on_exit(void* context) {
infrared_scene_universal_common_on_exit(context);
}

View file

@ -2,6 +2,20 @@
#define TAG "Mosgortrans"
void render_section_header(
FuriString* str,
const char* name,
uint8_t prefix_separator_cnt,
uint8_t suffix_separator_cnt) {
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
furi_string_cat_printf(str, ":");
}
furi_string_cat_printf(str, "[ %s ]", name);
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
furi_string_cat_printf(str, ":");
}
}
void from_days_to_datetime(uint32_t days, DateTime* datetime, uint16_t start_year) {
uint32_t timestamp = days * 24 * 60 * 60;
DateTime start_datetime = {0};

View file

@ -10,6 +10,11 @@
extern "C" {
#endif
void render_section_header(
FuriString* str,
const char* name,
uint8_t prefix_separator_cnt,
uint8_t suffix_separator_cnt);
bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result);
#ifdef __cplusplus

View file

@ -15,4 +15,11 @@ static constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(
API_METHOD(
mosgortrans_parse_transport_block,
bool,
(const MfClassicBlock* block, FuriString* result))));
(const MfClassicBlock* block, FuriString* result)),
API_METHOD(
render_section_header,
void,
(FuriString * str,
const char* name,
uint8_t prefix_separator_cnt,
uint8_t suffix_separator_cnt))));

View file

@ -246,8 +246,28 @@ App(
)
App(
appid="ndef_parser",
appid="ndef_ul_parser",
apptype=FlipperAppType.PLUGIN,
cdefines=[("NDEF_PROTO", "NDEF_PROTO_UL")],
entry_point="ndef_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/ndef.c"],
)
App(
appid="ndef_mfc_parser",
apptype=FlipperAppType.PLUGIN,
cdefines=[("NDEF_PROTO", "NDEF_PROTO_MFC")],
entry_point="ndef_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/ndef.c"],
)
App(
appid="ndef_slix_parser",
apptype=FlipperAppType.PLUGIN,
cdefines=[("NDEF_PROTO", "NDEF_PROTO_SLIX")],
entry_point="ndef_plugin_ep",
targets=["f7"],
requires=["nfc"],
@ -272,6 +292,24 @@ App(
sources=["plugins/supported_cards/skylanders.c"],
)
App(
appid="hworld_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="hworld_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/hworld.c"],
)
App(
appid="trt_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="trt_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/trt.c"],
)
App(
appid="nfc_cli",
targets=["f7"],

View file

@ -117,7 +117,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) {
if(!mf_classic_is_card_read(data)) {
submenu_add_item(
submenu,
"Extract MF Keys",
"Extract MFC Keys",
SubmenuIndexDetectReader,
nfc_protocol_support_common_submenu_callback,
instance);
@ -155,7 +155,7 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
if(!mf_classic_is_card_read(data)) {
submenu_add_item(
submenu,
"Extract MF Keys",
"Extract MFC Keys",
SubmenuIndexDetectReader,
nfc_protocol_support_common_submenu_callback,
instance);

View file

@ -559,6 +559,7 @@ static void nfc_protocol_support_scene_save_name_on_exit(NfcApp* instance) {
*/
enum {
NfcSceneEmulateStateWidget, /**< Widget view is displayed. */
NfcSceneEmulateStateWidgetLog, /**< Widget view with Log button is displayed */
NfcSceneEmulateStateTextBox, /**< TextBox view is displayed. */
};
@ -637,12 +638,14 @@ static bool
"Log",
nfc_protocol_support_common_widget_callback,
instance);
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidgetLog);
}
// Update TextBox data
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
consumed = true;
} else if(event.event == GuiButtonTypeCenter) {
if(state == NfcSceneEmulateStateWidget) {
if(state == NfcSceneEmulateStateWidgetLog) {
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateTextBox);
@ -653,7 +656,7 @@ static bool
if(state == NfcSceneEmulateStateTextBox) {
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget);
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidgetLog);
consumed = true;
}
}

View file

@ -75,8 +75,12 @@
#define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log"
#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME)
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_user_nested.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc")
typedef enum {
NfcRpcStateIdle,
@ -94,6 +98,12 @@ typedef struct {
bool is_key_attack;
uint8_t key_attack_current_sector;
bool is_card_present;
MfClassicNestedPhase nested_phase;
MfClassicPrngType prng_type;
MfClassicBackdoor backdoor;
uint16_t nested_target_key;
uint16_t msb_count;
bool enhanced_dict;
} NfcMfClassicDictAttackContext;
struct NfcApp {

View file

@ -107,7 +107,7 @@ static const IdMapping bart_zones[] = {
{.id = 0x0023, .name = "South Hayward"},
{.id = 0x0024, .name = "Union City"},
{.id = 0x0025, .name = "Fremont"},
{.id = 0x0026, .name = "Daly City(2)?"},
{.id = 0x0026, .name = "Castro Valley"},
{.id = 0x0027, .name = "Dublin/Pleasanton"},
{.id = 0x0028, .name = "South San Francisco"},
{.id = 0x0029, .name = "San Bruno"},
@ -116,6 +116,8 @@ static const IdMapping bart_zones[] = {
{.id = 0x002c, .name = "West Dublin/Pleasanton"},
{.id = 0x002d, .name = "OAK Airport"},
{.id = 0x002e, .name = "Warm Springs/South Fremont"},
{.id = 0x002f, .name = "Milpitas"},
{.id = 0x0030, .name = "Berryessa/North San Jose"},
};
static const size_t kNumBARTZones = COUNT(bart_zones);
@ -138,6 +140,19 @@ static const IdMapping actransit_zones[] = {
};
static const size_t kNumACTransitZones = COUNT(actransit_zones);
// Instead of persisting individual Station IDs, Caltrain saves Zone numbers.
// https://www.caltrain.com/stations-zones
static const IdMapping caltrain_zones[] = {
{.id = 0x0001, .name = "Zone 1"},
{.id = 0x0002, .name = "Zone 2"},
{.id = 0x0003, .name = "Zone 3"},
{.id = 0x0004, .name = "Zone 4"},
{.id = 0x0005, .name = "Zone 5"},
{.id = 0x0006, .name = "Zone 6"},
};
static const size_t kNumCaltrainZones = COUNT(caltrain_zones);
//
// Full agency+zone mapping.
//
@ -148,6 +163,7 @@ static const struct {
} agency_zone_map[] = {
{.agency_id = 0x0001, .zone_map = actransit_zones, .zone_count = kNumACTransitZones},
{.agency_id = 0x0004, .zone_map = bart_zones, .zone_count = kNumBARTZones},
{.agency_id = 0x0006, .zone_map = caltrain_zones, .zone_count = kNumCaltrainZones},
{.agency_id = 0x0012, .zone_map = muni_zones, .zone_count = kNumMUNIZones}};
static const size_t kNumAgencyZoneMaps = COUNT(agency_zone_map);

View file

@ -0,0 +1,243 @@
// Flipper Zero parser for H World Hotel Key Cards
// H World operates around 10,000 hotels, most of which in mainland China
// Reverse engineering and parser written by @Torron (Github: @zinongli)
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <bit_lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAG "H World"
#define ROOM_SECTOR 1
#define VIP_SECTOR 5
#define ROOM_SECTOR_KEY_BLOCK 7
#define VIP_SECTOR_KEY_BLOCK 23
#define ACCESS_INFO_BLOCK 5
#define ROOM_NUM_DECIMAL_BLOCK 6
#define H_WORLD_YEAR_OFFSET 2000
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static MfClassicKeyPair hworld_standard_keys[] = {
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 000
{.a = 0x543071543071, .b = 0x5F01015F0101}, // 001
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 002
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 003
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 004
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 005
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 006
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 007
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 008
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 009
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 010
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 011
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 012
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 013
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 014
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 015
};
static MfClassicKeyPair hworld_vip_keys[] = {
{.a = 0x000000000000, .b = 0xFFFFFFFFFFFF}, // 000
{.a = 0x543071543071, .b = 0x5F01015F0101}, // 001
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 002
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 003
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 004
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 005
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 006
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 007
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 008
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 009
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 010
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 011
{.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 012
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 013
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 014
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 015
};
static bool hworld_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t block_num = mf_classic_get_first_block_num_of_sector(ROOM_SECTOR);
MfClassicKey standard_key = {0};
bit_lib_num_to_bytes_be(
hworld_standard_keys[ROOM_SECTOR].a, COUNT_OF(standard_key.data), standard_key.data);
MfClassicAuthContext auth_context;
MfClassicError standard_error = mf_classic_poller_sync_auth(
nfc, block_num, &standard_key, MfClassicKeyTypeA, &auth_context);
if(standard_error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed static key check for block %u", block_num);
break;
}
MfClassicKey vip_key = {0};
bit_lib_num_to_bytes_be(
hworld_vip_keys[VIP_SECTOR].b, COUNT_OF(vip_key.data), vip_key.data);
MfClassicError vip_error = mf_classic_poller_sync_auth(
nfc, block_num, &vip_key, MfClassicKeyTypeB, &auth_context);
if(vip_error == MfClassicErrorNone) {
FURI_LOG_D(TAG, "VIP card detected");
} else {
FURI_LOG_D(TAG, "Standard card detected");
}
verified = true;
} while(false);
return verified;
}
static bool hworld_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError standard_error = mf_classic_poller_sync_detect_type(nfc, &type);
MfClassicError vip_error = MfClassicErrorNotPresent;
if(standard_error != MfClassicErrorNone) break;
data->type = type;
MfClassicDeviceKeys standard_keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
hworld_standard_keys[i].a, sizeof(MfClassicKey), standard_keys.key_a[i].data);
FURI_BIT_SET(standard_keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
hworld_standard_keys[i].b, sizeof(MfClassicKey), standard_keys.key_b[i].data);
FURI_BIT_SET(standard_keys.key_b_mask, i);
}
standard_error = mf_classic_poller_sync_read(nfc, &standard_keys, data);
if(standard_error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Standard card successfully read");
} else {
MfClassicDeviceKeys vip_keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
hworld_vip_keys[i].a, sizeof(MfClassicKey), vip_keys.key_a[i].data);
FURI_BIT_SET(vip_keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
hworld_vip_keys[i].b, sizeof(MfClassicKey), vip_keys.key_b[i].data);
FURI_BIT_SET(vip_keys.key_b_mask, i);
}
vip_error = mf_classic_poller_sync_read(nfc, &vip_keys, data);
if(vip_error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "VIP card successfully read");
} else {
break;
}
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = (standard_error == MfClassicErrorNone) | (vip_error == MfClassicErrorNone);
} while(false);
mf_classic_free(data);
return is_read;
}
bool hworld_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Check card type
if(data->type != MfClassicType1k) break;
// Check static key for verificaiton
const uint8_t* data_room_sec_key_a_ptr = &data->block[ROOM_SECTOR_KEY_BLOCK].data[0];
const uint8_t* data_room_sec_key_b_ptr = &data->block[ROOM_SECTOR_KEY_BLOCK].data[10];
uint64_t data_room_sec_key_a = bit_lib_get_bits_64(data_room_sec_key_a_ptr, 0, 48);
uint64_t data_room_sec_key_b = bit_lib_get_bits_64(data_room_sec_key_b_ptr, 0, 48);
if((data_room_sec_key_a != hworld_standard_keys[ROOM_SECTOR].a) |
(data_room_sec_key_b != hworld_standard_keys[ROOM_SECTOR].b))
break;
// Check whether this card is VIP
const uint8_t* data_vip_sec_key_b_ptr = &data->block[VIP_SECTOR_KEY_BLOCK].data[10];
uint64_t data_vip_sec_key_b = bit_lib_get_bits_64(data_vip_sec_key_b_ptr, 0, 48);
bool is_hworld_vip = (data_vip_sec_key_b == hworld_vip_keys[VIP_SECTOR].b);
uint8_t room_floor = data->block[ACCESS_INFO_BLOCK].data[13];
uint8_t room_num = data->block[ACCESS_INFO_BLOCK].data[14];
// Check in date & time
uint16_t check_in_year = data->block[ACCESS_INFO_BLOCK].data[2] + H_WORLD_YEAR_OFFSET;
uint8_t check_in_month = data->block[ACCESS_INFO_BLOCK].data[3];
uint8_t check_in_day = data->block[ACCESS_INFO_BLOCK].data[4];
uint8_t check_in_hour = data->block[ACCESS_INFO_BLOCK].data[5];
uint8_t check_in_minute = data->block[ACCESS_INFO_BLOCK].data[6];
// Expire date & time
uint16_t expire_year = data->block[ACCESS_INFO_BLOCK].data[7] + H_WORLD_YEAR_OFFSET;
uint8_t expire_month = data->block[ACCESS_INFO_BLOCK].data[8];
uint8_t expire_day = data->block[ACCESS_INFO_BLOCK].data[9];
uint8_t expire_hour = data->block[ACCESS_INFO_BLOCK].data[10];
uint8_t expire_minute = data->block[ACCESS_INFO_BLOCK].data[11];
furi_string_cat_printf(parsed_data, "\e#H World Card\n");
furi_string_cat_printf(
parsed_data, "%s\n", is_hworld_vip ? "VIP card" : "Standard room key");
furi_string_cat_printf(parsed_data, "Room Num: %u%02u\n", room_floor, room_num);
furi_string_cat_printf(
parsed_data,
"Check-in Date: \n%04u-%02d-%02d\n%02d:%02d:00\n",
check_in_year,
check_in_month,
check_in_day,
check_in_hour,
check_in_minute);
furi_string_cat_printf(
parsed_data,
"Expiration Date: \n%04u-%02d-%02d\n%02d:%02d:00",
expire_year,
expire_month,
expire_day,
expire_hour,
expire_minute);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin hworld_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = hworld_verify,
.read = hworld_read,
.parse = hworld_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor hworld_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &hworld_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* hworld_plugin_ep(void) {
return &hworld_plugin_descriptor;
}

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,24 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <bit_lib/bit_lib.h>
#include <datetime.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <bit_lib.h>
#define TAG "Plantain"
void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {
uint32_t timestamp = minutes * 60;
DateTime start_datetime = {0};
start_datetime.year = start_year - 1;
start_datetime.month = 12;
start_datetime.day = 31;
timestamp += datetime_datetime_to_timestamp(&start_datetime);
datetime_timestamp_to_datetime(timestamp, datetime);
}
typedef struct {
uint64_t a;
uint64_t b;
@ -189,8 +201,9 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
size_t uid_len = 0;
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
bool parsed = false;
@ -207,29 +220,161 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(key != cfg.keys[cfg.data_sector].a) break;
// Point to block 0 of sector 4, value 0
const uint8_t* temp_ptr = data->block[16].data;
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = data->block[0].data;
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
furi_string_printf(parsed_data, "\e#Plantain card\n");
const uint8_t* temp_ptr = &uid[0];
// UID is read from last to first byte
uint8_t card_number_tmp[uid_len];
if(uid_len == 4) {
for(size_t i = 0; i < 4; i++) {
card_number_tmp[i] = temp_ptr[3 - i];
}
} else if(uid_len == 7) {
for(size_t i = 0; i < 7; i++) {
card_number_tmp[i] = temp_ptr[6 - i];
}
} else {
break;
}
// Copy card number to uint64_t
//UID is converted to a card number
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
for(size_t i = 0; i < uid_len; i++) {
card_number = (card_number << 8) | card_number_tmp[i];
}
furi_string_printf(
parsed_data, "\e#Plantain\nNo.: %lluX\nBalance: %lu\n", card_number, balance);
// Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards.
furi_string_cat_printf(parsed_data, "Number: ");
FuriString* card_number_s = furi_string_alloc();
furi_string_cat_printf(card_number_s, "%llu", card_number);
FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 ");
for(uint8_t i = 0; i < 24; i += 4) {
for(uint8_t j = 0; j < 4; j++) {
furi_string_push_back(tmp_s, furi_string_get_char(card_number_s, i + j));
}
furi_string_push_back(tmp_s, ' ');
}
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tmp_s));
// this works for 2K Plantain
if(data->type == MfClassicType1k) {
//balance
uint32_t balance = 0;
for(uint8_t i = 0; i < 4; i++) {
balance = (balance << 8) | data->block[16].data[3 - i];
}
furi_string_cat_printf(parsed_data, "Balance: %ld rub\n", balance / 100);
//trips
uint8_t trips_metro = data->block[21].data[0];
uint8_t trips_ground = data->block[21].data[1];
furi_string_cat_printf(parsed_data, "Trips: %d\n", trips_metro + trips_ground);
//trip time
uint32_t last_trip_timestamp = 0;
for(uint8_t i = 0; i < 3; i++) {
last_trip_timestamp = (last_trip_timestamp << 8) | data->block[21].data[4 - i];
}
DateTime last_trip = {0};
from_minutes_to_datetime(last_trip_timestamp + 24 * 60, &last_trip, 2010);
furi_string_cat_printf(
parsed_data,
"Trip start: %02d.%02d.%04d %02d:%02d\n",
last_trip.day,
last_trip.month,
last_trip.year,
last_trip.hour,
last_trip.minute);
//validator
uint16_t validator = (data->block[20].data[5] << 8) | data->block[20].data[4];
furi_string_cat_printf(parsed_data, "Validator: %d\n", validator);
//tariff
uint16_t fare = (data->block[20].data[7] << 8) | data->block[20].data[6];
furi_string_cat_printf(parsed_data, "Tariff: %d rub\n", fare / 100);
//trips in metro
furi_string_cat_printf(parsed_data, "Trips (Metro): %d\n", trips_metro);
//trips on ground
furi_string_cat_printf(parsed_data, "Trips (Ground): %d\n", trips_ground);
//last payment
uint32_t last_payment_timestamp = 0;
for(uint8_t i = 0; i < 3; i++) {
last_payment_timestamp = (last_payment_timestamp << 8) |
data->block[18].data[4 - i];
}
DateTime last_payment_date = {0};
from_minutes_to_datetime(last_payment_timestamp + 24 * 60, &last_payment_date, 2010);
furi_string_cat_printf(
parsed_data,
"Last pay: %02d.%02d.%04d %02d:%02d\n",
last_payment_date.day,
last_payment_date.month,
last_payment_date.year,
last_payment_date.hour,
last_payment_date.minute);
//payment amount. This needs to be investigated more, currently it shows incorrect amount on some cards.
uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8];
furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100);
furi_string_free(card_number_s);
furi_string_free(tmp_s);
//This is for 4K Plantains.
} else if(data->type == MfClassicType4k) {
//balance
uint32_t balance = 0;
for(uint8_t i = 0; i < 4; i++) {
balance = (balance << 8) | data->block[16].data[3 - i];
}
furi_string_cat_printf(parsed_data, "Balance: %ld rub\n", balance / 100);
//trips
uint8_t trips_metro = data->block[21].data[0];
uint8_t trips_ground = data->block[21].data[1];
furi_string_cat_printf(parsed_data, "Trips: %d\n", trips_metro + trips_ground);
//trip time
uint32_t last_trip_timestamp = 0;
for(uint8_t i = 0; i < 3; i++) {
last_trip_timestamp = (last_trip_timestamp << 8) | data->block[21].data[4 - i];
}
DateTime last_trip = {0};
from_minutes_to_datetime(last_trip_timestamp + 24 * 60, &last_trip, 2010);
furi_string_cat_printf(
parsed_data,
"Trip start: %02d.%02d.%04d %02d:%02d\n",
last_trip.day,
last_trip.month,
last_trip.year,
last_trip.hour,
last_trip.minute);
//validator
uint16_t validator = (data->block[20].data[5] << 8) | data->block[20].data[4];
furi_string_cat_printf(parsed_data, "Validator: %d\n", validator);
//tariff
uint16_t fare = (data->block[20].data[7] << 8) | data->block[20].data[6];
furi_string_cat_printf(parsed_data, "Tariff: %d rub\n", fare / 100);
//trips in metro
furi_string_cat_printf(parsed_data, "Trips (Metro): %d\n", trips_metro);
//trips on ground
furi_string_cat_printf(parsed_data, "Trips (Ground): %d\n", trips_ground);
//last payment
uint32_t last_payment_timestamp = 0;
for(uint8_t i = 0; i < 3; i++) {
last_payment_timestamp = (last_payment_timestamp << 8) |
data->block[18].data[4 - i];
}
DateTime last_payment_date = {0};
from_minutes_to_datetime(last_payment_timestamp + 24 * 60, &last_payment_date, 2010);
furi_string_cat_printf(
parsed_data,
"Last pay: %02d.%02d.%04d %02d:%02d\n",
last_payment_date.day,
last_payment_date.month,
last_payment_date.year,
last_payment_date.hour,
last_payment_date.minute);
//payment amount
uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8];
furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100);
furi_string_free(card_number_s);
furi_string_free(tmp_s);
}
parsed = true;
} while(false);

File diff suppressed because it is too large Load diff

View file

@ -82,20 +82,6 @@ static const MfClassicKeyPair troika_4k_keys[] = {
{.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40
};
static void troika_render_section_header(
FuriString* str,
const char* name,
uint8_t prefix_separator_cnt,
uint8_t suffix_separator_cnt) {
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
furi_string_cat_printf(str, ":");
}
furi_string_cat_printf(str, "[ %s ]", name);
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
furi_string_cat_printf(str, ":");
}
}
static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
bool success = true;
@ -212,23 +198,25 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
FuriString* ground_result = furi_string_alloc();
FuriString* tat_result = furi_string_alloc();
bool result1 = mosgortrans_parse_transport_block(&data->block[32], metro_result);
bool result2 = mosgortrans_parse_transport_block(&data->block[28], ground_result);
bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result);
bool is_metro_data_present =
mosgortrans_parse_transport_block(&data->block[32], metro_result);
bool is_ground_data_present =
mosgortrans_parse_transport_block(&data->block[28], ground_result);
bool is_tat_data_present = mosgortrans_parse_transport_block(&data->block[16], tat_result);
furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
if(result1 && !furi_string_empty(metro_result)) {
troika_render_section_header(parsed_data, "Metro", 22, 21);
if(is_metro_data_present && !furi_string_empty(metro_result)) {
render_section_header(parsed_data, "Metro", 22, 21);
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(metro_result));
}
if(result2 && !furi_string_empty(ground_result)) {
troika_render_section_header(parsed_data, "Ediny", 22, 22);
if(is_ground_data_present && !furi_string_empty(ground_result)) {
render_section_header(parsed_data, "Ediny", 22, 22);
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(ground_result));
}
if(result3 && !furi_string_empty(tat_result)) {
troika_render_section_header(parsed_data, "TAT", 24, 23);
if(is_tat_data_present && !furi_string_empty(tat_result)) {
render_section_header(parsed_data, "TAT", 24, 23);
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tat_result));
}
@ -236,7 +224,7 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_string_free(ground_result);
furi_string_free(metro_result);
parsed = result1 || result2 || result3;
parsed = is_metro_data_present || is_ground_data_present || is_tat_data_present;
} while(false);
return parsed;

View file

@ -0,0 +1,97 @@
// Flipper Zero parser for for Tianjin Railway Transit (TRT)
// https://en.wikipedia.org/wiki/Tianjin_Metro
// Reverse engineering and parser development by @Torron (Github: @zinongli)
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <bit_lib.h>
#define TAG "TrtParser"
#define LATEST_SALE_MARKER 0x02
#define FULL_SALE_TIME_STAMP_PAGE 0x09
#define BALANCE_PAGE 0x08
#define SALE_RECORD_TIME_STAMP_A 0x0C
#define SALE_RECORD_TIME_STAMP_B 0x0E
#define SALE_YEAR_OFFSET 2000
static bool trt_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
bool parsed = false;
do {
uint8_t latest_sale_page = 0;
// Look for sale record signature
if(data->page[SALE_RECORD_TIME_STAMP_A].data[0] == LATEST_SALE_MARKER) {
latest_sale_page = SALE_RECORD_TIME_STAMP_A;
} else if(data->page[SALE_RECORD_TIME_STAMP_B].data[0] == LATEST_SALE_MARKER) {
latest_sale_page = SALE_RECORD_TIME_STAMP_B;
} else {
break;
}
// Check if the sale record was backed up
const uint8_t* partial_record_pointer = &data->page[latest_sale_page - 1].data[0];
const uint8_t* full_record_pointer = &data->page[FULL_SALE_TIME_STAMP_PAGE].data[0];
uint32_t latest_sale_record = bit_lib_get_bits_32(partial_record_pointer, 3, 20);
uint32_t latest_sale_full_record = bit_lib_get_bits_32(full_record_pointer, 0, 27);
if(latest_sale_record != (latest_sale_full_record & 0xFFFFF))
break; // check if the copy matches
if((latest_sale_record == 0) || (latest_sale_full_record == 0))
break; // prevent false positive
// Parse date
// yyy yyyymmmm dddddhhh hhnnnnnn
uint16_t sale_year = ((latest_sale_full_record & 0x7F00000) >> 20) + SALE_YEAR_OFFSET;
uint8_t sale_month = (latest_sale_full_record & 0xF0000) >> 16;
uint8_t sale_day = (latest_sale_full_record & 0xF800) >> 11;
uint8_t sale_hour = (latest_sale_full_record & 0x7C0) >> 6;
uint8_t sale_minute = latest_sale_full_record & 0x3F;
// Parse balance
uint16_t balance = bit_lib_get_bits_16(&data->page[BALANCE_PAGE].data[2], 0, 16);
uint16_t balance_yuan = balance / 100;
uint8_t balance_cent = balance % 100;
// Format string for rendering
furi_string_cat_printf(parsed_data, "\e#TRT Tianjin Metro\n");
furi_string_cat_printf(parsed_data, "Single-Use Ticket\n");
furi_string_cat_printf(parsed_data, "Balance: %u.%02u RMB\n", balance_yuan, balance_cent);
furi_string_cat_printf(
parsed_data,
"Sale Date: \n%04u-%02d-%02d %02d:%02d",
sale_year,
sale_month,
sale_day,
sale_hour,
sale_minute);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin trt_plugin = {
.protocol = NfcProtocolMfUltralight,
.verify = NULL,
.read = NULL,
.parse = trt_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor trt_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &trt_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* trt_plugin_ep(void) {
return &trt_plugin_descriptor;
}

View file

@ -4489,3 +4489,9 @@ EA19E58DD046
3F41891454EE
7EDAE7923287
11DDA4862A1C
# H World Hotel Chain Room Keys
543071543071
5F01015F0101
200510241234
# iq aparts hotel
505209266A1F

View file

@ -1,11 +1,15 @@
#include "../nfc_app_i.h"
#include <bit_lib/bit_lib.h>
#include <dolphin/dolphin.h>
#include <lib/nfc/protocols/mf_classic/mf_classic_poller.h>
#define TAG "NfcMfClassicDictAttack"
// TODO FL-3926: Fix lag when leaving the dictionary attack view after Hardnested
// TODO FL-3926: Re-enters backdoor detection between user and system dictionary if no backdoor is found
typedef enum {
DictAttackStateCUIDDictInProgress,
DictAttackStateUserDictInProgress,
DictAttackStateSystemDictInProgress,
} DictAttackState;
@ -29,7 +33,9 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
const MfClassicData* mfc_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttack;
mfc_event->data->poller_mode.mode = (instance->nfc_dict_context.enhanced_dict) ?
MfClassicPollerModeDictAttackEnhanced :
MfClassicPollerModeDictAttackStandard;
mfc_event->data->poller_mode.data = mfc_data;
instance->nfc_dict_context.sectors_total =
mf_classic_get_total_sectors_num(mfc_data->type);
@ -58,6 +64,11 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->nfc_dict_context.sectors_read = data_update->sectors_read;
instance->nfc_dict_context.keys_found = data_update->keys_found;
instance->nfc_dict_context.current_sector = data_update->current_sector;
instance->nfc_dict_context.nested_phase = data_update->nested_phase;
instance->nfc_dict_context.prng_type = data_update->prng_type;
instance->nfc_dict_context.backdoor = data_update->backdoor;
instance->nfc_dict_context.nested_target_key = data_update->nested_target_key;
instance->nfc_dict_context.msb_count = data_update->msb_count;
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
@ -117,19 +128,75 @@ static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) {
dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found);
dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current);
dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector);
dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase);
dict_attack_set_prng_type(instance->dict_attack, mfc_dict->prng_type);
dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);
dict_attack_set_nested_target_key(instance->dict_attack, mfc_dict->nested_target_key);
dict_attack_set_msb_count(instance->dict_attack, mfc_dict->msb_count);
}
}
static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
uint32_t state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateCUIDDictInProgress) {
size_t cuid_len = 0;
const uint8_t* cuid = nfc_device_get_uid(instance->nfc_device, &cuid_len);
FuriString* cuid_dict_path = furi_string_alloc_printf(
"%s/mf_classic_dict_%08lx.nfc",
EXT_PATH("nfc/assets"),
(uint32_t)bit_lib_bytes_to_num_be(cuid + (cuid_len - 4), 4));
do {
if(!keys_dict_check_presence(furi_string_get_cstr(cuid_dict_path))) {
state = DictAttackStateUserDictInProgress;
break;
}
instance->nfc_dict_context.dict = keys_dict_alloc(
furi_string_get_cstr(cuid_dict_path),
KeysDictModeOpenExisting,
sizeof(MfClassicKey));
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
keys_dict_free(instance->nfc_dict_context.dict);
state = DictAttackStateUserDictInProgress;
break;
}
dict_attack_set_header(instance->dict_attack, "MF Classic CUID Dictionary");
} while(false);
furi_string_free(cuid_dict_path);
}
if(state == DictAttackStateUserDictInProgress) {
do {
instance->nfc_dict_context.enhanced_dict = true;
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {
storage_common_remove(
instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH)) {
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}
if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress;
break;
}
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
}
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_USER_PATH,
NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
@ -164,7 +231,7 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
NfcApp* instance = context;
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
dict_attack_set_card_state(instance->dict_attack, true);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
@ -193,7 +260,21 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventDictAttackComplete) {
if(state == DictAttackStateUserDictInProgress) {
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
MfClassicNestedPhaseNone;
if(state == DictAttackStateCUIDDictInProgress) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
DictAttackStateUserDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
consumed = true;
} else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
@ -222,7 +303,27 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
} else if(event.event == NfcCustomEventDictAttackSkip) {
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);
if(state == DictAttackStateUserDictInProgress) {
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
MfClassicNestedPhaseNone;
if(state == DictAttackStateCUIDDictInProgress) {
if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
DictAttackStateUserDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
} else {
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
}
consumed = true;
} else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
@ -240,7 +341,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
dolphin_deed(DolphinDeedNfcReadSuccess);
}
consumed = true;
} else if(state == DictAttackStateSystemDictInProgress) {
} else {
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
@ -262,7 +363,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
dict_attack_reset(instance->dict_attack);
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);
keys_dict_free(instance->nfc_dict_context.dict);
@ -275,6 +376,20 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
instance->nfc_dict_context.is_key_attack = false;
instance->nfc_dict_context.key_attack_current_sector = 0;
instance->nfc_dict_context.is_card_present = false;
instance->nfc_dict_context.nested_phase = MfClassicNestedPhaseNone;
instance->nfc_dict_context.prng_type = MfClassicPrngTypeUnknown;
instance->nfc_dict_context.backdoor = MfClassicBackdoorUnknown;
instance->nfc_dict_context.nested_target_key = 0;
instance->nfc_dict_context.msb_count = 0;
instance->nfc_dict_context.enhanced_dict = false;
// Clean up temporary files used for nested dictionary attack
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
}
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}
nfc_blink_stop(instance);
notification_message(instance->notifications, &sequence_display_backlight_enforce_auto);

View file

@ -30,7 +30,7 @@ void nfc_scene_start_on_enter(void* context) {
submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc);
submenu_add_item(
submenu,
"Extract MF Keys",
"Extract MFC Keys",
SubmenuIndexDetectReader,
nfc_scene_start_submenu_callback,
nfc);

View file

@ -21,6 +21,11 @@ typedef struct {
size_t dict_keys_current;
bool is_key_attack;
uint8_t key_attack_current_sector;
MfClassicNestedPhase nested_phase;
MfClassicPrngType prng_type;
MfClassicBackdoor backdoor;
uint16_t nested_target_key;
uint16_t msb_count;
} DictAttackViewModel;
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
@ -34,9 +39,47 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
} else {
char draw_str[32] = {};
canvas_set_font(canvas, FontSecondary);
switch(m->nested_phase) {
case MfClassicNestedPhaseAnalyzePRNG:
furi_string_set(m->header, "PRNG Analysis");
break;
case MfClassicNestedPhaseDictAttack:
case MfClassicNestedPhaseDictAttackVerify:
case MfClassicNestedPhaseDictAttackResume:
furi_string_set(m->header, "Nested Dictionary");
break;
case MfClassicNestedPhaseCalibrate:
case MfClassicNestedPhaseRecalibrate:
furi_string_set(m->header, "Calibration");
break;
case MfClassicNestedPhaseCollectNtEnc:
furi_string_set(m->header, "Nonce Collection");
break;
default:
break;
}
if(m->prng_type == MfClassicPrngTypeHard) {
furi_string_cat(m->header, " (Hard)");
}
if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) {
if(m->nested_phase != MfClassicNestedPhaseNone) {
furi_string_cat(m->header, " (Backdoor)");
} else {
furi_string_set(m->header, "Backdoor Read");
}
}
canvas_draw_str_aligned(
canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
if(m->is_key_attack) {
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
uint8_t nonce_sector =
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector);
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
} else if(m->is_key_attack) {
snprintf(
draw_str,
sizeof(draw_str),
@ -46,21 +89,48 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector);
}
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
float dict_progress = m->dict_keys_total == 0 ?
0 :
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
float progress = m->sectors_total == 0 ? 0 :
((float)(m->current_sector) + dict_progress) /
(float)(m->sectors_total);
if(progress > 1.0f) {
progress = 1.0f;
}
if(m->dict_keys_current == 0) {
// Cause when people see 0 they think it's broken
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
float dict_progress = 0;
if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||
m->nested_phase == MfClassicNestedPhaseDictAttack ||
m->nested_phase == MfClassicNestedPhaseDictAttackVerify ||
m->nested_phase == MfClassicNestedPhaseDictAttackResume) {
// Phase: Nested dictionary attack
uint8_t target_sector =
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16);
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
} else if(
m->nested_phase == MfClassicNestedPhaseCalibrate ||
m->nested_phase == MfClassicNestedPhaseRecalibrate ||
m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
// Phase: Nonce collection
if(m->prng_type == MfClassicPrngTypeWeak) {
uint8_t target_sector = m->nested_target_key / 4;
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
} else {
uint16_t max_msb = UINT8_MAX + 1;
dict_progress = (float)(m->msb_count) / (float)(max_msb);
snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb);
}
} else {
snprintf(
draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total);
dict_progress = m->dict_keys_total == 0 ?
0 :
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
if(m->dict_keys_current == 0) {
// Cause when people see 0 they think it's broken
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
} else {
snprintf(
draw_str,
sizeof(draw_str),
"%zu/%zu",
m->dict_keys_current,
m->dict_keys_total);
}
}
if(dict_progress > 1.0f) {
dict_progress = 1.0f;
}
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
canvas_set_font(canvas, FontSecondary);
@ -132,6 +202,11 @@ void dict_attack_reset(DictAttack* instance) {
model->dict_keys_total = 0;
model->dict_keys_current = 0;
model->is_key_attack = false;
model->nested_phase = MfClassicNestedPhaseNone;
model->prng_type = MfClassicPrngTypeUnknown;
model->backdoor = MfClassicBackdoorUnknown;
model->nested_target_key = 0;
model->msb_count = 0;
furi_string_reset(model->header);
},
false);
@ -242,3 +317,41 @@ void dict_attack_reset_key_attack(DictAttack* instance) {
with_view_model(
instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true);
}
void dict_attack_set_nested_phase(DictAttack* instance, MfClassicNestedPhase nested_phase) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->nested_phase = nested_phase; }, true);
}
void dict_attack_set_prng_type(DictAttack* instance, MfClassicPrngType prng_type) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->prng_type = prng_type; }, true);
}
void dict_attack_set_backdoor(DictAttack* instance, MfClassicBackdoor backdoor) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true);
}
void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t nested_target_key) {
furi_assert(instance);
with_view_model(
instance->view,
DictAttackViewModel * model,
{ model->nested_target_key = nested_target_key; },
true);
}
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true);
}

View file

@ -2,6 +2,7 @@
#include <stdint.h>
#include <gui/view.h>
#include <lib/nfc/protocols/mf_classic/mf_classic_poller.h>
#ifdef __cplusplus
extern "C" {
@ -45,6 +46,16 @@ void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector);
void dict_attack_reset_key_attack(DictAttack* instance);
void dict_attack_set_nested_phase(DictAttack* instance, MfClassicNestedPhase nested_phase);
void dict_attack_set_prng_type(DictAttack* instance, MfClassicPrngType prng_type);
void dict_attack_set_backdoor(DictAttack* instance, MfClassicBackdoor backdoor);
void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t target_key);
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count);
#ifdef __cplusplus
}
#endif

View file

@ -81,6 +81,7 @@ typedef enum {
SetTypeAllmatic433,
SetTypeAllmatic868,
SetTypeCenturion433,
SetTypeMonarch433,
SetTypeSommer_FM_434,
SetTypeSommer_FM_868,
SetTypeSommer_FM238_434,

View file

@ -28,10 +28,6 @@ struct SubGhzFrequencyAnalyzerWorker {
FrequencyRSSI frequency_rssi_buf;
SubGhzSetting* setting;
const SubGhzDevice* radio_device;
FuriHalSpiBusHandle* spi_bus;
bool ext_radio;
float filVal;
float trigger_level;
@ -39,16 +35,14 @@ struct SubGhzFrequencyAnalyzerWorker {
void* context;
};
static void subghz_frequency_analyzer_worker_load_registers(
FuriHalSpiBusHandle* spi_bus,
const uint8_t data[][2]) {
furi_hal_spi_acquire(spi_bus);
static void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
size_t i = 0;
while(data[i][0]) {
cc1101_write_reg(spi_bus, data[i][0], data[i][1]);
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]);
i++;
}
furi_hal_spi_release(spi_bus);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
}
// running average with adaptive coefficient
@ -82,35 +76,29 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
float rssi_temp = 0;
uint32_t frequency_temp = 0;
FuriHalSpiBusHandle* spi_bus = instance->spi_bus;
const SubGhzDevice* radio_device = instance->radio_device;
//Start CC1101
// furi_hal_subghz_reset();
subghz_devices_reset(radio_device);
furi_hal_subghz_reset();
furi_hal_spi_acquire(spi_bus);
cc1101_flush_rx(spi_bus);
cc1101_flush_tx(spi_bus);
// TODO probably can be used device.load_preset(FuriHalSubGhzPresetCustom, ...) for external cc1101
cc1101_write_reg(spi_bus, CC1101_IOCFG0, CC1101IocfgHW);
cc1101_write_reg(spi_bus, CC1101_MDMCFG3,
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz);
cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW);
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3,
0b01111111); // symbol rate
cc1101_write_reg(
spi_bus,
&furi_hal_spi_bus_handle_subghz,
CC1101_AGCCTRL2,
0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB
cc1101_write_reg(
spi_bus,
&furi_hal_spi_bus_handle_subghz,
CC1101_AGCCTRL1,
0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled
cc1101_write_reg(
spi_bus,
&furi_hal_spi_bus_handle_subghz,
CC1101_AGCCTRL0,
0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary
furi_hal_spi_release(spi_bus);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
@ -123,35 +111,32 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
frequency_rssi.rssi_coarse = -127.0f;
frequency_rssi.rssi_fine = -127.0f;
// furi_hal_subghz_idle();
subghz_devices_idle(radio_device);
subghz_frequency_analyzer_worker_load_registers(spi_bus, subghz_preset_ook_650khz);
furi_hal_subghz_idle();
subghz_frequency_analyzer_worker_load_registers(subghz_preset_ook_650khz);
// First stage: coarse scan
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
// if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
if(subghz_devices_is_frequency_valid(radio_device, current_frequency) &&
(current_frequency != 467750000) && (current_frequency != 464000000) &&
!((instance->ext_radio) &&
((current_frequency == 390000000) || (current_frequency == 312000000) ||
(current_frequency == 312100000) || (current_frequency == 312200000) ||
(current_frequency == 440175000)))) {
furi_hal_spi_acquire(spi_bus);
cc1101_switch_to_idle(spi_bus);
frequency = cc1101_set_frequency(spi_bus, current_frequency);
if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
(((current_frequency != 467750000) && (current_frequency != 464000000)) &&
(current_frequency <= 920000000))) {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
frequency = cc1101_set_frequency(
&furi_hal_spi_bus_handle_subghz,
subghz_setting_get_frequency(instance->setting, i));
cc1101_calibrate(spi_bus);
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000));
furi_check(cc1101_wait_status_state(
&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
cc1101_switch_to_rx(spi_bus);
furi_hal_spi_release(spi_bus);
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
furi_delay_ms(2);
// rssi = furi_hal_subghz_get_rssi();
rssi = subghz_devices_get_rssi(radio_device);
rssi = furi_hal_subghz_get_rssi();
rssi_avg += rssi;
rssi_avg_samples++;
@ -175,30 +160,28 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// Second stage: fine scan
if(frequency_rssi.rssi_coarse > instance->trigger_level) {
// furi_hal_subghz_idle();
subghz_devices_idle(radio_device);
subghz_frequency_analyzer_worker_load_registers(spi_bus, subghz_preset_ook_58khz);
furi_hal_subghz_idle();
subghz_frequency_analyzer_worker_load_registers(subghz_preset_ook_58khz);
//for example -0.3 ... 433.92 ... +0.3 step 20KHz
for(uint32_t i = frequency_rssi.frequency_coarse - 300000;
i < frequency_rssi.frequency_coarse + 300000;
i += 20000) {
// if(furi_hal_subghz_is_frequency_valid(i)) {
if(subghz_devices_is_frequency_valid(radio_device, i)) {
furi_hal_spi_acquire(spi_bus);
cc1101_switch_to_idle(spi_bus);
frequency = cc1101_set_frequency(spi_bus, i);
if(furi_hal_subghz_is_frequency_valid(i)) {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i);
cc1101_calibrate(spi_bus);
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000));
furi_check(cc1101_wait_status_state(
&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
cc1101_switch_to_rx(spi_bus);
furi_hal_spi_release(spi_bus);
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
furi_delay_ms(2);
// rssi = furi_hal_subghz_get_rssi();
rssi = subghz_devices_get_rssi(radio_device);
rssi = furi_hal_subghz_get_rssi();
FURI_LOG_T(TAG, "#:%lu:%f", frequency, (double)rssi);
@ -275,10 +258,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
}
//Stop CC1101
// furi_hal_subghz_idle();
// furi_hal_subghz_sleep();
subghz_devices_idle(radio_device);
subghz_devices_sleep(radio_device);
furi_hal_subghz_idle();
furi_hal_subghz_sleep();
return 0;
}
@ -287,12 +268,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
furi_assert(context);
SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzFAWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread);
instance->thread = furi_thread_alloc_ex(
"SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance);
SubGhz* subghz = context;
instance->setting = subghz_txrx_get_setting(subghz->txrx);
instance->trigger_level = subghz->last_settings->frequency_analyzer_trigger;
@ -317,26 +294,10 @@ void subghz_frequency_analyzer_worker_set_pair_callback(
instance->context = context;
}
void subghz_frequency_analyzer_worker_start(
SubGhzFrequencyAnalyzerWorker* instance,
SubGhzTxRx* txrx) {
void subghz_frequency_analyzer_worker_start(SubGhzFrequencyAnalyzerWorker* instance) {
furi_assert(instance);
furi_assert(!instance->worker_running);
SubGhzRadioDeviceType radio_type = subghz_txrx_radio_device_get(txrx);
if(radio_type == SubGhzRadioDeviceTypeExternalCC1101) {
instance->spi_bus = &furi_hal_spi_bus_handle_external;
instance->ext_radio = true;
} else if(radio_type == SubGhzRadioDeviceTypeInternal) {
instance->spi_bus = &furi_hal_spi_bus_handle_subghz;
instance->ext_radio = false;
} else {
furi_crash("Unsuported external module");
}
instance->radio_device = subghz_devices_get_by_name(subghz_txrx_radio_device_get_name(txrx));
instance->worker_running = true;
furi_thread_start(instance->thread);
@ -365,3 +326,33 @@ void subghz_frequency_analyzer_worker_set_trigger_level(
float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) {
return instance->trigger_level;
}
uint32_t subghz_frequency_analyzer_get_nearest_frequency(
SubGhzFrequencyAnalyzerWorker* instance,
uint32_t input) {
uint32_t prev_freq = 0;
uint32_t result = 0;
uint32_t current;
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
current = subghz_setting_get_frequency(instance->setting, i);
if(current == 0) {
continue;
}
if(current == input) {
result = current;
break;
}
if(current > input && prev_freq < input) {
if(current - input < input - prev_freq) {
result = current;
} else {
result = prev_freq;
}
break;
}
prev_freq = current;
}
return result;
}

View file

@ -47,9 +47,7 @@ void subghz_frequency_analyzer_worker_set_pair_callback(
* @param instance SubGhzFrequencyAnalyzerWorker instance
* @param txrx pointer to SubGhzTxRx
*/
void subghz_frequency_analyzer_worker_start(
SubGhzFrequencyAnalyzerWorker* instance,
SubGhzTxRx* txrx);
void subghz_frequency_analyzer_worker_start(SubGhzFrequencyAnalyzerWorker* instance);
/** Stop SubGhzFrequencyAnalyzerWorker
*
@ -78,3 +76,8 @@ void subghz_frequency_analyzer_worker_set_trigger_level(
* @return RSSI trigger level
*/
float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance);
// Round up the frequency
uint32_t subghz_frequency_analyzer_get_nearest_frequency(
SubGhzFrequencyAnalyzerWorker* instance,
uint32_t input);

View file

@ -1,60 +1,61 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: 41 72 65 20 79 6F 75 20 61 20 46 75 72 72 79 3F
8B441920A9F30EA40001F37E0F3C0D72161467678511928D219A7DBC42F8D9B7
1ADE5B8541491C46674EECC05A1C4297CB28259954FD7C6DA0EAADE8D57773FC
0497A50135FA4468B49694974DAADEF2A9CC2B7BAFC8465E0AA58E940C085850
4BC77520AE09B4F6A5A5365B6B9C16CA53566E096159A98EF0AB6E7FE5C2DD1C
B674AC1674312E74471E44917F6FC049DAB48BBB65B84F14A4CD64223A49E93DC0C507B8E17BFFDCBB67B55F1B4091C6
FBBE4E4F703A9C1F17268F73EC00A3F76517A4470783363102D2B7647F0F2E7F
21BE8A51F32CE62DA6B5E207E627D224639877860C71933C4F54AC7BD742B265
74E3EA89CC7AA99855C1BC87734506413B882CF36D68C3329022415EB29D1360
567937E84052F230610B3A2FC26CA1BE32B9617F7554EF5E6E7CB35A76718ADA
4C34F7FE5D6EAE40B0EB6BAB7DE4F0D059C32E4F312F9C62F2C50C54C5486276
74C8656F8EA033C60F3183FA0923C9AF1EB6689FAACC39E6548ECE16E3312D39DCF45AC6EC70A70421FBFFA43D91E19E
275903678A81B6E62CD2BB4C50E93CB3E4B092E2894EBAA04AC4E0244E4D5BA9FCDFC6BCF845EA41DF0691BF2AEDF300
BFE52324D666ADEB3D33D94331376B33A3D31566331DE0F372548FE7255F8085
8E1B2E82B4C78360B3174741FB8031EE0DFEB479859BE8708F8AF40C5228BFDF
6014A4A9D9703DAC11DA6B93FB7A0E0E0856E9379148F3CC125D77BA4C3D4931
B8C22EA72B35E60A7A3DD02956D2375EE7C07E243F17F413CA4AC3A1445AA250
B545037B8798370A8638B7ADCBE9EEF748F73C89EB5E04B460057BAFA52B1D7DF29B009FBB3FA07CE87247D185BE4926
09AEA3223E4FFB06333425BA977403E7C2F9B5F121FDA8313E73FE98D0041425
E7A97F65883E4D3CDA02483738219E219EED0681C2BB140EF3E5D02230433D42
4AE67B9EA288183047A9498F58978E7C41BB8905533F42FF5B94E41AFEFF0EB4907A8C472B9E32F6D659CFA6C391179B
1F9752FBA7A7A00D518D91BA8A8C1ADEF2EDF6282A5AFB2E4332B4351CF67F18317D2BCFF2DA6B5216EA138A4CCBDB81
EDC508FE0D0E894B529C5E4AB82A7188072592BE9D4D9A3D2485822A9F4C5A67
780582645A71D3A9841B0E8F5E84A2285F783872102AA1A98A956B98AEA19421
37E2858B90868BCAA6B3198C3B205DC1413A9D378AF1671BEA3B8F47FCE1C5CE
229719C5AFE1E567B8BD5F07EFF89C30B11CB79C9C841FFE7B53404CA5422EDB
CDA16E59B8C24DBE95ABF978CAD4D04A4F124DED1965124F92AACA4C5AEB8721
5C61D5C655DD9EB78C4F8E965101F98D7899155D2379EF6553FA9DEA0302A232
7D3DCFDE942398244624C7ABFE08363404BA688EDA39D45A05BB6FB982BB9042
ECCB10BCB2A8CF2093C61634E9AFEE9B69EB4A7C8B7139061730B5FA869F65BF
7AE6C19ED3C97F45D1167FC9EF52CCE321EA002D31D8AEA94106A2F711626551
793A984F6EBD35253C319FAB671348A4C6843AEA7CCCD5A52ACA9BBE96484B5E
BB3ADBB5FD60F0FB01E5A8B1610D30A24975DC67ED1B70472E509218E213393E
BB971C6304723B9928F50FA2E5C30A48ED5527556EE1BA5A7EA75AB7A4ED4137
489579673025F7A351072EB8D5C7FC1DBF651AA6233FB01D99DAC4BDC0A60F2F
ACA000B78B091A9B2621A9FA1A50FA6A3B63DDDF7D966224837028F93D570C08
C1EB4EA179EC0437D5B0E72C47AAD06803F7619C53D3D2FEF38DD3C068118766B73202B75C534C6249312245218C9285
639C6F53600C0F01410361164FEF715DB8E08C68D68B8AE76CF2D09A0C6C4CA1
F5F2D77DD3AF60489312055EBA3B511F7D9E312F02465639FFF3E71A862BB9EB
3EC1F7B2C43646249C4D14B4B1B85B3416B8D3B0B8DA9E87BD6199DC27BD45C2
AA24DB7345317FE980C917909F364EAA13591A664E0893271E9DBCC250782EA5
32C18DBA479AD57BFF9FE7B7BD1022D4969D8E2EC99BC0799148809796E227D5
494B44697DACDFDBBFEB0FDBEF6E9B5D7291B6CB43B4C93088F3ED8632AF23B5
B79CC1A90C7AC36DA2F318BF69E6BB1FAC32C178453663069C0CF2C4AE77B211
A059D006EF98446BDF9E1F5E09641A641CA88348E79499D79BCFDE6646C716A0BE2253421858D327E796EB5237B6E2C4
9E90AE6138A3676E3E5058FF277331032CD6BBF114768104A33D684B0825C5A5
7477B55332666FC6B1279F5FDF84DCA01176498984809B74E17AB726F888000F
6A0300B90BC11127A031E62537468A1CA7A0775BE85ED12069074E26181FBA50
4DAF986F9A850EB18F0FE3B88B9796D80EEC3984C92F4F8B32D513A176CCAA9A
EB84517DCAA7D10A6E7C4D0A7995AE278AAF3FDE52DAE30C7FD3FB853C799369
0941FB8965604ABC852B94F29396783783632E4C80AEE665E42C233B6E7AA7D0
71FEF69734589BE3C0893A1584FC1C0A75A5D4BF88CFF323C8C68E0EEC06993C
790A26B77912EE07BABCDF478D8E492422B73465DC209E5DBC8E31F43AA2DA7448EAB6312AC1DC4FE3F20D7A2EC93968
CE0AA966F33B7AC9E9982D2241524F2FC7613DAAC6C5C768AAAE29A630B414CE
93FA632D945E18DE6358D4F443CB6F023C640407B7FAD6E8AF6EF15139B044E3
059ABA7A866A865159D4E0B3E30FA1900BAB2BAB82A1D1F9D74CAEC11FA95393
9C5A4CF1D49E0F741577BAB868575739AEBBDC15D7178C87A46EA3B3E7E856EFD815AD5F998778E8FBAA2832F387B967
IV: 46 75 72 72 79 20 63 68 65 63 6B 20 3F 3F 21 21
3FF98B52C38558ECDB26C3E86D118FAC9AF22EDDDE7649CB53726855CEBCCF9E
06B901C6E36E381B4A83554ED972288977F999C232106D337C3BE4A9A44608F8
8153E6CAF4B272BA0EA168B9F0F29CF02CEB33E4ABBD4D5C858D1ADBFF474A25
F61216CE3A7E500E0C9B2173F91C2E7B1BB7D7AA65D4150EAC28169116647DDD
A3ABC262415035A190447EC9C15CFD1AB1720560AB7C82D7447215342305299732776A9A6DEA4D359C52A23BFCE6B015
5784D0E77A55E1361F47A1F6CFFBFAF715CFCAD2BD502ED266AA86DC47F98541
E082BF64C75023F23FB333C53F2590F408FD932EB71DF4ED4D0E0EAF2AEBD488
EFF328889D57D9F4B9111918F9C9BC641645104223009842FFF7B6F73E24E5B7
BDCD1DBCBCA789C5A5C3623C0A287D791F1CACCBBD7D144E2EF1F92DA5513D90
6310362BE6ED7CB5E3F4CA84D3093DF620BABD2C9D419A2BA5AA05F241EE7592
4C6F97C553276D5403103A6BA6C4DBBE017A9E4159E6E5AC4C28ACD645DB1E8D4551CDC228A0457BEEF49179A081E1FD
861ECA071AEEF3854005FEC9BF22C7DB76D07B7930968314FFAC70995E28680E473F8A1A7E56E8776EDD1C6E2DD6DD26
55ADDFBE97BCB0FB43EDEB9CAFF9F32DD788A6306D3702BC2923DA5C69F5F0A5
AB838DA2B25547AA43DEB7FB40B15E289B9209057BE564E7CB78F0D5DCFC65FD
21BACE924E522AD0E97F0AAF64A9DB6A5F7BA09B3A1759AAFD4016F3ADD4353D
4B37D449E44BAE7F377EE5CE52E94882E42617417F77ECE9803D9435892167F2
E63BA93D98BB3EEEAD34B38C5F271FB777AEABD9E6A05597AE09815E578AA811900AE9F144FBEC7DBFDDEC87D1ED368B
7DF24DCB7D73F1638FFD22325463B23C101653EE770F086D323BE90868A7E267
18E533990C408061309323675116429652B3F4D228F00D704310E7AB26F6BAF4
26042EC21D73490D9E7F968EDCC4CECF3989E6E982427DEB7012478E214CED8797A9BEBC481C81E9646214809A409B4A
AC4169C26E402720F1E2A0AD690AE2CF708CE203898BC7102178A738C70F361718E794F0D0CB1A2F1938EF35CCA11887
6962CADB9BD1B86B779DF7F4D7E06E92EB42474C7FD9EA23879F9982D7127357
23AEC0F6F9FF529DDDCD1CDAA77F7B136CE2CCD3AC8B949954D5D3B2ECFA8CBC
C6A3A849BC4A8DB438065255BD387DCD77AD7E7FEB3B0E11B6D3A43279AB9087
CCB71F8BDC8B31F36D6141B3B57BF31B7EDF72B87593B7497871F5738A0B7E00
345FE0FAD3F60C017D9793406981878EA2226072D624208AC33682793B415C41
78AF1FCFDB780744708DBB547F7C3F095BA5FAFB34FA83BF4323B32829836ED1
C2F19F077E71E710D39F5F11C1FE55C2A3CD6F33384CD8603288765F266F1BDF
084B1A9565AFC88900D0FF25413E659E17AAB649DB2B7A9F0381CB0DF6D2B8E5
568CECD994676CF3D6B225DEF21D40787DCB41C101F52B0C5AA43ED66709D158
0742E0AAB6504AC63A9C59FBDD980BD4C4760EFF3E556C8A6241442CDDD23A74
37BB60EE11BEC7D25A607DE9B0B6D3EAD23321AFEB94995FAB42184A95D1CADA
42FD71B98A3BEE9CF372B03E158D51181BFAA9CF54F7300A93FE7665402B3D1F
9346B7D12346E264E84F91145EBF86C53DD0061E0FA6556FAE5F6FBB8CEB799E
430B0D7FC09C09051AEB667A370E6D9D9BDF0C0C553AD2791682D43CE5DC48B7
5BBB5129797BC07CDC1D25A8A919A637B9FFF2F920BD42D1721018ABF8D34959AF877AFF450AD91548184E1A0D991CC8
0BC8D3E1C2D9A8FB445FC55564471007A28C09C9CE602203176F1BFF02AC6328
1C4CA912791BCD9CB231C64AB479AF240D02EC9F431D7C479B9A172E5B97F5CD
15A9CDF17E72255DE0942C09E67CD251C3D465246845F9C0B97A7EFEE4AF9110
F95543395BFF39B0A093AC80D0EF1BEFA218A3769D074250414D1357105A4D34
138D433824E691C6B67A08CF1DDE55FBCA2D65AC0B7D9320EA1FDCB1742B11BF
3BE3A385F1C0B8922C4E23EFF6912748DF715A4546CECB5E8972C1D1E47D0D3A
9ECA2554ED36700326F2E5140C434ADA8DFF55A53382F19541E9AAE45DA5CBF2
5C75D528678AF199E191C49F310913F401014F97EEA5FA507C7310B48A98FDE3297EA398B08959951FF99EDDB64C5E0D
22A9C66645B3944925A496D9F2312429CC787B6314948B482EAD9360124F59DA
DA3A8571664DBBFC1DE97B53E7C141554A2FBCEBA980696D32409CC5ADB7FC41
20A52AABC518FCC2FC75AE3F5CC7C4838AA4973111DA696B890D884A18098D91
EBB7163F580A1A5D26F12FBE650A227791193BA9AFCE277584B171F2FE1C77CF
86369EE5277CB81B9417B6232F8D994FAEAB34D0D5363B143257C62B10CACAFD
2E2EAB32891E172A3C31D434703480E69793435BB198E6AA06AA066EE8234D85
745FC576D77C41BAEFF15A822E6B4058A485A2CAA0A3B283928D17AC02299AD9
1FF8D49F2F7D785D64B6FE365CD9C2BD958E9527F66BB8A85C9AEBC73ECFE064
3CFB77F3E274C1EB2772CDFA7B5B17255C2554198BE60C058A3405AEC644FABC5AAECC8F9C7F4A4E5B2D5252E8C62628
AE514C44B55A1A4744E1106FD226C587D1B71CA7B5DCF010265D769E22012866
2B2D787A4B0F30CAB9CD3DBC7686165637F091B31745CAB53B369A804F76F9EE
EA4279C80F0B4AC0B32AB9E8B8CFB9C25FF81840BEE65B2160F85E56FA576C48
E41D853750D68643E929F94F46BCDC050040935883E9A0C45BB238CDE06340DD745BDA7D6C16AE2B028E073EDDC0FC49

View file

@ -14,8 +14,8 @@ const uint32_t radio_device_value[RADIO_DEVICE_COUNT] = {
SubGhzRadioDeviceTypeExternalCC1101,
};
#define TIMESTAMP_NAMES_COUNT 2
const char* const timestamp_names_text[TIMESTAMP_NAMES_COUNT] = {
#define ON_OFF_COUNT 2
const char* const on_off_text[ON_OFF_COUNT] = {
"OFF",
"ON",
};
@ -81,6 +81,22 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
subghz_txrx_set_debug_pin_state(subghz->txrx, index == 1);
}
static void subghz_scene_reciever_config_set_ext_amp_leds_control(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, on_off_text[index]);
subghz->last_settings->leds_and_amp = index == 1;
// Set globally in furi hal
furi_hal_subghz_set_ext_leds_and_amp(subghz->last_settings->leds_and_amp);
subghz_last_settings_save(subghz->last_settings);
// reinit external device
const SubGhzRadioDeviceType current = subghz_txrx_radio_device_get(subghz->txrx);
if(current != SubGhzRadioDeviceTypeInternal) {
subghz_txrx_radio_device_set(subghz->txrx, SubGhzRadioDeviceTypeInternal);
subghz_txrx_radio_device_set(subghz->txrx, current);
}
}
static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
@ -92,7 +108,7 @@ static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem*
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, timestamp_names_text[index]);
variable_item_set_current_value_text(item, on_off_text[index]);
subghz->last_settings->protocol_file_names = (index == 1);
subghz_last_settings_save(subghz->last_settings);
@ -123,12 +139,12 @@ void subghz_scene_radio_settings_on_enter(void* context) {
item = variable_item_list_add(
variable_item_list,
"Protocol Names",
TIMESTAMP_NAMES_COUNT,
ON_OFF_COUNT,
subghz_scene_receiver_config_set_timestamp_file_names,
subghz);
value_index = subghz->last_settings->protocol_file_names;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, timestamp_names_text[value_index]);
variable_item_set_current_value_text(item, on_off_text[value_index]);
item = variable_item_list_add(
variable_item_list,
@ -146,6 +162,16 @@ void subghz_scene_radio_settings_on_enter(void* context) {
variable_item_set_current_value_text(item, debug_counter_text[value_index]);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
item = variable_item_list_add(
variable_item_list,
"Ext Amp & LEDs",
ON_OFF_COUNT,
subghz_scene_reciever_config_set_ext_amp_leds_control,
subghz);
value_index = subghz->last_settings->leds_and_amp ? 1 : 0;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, on_off_text[value_index]);
item = variable_item_list_add(
variable_item_list,
"Debug Pin",

View file

@ -28,6 +28,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeAllmatic433] = "KL: Allmatic 433MHz",
[SetTypeAllmatic868] = "KL: Allmatic 868MHz",
[SetTypeCenturion433] = "KL: Centurion 433MHz",
[SetTypeMonarch433] = "KL: Monarch 433MHz",
[SetTypeSommer_FM_434] = "KL: Sommer 434MHz",
[SetTypeSommer_FM_868] = "KL: Sommer 868MHz",
[SetTypeSommer_FM238_434] = "KL: Sommer fm2 434Mhz",
@ -417,6 +418,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.keeloq.cnt = 0x03,
.keeloq.manuf = "Centurion"};
break;
case SetTypeMonarch433:
gen_info = (GenInfo){
.type = GenKeeloq,
.mod = "AM650",
.freq = 433920000,
.keeloq.serial = (key & 0x0000FFFF),
.keeloq.btn = 0x0A,
.keeloq.cnt = 0x03,
.keeloq.manuf = "Monarch"};
break;
case SetTypeElmesElectronic:
gen_info = (GenInfo){
.type = GenKeeloq,

View file

@ -197,6 +197,10 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->last_settings = subghz_last_settings_alloc();
size_t preset_count = subghz_setting_get_preset_count(setting);
subghz_last_settings_load(subghz->last_settings, preset_count);
// Set LED and Amp GPIO control state
furi_hal_subghz_set_ext_leds_and_amp(subghz->last_settings->leds_and_amp);
if(!alloc_for_tx_only) {
subghz_txrx_set_preset_internal(
subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index);

View file

@ -27,6 +27,9 @@ void subghz_dangerous_freq() {
SubGhzLastSettings* last_settings = subghz_last_settings_alloc();
subghz_last_settings_load(last_settings, 0);
// Set LED and Amp GPIO control state
furi_hal_subghz_set_ext_leds_and_amp(last_settings->leds_and_amp);
subghz_last_settings_free(last_settings);
furi_record_close(RECORD_STORAGE);

View file

@ -18,6 +18,7 @@
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
#define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals"
#define SUBGHZ_LAST_SETTING_FIELD_HOPPING_THRESHOLD "HoppingThreshold"
#define SUBGHZ_LAST_SETTING_FIELD_LED_AND_POWER_AMP "LedAndPowerAmp"
SubGhzLastSettings* subghz_last_settings_alloc(void) {
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
@ -42,6 +43,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->filter = SubGhzProtocolFlag_Decodable;
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
instance->hopping_threshold = -90.0f;
instance->leds_and_amp = true;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
@ -123,6 +125,13 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
1)) {
flipper_format_rewind(fff_data_file);
}
if(!flipper_format_read_bool(
fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_LED_AND_POWER_AMP,
&instance->leds_and_amp,
1)) {
flipper_format_rewind(fff_data_file);
}
} while(0);
} else {
@ -219,6 +228,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
1)) {
break;
}
if(!flipper_format_write_bool(
file, SUBGHZ_LAST_SETTING_FIELD_LED_AND_POWER_AMP, &instance->leds_and_amp, 1)) {
break;
}
saved = true;
} while(0);

View file

@ -25,6 +25,7 @@ typedef struct {
float rssi;
bool delete_old_signals;
float hopping_threshold;
bool leds_and_amp;
} SubGhzLastSettings;
SubGhzLastSettings* subghz_last_settings_alloc(void);

View file

@ -20,71 +20,6 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif
static const uint32_t subghz_frequency_list[] = {
/* 300 - 348 */
300000000,
302757000,
303875000,
303900000,
304250000,
307000000,
307500000,
307800000,
309000000,
310000000,
312000000,
312100000,
312200000,
313000000,
313850000,
314000000,
314350000,
314980000,
315000000,
318000000,
330000000,
345000000,
348000000,
350000000,
/* 387 - 464 */
387000000,
390000000,
418000000,
430000000,
430500000,
431000000,
431500000,
433075000, /* LPD433 first */
433220000,
433420000,
433657070,
433889000,
433920000, /* LPD433 mid */
434075000,
434176948,
434190000,
434390000,
434420000,
434620000,
434775000, /* LPD433 last channels */
438900000,
440175000,
464000000,
467750000,
/* 779 - 928 */
779000000,
868350000,
868400000,
868800000,
868950000,
906400000,
915000000,
925000000,
928000000,
};
typedef enum {
SubGhzFrequencyAnalyzerStatusIDLE,
} SubGhzFrequencyAnalyzerStatus;
@ -225,7 +160,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 7, model->is_ext_radio ? "Ext" : "Int");
//canvas_draw_str(canvas, 0, 7, model->is_ext_radio ? "Ext" : "Int");
canvas_draw_str(canvas, 20, 7, "Frequency Analyzer");
// RSSI
@ -278,34 +213,6 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
elements_button_right(canvas, "+T");
}
uint32_t subghz_frequency_find_correct(uint32_t input) {
uint32_t prev_freq = 0;
uint32_t result = 0;
uint32_t current;
for(size_t i = 0; i < ARRAY_SIZE(subghz_frequency_list) - 1; i++) {
current = subghz_frequency_list[i];
if(current == 0) {
continue;
}
if(current == input) {
result = current;
break;
}
if(current > input && prev_freq < input) {
if(current - input < input - prev_freq) {
result = current;
} else {
result = prev_freq;
}
break;
}
prev_freq = current;
}
return result;
}
bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = (SubGhzFrequencyAnalyzer*)context;
@ -347,7 +254,9 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
instance->selected_index = (instance->selected_index + 1) % instance->max_index;
need_redraw = true;
}
} else if(event->key == InputKeyOk) {
} else if(
(event->type != InputTypeRelease && event->type != InputTypeRepeat) &&
event->key == InputKeyOk) {
need_redraw = true;
bool updated = false;
uint32_t frequency_to_save;
@ -364,7 +273,8 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
} else if(
(model->show_frame && model->signal) ||
(!model->show_frame && model->signal)) {
frequency_candidate = subghz_frequency_find_correct(model->frequency);
frequency_candidate = subghz_frequency_analyzer_get_nearest_frequency(
instance->worker, model->frequency);
}
frequency_candidate = frequency_candidate == 0 ||
@ -372,7 +282,8 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
instance->txrx, frequency_candidate) ||
prev_freq_to_save == frequency_candidate ?
0 :
subghz_frequency_find_correct(frequency_candidate);
subghz_frequency_analyzer_get_nearest_frequency(
instance->worker, frequency_candidate);
if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) {
model->frequency_to_save = frequency_candidate;
updated = true;
@ -445,7 +356,8 @@ void subghz_frequency_analyzer_pair_callback(
SubGhzFrequencyAnalyzerModel * model,
{
bool in_array = false;
uint32_t normal_frequency = subghz_frequency_find_correct(model->frequency);
uint32_t normal_frequency = subghz_frequency_analyzer_get_nearest_frequency(
instance->worker, model->frequency);
for(size_t i = 0; i < MAX_HISTORY; i++) {
if(model->history_frequency[i] == normal_frequency) {
in_array = true;
@ -544,7 +456,7 @@ void subghz_frequency_analyzer_enter(void* context) {
(SubGhzFrequencyAnalyzerWorkerPairCallback)subghz_frequency_analyzer_pair_callback,
instance);
subghz_frequency_analyzer_worker_start(instance->worker, instance->txrx);
subghz_frequency_analyzer_worker_start(instance->worker);
instance->rssi_last = 0;
instance->selected_index = 0;

View file

@ -445,13 +445,11 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
*message->profile_instance = NULL;
}
}
if(message->lock) api_lock_unlock(message->lock);
}
static void bt_close_connection(Bt* bt, BtMessage* message) {
static void bt_close_connection(Bt* bt) {
bt_close_rpc_connection(bt);
furi_hal_bt_stop_advertising();
if(message->lock) api_lock_unlock(message->lock);
}
static void bt_apply_settings(Bt* bt) {
@ -499,19 +497,13 @@ static void bt_load_settings(Bt* bt) {
}
static void bt_handle_get_settings(Bt* bt, BtMessage* message) {
furi_assert(message->lock);
*message->data.settings = bt->bt_settings;
api_lock_unlock(message->lock);
}
static void bt_handle_set_settings(Bt* bt, BtMessage* message) {
furi_assert(message->lock);
bt->bt_settings = *message->data.csettings;
bt_apply_settings(bt);
bt_settings_save(&bt->bt_settings);
api_lock_unlock(message->lock);
}
static void bt_handle_reload_keys_settings(Bt* bt) {
@ -576,6 +568,12 @@ int32_t bt_srv(void* p) {
while(1) {
furi_check(
furi_message_queue_get(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
FURI_LOG_D(
TAG,
"call %d, lock 0x%p, result 0x%p",
message.type,
(void*)message.lock,
(void*)message.result);
if(message.type == BtMessageTypeUpdateStatus) {
// Update view ports
bt_statusbar_update(bt);
@ -599,7 +597,7 @@ int32_t bt_srv(void* p) {
} else if(message.type == BtMessageTypeSetProfile) {
bt_change_profile(bt, &message);
} else if(message.type == BtMessageTypeDisconnect) {
bt_close_connection(bt, &message);
bt_close_connection(bt);
} else if(message.type == BtMessageTypeForgetBondedDevices) {
bt_keys_storage_delete(bt->keys_storage);
} else if(message.type == BtMessageTypeGetSettings) {
@ -609,6 +607,8 @@ int32_t bt_srv(void* p) {
} else if(message.type == BtMessageTypeReloadKeysSettings) {
bt_handle_reload_keys_settings(bt);
}
if(message.lock) api_lock_unlock(message.lock);
}
return 0;

View file

@ -7,6 +7,7 @@
#include <task_control_block.h>
#include <time.h>
#include <notification/notification_messages.h>
#include <notification/notification_app.h>
#include <loader/loader.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
@ -326,13 +327,24 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) {
void cli_command_vibro(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
if(!furi_string_cmp(args, "0")) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_reset_vibro);
furi_record_close(RECORD_NOTIFICATION);
} else if(!furi_string_cmp(args, "1")) {
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {
printf("Flipper is in stealth mode. Unmute the device to control vibration.");
return;
}
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_set_vibro_on);
if(notification->settings.vibro_on) {
notification_message_block(notification, &sequence_set_vibro_on);
} else {
printf("Vibro is disabled in settings. Enable it to control vibration.");
}
furi_record_close(RECORD_NOTIFICATION);
} else {
cli_print_usage("vibro", "<1|0>", furi_string_get_cstr(args));

View file

@ -523,7 +523,7 @@ int32_t desktop_srv(void* p) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
if(desktop_pin_code_is_set()) {
desktop_lock(desktop);
}

View file

@ -54,11 +54,14 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
ret = file_browser_context->result;
view_holder_set_view(view_holder, NULL);
view_holder_free(view_holder);
file_browser_stop(file_browser);
file_browser_free(file_browser);
view_holder_free(view_holder);
api_lock_free(file_browser_context->lock);
free(file_browser_context);
furi_record_close(RECORD_GUI);
return ret;

View file

@ -212,7 +212,7 @@ static void dolphin_update_clear_limits_timer_period(void* context) {
FURI_LOG_D(TAG, "Daily limits reset in %lu ms", time_to_clear_limits);
}
static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
static void dolphin_process_event(FuriEventLoopObject* object, void* context) {
UNUSED(object);
Dolphin* dolphin = context;
@ -264,8 +264,6 @@ static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
}
dolphin_event_release(&event);
return true;
}
static void dolphin_storage_callback(const void* message, void* context) {

View file

@ -520,9 +520,21 @@ void canvas_draw_xbm(
size_t height,
const uint8_t* bitmap) {
furi_check(canvas);
canvas_draw_xbm_ex(canvas, x, y, width, height, IconRotation0, bitmap);
}
void canvas_draw_xbm_ex(
Canvas* canvas,
int32_t x,
int32_t y,
size_t width,
size_t height,
IconRotation rotation,
const uint8_t* bitmap_data) {
furi_check(canvas);
x += canvas->offset_x;
y += canvas->offset_y;
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap, IconRotation0);
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, rotation);
}
void canvas_draw_glyph(Canvas* canvas, int32_t x, int32_t y, uint16_t ch) {

View file

@ -296,6 +296,25 @@ void canvas_draw_xbm(
size_t height,
const uint8_t* bitmap);
/** Draw rotated XBM bitmap
*
* @param canvas Canvas instance
* @param x x coordinate
* @param y y coordinate
* @param[in] width bitmap width
* @param[in] height bitmap height
* @param[in] rotation bitmap rotation
* @param bitmap_data pointer to XBM bitmap data
*/
void canvas_draw_xbm_ex(
Canvas* canvas,
int32_t x,
int32_t y,
size_t width,
size_t height,
IconRotation rotation,
const uint8_t* bitmap_data);
/** Draw dot at x,y
*
* @param canvas Canvas instance

View file

@ -65,13 +65,18 @@ void text_input_set_result_callback(
size_t text_buffer_size,
bool clear_default_text);
/**
* @brief Sets the minimum length of a TextInput
* @param [in] text_input TextInput
* @param [in] minimum_length Minimum input length
*/
void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length);
void text_input_set_validator(
TextInput* text_input,
TextInputValidatorCallback callback,
void* callback_context);
void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length);
TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input);
void* text_input_get_validator_callback_context(TextInput* text_input);

View file

@ -5,6 +5,12 @@
#define VIEW_DISPATCHER_QUEUE_LEN (16U)
ViewDispatcher* view_dispatcher_alloc(void) {
ViewDispatcher* dispatcher = view_dispatcher_alloc_ex(furi_event_loop_alloc());
dispatcher->is_event_loop_owned = true;
return dispatcher;
}
ViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop) {
ViewDispatcher* view_dispatcher = malloc(sizeof(ViewDispatcher));
view_dispatcher->view_port = view_port_alloc();
@ -16,7 +22,7 @@ ViewDispatcher* view_dispatcher_alloc(void) {
ViewDict_init(view_dispatcher->views);
view_dispatcher->event_loop = furi_event_loop_alloc();
view_dispatcher->event_loop = loop;
view_dispatcher->input_queue =
furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(InputEvent));
@ -57,7 +63,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
furi_message_queue_free(view_dispatcher->input_queue);
furi_message_queue_free(view_dispatcher->event_queue);
furi_event_loop_free(view_dispatcher->event_loop);
if(view_dispatcher->is_event_loop_owned) furi_event_loop_free(view_dispatcher->event_loop);
// Free dispatcher
free(view_dispatcher);
}
@ -85,6 +91,7 @@ void view_dispatcher_set_tick_event_callback(
ViewDispatcherTickEventCallback callback,
uint32_t tick_period) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->is_event_loop_owned);
view_dispatcher->tick_event_callback = callback;
view_dispatcher->tick_period = tick_period;
}
@ -106,11 +113,12 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever :
view_dispatcher->tick_period;
furi_event_loop_tick_set(
view_dispatcher->event_loop,
tick_period,
view_dispatcher_handle_tick_event,
view_dispatcher);
if(view_dispatcher->is_event_loop_owned)
furi_event_loop_tick_set(
view_dispatcher->event_loop,
tick_period,
view_dispatcher_handle_tick_event,
view_dispatcher);
furi_event_loop_run(view_dispatcher->event_loop);
@ -368,7 +376,7 @@ void view_dispatcher_update(View* view, void* context) {
}
}
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {
void view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
ViewDispatcher* instance = context;
furi_assert(instance->event_queue == object);
@ -376,11 +384,9 @@ bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* conte
uint32_t event;
furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);
view_dispatcher_handle_custom_event(instance, event);
return true;
}
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {
void view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
ViewDispatcher* instance = context;
furi_assert(instance->input_queue == object);
@ -388,6 +394,4 @@ bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* conte
InputEvent input;
furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);
view_dispatcher_handle_input(instance, &input);
return true;
}

View file

@ -47,6 +47,15 @@ typedef void (*ViewDispatcherTickEventCallback)(void* context);
*/
ViewDispatcher* view_dispatcher_alloc(void);
/** Allocate ViewDispatcher instance with an externally owned event loop. If
* this constructor is used instead of `view_dispatcher_alloc`, the burden of
* freeing the event loop is placed on the caller.
*
* @param loop pointer to FuriEventLoop instance
* @return pointer to ViewDispatcher instance
*/
ViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop);
/** Free ViewDispatcher instance
*
* @warning All added views MUST be removed using view_dispatcher_remove_view()
@ -97,6 +106,10 @@ void view_dispatcher_set_navigation_event_callback(
/** Set tick event handler
*
* @warning Requires the event loop to be owned by the view dispatcher, i.e.
* it should have been instantiated with `view_dispatcher_alloc`, not
* `view_dispatcher_alloc_ex`.
*
* @param view_dispatcher ViewDispatcher instance
* @param callback ViewDispatcherTickEventCallback
* @param tick_period callback call period

View file

@ -14,6 +14,7 @@
DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) // NOLINT
struct ViewDispatcher {
bool is_event_loop_owned;
FuriEventLoop* event_loop;
FuriMessageQueue* input_queue;
FuriMessageQueue* event_queue;
@ -56,7 +57,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
void view_dispatcher_update(View* view, void* context);
/** ViewDispatcher run event loop event callback */
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);
void view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);
/** ViewDispatcher run event loop input callback */
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);
void view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);

View file

@ -309,12 +309,14 @@ static void loader_applications_closed_callback(void* context) {
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
static void
loader_thread_state_callback(FuriThread* thread, FuriThreadState thread_state, void* context) {
UNUSED(thread);
furi_assert(context);
Loader* loader = context;
if(thread_state == FuriThreadStateStopped) {
Loader* loader = context;
LoaderMessage message;
message.type = LoaderMessageTypeAppClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);

View file

@ -593,3 +593,7 @@ const NotificationSequence sequence_lcd_contrast_update = {
&message_lcd_contrast_update,
NULL,
};
const NotificationSequence sequence_empty = {
NULL,
};

View file

@ -145,6 +145,9 @@ extern const NotificationSequence sequence_audiovisual_alert;
// LCD
extern const NotificationSequence sequence_lcd_contrast_update;
// Wait for notification queue become empty
extern const NotificationSequence sequence_empty;
#ifdef __cplusplus
}
#endif

View file

@ -380,7 +380,7 @@ static void power_handle_reboot(PowerBootMode mode) {
furi_hal_power_reset();
}
static bool power_message_callback(FuriEventLoopObject* object, void* context) {
static void power_message_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
Power* power = context;
@ -412,8 +412,6 @@ static bool power_message_callback(FuriEventLoopObject* object, void* context) {
if(msg.lock) {
api_lock_unlock(msg.lock);
}
return true;
}
static void power_tick_callback(void* context) {

View file

@ -104,19 +104,12 @@ static int32_t region_load_file(void* context) {
return 0;
}
static void region_loader_pending_callback(void* context, uint32_t arg) {
UNUSED(arg);
FuriThread* loader = context;
furi_thread_join(loader);
furi_thread_free(loader);
}
static void region_loader_state_callback(FuriThreadState state, void* context) {
static void
region_loader_release_callback(FuriThread* thread, FuriThreadState state, void* context) {
UNUSED(context);
if(state == FuriThreadStateStopped) {
furi_timer_pending_callback(region_loader_pending_callback, furi_thread_get_current(), 0);
furi_thread_free(thread);
}
}
@ -126,7 +119,7 @@ static void region_storage_callback(const void* message, void* context) {
if(event->type == StorageEventTypeCardMount) {
FuriThread* loader = furi_thread_alloc_ex(NULL, 2048, region_load_file, NULL);
furi_thread_set_state_callback(loader, region_loader_state_callback);
furi_thread_set_state_callback(loader, region_loader_release_callback);
furi_thread_start(loader);
}
}

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