Merge branch 'dev' into nfcf

This commit is contained in:
nullableVoidPtr
2022-12-25 18:03:54 +08:00
127 changed files with 4809 additions and 388 deletions

3
.github/CODEOWNERS vendored
View File

@@ -42,6 +42,9 @@
/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm
# Assets
/assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
# Documentation # Documentation
/documentation/ @skotopes @DrZlo13 @hedger @drunkbatya /documentation/ @skotopes @DrZlo13 @hedger @drunkbatya
/scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya /scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya

View File

@@ -11,6 +11,7 @@ on:
env: env:
TARGETS: f7 TARGETS: f7
FBT_TOOLCHAIN_PATH: /opt
jobs: jobs:
amap_analyse: amap_analyse:
@@ -78,7 +79,7 @@ jobs:
- name: 'Upload report to DB' - name: 'Upload report to DB'
run: | run: |
FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
get_size() get_size()
{ {
SECTION="$1"; SECTION="$1";

View File

@@ -12,6 +12,7 @@ on:
env: env:
TARGETS: f7 TARGETS: f7
DEFAULT_TARGET: f7 DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /runner/_work
jobs: jobs:
main: main:
@@ -55,7 +56,7 @@ jobs:
run: | run: |
set -e set -e
for TARGET in ${TARGETS}; do for TARGET in ${TARGETS}; do
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done done
@@ -157,6 +158,6 @@ jobs:
run: | run: |
set -e set -e
for TARGET in ${TARGETS}; do for TARGET in ${TARGETS}; do
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
updater_package DEBUG=0 COMPACT=1 updater_package DEBUG=0 COMPACT=1
done done

View File

@@ -11,6 +11,8 @@ on:
env: env:
TARGETS: f7 TARGETS: f7
FBT_TOOLCHAIN_PATH: /runner/_work
SET_GH_OUTPUT: 1
jobs: jobs:
lint_c_cpp: lint_c_cpp:
@@ -30,7 +32,7 @@ jobs:
- name: 'Check code formatting' - name: 'Check code formatting'
id: syntax_check id: syntax_check
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint run: ./fbt lint
- name: Report code formatting errors - name: Report code formatting errors
if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request

View File

@@ -9,6 +9,10 @@ on:
- '*' - '*'
pull_request: pull_request:
env:
FBT_TOOLCHAIN_PATH: /runner/_work
SET_GH_OUTPUT: 1
jobs: jobs:
lint_python: lint_python:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
@@ -26,4 +30,4 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: 'Check code formatting' - name: 'Check code formatting'
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint_py run: ./fbt lint_py

View File

@@ -4,6 +4,10 @@ on:
push: push:
branches: branches:
- dev - dev
env:
FBT_TOOLCHAIN_PATH: /runner/_work
jobs: jobs:
merge_report: merge_report:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
@@ -33,7 +37,7 @@ jobs:
- name: 'Check ticket and report' - name: 'Check ticket and report'
run: | run: |
FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
python3 -m pip install slack_sdk python3 -m pip install slack_sdk
python3 scripts/merge_report_qa.py \ python3 scripts/merge_report_qa.py \
${{ secrets.QA_REPORT_SLACK_TOKEN }} \ ${{ secrets.QA_REPORT_SLACK_TOKEN }} \

View File

@@ -12,6 +12,7 @@ on:
env: env:
TARGETS: f7 TARGETS: f7
DEFAULT_TARGET: f7 DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /runner/_work
jobs: jobs:
analyse_c_cpp: analyse_c_cpp:
@@ -49,11 +50,11 @@ jobs:
- name: 'Generate compile_comands.json' - name: 'Generate compile_comands.json'
run: | run: |
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons
- name: 'Static code analysis' - name: 'Static code analysis'
run: | run: |
FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
pvs-studio-analyzer analyze \ pvs-studio-analyzer analyze \
@.pvsoptions \ @.pvsoptions \

View File

@@ -6,6 +6,7 @@ on:
env: env:
TARGETS: f7 TARGETS: f7
DEFAULT_TARGET: f7 DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /opt
jobs: jobs:
run_units_on_test_bench: run_units_on_test_bench:
@@ -28,35 +29,81 @@ jobs:
run: | run: |
echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT
- name: 'Flashing target firmware'
id: first_full_flash
run: |
./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1
source scripts/toolchain/fbtenv.sh
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
- name: 'Validating updater'
id: second_full_flash
if: success()
run: |
./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1
source scripts/toolchain/fbtenv.sh
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
- name: 'Flash unit tests firmware' - name: 'Flash unit tests firmware'
id: flashing id: flashing
if: success()
run: | run: |
FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
- name: 'Wait for flipper to finish updating' - name: 'Wait for flipper to finish updating'
id: connect id: connect
if: steps.flashing.outcome == 'success' if: steps.flashing.outcome == 'success'
run: | run: |
. scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
- name: 'Format flipper SD card'
id: format
if: steps.connect.outcome == 'success'
run: |
. scripts/toolchain/fbtenv.sh
./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext
- name: 'Copy assets and unit tests data to flipper' - name: 'Copy assets and unit tests data to flipper'
id: copy id: copy
if: steps.format.outcome == 'success' if: steps.connect.outcome == 'success'
run: | run: |
. scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests
./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests
- name: 'Run units and validate results' - name: 'Run units and validate results'
if: steps.copy.outcome == 'success' if: steps.copy.outcome == 'success'
run: | run: |
. scripts/toolchain/fbtenv.sh source scripts/toolchain/fbtenv.sh
./scripts/testing/units.py ${{steps.device.outputs.flipper}} python3 scripts/testing/units.py ${{steps.device.outputs.flipper}}
- name: 'Get last release tag'
id: release_tag
if: always()
run: |
echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
- name: 'Decontaminate previous build leftovers'
if: always()
run: |
if [ -d .git ]; then
git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
fi
- name: 'Checkout latest release'
uses: actions/checkout@v3
if: always()
with:
fetch-depth: 0
ref: ${{ steps.release_tag.outputs.tag }}
- name: 'Flash last release'
if: always()
run: |
./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
- name: 'Wait for flipper to finish updating'
if: always()
run: |
source scripts/toolchain/fbtenv.sh
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
- name: 'Format flipper SD card'
id: format
if: always()
run: |
source scripts/toolchain/fbtenv.sh
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext

View File

@@ -128,6 +128,38 @@
"group": "build", "group": "build",
"type": "shell", "type": "shell",
"command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}" "command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}"
},
{
"label": "[Debug] Launch App on Flipper with Serial Console",
"dependsOrder": "sequence",
"group": "build",
"dependsOn": [
"[Debug] Launch App on Flipper",
"Serial Console"
]
},
{
// Press Ctrl+] to quit
"label": "Serial Console",
"type": "shell",
"command": "./fbt cli",
"group": "none",
"isBackground": true,
"options": {
"env": {
"FBT_NO_SYNC": "0"
}
},
"presentation": {
"reveal": "always",
"revealProblems": "never",
"showReuseMessage": false,
"panel": "dedicated",
"focus": true,
"echo": true,
"close": true,
"group": "Logger"
}
} }
] ]
} }

View File

@@ -0,0 +1,11 @@
App(
appid="locale_test",
name="Locale Test",
apptype=FlipperAppType.DEBUG,
entry_point="locale_test_app",
cdefines=["APP_LOCALE"],
requires=["gui", "locale"],
stack_size=2 * 1024,
order=70,
fap_category="Debug",
)

View File

@@ -0,0 +1,102 @@
#include <furi.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/dialog_ex.h>
#include <locale/locale.h>
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
View* view;
} LocaleTestApp;
static void locale_test_view_draw_callback(Canvas* canvas, void* _model) {
UNUSED(_model);
// Prepare canvas
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
FuriString* tmp_string = furi_string_alloc();
float temp = 25.3f;
LocaleMeasurementUnits units = locale_get_measurement_unit();
if(units == LocaleMeasurementUnitsMetric) {
furi_string_printf(tmp_string, "Temp: %5.1fC", (double)temp);
} else {
temp = locale_celsius_to_fahrenheit(temp);
furi_string_printf(tmp_string, "Temp: %5.1fF", (double)temp);
}
canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string));
FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime);
locale_format_time(tmp_string, &datetime, locale_get_time_format(), false);
canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string));
locale_format_date(tmp_string, &datetime, locale_get_date_format(), "/");
canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string));
furi_string_free(tmp_string);
}
static bool locale_test_view_input_callback(InputEvent* event, void* context) {
UNUSED(event);
UNUSED(context);
return false;
}
static uint32_t locale_test_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static LocaleTestApp* locale_test_alloc() {
LocaleTestApp* app = malloc(sizeof(LocaleTestApp));
// Gui
app->gui = furi_record_open(RECORD_GUI);
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Views
app->view = view_alloc();
view_set_draw_callback(app->view, locale_test_view_draw_callback);
view_set_input_callback(app->view, locale_test_view_input_callback);
view_set_previous_callback(app->view, locale_test_exit);
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
return app;
}
static void locale_test_free(LocaleTestApp* app) {
furi_assert(app);
// Free views
view_dispatcher_remove_view(app->view_dispatcher, 0);
view_free(app->view);
view_dispatcher_free(app->view_dispatcher);
// Close gui record
furi_record_close(RECORD_GUI);
app->gui = NULL;
// Free rest
free(app);
}
int32_t locale_test_app(void* p) {
UNUSED(p);
LocaleTestApp* app = locale_test_alloc();
view_dispatcher_run(app->view_dispatcher);
locale_test_free(app);
return 0;
}

View File

@@ -0,0 +1,110 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include <bt/bt_service/bt_keys_storage.h>
#include <storage/storage.h>
#define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH("unit_tests/bt_test.keys")
#define BT_TEST_NVM_RAM_BUFF_SIZE (507 * 4) // The same as in ble NVM storage
typedef struct {
Storage* storage;
BtKeysStorage* bt_keys_storage;
uint8_t* nvm_ram_buff_dut;
uint8_t* nvm_ram_buff_ref;
} BtTest;
BtTest* bt_test = NULL;
void bt_test_alloc() {
bt_test = malloc(sizeof(BtTest));
bt_test->storage = furi_record_open(RECORD_STORAGE);
bt_test->nvm_ram_buff_dut = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);
bt_test->nvm_ram_buff_ref = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);
bt_test->bt_keys_storage = bt_keys_storage_alloc(BT_TEST_KEY_STORAGE_FILE_PATH);
bt_keys_storage_set_ram_params(
bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, BT_TEST_NVM_RAM_BUFF_SIZE);
}
void bt_test_free() {
furi_assert(bt_test);
free(bt_test->nvm_ram_buff_ref);
free(bt_test->nvm_ram_buff_dut);
bt_keys_storage_free(bt_test->bt_keys_storage);
furi_record_close(RECORD_STORAGE);
free(bt_test);
bt_test = NULL;
}
static void bt_test_keys_storage_profile() {
// Emulate nvm change on initial connection
const int nvm_change_size_on_connection = 88;
for(size_t i = 0; i < nvm_change_size_on_connection; i++) {
bt_test->nvm_ram_buff_dut[i] = rand();
bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];
}
// Emulate update storage on initial connect
mu_assert(
bt_keys_storage_update(
bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection),
"Failed to update key storage on initial connect");
memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);
mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM");
mu_assert(
memcmp(
bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection) ==
0,
"Wrong buffer loaded");
const int nvm_disconnect_update_offset = 84;
const int nvm_disconnect_update_size = 324;
const int nvm_total_size = nvm_change_size_on_connection -
(nvm_change_size_on_connection - nvm_disconnect_update_offset) +
nvm_disconnect_update_size;
// Emulate update storage on initial disconnect
for(size_t i = nvm_disconnect_update_offset;
i < nvm_disconnect_update_offset + nvm_disconnect_update_size;
i++) {
bt_test->nvm_ram_buff_dut[i] = rand();
bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];
}
mu_assert(
bt_keys_storage_update(
bt_test->bt_keys_storage,
&bt_test->nvm_ram_buff_dut[nvm_disconnect_update_offset],
nvm_disconnect_update_size),
"Failed to update key storage on initial disconnect");
memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);
mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM");
mu_assert(
memcmp(bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_total_size) == 0,
"Wrong buffer loaded");
}
static void bt_test_keys_remove_test_file() {
mu_assert(
storage_simply_remove(bt_test->storage, BT_TEST_KEY_STORAGE_FILE_PATH),
"Can't remove test file");
}
MU_TEST(bt_test_keys_storage_serial_profile) {
furi_assert(bt_test);
bt_test_keys_remove_test_file();
bt_test_keys_storage_profile();
bt_test_keys_remove_test_file();
}
MU_TEST_SUITE(test_bt) {
bt_test_alloc();
MU_RUN_TEST(bt_test_keys_storage_serial_profile);
bt_test_free();
}
int run_minunit_test_bt() {
MU_RUN_SUITE(test_bt);
return MU_EXIT_CODE;
}

View File

@@ -3,98 +3,37 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
// this test is not accurate, but gives a basic understanding
// that memory management is working fine
// do not include memmgr.h here
// we also test that we are linking against stdlib
extern size_t memmgr_get_free_heap(void);
extern size_t memmgr_get_minimum_free_heap(void);
// current heap management realization consume:
// X bytes after allocate and 0 bytes after allocate and free,
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
bool heap_equal(size_t heap_size, size_t heap_size_old) {
// heap borders with overhead
const size_t heap_low = heap_size_old - heap_overhead_max_size;
const size_t heap_high = heap_size_old + heap_overhead_max_size;
// not exact, so we must test it against bigger numbers than "overhead size"
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
// debug allocation info
if(!result) {
printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high);
}
return result;
}
void test_furi_memmgr() { void test_furi_memmgr() {
size_t heap_size = 0; void* ptr;
size_t heap_size_old = 0;
const int alloc_size = 128;
void* ptr = NULL;
void* original_ptr = NULL;
// do not include furi memmgr.h case
#ifdef FURI_MEMMGR_GUARD
mu_fail("do not link against furi memmgr.h");
#endif
// allocate memory case // allocate memory case
heap_size_old = memmgr_get_free_heap(); ptr = malloc(100);
ptr = malloc(alloc_size); mu_check(ptr != NULL);
heap_size = memmgr_get_free_heap(); // test that memory is zero-initialized after allocation
mu_assert_pointers_not_eq(ptr, NULL); for(int i = 0; i < 100; i++) {
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed"); mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
}
// free memory case
heap_size_old = memmgr_get_free_heap();
free(ptr); free(ptr);
ptr = NULL;
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed");
// reallocate memory case // reallocate memory case
ptr = malloc(100);
memset(ptr, 66, 100);
ptr = realloc(ptr, 200);
mu_check(ptr != NULL);
// get filled array with some data // test that memory is really reallocated
original_ptr = malloc(alloc_size); for(int i = 0; i < 100; i++) {
mu_assert_pointers_not_eq(original_ptr, NULL); mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
for(int i = 0; i < alloc_size; i++) {
*(unsigned char*)(original_ptr + i) = i;
} }
// malloc array and copy data // TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
ptr = malloc(alloc_size);
mu_assert_pointers_not_eq(ptr, NULL);
memcpy(ptr, original_ptr, alloc_size);
// reallocate array
heap_size_old = memmgr_get_free_heap();
ptr = realloc(ptr, alloc_size * 2);
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed");
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
free(original_ptr);
free(ptr); free(ptr);
// allocate and zero-initialize array (calloc) // allocate and zero-initialize array (calloc)
original_ptr = malloc(alloc_size); ptr = calloc(100, 2);
mu_assert_pointers_not_eq(original_ptr, NULL); mu_check(ptr != NULL);
for(int i = 0; i < 100 * 2; i++) {
for(int i = 0; i < alloc_size; i++) { mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
*(unsigned char*)(original_ptr + i) = 0;
} }
heap_size_old = memmgr_get_free_heap();
ptr = calloc(1, alloc_size);
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed");
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
free(original_ptr);
free(ptr); free(ptr);
} }

View File

@@ -13,7 +13,7 @@
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 244 #define TEST_RANDOM_COUNT_PARSE 253
#define TEST_TIMEOUT 10000 #define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler; static SubGhzEnvironment* environment_handler;
@@ -318,7 +318,10 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
furi_hal_subghz_set_frequency_and_path(433920000); furi_hal_subghz_set_frequency_and_path(433920000);
furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test); if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) {
return false;
}
while(!furi_hal_subghz_is_async_tx_complete()) { while(!furi_hal_subghz_is_async_tx_complete()) {
furi_delay_ms(10); furi_delay_ms(10);
} }
@@ -587,6 +590,13 @@ MU_TEST(subghz_decoder_ansonic_test) {
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_smc5326_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/smc5326_raw.sub"), SUBGHZ_PROTOCOL_SMC5326_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
}
//test encoders //test encoders
MU_TEST(subghz_encoder_princeton_test) { MU_TEST(subghz_encoder_princeton_test) {
mu_assert( mu_assert(
@@ -714,6 +724,12 @@ MU_TEST(subghz_encoder_ansonic_test) {
"Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_smc5326_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/smc5326.sub")),
"Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
}
MU_TEST(subghz_random_test) { MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
} }
@@ -757,6 +773,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_decoder_ansonic_test);
MU_RUN_TEST(subghz_decoder_smc5326_test);
MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test); MU_RUN_TEST(subghz_encoder_came_test);
@@ -779,6 +796,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_encoder_clemsa_test);
MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_smc5326_test);
MU_RUN_TEST(subghz_random_test); MU_RUN_TEST(subghz_random_test);
subghz_test_deinit(); subghz_test_deinit();

View File

@@ -24,6 +24,7 @@ int run_minunit_test_protocol_dict();
int run_minunit_test_lfrfid_protocols(); int run_minunit_test_lfrfid_protocols();
int run_minunit_test_nfc(); int run_minunit_test_nfc();
int run_minunit_test_bit_lib(); int run_minunit_test_bit_lib();
int run_minunit_test_bt();
typedef int (*UnitTestEntry)(); typedef int (*UnitTestEntry)();
@@ -49,6 +50,7 @@ const UnitTest unit_tests[] = {
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
{.name = "bit_lib", .entry = run_minunit_test_bit_lib}, {.name = "bit_lib", .entry = run_minunit_test_bit_lib},
{.name = "bt", .entry = run_minunit_test_bt},
}; };
void minunit_print_progress() { void minunit_print_progress() {
@@ -71,7 +73,6 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli); UNUSED(cli);
UNUSED(args); UNUSED(args);
UNUSED(context); UNUSED(context);
uint32_t failed_tests = 0;
minunit_run = 0; minunit_run = 0;
minunit_assert = 0; minunit_assert = 0;
minunit_fail = 0; minunit_fail = 0;
@@ -97,32 +98,35 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
if(furi_string_size(args)) { if(furi_string_size(args)) {
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) { if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
failed_tests += unit_tests[i].entry(); unit_tests[i].entry();
} else { } else {
printf("Skipping %s\r\n", unit_tests[i].name); printf("Skipping %s\r\n", unit_tests[i].name);
} }
} else { } else {
failed_tests += unit_tests[i].entry(); unit_tests[i].entry();
} }
} }
printf("\r\nFailed tests: %lu\r\n", failed_tests);
// Time report if(minunit_run != 0) {
cycle_counter = (furi_get_tick() - cycle_counter); printf("\r\nFailed tests: %u\r\n", minunit_fail);
printf("Consumed: %lu ms\r\n", cycle_counter);
// Wait for tested services and apps to deallocate memory // Time report
furi_delay_ms(200); cycle_counter = (furi_get_tick() - cycle_counter);
uint32_t heap_after = memmgr_get_free_heap(); printf("Consumed: %lu ms\r\n", cycle_counter);
printf("Leaked: %ld\r\n", heap_before - heap_after);
// Final Report // Wait for tested services and apps to deallocate memory
if(failed_tests == 0) { furi_delay_ms(200);
notification_message(notification, &sequence_success); uint32_t heap_after = memmgr_get_free_heap();
printf("Status: PASSED\r\n"); printf("Leaked: %ld\r\n", heap_before - heap_after);
} else {
notification_message(notification, &sequence_error); // Final Report
printf("Status: FAILED\r\n"); if(minunit_fail == 0) {
notification_message(notification, &sequence_success);
printf("Status: PASSED\r\n");
} else {
notification_message(notification, &sequence_error);
printf("Status: FAILED\r\n");
}
} }
} }

View File

@@ -143,6 +143,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
break; break;
case ArchiveBrowserEventFileMenuDelete: case ArchiveBrowserEventFileMenuDelete:
if(archive_get_tab(browser) != ArchiveTabFavorites) { if(archive_get_tab(browser) != ArchiveTabFavorites) {
scene_manager_set_scene_state(
archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH);
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);
} }
consumed = true; consumed = true;

View File

@@ -5,6 +5,9 @@
#include "archive_browser_view.h" #include "archive_browser_view.h"
#include "../helpers/archive_browser.h" #include "../helpers/archive_browser.h"
#define SCROLL_INTERVAL (333)
#define SCROLL_DELAY (2)
static const char* ArchiveTabNames[] = { static const char* ArchiveTabNames[] = {
[ArchiveTabFavorites] = "Favorites", [ArchiveTabFavorites] = "Favorites",
[ArchiveTabIButton] = "iButton", [ArchiveTabIButton] = "iButton",
@@ -146,13 +149,18 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
furi_string_set(str_buf, "---"); furi_string_set(str_buf, "---");
} }
elements_string_fit_width( size_t scroll_counter = model->scroll_counter;
canvas, str_buf, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset);
if(model->item_idx == idx) { if(model->item_idx == idx) {
archive_draw_frame(canvas, i, scrollbar, model->move_fav); archive_draw_frame(canvas, i, scrollbar, model->move_fav);
if(scroll_counter < SCROLL_DELAY) {
scroll_counter = 0;
} else {
scroll_counter -= SCROLL_DELAY;
}
} else { } else {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
scroll_counter = 0;
} }
if(custom_icon_data) { if(custom_icon_data) {
@@ -162,8 +170,15 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
canvas_draw_icon( canvas_draw_icon(
canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]);
} }
canvas_draw_str(
canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buf)); elements_scrollable_text_line(
canvas,
15 + x_offset,
24 + i * FRAME_HEIGHT,
((scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset),
str_buf,
scroll_counter,
(model->item_idx != idx));
furi_string_free(str_buf); furi_string_free(str_buf);
} }
@@ -329,6 +344,7 @@ static bool archive_view_input(InputEvent* event, void* context) {
if(move_fav_mode) { if(move_fav_mode) {
browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);
} }
model->scroll_counter = 0;
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->item_idx = (model->item_idx + 1) % model->item_cnt; model->item_idx = (model->item_idx + 1) % model->item_cnt;
if(is_file_list_load_required(model)) { if(is_file_list_load_required(model)) {
@@ -338,6 +354,7 @@ static bool archive_view_input(InputEvent* event, void* context) {
if(move_fav_mode) { if(move_fav_mode) {
browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); browser->callback(ArchiveBrowserEventFavMoveDown, browser->context);
} }
model->scroll_counter = 0;
} }
}, },
true); true);
@@ -377,6 +394,27 @@ static bool archive_view_input(InputEvent* event, void* context) {
return true; return true;
} }
static void browser_scroll_timer(void* context) {
furi_assert(context);
ArchiveBrowserView* browser = context;
with_view_model(
browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter++; }, true);
}
static void browser_view_enter(void* context) {
furi_assert(context);
ArchiveBrowserView* browser = context;
with_view_model(
browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter = 0; }, true);
furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL);
}
static void browser_view_exit(void* context) {
furi_assert(context);
ArchiveBrowserView* browser = context;
furi_timer_stop(browser->scroll_timer);
}
ArchiveBrowserView* browser_alloc() { ArchiveBrowserView* browser_alloc() {
ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView)); ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView));
browser->view = view_alloc(); browser->view = view_alloc();
@@ -384,6 +422,10 @@ ArchiveBrowserView* browser_alloc() {
view_set_context(browser->view, browser); view_set_context(browser->view, browser);
view_set_draw_callback(browser->view, archive_view_render); view_set_draw_callback(browser->view, archive_view_render);
view_set_input_callback(browser->view, archive_view_input); view_set_input_callback(browser->view, archive_view_input);
view_set_enter_callback(browser->view, browser_view_enter);
view_set_exit_callback(browser->view, browser_view_exit);
browser->scroll_timer = furi_timer_alloc(browser_scroll_timer, FuriTimerTypePeriodic, browser);
browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT)); browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT));
@@ -402,6 +444,8 @@ ArchiveBrowserView* browser_alloc() {
void browser_free(ArchiveBrowserView* browser) { void browser_free(ArchiveBrowserView* browser) {
furi_assert(browser); furi_assert(browser);
furi_timer_free(browser->scroll_timer);
if(browser->worker_running) { if(browser->worker_running) {
file_browser_worker_free(browser->worker); file_browser_worker_free(browser->worker);
} }

View File

@@ -81,6 +81,7 @@ struct ArchiveBrowserView {
FuriString* path; FuriString* path;
InputKey last_tab_switch_dir; InputKey last_tab_switch_dir;
bool is_root; bool is_root;
FuriTimer* scroll_timer;
}; };
typedef struct { typedef struct {
@@ -97,6 +98,7 @@ typedef struct {
int32_t item_idx; int32_t item_idx;
int32_t array_offset; int32_t array_offset;
int32_t list_offset; int32_t list_offset;
size_t scroll_counter;
} ArchiveBrowserViewModel; } ArchiveBrowserViewModel;
void archive_browser_set_callback( void archive_browser_set_callback(

View File

@@ -52,7 +52,6 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
if(success) { if(success) {
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
} }

View File

@@ -48,6 +48,8 @@ void ibutton_scene_read_success_on_enter(void* context) {
dialog_ex_set_context(dialog_ex, ibutton); dialog_ex_set_context(dialog_ex, ibutton);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
} }
bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) { bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {

View File

@@ -46,6 +46,7 @@ Nfc* nfc_alloc() {
// Nfc device // Nfc device
nfc->dev = nfc_device_alloc(); nfc->dev = nfc_device_alloc();
furi_string_set(nfc->dev->folder, NFC_APP_FOLDER);
// Open GUI record // Open GUI record
nfc->gui = furi_record_open(RECORD_GUI); nfc->gui = furi_record_open(RECORD_GUI);

View File

@@ -46,6 +46,7 @@ ARRAY_DEF(FuriStringStack, FuriString*, M_PTR_OPLIST);
ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST); ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
#define NFC_TEXT_STORE_SIZE 128 #define NFC_TEXT_STORE_SIZE 128
#define NFC_APP_FOLDER ANY_PATH("nfc")
typedef struct { typedef struct {

View File

@@ -28,6 +28,13 @@ typedef enum {
SubGhzHopperStateRSSITimeOut, SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState; } SubGhzHopperState;
/** SubGhzSpeakerState state */
typedef enum {
SubGhzSpeakerStateDisable,
SubGhzSpeakerStateShutdown,
SubGhzSpeakerStateEnable,
} SubGhzSpeakerState;
/** SubGhzRxKeyState state */ /** SubGhzRxKeyState state */
typedef enum { typedef enum {
SubGhzRxKeyStateIDLE, SubGhzRxKeyStateIDLE,

View File

@@ -259,6 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReadRAWSendStop: case SubGhzCustomEventViewReadRAWSendStop:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_speaker_unmute(subghz);
subghz_tx_stop(subghz); subghz_tx_stop(subghz);
subghz_sleep(subghz); subghz_sleep(subghz);
} }
@@ -376,10 +377,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
subghz_protocol_raw_save_to_file_pause( subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true);
subghz_speaker_mute(subghz);
} else { } else {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
subghz_protocol_raw_save_to_file_pause( subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
subghz_speaker_unmute(subghz);
} }
} }

View File

@@ -5,6 +5,7 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexFrequency, SubGhzSettingIndexFrequency,
SubGhzSettingIndexHopping, SubGhzSettingIndexHopping,
SubGhzSettingIndexModulation, SubGhzSettingIndexModulation,
SubGhzSettingIndexSound,
SubGhzSettingIndexLock, SubGhzSettingIndexLock,
SubGhzSettingIndexRAWThesholdRSSI, SubGhzSettingIndexRAWThesholdRSSI,
}; };
@@ -48,6 +49,16 @@ const uint32_t hopping_value[HOPPING_COUNT] = {
SubGhzHopperStateRunnig, SubGhzHopperStateRunnig,
}; };
#define SPEAKER_COUNT 2
const char* const speaker_text[SPEAKER_COUNT] = {
"OFF",
"ON",
};
const uint32_t speaker_value[SPEAKER_COUNT] = {
SubGhzSpeakerStateShutdown,
SubGhzSpeakerStateEnable,
};
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
@@ -167,6 +178,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
subghz->txrx->hopper_state = hopping_value[index]; subghz->txrx->hopper_state = hopping_value[index];
} }
static void subghz_scene_receiver_config_set_speaker(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, speaker_text[index]);
subghz->txrx->speaker_state = speaker_value[index];
}
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
@@ -235,6 +254,16 @@ void subghz_scene_receiver_config_on_enter(void* context) {
variable_item_set_current_value_text( variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(subghz->setting, value_index)); item, subghz_setting_get_preset_name(subghz->setting, value_index));
item = variable_item_list_add(
subghz->variable_item_list,
"Sound:",
SPEAKER_COUNT,
subghz_scene_receiver_config_set_speaker,
subghz);
value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, speaker_text[value_index]);
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerSet) { SubGhzCustomEventManagerSet) {
variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL);

View File

@@ -177,6 +177,7 @@ SubGhz* subghz_alloc() {
subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
subghz->txrx->history = subghz_history_alloc(); subghz->txrx->history = subghz_history_alloc();

View File

@@ -86,6 +86,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) {
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx(); furi_hal_subghz_flush_rx();
subghz_speaker_on(subghz);
furi_hal_subghz_rx(); furi_hal_subghz_rx();
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker); furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker);
@@ -104,6 +105,7 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_write(&gpio_cc1101_g0, false);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
subghz_speaker_on(subghz);
bool ret = furi_hal_subghz_tx(); bool ret = furi_hal_subghz_tx();
subghz->txrx->txrx_state = SubGhzTxRxStateTx; subghz->txrx->txrx_state = SubGhzTxRxStateTx;
return ret; return ret;
@@ -119,11 +121,13 @@ void subghz_idle(SubGhz* subghz) {
void subghz_rx_end(SubGhz* subghz) { void subghz_rx_end(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx); furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(subghz->txrx->worker)) { if(subghz_worker_is_running(subghz->txrx->worker)) {
subghz_worker_stop(subghz->txrx->worker); subghz_worker_stop(subghz->txrx->worker);
furi_hal_subghz_stop_async_rx(); furi_hal_subghz_stop_async_rx();
} }
furi_hal_subghz_idle(); furi_hal_subghz_idle();
subghz_speaker_off(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
} }
@@ -212,6 +216,7 @@ void subghz_tx_stop(SubGhz* subghz) {
subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path));
} }
subghz_idle(subghz); subghz_idle(subghz);
subghz_speaker_off(subghz);
notification_message(subghz->notifications, &sequence_reset_red); notification_message(subghz->notifications, &sequence_reset_red);
} }
@@ -585,3 +590,40 @@ void subghz_hopper_update(SubGhz* subghz) {
subghz_rx(subghz, subghz->txrx->preset->frequency); subghz_rx(subghz, subghz->txrx->preset->frequency);
} }
} }
void subghz_speaker_on(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
} else {
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_speaker_off(SubGhz* subghz) {
if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
furi_hal_speaker_release();
if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown)
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_speaker_mute(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
}
}
}
void subghz_speaker_unmute(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
}
}
}

View File

@@ -53,6 +53,7 @@ struct SubGhzTxRx {
uint16_t idx_menu_chosen; uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state; SubGhzTxRxState txrx_state;
SubGhzHopperState hopper_state; SubGhzHopperState hopper_state;
SubGhzSpeakerState speaker_state;
uint8_t hopper_timeout; uint8_t hopper_timeout;
uint8_t hopper_idx_frequency; uint8_t hopper_idx_frequency;
SubGhzRxKeyState rx_key_state; SubGhzRxKeyState rx_key_state;
@@ -131,3 +132,7 @@ void subghz_file_name_clear(SubGhz* subghz);
bool subghz_path_is_file(FuriString* path); bool subghz_path_is_file(FuriString* path);
uint32_t subghz_random_serial(void); uint32_t subghz_random_serial(void);
void subghz_hopper_update(SubGhz* subghz); void subghz_hopper_update(SubGhz* subghz);
void subghz_speaker_on(SubGhz* subghz);
void subghz_speaker_off(SubGhz* subghz);
void subghz_speaker_mute(SubGhz* subghz);
void subghz_speaker_unmute(SubGhz* subghz);

View File

@@ -309,7 +309,8 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ {
if(model->idx != model->history_item - 1) model->idx++; if((model->history_item != 0) && (model->idx != model->history_item - 1))
model->idx++;
}, },
true); true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) { } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {

View File

@@ -0,0 +1,10 @@
App(
appid="clock",
name="Clock",
apptype=FlipperAppType.PLUGIN,
entry_point="clock_app",
requires=["gui"],
stack_size=2 * 1024,
fap_icon="clock.png",
fap_category="Tools",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,136 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <locale/locale.h>
typedef enum {
ClockEventTypeTick,
ClockEventTypeKey,
} ClockEventType;
typedef struct {
ClockEventType type;
InputEvent input;
} ClockEvent;
typedef struct {
FuriString* buffer;
FuriHalRtcDateTime datetime;
LocaleTimeFormat timeformat;
LocaleDateFormat dateformat;
} ClockData;
typedef struct {
FuriMutex* mutex;
FuriMessageQueue* queue;
ClockData* data;
} Clock;
static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
furi_assert(queue);
ClockEvent event = {.type = ClockEventTypeKey, .input = *input_event};
furi_message_queue_put(queue, &event, FuriWaitForever);
}
static void clock_render_callback(Canvas* canvas, void* ctx) {
Clock* clock = ctx;
if(furi_mutex_acquire(clock->mutex, 200) != FuriStatusOk) {
return;
}
ClockData* data = clock->data;
canvas_set_font(canvas, FontBigNumbers);
locale_format_time(data->buffer, &data->datetime, data->timeformat, true);
canvas_draw_str_aligned(
canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
// Special case to cover missing glyphs in FontBigNumbers
if(data->timeformat == LocaleTimeFormat12h) {
size_t time_width = canvas_string_width(canvas, furi_string_get_cstr(data->buffer));
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas,
64 + (time_width / 2) - 10,
31,
AlignLeft,
AlignCenter,
(data->datetime.hour > 12) ? "PM" : "AM");
}
canvas_set_font(canvas, FontSecondary);
locale_format_date(data->buffer, &data->datetime, data->dateformat, "/");
canvas_draw_str_aligned(
canvas, 64, 42, AlignCenter, AlignTop, furi_string_get_cstr(data->buffer));
furi_mutex_release(clock->mutex);
}
static void clock_tick(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* queue = ctx;
ClockEvent event = {.type = ClockEventTypeTick};
// It's OK to loose this event if system overloaded
furi_message_queue_put(queue, &event, 0);
}
int32_t clock_app(void* p) {
UNUSED(p);
Clock* clock = malloc(sizeof(Clock));
clock->data = malloc(sizeof(ClockData));
clock->data->buffer = furi_string_alloc();
clock->queue = furi_message_queue_alloc(8, sizeof(ClockEvent));
clock->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
furi_hal_rtc_get_datetime(&clock->data->datetime);
clock->data->timeformat = locale_get_time_format();
clock->data->dateformat = locale_get_date_format();
// Set ViewPort callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, clock_render_callback, clock);
view_port_input_callback_set(view_port, clock_input_callback, clock->queue);
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, clock->queue);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
furi_timer_start(timer, 100);
// Main loop
ClockEvent event;
for(bool processing = true; processing;) {
furi_check(furi_message_queue_get(clock->queue, &event, FuriWaitForever) == FuriStatusOk);
furi_mutex_acquire(clock->mutex, FuriWaitForever);
if(event.type == ClockEventTypeKey) {
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
processing = false;
}
} else if(event.type == ClockEventTypeTick) {
furi_hal_rtc_get_datetime(&clock->data->datetime);
}
furi_mutex_release(clock->mutex);
view_port_update(view_port);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_record_close(RECORD_GUI);
furi_message_queue_free(clock->queue);
furi_mutex_free(clock->mutex);
furi_string_free(clock->data->buffer);
free(clock->data);
free(clock);
return 0;
}

View File

@@ -9,10 +9,10 @@ enum HidDebugSubmenuIndex {
HidSubmenuIndexKeynote, HidSubmenuIndexKeynote,
HidSubmenuIndexKeyboard, HidSubmenuIndexKeyboard,
HidSubmenuIndexMedia, HidSubmenuIndexMedia,
BtHidSubmenuIndexTikTok, HidSubmenuIndexTikTok,
HidSubmenuIndexMouse, HidSubmenuIndexMouse,
HidSubmenuIndexMouseJiggler,
}; };
typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex;
static void hid_submenu_callback(void* context, uint32_t index) { static void hid_submenu_callback(void* context, uint32_t index) {
furi_assert(context); furi_assert(context);
@@ -29,9 +29,12 @@ static void hid_submenu_callback(void* context, uint32_t index) {
} else if(index == HidSubmenuIndexMouse) { } else if(index == HidSubmenuIndexMouse) {
app->view_id = HidViewMouse; app->view_id = HidViewMouse;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse);
} else if(index == BtHidSubmenuIndexTikTok) { } else if(index == HidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok; app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
} else if(index == HidSubmenuIndexMouseJiggler) {
app->view_id = HidViewMouseJiggler;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
} }
} }
@@ -48,6 +51,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_media_set_connected_status(hid->hid_media, connected); hid_media_set_connected_status(hid->hid_media, connected);
hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected);
hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
hid_tiktok_set_connected_status(hid->hid_tiktok, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
} }
@@ -104,10 +108,16 @@ Hid* hid_alloc(HidTransport transport) {
submenu_add_item( submenu_add_item(
app->device_type_submenu, app->device_type_submenu,
"TikTok Controller", "TikTok Controller",
BtHidSubmenuIndexTikTok, HidSubmenuIndexTikTok,
hid_submenu_callback, hid_submenu_callback,
app); app);
} }
submenu_add_item(
app->device_type_submenu,
"Mouse Jiggler",
HidSubmenuIndexMouseJiggler,
hid_submenu_callback,
app);
view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit);
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu));
@@ -160,6 +170,15 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
// Mouse jiggler view
app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
view_set_previous_callback(
hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher,
HidViewMouseJiggler,
hid_mouse_jiggler_get_view(app->hid_mouse_jiggler));
return app; return app;
} }
@@ -182,6 +201,8 @@ void hid_free(Hid* app) {
hid_media_free(app->hid_media); hid_media_free(app->hid_media);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
hid_mouse_free(app->hid_mouse); hid_mouse_free(app->hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
hid_mouse_jiggler_free(app->hid_mouse_jiggler);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
hid_tiktok_free(app->hid_tiktok); hid_tiktok_free(app->hid_tiktok);
view_dispatcher_free(app->view_dispatcher); view_dispatcher_free(app->view_dispatcher);
@@ -346,9 +367,17 @@ int32_t hid_ble_app(void* p) {
Hid* app = hid_alloc(HidTransportBle); Hid* app = hid_alloc(HidTransportBle);
app = hid_app_alloc_view(app); app = hid_app_alloc_view(app);
bt_disconnect(app->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_storage_path(app->bt, HID_BT_KEYS_STORAGE_PATH);
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch profile"); FURI_LOG_E(TAG, "Failed to switch to HID profile");
} }
furi_hal_bt_start_advertising(); furi_hal_bt_start_advertising();
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
@@ -357,7 +386,17 @@ int32_t hid_ble_app(void* p) {
view_dispatcher_run(app->view_dispatcher); view_dispatcher_run(app->view_dispatcher);
bt_set_status_changed_callback(app->bt, NULL, NULL); bt_set_status_changed_callback(app->bt, NULL, NULL);
bt_set_profile(app->bt, BtProfileSerial);
bt_disconnect(app->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_default_path(app->bt);
if(!bt_set_profile(app->bt, BtProfileSerial)) {
FURI_LOG_E(TAG, "Failed to switch to Serial profile");
}
hid_free(app); hid_free(app);

View File

@@ -11,6 +11,7 @@
#include <gui/view.h> #include <gui/view.h>
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <notification/notification.h> #include <notification/notification.h>
#include <storage/storage.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h> #include <gui/modules/dialog_ex.h>
@@ -19,8 +20,11 @@
#include "views/hid_keyboard.h" #include "views/hid_keyboard.h"
#include "views/hid_media.h" #include "views/hid_media.h"
#include "views/hid_mouse.h" #include "views/hid_mouse.h"
#include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h" #include "views/hid_tiktok.h"
#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
typedef enum { typedef enum {
HidTransportUsb, HidTransportUsb,
HidTransportBle, HidTransportBle,
@@ -39,6 +43,7 @@ struct Hid {
HidKeyboard* hid_keyboard; HidKeyboard* hid_keyboard;
HidMedia* hid_media; HidMedia* hid_media;
HidMouse* hid_mouse; HidMouse* hid_mouse;
HidMouseJiggler* hid_mouse_jiggler;
HidTikTok* hid_tiktok; HidTikTok* hid_tiktok;
HidTransport transport; HidTransport transport;

View File

@@ -4,6 +4,7 @@ typedef enum {
HidViewKeyboard, HidViewKeyboard,
HidViewMedia, HidViewMedia,
HidViewMouse, HidViewMouse,
HidViewMouseJiggler,
BtHidViewTikTok, BtHidViewTikTok,
HidViewExitConfirm, HidViewExitConfirm,
} HidView; } HidView;

View File

@@ -0,0 +1,149 @@
#include "hid_mouse_jiggler.h"
#include <gui/elements.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidMouseJiggler"
struct HidMouseJiggler {
View* view;
Hid* hid;
FuriTimer* timer;
};
typedef struct {
bool connected;
bool running;
uint8_t counter;
} HidMouseJigglerModel;
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseJigglerModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle");
canvas_set_font(canvas, FontSecondary);
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
if(model->running) {
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
if(model->running) {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
} else {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_jiggler_timer_callback(void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{
if(model->running) {
model->counter++;
hid_hal_mouse_move(
hid_mouse_jiggler->hid,
(model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT,
0);
}
},
false);
}
static void hid_mouse_jiggler_enter_callback(void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
furi_timer_start(hid_mouse_jiggler->timer, 500);
}
static void hid_mouse_jiggler_exit_callback(void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
furi_timer_stop(hid_mouse_jiggler->timer);
}
static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
bool consumed = false;
if(event->key == InputKeyOk) {
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{ model->running = !model->running; },
true);
consumed = true;
}
return consumed;
}
HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler));
hid_mouse_jiggler->view = view_alloc();
view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler);
view_allocate_model(
hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel));
view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback);
view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback);
view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback);
view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback);
hid_mouse_jiggler->hid = hid;
hid_mouse_jiggler->timer = furi_timer_alloc(
hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);
return hid_mouse_jiggler;
}
void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) {
furi_assert(hid_mouse_jiggler);
furi_timer_stop(hid_mouse_jiggler->timer);
furi_timer_free(hid_mouse_jiggler->timer);
view_free(hid_mouse_jiggler->view);
free(hid_mouse_jiggler);
}
View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) {
furi_assert(hid_mouse_jiggler);
return hid_mouse_jiggler->view;
}
void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) {
furi_assert(hid_mouse_jiggler);
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{ model->connected = connected; },
true);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <gui/view.h>
#define MOUSE_MOVE_SHORT 5
#define MOUSE_MOVE_LONG 20
typedef struct Hid Hid;
typedef struct HidMouseJiggler HidMouseJiggler;
HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid);
void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler);
View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler);
void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected);

View File

@@ -47,47 +47,51 @@ static int32_t music_player_worker_thread_callback(void* context) {
NoteBlockArray_it_t it; NoteBlockArray_it_t it;
NoteBlockArray_it(it, instance->notes); NoteBlockArray_it(it, instance->notes);
if(furi_hal_speaker_acquire(1000)) {
while(instance->should_work) {
if(NoteBlockArray_end_p(it)) {
NoteBlockArray_it(it, instance->notes);
furi_delay_ms(10);
} else {
NoteBlock* note_block = NoteBlockArray_ref(it);
while(instance->should_work) { float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;
if(NoteBlockArray_end_p(it)) { float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);
NoteBlockArray_it(it, instance->notes); float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm /
furi_delay_ms(10); note_block->duration;
} else { uint32_t dots = note_block->dots;
NoteBlock* note_block = NoteBlockArray_ref(it); while(dots > 0) {
duration += duration / 2;
dots--;
}
uint32_t next_tick = furi_get_tick() + duration;
float volume = instance->volume;
float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; if(instance->callback) {
float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); instance->callback(
float duration = note_block->semitone,
60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / note_block->duration; note_block->dots,
uint32_t dots = note_block->dots; note_block->duration,
while(dots > 0) { 0.0,
duration += duration / 2; instance->callback_context);
dots--; }
furi_hal_speaker_stop();
furi_hal_speaker_start(frequency, volume);
while(instance->should_work && furi_get_tick() < next_tick) {
volume *= 0.9945679;
furi_hal_speaker_set_volume(volume);
furi_delay_ms(2);
}
NoteBlockArray_next(it);
} }
uint32_t next_tick = furi_get_tick() + duration;
float volume = instance->volume;
if(instance->callback) {
instance->callback(
note_block->semitone,
note_block->dots,
note_block->duration,
0.0,
instance->callback_context);
}
furi_hal_speaker_stop();
furi_hal_speaker_start(frequency, volume);
while(instance->should_work && furi_get_tick() < next_tick) {
volume *= 0.9945679;
furi_hal_speaker_set_volume(volume);
furi_delay_ms(2);
}
NoteBlockArray_next(it);
} }
}
furi_hal_speaker_stop(); furi_hal_speaker_stop();
furi_hal_speaker_release();
} else {
FURI_LOG_E(TAG, "Speaker system is busy with another process.");
}
return 0; return 0;
} }

View File

@@ -49,6 +49,7 @@ NfcMagic* nfc_magic_alloc() {
// Nfc device // Nfc device
nfc_magic->nfc_dev = nfc_device_alloc(); nfc_magic->nfc_dev = nfc_device_alloc();
furi_string_set(nfc_magic->nfc_dev->folder, NFC_APP_FOLDER);
// Open GUI record // Open GUI record
nfc_magic->gui = furi_record_open(RECORD_GUI); nfc_magic->gui = furi_record_open(RECORD_GUI);

View File

@@ -27,6 +27,8 @@
#include <lib/nfc/nfc_device.h> #include <lib/nfc/nfc_device.h>
#include "nfc_magic_icons.h" #include "nfc_magic_icons.h"
#define NFC_APP_FOLDER ANY_PATH("nfc")
enum NfcMagicCustomEvent { enum NfcMagicCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0 // Reserve first 100 events for button types and indexes, starting from 0
NfcMagicCustomEventReserved = 100, NfcMagicCustomEventReserved = 100,

View File

@@ -3,7 +3,7 @@
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#define WS_VERSION_APP "0.5" #define WS_VERSION_APP "0.6.1"
#define WS_DEVELOPED "SkorP" #define WS_DEVELOPED "SkorP"
#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"

View File

@@ -134,8 +134,8 @@ static void ws_protocol_ambient_weather_remote_controller(WSBlockGeneric* instan
instance->id = (instance->data >> 32) & 0xFF; instance->id = (instance->data >> 32) & 0xFF;
instance->battery_low = (instance->data >> 31) & 1; instance->battery_low = (instance->data >> 31) & 1;
instance->channel = ((instance->data >> 28) & 0x07) + 1; instance->channel = ((instance->data >> 28) & 0x07) + 1;
instance->temp = ws_block_generic_fahrenheit_to_celsius( instance->temp =
((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f); locale_fahrenheit_to_celsius(((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
instance->humidity = (instance->data >> 8) & 0xFF; instance->humidity = (instance->data >> 8) & 0xFF;
instance->btn = WS_NO_BTN; instance->btn = WS_NO_BTN;

View File

@@ -143,8 +143,8 @@ static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) {
instance->id = instance->data >> 32; instance->id = instance->data >> 32;
instance->battery_low = (instance->data >> 26) & 1; instance->battery_low = (instance->data >> 26) & 1;
instance->btn = WS_NO_BTN; instance->btn = WS_NO_BTN;
instance->temp = ws_block_generic_fahrenheit_to_celsius( instance->temp =
((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); locale_fahrenheit_to_celsius(((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
instance->humidity = instance->humidity =
(((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH (((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH
instance->channel = instance->data & 0x03; instance->channel = instance->data & 0x03;

View File

@@ -135,6 +135,10 @@ static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) {
} }
instance->humidity = instance->data & 0xFF; instance->humidity = instance->data & 0xFF;
if(instance->humidity > 95)
instance->humidity = 95;
else if(instance->humidity < 20)
instance->humidity = 20;
} }
void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) {

View File

@@ -0,0 +1,331 @@
#include "oregon_v1.h"
#include <lib/toolbox/manchester_decoder.h>
#define TAG "WSProtocolOregon_V1"
/*
* Help
* https://github.dev/merbanan/rtl_433/blob/bb1be7f186ac0fdb7dc5d77693847d96fb95281e/src/devices/oregon_scientific_v1.c
*
* OSv1 protocol.
*
* MC with nominal bit width of 2930 us.
* Pulses are somewhat longer than nominal half-bit width, 1748 us / 3216 us,
* Gaps are somewhat shorter than nominal half-bit width, 1176 us / 2640 us.
* After 12 preamble bits there is 4200 us gap, 5780 us pulse, 5200 us gap.
* And next 32 bit data
*
* Care must be taken with the gap after the sync pulse since it
* is outside of the normal clocking. Because of this a data stream
* beginning with a 0 will have data in this gap.
*
*
* Data is in reverse order of bits
* RevBit(data32bit)=> tib23atad
*
* tib23atad => xxxxxxxx | busuTTTT | ttttzzzz | ccuuiiii
*
* - i: ID
* - x: CRC;
* - u: unknown;
* - b: battery low; flag to indicate low battery voltage
* - s: temperature sign
* - T: BCD, Temperature; in °C * 10
* - t: BCD, Temperature; in °C * 1
* - z: BCD, Temperature; in °C * 0.1
* - c: Channel 00=CH1, 01=CH2, 10=CH3
*
*/
#define OREGON_V1_HEADER_OK 0xFF
static const SubGhzBlockConst ws_protocol_oregon_v1_const = {
.te_short = 1465,
.te_long = 2930,
.te_delta = 350,
.min_count_bit_for_found = 32,
};
struct WSProtocolDecoderOregon_V1 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
ManchesterState manchester_state;
uint16_t header_count;
uint8_t first_bit;
};
struct WSProtocolEncoderOregon_V1 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
Oregon_V1DecoderStepReset = 0,
Oregon_V1DecoderStepFoundPreamble,
Oregon_V1DecoderStepParse,
} Oregon_V1DecoderStep;
const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder = {
.alloc = ws_protocol_decoder_oregon_v1_alloc,
.free = ws_protocol_decoder_oregon_v1_free,
.feed = ws_protocol_decoder_oregon_v1_feed,
.reset = ws_protocol_decoder_oregon_v1_reset,
.get_hash_data = ws_protocol_decoder_oregon_v1_get_hash_data,
.serialize = ws_protocol_decoder_oregon_v1_serialize,
.deserialize = ws_protocol_decoder_oregon_v1_deserialize,
.get_string = ws_protocol_decoder_oregon_v1_get_string,
};
const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_oregon_v1 = {
.name = WS_PROTOCOL_OREGON_V1_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_oregon_v1_decoder,
.encoder = &ws_protocol_oregon_v1_encoder,
};
void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderOregon_V1* instance = malloc(sizeof(WSProtocolDecoderOregon_V1));
instance->base.protocol = &ws_protocol_oregon_v1;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_oregon_v1_free(void* context) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
free(instance);
}
void ws_protocol_decoder_oregon_v1_reset(void* context) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
instance->decoder.parser_step = Oregon_V1DecoderStepReset;
}
static bool ws_protocol_oregon_v1_check(WSProtocolDecoderOregon_V1* instance) {
if(!instance->decoder.decode_data) return false;
uint64_t data = subghz_protocol_blocks_reverse_key(instance->decoder.decode_data, 32);
uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff);
crc = (crc & 0xff) + ((crc >> 8) & 0xff);
return (crc == ((data >> 24) & 0xFF));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_oregon_v1_remote_controller(WSBlockGeneric* instance) {
uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 32);
instance->id = data & 0xFF;
instance->channel = ((data >> 6) & 0x03) + 1;
float temp_raw =
((data >> 8) & 0x0F) * 0.1f + ((data >> 12) & 0x0F) + ((data >> 16) & 0x0F) * 10.0f;
if(!((data >> 21) & 1)) {
instance->temp = temp_raw;
} else {
instance->temp = -temp_raw;
}
instance->battery_low = !(instance->data >> 23) & 1;
instance->btn = WS_NO_BTN;
instance->humidity = WS_NO_HUMIDITY;
}
void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
ManchesterEvent event = ManchesterEventReset;
switch(instance->decoder.parser_step) {
case Oregon_V1DecoderStepReset:
if((level) && (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta)) {
instance->decoder.parser_step = Oregon_V1DecoderStepFoundPreamble;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case Oregon_V1DecoderStepFoundPreamble:
if(level) {
//keep high levels, if they suit our durations
if((DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta) ||
(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 4) <
ws_protocol_oregon_v1_const.te_delta)) {
instance->decoder.te_last = duration;
} else {
instance->decoder.parser_step = Oregon_V1DecoderStepReset;
}
} else if(
//checking low levels
(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta) &&
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta)) {
// Found header
instance->header_count++;
} else if(
(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 3) <
ws_protocol_oregon_v1_const.te_delta) &&
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta)) {
// check header
if(instance->header_count > 7) {
instance->header_count = OREGON_V1_HEADER_OK;
}
} else if(
(instance->header_count == OREGON_V1_HEADER_OK) &&
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short * 4) <
ws_protocol_oregon_v1_const.te_delta)) {
//found all the necessary patterns
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 1;
manchester_advance(
instance->manchester_state,
ManchesterEventReset,
&instance->manchester_state,
NULL);
instance->decoder.parser_step = Oregon_V1DecoderStepParse;
if(duration < ws_protocol_oregon_v1_const.te_short * 4) {
instance->first_bit = 1;
} else {
instance->first_bit = 0;
}
} else {
instance->decoder.parser_step = Oregon_V1DecoderStepReset;
}
break;
case Oregon_V1DecoderStepParse:
if(level) {
if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta) {
event = ManchesterEventShortHigh;
} else if(
DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) <
ws_protocol_oregon_v1_const.te_delta) {
event = ManchesterEventLongHigh;
} else {
instance->decoder.parser_step = Oregon_V1DecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) <
ws_protocol_oregon_v1_const.te_delta) {
event = ManchesterEventShortLow;
} else if(
DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) <
ws_protocol_oregon_v1_const.te_delta) {
event = ManchesterEventLongLow;
} else if(duration >= ((uint32_t)ws_protocol_oregon_v1_const.te_long * 2)) {
if(instance->decoder.decode_count_bit ==
ws_protocol_oregon_v1_const.min_count_bit_for_found) {
if(instance->first_bit) {
instance->decoder.decode_data = ~instance->decoder.decode_data | (1 << 31);
}
if(ws_protocol_oregon_v1_check(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_oregon_v1_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
manchester_advance(
instance->manchester_state,
ManchesterEventReset,
&instance->manchester_state,
NULL);
} else {
instance->decoder.parser_step = Oregon_V1DecoderStepReset;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(
instance->manchester_state, event, &instance->manchester_state, &data);
if(data_ok) {
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
instance->decoder.decode_count_bit++;
}
}
break;
}
}
uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool ws_protocol_decoder_oregon_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
bool ret = false;
do {
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit !=
ws_protocol_oregon_v1_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderOregon_V1* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_OREGON_V1_NAME "Oregon-v1"
typedef struct WSProtocolDecoderOregon_V1 WSProtocolDecoderOregon_V1;
typedef struct WSProtocolEncoderOregon_V1 WSProtocolEncoderOregon_V1;
extern const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder;
extern const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder;
extern const SubGhzProtocol ws_protocol_oregon_v1;
/**
* Allocate WSProtocolDecoderOregon_V1.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderOregon_V1* pointer to a WSProtocolDecoderOregon_V1 instance
*/
void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderOregon_V1.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
*/
void ws_protocol_decoder_oregon_v1_free(void* context);
/**
* Reset decoder WSProtocolDecoderOregon_V1.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
*/
void ws_protocol_decoder_oregon_v1_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderOregon_V1.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return true On success
*/
bool ws_protocol_decoder_oregon_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderOregon_V1.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderOregon_V1 instance
* @param output Resulting text
*/
void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output);

View File

@@ -13,6 +13,8 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_acurite_592txr, &ws_protocol_acurite_592txr,
&ws_protocol_ambient_weather, &ws_protocol_ambient_weather,
&ws_protocol_auriol_th, &ws_protocol_auriol_th,
&ws_protocol_oregon_v1,
&ws_protocol_tx_8300,
}; };
const SubGhzProtocolRegistry weather_station_protocol_registry = { const SubGhzProtocolRegistry weather_station_protocol_registry = {

View File

@@ -13,5 +13,7 @@
#include "acurite_592txr.h" #include "acurite_592txr.h"
#include "ambient_weather.h" #include "ambient_weather.h"
#include "auriol_hg0601a.h" #include "auriol_hg0601a.h"
#include "oregon_v1.h"
#include "tx_8300.h"
extern const SubGhzProtocolRegistry weather_station_protocol_registry; extern const SubGhzProtocolRegistry weather_station_protocol_registry;

View File

@@ -0,0 +1,293 @@
#include "tx_8300.h"
#define TAG "WSProtocolTX_8300"
/*
* Help
* https://github.com/merbanan/rtl_433/blob/master/src/devices/ambientweather_tx8300.c
*
* Ambient Weather TX-8300 (also sold as TFA 30.3211.02).
* 1970us pulse with variable gap (third pulse 3920 us).
* Above 79% humidity, gap after third pulse is 5848 us.
* - Bit 1 : 1970us pulse with 3888 us gap
* - Bit 0 : 1970us pulse with 1936 us gap
* 74 bit (2 bit preamble and 72 bit data => 9 bytes => 18 nibbles)
* The preamble seems to be a repeat counter (00, and 01 seen),
* the first 4 bytes are data,
* the second 4 bytes the same data inverted,
* the last byte is a checksum.
* Preamble format (2 bits):
* [1 bit (0)] [1 bit rolling count]
* Payload format (32 bits):
* HHHHhhhh ??CCNIII IIIITTTT ttttuuuu
* - H = First BCD digit humidity (the MSB might be distorted by the demod)
* - h = Second BCD digit humidity, invalid humidity seems to be 0x0e
* - ? = Likely battery flag, 2 bits
* - C = Channel, 2 bits
* - N = Negative temperature sign bit
* - I = ID, 7-bit
* - T = First BCD digit temperature
* - t = Second BCD digit temperature
* - u = Third BCD digit temperature
* The Checksum seems to covers the 4 data bytes and is something like Fletcher-8.
**/
#define TX_8300_PACKAGE_SIZE 32
static const SubGhzBlockConst ws_protocol_tx_8300_const = {
.te_short = 1940,
.te_long = 3880,
.te_delta = 250,
.min_count_bit_for_found = 72,
};
struct WSProtocolDecoderTX_8300 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
uint32_t package_1;
uint32_t package_2;
};
struct WSProtocolEncoderTX_8300 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
TX_8300DecoderStepReset = 0,
TX_8300DecoderStepCheckPreambule,
TX_8300DecoderStepSaveDuration,
TX_8300DecoderStepCheckDuration,
} TX_8300DecoderStep;
const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder = {
.alloc = ws_protocol_decoder_tx_8300_alloc,
.free = ws_protocol_decoder_tx_8300_free,
.feed = ws_protocol_decoder_tx_8300_feed,
.reset = ws_protocol_decoder_tx_8300_reset,
.get_hash_data = ws_protocol_decoder_tx_8300_get_hash_data,
.serialize = ws_protocol_decoder_tx_8300_serialize,
.deserialize = ws_protocol_decoder_tx_8300_deserialize,
.get_string = ws_protocol_decoder_tx_8300_get_string,
};
const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_tx_8300 = {
.name = WS_PROTOCOL_TX_8300_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_tx_8300_decoder,
.encoder = &ws_protocol_tx_8300_encoder,
};
void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderTX_8300* instance = malloc(sizeof(WSProtocolDecoderTX_8300));
instance->base.protocol = &ws_protocol_tx_8300;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_tx_8300_free(void* context) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
free(instance);
}
void ws_protocol_decoder_tx_8300_reset(void* context) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
instance->decoder.parser_step = TX_8300DecoderStepReset;
}
static bool ws_protocol_tx_8300_check_crc(WSProtocolDecoderTX_8300* instance) {
if(!instance->package_2) return false;
if(instance->package_1 != ~instance->package_2) return false;
uint16_t x = 0;
uint16_t y = 0;
for(int i = 0; i < 32; i += 4) {
x += (instance->package_1 >> i) & 0x0F;
y += (instance->package_1 >> i) & 0x05;
}
uint8_t crc = (~x & 0xF) << 4 | (~y & 0xF);
return (crc == ((instance->decoder.decode_data) & 0xFF));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_tx_8300_remote_controller(WSBlockGeneric* instance) {
instance->humidity = (((instance->data >> 28) & 0x0F) * 10) + ((instance->data >> 24) & 0x0F);
instance->btn = WS_NO_BTN;
if(!((instance->data >> 22) & 0x03))
instance->battery_low = 0;
else
instance->battery_low = 1;
instance->channel = (instance->data >> 20) & 0x03;
instance->id = (instance->data >> 12) & 0x7F;
float temp_raw = ((instance->data >> 8) & 0x0F) * 10.0f + ((instance->data >> 4) & 0x0F) +
(instance->data & 0x0F) * 0.1f;
if(!((instance->data >> 19) & 1)) {
instance->temp = temp_raw;
} else {
instance->temp = -temp_raw;
}
}
void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
switch(instance->decoder.parser_step) {
case TX_8300DecoderStepReset:
if((level) && (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) <
ws_protocol_tx_8300_const.te_delta)) {
instance->decoder.parser_step = TX_8300DecoderStepCheckPreambule;
}
break;
case TX_8300DecoderStepCheckPreambule:
if((!level) && ((DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) <
ws_protocol_tx_8300_const.te_delta) ||
(DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 3) <
ws_protocol_tx_8300_const.te_delta))) {
instance->decoder.parser_step = TX_8300DecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 1;
instance->package_1 = 0;
instance->package_2 = 0;
} else {
instance->decoder.parser_step = TX_8300DecoderStepReset;
}
break;
case TX_8300DecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = TX_8300DecoderStepCheckDuration;
} else {
instance->decoder.parser_step = TX_8300DecoderStepReset;
}
break;
case TX_8300DecoderStepCheckDuration:
if(!level) {
if(duration >= ((uint32_t)ws_protocol_tx_8300_const.te_short * 5)) {
//Found syncPostfix
if((instance->decoder.decode_count_bit ==
ws_protocol_tx_8300_const.min_count_bit_for_found) &&
ws_protocol_tx_8300_check_crc(instance)) {
instance->generic.data = instance->package_1;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_tx_8300_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 1;
instance->decoder.parser_step = TX_8300DecoderStepReset;
break;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) <
ws_protocol_tx_8300_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_long) <
ws_protocol_tx_8300_const.te_delta * 2)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = TX_8300DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) <
ws_protocol_tx_8300_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short) <
ws_protocol_tx_8300_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = TX_8300DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = TX_8300DecoderStepReset;
}
if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE) {
instance->package_1 = instance->decoder.decode_data;
instance->decoder.decode_data = 0;
} else if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE * 2) {
instance->package_2 = instance->decoder.decode_data;
instance->decoder.decode_data = 0;
}
} else {
instance->decoder.parser_step = TX_8300DecoderStepReset;
}
break;
}
}
uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool ws_protocol_decoder_tx_8300_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
bool ret = false;
do {
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit != ws_protocol_tx_8300_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderTX_8300* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_TX_8300_NAME "TX8300"
typedef struct WSProtocolDecoderTX_8300 WSProtocolDecoderTX_8300;
typedef struct WSProtocolEncoderTX_8300 WSProtocolEncoderTX_8300;
extern const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder;
extern const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder;
extern const SubGhzProtocol ws_protocol_tx_8300;
/**
* Allocate WSProtocolDecoderTX_8300.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderTX_8300* pointer to a WSProtocolDecoderTX_8300 instance
*/
void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderTX_8300.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
*/
void ws_protocol_decoder_tx_8300_free(void* context);
/**
* Reset decoder WSProtocolDecoderTX_8300.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
*/
void ws_protocol_decoder_tx_8300_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderTX_8300.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return true On success
*/
bool ws_protocol_decoder_tx_8300_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderTX_8300.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderTX_8300 instance
* @param output Resulting text
*/
void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output);

View File

@@ -209,7 +209,3 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp
return res; return res;
} }
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) {
return (fahrenheit - 32.0f) / 1.8f;
}

View File

@@ -8,6 +8,7 @@
#include "furi.h" #include "furi.h"
#include "furi_hal.h" #include "furi_hal.h"
#include <lib/subghz/types.h> #include <lib/subghz/types.h>
#include <locale/locale.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -62,8 +63,6 @@ bool ws_block_generic_serialize(
*/ */
bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format);
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -303,7 +303,7 @@ bool ws_view_receiver_input(InputEvent* event, void* context) {
ws_receiver->view, ws_receiver->view,
WSReceiverModel * model, WSReceiverModel * model,
{ {
if(model->idx != model->history_item - 1) model->idx++; if(model->history_item && model->idx != model->history_item - 1) model->idx++;
}, },
true); true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) { } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {

View File

@@ -81,9 +81,35 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
if(model->generic->temp != WS_NO_TEMPERATURE) { if(model->generic->temp != WS_NO_TEMPERATURE) {
canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16); canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16);
snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp);
canvas_draw_str_aligned(canvas, 47, 47, AlignRight, AlignTop, buffer); uint8_t temp_x1 = 0;
canvas_draw_circle(canvas, 38, 46, 1); uint8_t temp_x2 = 0;
if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {
snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp);
if(model->generic->temp < -9.0f) {
temp_x1 = 49;
temp_x2 = 40;
} else {
temp_x1 = 47;
temp_x2 = 38;
}
} else {
snprintf(
buffer,
sizeof(buffer),
"%3.1f F",
(double)locale_celsius_to_fahrenheit(model->generic->temp));
if((model->generic->temp < -27.77f) || (model->generic->temp > 37.77f)) {
temp_x1 = 50;
temp_x2 = 42;
} else {
temp_x1 = 48;
temp_x2 = 40;
}
}
canvas_draw_str_aligned(canvas, temp_x1, 47, AlignRight, AlignTop, buffer);
canvas_draw_circle(canvas, temp_x2, 46, 1);
} }
if(model->generic->humidity != WS_NO_HUMIDITY) { if(model->generic->humidity != WS_NO_HUMIDITY) {

View File

@@ -117,6 +117,8 @@ Bt* bt_alloc() {
if(!bt_settings_load(&bt->bt_settings)) { if(!bt_settings_load(&bt->bt_settings)) {
bt_settings_save(&bt->bt_settings); bt_settings_save(&bt->bt_settings);
} }
// Keys storage
bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH);
// Alloc queue // Alloc queue
bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage)); bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage));
@@ -285,8 +287,10 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) { static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) {
furi_assert(context); furi_assert(context);
Bt* bt = context; Bt* bt = context;
FURI_LOG_I(TAG, "Changed addr start: %p, size changed: %d", addr, size); BtMessage message = {
BtMessage message = {.type = BtMessageTypeKeysStorageUpdated}; .type = BtMessageTypeKeysStorageUpdated,
.data.key_storage_data.start_address = addr,
.data.key_storage_data.size = size};
furi_check( furi_check(
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
} }
@@ -331,6 +335,8 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
furi_profile = FuriHalBtProfileSerial; furi_profile = FuriHalBtProfileSerial;
} }
bt_keys_storage_load(bt->keys_storage);
if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) {
FURI_LOG_I(TAG, "Bt App started"); FURI_LOG_I(TAG, "Bt App started");
if(bt->bt_settings.enabled) { if(bt->bt_settings.enabled) {
@@ -358,6 +364,7 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
static void bt_close_connection(Bt* bt) { static void bt_close_connection(Bt* bt) {
bt_close_rpc_connection(bt); bt_close_rpc_connection(bt);
furi_hal_bt_stop_advertising();
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
} }
@@ -372,8 +379,8 @@ int32_t bt_srv(void* p) {
return 0; return 0;
} }
// Read keys // Load keys
if(!bt_keys_storage_load(bt)) { if(!bt_keys_storage_load(bt->keys_storage)) {
FURI_LOG_W(TAG, "Failed to load bonding keys"); FURI_LOG_W(TAG, "Failed to load bonding keys");
} }
@@ -418,13 +425,16 @@ int32_t bt_srv(void* p) {
// Display PIN code // Display PIN code
bt_pin_code_show(bt, message.data.pin_code); bt_pin_code_show(bt, message.data.pin_code);
} else if(message.type == BtMessageTypeKeysStorageUpdated) { } else if(message.type == BtMessageTypeKeysStorageUpdated) {
bt_keys_storage_save(bt); bt_keys_storage_update(
bt->keys_storage,
message.data.key_storage_data.start_address,
message.data.key_storage_data.size);
} else if(message.type == BtMessageTypeSetProfile) { } else if(message.type == BtMessageTypeSetProfile) {
bt_change_profile(bt, &message); bt_change_profile(bt, &message);
} else if(message.type == BtMessageTypeDisconnect) { } else if(message.type == BtMessageTypeDisconnect) {
bt_close_connection(bt); bt_close_connection(bt);
} else if(message.type == BtMessageTypeForgetBondedDevices) { } else if(message.type == BtMessageTypeForgetBondedDevices) {
bt_keys_storage_delete(bt); bt_keys_storage_delete(bt->keys_storage);
} }
} }
return 0; return 0;

View File

@@ -56,6 +56,19 @@ void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, vo
*/ */
void bt_forget_bonded_devices(Bt* bt); void bt_forget_bonded_devices(Bt* bt);
/** Set keys storage file path
*
* @param bt Bt instance
* @param keys_storage_path Path to file with saved keys
*/
void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path);
/** Set default keys storage file path
*
* @param bt Bt instance
*/
void bt_keys_storage_set_default_path(Bt* bt);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -39,3 +39,18 @@ void bt_forget_bonded_devices(Bt* bt) {
furi_check( furi_check(
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
} }
void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path) {
furi_assert(bt);
furi_assert(bt->keys_storage);
furi_assert(keys_storage_path);
bt_keys_storage_set_file_path(bt->keys_storage, keys_storage_path);
}
void bt_keys_storage_set_default_path(Bt* bt) {
furi_assert(bt);
furi_assert(bt->keys_storage);
bt_keys_storage_set_file_path(bt->keys_storage, BT_KEYS_STORAGE_PATH);
}

View File

@@ -13,8 +13,14 @@
#include <power/power_service/power.h> #include <power/power_service/power.h>
#include <rpc/rpc.h> #include <rpc/rpc.h>
#include <notification/notification.h> #include <notification/notification.h>
#include <storage/storage.h>
#include <bt/bt_settings.h> #include <bt/bt_settings.h>
#include <bt/bt_service/bt_keys_storage.h>
#include "bt_keys_filename.h"
#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME)
#define BT_API_UNLOCK_EVENT (1UL << 0) #define BT_API_UNLOCK_EVENT (1UL << 0)
@@ -29,10 +35,16 @@ typedef enum {
BtMessageTypeForgetBondedDevices, BtMessageTypeForgetBondedDevices,
} BtMessageType; } BtMessageType;
typedef struct {
uint8_t* start_address;
uint16_t size;
} BtKeyStorageUpdateData;
typedef union { typedef union {
uint32_t pin_code; uint32_t pin_code;
uint8_t battery_level; uint8_t battery_level;
BtProfile profile; BtProfile profile;
BtKeyStorageUpdateData key_storage_data;
} BtMessageData; } BtMessageData;
typedef struct { typedef struct {
@@ -46,6 +58,7 @@ struct Bt {
uint16_t bt_keys_size; uint16_t bt_keys_size;
uint16_t max_packet_size; uint16_t max_packet_size;
BtSettings bt_settings; BtSettings bt_settings;
BtKeysStorage* keys_storage;
BtStatus status; BtStatus status;
BtProfile profile; BtProfile profile;
FuriMessageQueue* message_queue; FuriMessageQueue* message_queue;

View File

@@ -1,49 +1,24 @@
#include "bt_keys_storage.h" #include "bt_keys_storage.h"
#include <furi.h> #include <furi.h>
#include <furi_hal_bt.h>
#include <lib/toolbox/saved_struct.h> #include <lib/toolbox/saved_struct.h>
#include <storage/storage.h> #include <storage/storage.h>
#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME)
#define BT_KEYS_STORAGE_VERSION (0) #define BT_KEYS_STORAGE_VERSION (0)
#define BT_KEYS_STORAGE_MAGIC (0x18) #define BT_KEYS_STORAGE_MAGIC (0x18)
bool bt_keys_storage_load(Bt* bt) { #define TAG "BtKeyStorage"
furi_assert(bt);
bool file_loaded = false;
furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); struct BtKeysStorage {
furi_hal_bt_nvm_sram_sem_acquire(); uint8_t* nvm_sram_buff;
file_loaded = saved_struct_load( uint16_t nvm_sram_buff_size;
BT_KEYS_STORAGE_PATH, FuriString* file_path;
bt->bt_keys_addr_start, };
bt->bt_keys_size,
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION);
furi_hal_bt_nvm_sram_sem_release();
return file_loaded; bool bt_keys_storage_delete(BtKeysStorage* instance) {
} furi_assert(instance);
bool bt_keys_storage_save(Bt* bt) {
furi_assert(bt);
furi_assert(bt->bt_keys_addr_start);
bool file_saved = false;
furi_hal_bt_nvm_sram_sem_acquire();
file_saved = saved_struct_save(
BT_KEYS_STORAGE_PATH,
bt->bt_keys_addr_start,
bt->bt_keys_size,
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION);
furi_hal_bt_nvm_sram_sem_release();
return file_saved;
}
bool bt_keys_storage_delete(Bt* bt) {
furi_assert(bt);
bool delete_succeed = false; bool delete_succeed = false;
bool bt_is_active = furi_hal_bt_is_active(); bool bt_is_active = furi_hal_bt_is_active();
@@ -55,3 +30,117 @@ bool bt_keys_storage_delete(Bt* bt) {
return delete_succeed; return delete_succeed;
} }
BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path) {
furi_assert(keys_storage_path);
BtKeysStorage* instance = malloc(sizeof(BtKeysStorage));
// Set default nvm ram parameters
furi_hal_bt_get_key_storage_buff(&instance->nvm_sram_buff, &instance->nvm_sram_buff_size);
// Set key storage file
instance->file_path = furi_string_alloc();
furi_string_set_str(instance->file_path, keys_storage_path);
return instance;
}
void bt_keys_storage_free(BtKeysStorage* instance) {
furi_assert(instance);
furi_string_free(instance->file_path);
free(instance);
}
void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path) {
furi_assert(instance);
furi_assert(path);
furi_string_set_str(instance->file_path, path);
}
void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size) {
furi_assert(instance);
furi_assert(buff);
instance->nvm_sram_buff = buff;
instance->nvm_sram_buff_size = size;
}
bool bt_keys_storage_load(BtKeysStorage* instance) {
furi_assert(instance);
bool loaded = false;
do {
// Get payload size
size_t payload_size = 0;
if(!saved_struct_get_payload_size(
furi_string_get_cstr(instance->file_path),
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION,
&payload_size)) {
FURI_LOG_E(TAG, "Failed to read payload size");
break;
}
if(payload_size > instance->nvm_sram_buff_size) {
FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer");
break;
}
// Load saved data to ram
furi_hal_bt_nvm_sram_sem_acquire();
bool data_loaded = saved_struct_load(
furi_string_get_cstr(instance->file_path),
instance->nvm_sram_buff,
payload_size,
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION);
furi_hal_bt_nvm_sram_sem_release();
if(!data_loaded) {
FURI_LOG_E(TAG, "Failed to load struct");
break;
}
loaded = true;
} while(false);
return loaded;
}
bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size) {
furi_assert(instance);
furi_assert(start_addr);
bool updated = false;
FURI_LOG_I(
TAG,
"Base address: %p. Start update address: %p. Size changed: %ld",
(void*)instance->nvm_sram_buff,
start_addr,
size);
do {
size_t new_size = start_addr - instance->nvm_sram_buff + size;
if(new_size > instance->nvm_sram_buff_size) {
FURI_LOG_E(TAG, "NVM RAM buffer overflow");
break;
}
furi_hal_bt_nvm_sram_sem_acquire();
bool data_updated = saved_struct_save(
furi_string_get_cstr(instance->file_path),
instance->nvm_sram_buff,
new_size,
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION);
furi_hal_bt_nvm_sram_sem_release();
if(!data_updated) {
FURI_LOG_E(TAG, "Failed to update key storage");
break;
}
updated = true;
} while(false);
return updated;
}

View File

@@ -1,10 +1,20 @@
#pragma once #pragma once
#include "bt_i.h" #include <stdint.h>
#include "bt_keys_filename.h" #include <stdbool.h>
bool bt_keys_storage_load(Bt* bt); typedef struct BtKeysStorage BtKeysStorage;
bool bt_keys_storage_save(Bt* bt); BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path);
bool bt_keys_storage_delete(Bt* bt); void bt_keys_storage_free(BtKeysStorage* instance);
void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path);
void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size);
bool bt_keys_storage_load(BtKeysStorage* instance);
bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size);
bool bt_keys_storage_delete(BtKeysStorage* instance);

View File

@@ -52,6 +52,7 @@ struct AnimationManager {
FuriString* freezed_animation_name; FuriString* freezed_animation_name;
int32_t freezed_animation_time_left; int32_t freezed_animation_time_left;
ViewStack* view_stack; ViewStack* view_stack;
bool dummy_mode;
}; };
static StorageAnimation* static StorageAnimation*
@@ -93,6 +94,12 @@ void animation_manager_set_interact_callback(
animation_manager->interact_callback = callback; animation_manager->interact_callback = callback;
} }
void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) {
furi_assert(animation_manager);
animation_manager->dummy_mode = enabled;
animation_manager_start_new_idle(animation_manager);
}
static void animation_manager_check_blocking_callback(const void* message, void* context) { static void animation_manager_check_blocking_callback(const void* message, void* context) {
const StorageEvent* storage_event = message; const StorageEvent* storage_event = message;
@@ -363,7 +370,9 @@ static bool animation_manager_is_valid_idle_animation(
static StorageAnimation* static StorageAnimation*
animation_manager_select_idle_animation(AnimationManager* animation_manager) { animation_manager_select_idle_animation(AnimationManager* animation_manager) {
UNUSED(animation_manager); if(animation_manager->dummy_mode) {
return animation_storage_find_animation(HARDCODED_ANIMATION_NAME);
}
StorageAnimationList_t animation_list; StorageAnimationList_t animation_list;
StorageAnimationList_init(animation_list); StorageAnimationList_init(animation_list);
animation_storage_fill_animation_list(&animation_list); animation_storage_fill_animation_list(&animation_list);

View File

@@ -157,3 +157,11 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma
* @animation_manager instance * @animation_manager instance
*/ */
void animation_manager_load_and_continue_animation(AnimationManager* animation_manager); void animation_manager_load_and_continue_animation(AnimationManager* animation_manager);
/**
* Enable or disable dummy mode backgrounds of animation manager.
*
* @animation_manager instance
* @enabled bool
*/
void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled);

View File

@@ -144,6 +144,7 @@ void desktop_unlock(Desktop* desktop) {
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled); view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled);
desktop_main_set_dummy_mode_state(desktop->main_view, enabled); desktop_main_set_dummy_mode_state(desktop->main_view, enabled);
animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled);
desktop->settings.dummy_mode = enabled; desktop->settings.dummy_mode = enabled;
DESKTOP_SETTINGS_SAVE(&desktop->settings); DESKTOP_SETTINGS_SAVE(&desktop->settings);
} }
@@ -330,6 +331,8 @@ int32_t desktop_srv(void* p) {
view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode); view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode);
desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode); desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode);
animation_manager_set_dummy_mode_state(
desktop->animation_manager, desktop->settings.dummy_mode);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);

View File

@@ -12,6 +12,10 @@
#define TAG "DesktopSrv" #define TAG "DesktopSrv"
#define MUSIC_PLAYER_APP EXT_PATH("/apps/Misc/music_player.fap")
#define SNAKE_GAME_APP EXT_PATH("/apps/Games/snake_game.fap")
#define CLOCK_APP EXT_PATH("/apps/Tools/clock.fap")
static void desktop_scene_main_new_idle_animation_callback(void* context) { static void desktop_scene_main_new_idle_animation_callback(void* context) {
furi_assert(context); furi_assert(context);
Desktop* desktop = context; Desktop* desktop = context;
@@ -60,6 +64,19 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl
} }
#endif #endif
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) {
do {
LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, path);
if(status == LoaderStatusOk) break;
FURI_LOG_E(TAG, "loader_start failed: %d", status);
status = loader_start(desktop->loader, "Passport", NULL);
if(status != LoaderStatusOk) {
FURI_LOG_E(TAG, "loader_start failed: %d", status);
}
} while(false);
}
void desktop_scene_main_callback(DesktopEvent event, void* context) { void desktop_scene_main_callback(DesktopEvent event, void* context) {
Desktop* desktop = (Desktop*)context; Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event); view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
@@ -181,12 +198,16 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
} }
break; break;
} }
case DesktopMainEventOpenGameMenu: { case DesktopMainEventOpenGame: {
LoaderStatus status = loader_start( desktop_scene_main_open_app_or_profile(desktop, SNAKE_GAME_APP);
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/snake_game.fap")); break;
if(status != LoaderStatusOk) { }
FURI_LOG_E(TAG, "loader_start failed: %d", status); case DesktopMainEventOpenClock: {
} desktop_scene_main_open_app_or_profile(desktop, CLOCK_APP);
break;
}
case DesktopMainEventOpenMusicPlayer: {
desktop_scene_main_open_app_or_profile(desktop, MUSIC_PLAYER_APP);
break; break;
} }
case DesktopLockedEventUpdate: case DesktopLockedEventUpdate:

View File

@@ -10,7 +10,9 @@ typedef enum {
DesktopMainEventOpenPassport, DesktopMainEventOpenPassport,
DesktopMainEventOpenPowerOff, DesktopMainEventOpenPowerOff,
DesktopMainEventOpenGameMenu, DesktopMainEventOpenGame,
DesktopMainEventOpenClock,
DesktopMainEventOpenMusicPlayer,
DesktopLockedEventUnlocked, DesktopLockedEventUnlocked,
DesktopLockedEventUpdate, DesktopLockedEventUpdate,

View File

@@ -72,13 +72,13 @@ bool desktop_main_input_callback(InputEvent* event, void* context) {
} else { } else {
if(event->type == InputTypeShort) { if(event->type == InputTypeShort) {
if(event->key == InputKeyOk) { if(event->key == InputKeyOk) {
main_view->callback(DesktopMainEventOpenGameMenu, main_view->context); main_view->callback(DesktopMainEventOpenGame, main_view->context);
} else if(event->key == InputKeyUp) { } else if(event->key == InputKeyUp) {
main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); main_view->callback(DesktopMainEventOpenLockMenu, main_view->context);
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
main_view->callback(DesktopMainEventOpenPassport, main_view->context); main_view->callback(DesktopMainEventOpenMusicPlayer, main_view->context);
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
main_view->callback(DesktopMainEventOpenPassport, main_view->context); main_view->callback(DesktopMainEventOpenClock, main_view->context);
} }
// Right key is handled by animation manager // Right key is handled by animation manager
} }

View File

@@ -50,6 +50,10 @@ void view_holder_free(ViewHolder* view_holder) {
void view_holder_set_view(ViewHolder* view_holder, View* view) { void view_holder_set_view(ViewHolder* view_holder, View* view) {
furi_assert(view_holder); furi_assert(view_holder);
if(view_holder->view) { if(view_holder->view) {
if(view_holder->view->exit_callback) {
view_holder->view->exit_callback(view_holder->view->context);
}
view_set_update_callback(view_holder->view, NULL); view_set_update_callback(view_holder->view, NULL);
view_set_update_callback_context(view_holder->view, NULL); view_set_update_callback_context(view_holder->view, NULL);
} }
@@ -59,6 +63,10 @@ void view_holder_set_view(ViewHolder* view_holder, View* view) {
if(view_holder->view) { if(view_holder->view) {
view_set_update_callback(view_holder->view, view_holder_update); view_set_update_callback(view_holder->view, view_holder_update);
view_set_update_callback_context(view_holder->view, view_holder); view_set_update_callback_context(view_holder->view, view_holder);
if(view_holder->view->enter_callback) {
view_holder->view->enter_callback(view_holder->view->context);
}
} }
} }

View File

@@ -547,6 +547,53 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width
} }
} }
void elements_scrollable_text_line(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
FuriString* string,
size_t scroll,
bool ellipsis) {
FuriString* line = furi_string_alloc_set(string);
size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
if(len_px > width) {
if(ellipsis) {
width -= canvas_string_width(canvas, "...");
}
// Calculate scroll size
size_t scroll_size = furi_string_size(line);
size_t right_width = 0;
for(size_t i = scroll_size; i > 0; i--) {
right_width += canvas_glyph_width(canvas, furi_string_get_char(line, i));
if(right_width > width) break;
scroll_size--;
if(!scroll_size) break;
}
// Ensure that we have something to scroll
if(scroll_size) {
scroll_size += 3;
scroll = scroll % scroll_size;
furi_string_right(line, scroll);
}
len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
while(len_px > width) {
furi_string_left(line, furi_string_size(line) - 1);
len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
}
if(ellipsis) {
furi_string_cat(line, "...");
}
}
canvas_draw_str(canvas, x, y, furi_string_get_cstr(line));
furi_string_free(line);
}
void elements_text_box( void elements_text_box(
Canvas* canvas, Canvas* canvas,
uint8_t x, uint8_t x,

View File

@@ -192,6 +192,25 @@ void elements_bubble_str(
*/ */
void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width); void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width);
/** Draw scrollable text line
*
* @param canvas The canvas
* @param[in] x X coordinate
* @param[in] y Y coordinate
* @param[in] width The width
* @param string The string
* @param[in] scroll The scroll counter: 0 - no scroll, any other number - scroll. Just count up, everything else will be calculated on the inside.
* @param[in] ellipsis The ellipsis flag: true to add ellipse
*/
void elements_scrollable_text_line(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
FuriString* string,
size_t scroll,
bool ellipsis);
/** Draw text box element /** Draw text box element
* *
* @param canvas Canvas instance * @param canvas Canvas instance

View File

@@ -19,6 +19,9 @@
#define CUSTOM_ICON_MAX_SIZE 32 #define CUSTOM_ICON_MAX_SIZE 32
#define SCROLL_INTERVAL (333)
#define SCROLL_DELAY (2)
typedef enum { typedef enum {
BrowserItemTypeLoading, BrowserItemTypeLoading,
BrowserItemTypeBack, BrowserItemTypeBack,
@@ -95,6 +98,7 @@ struct FileBrowser {
void* item_context; void* item_context;
FuriString* result_path; FuriString* result_path;
FuriTimer* scroll_timer;
}; };
typedef struct { typedef struct {
@@ -110,6 +114,7 @@ typedef struct {
const Icon* file_icon; const Icon* file_icon;
bool hide_ext; bool hide_ext;
size_t scroll_counter;
} FileBrowserModel; } FileBrowserModel;
static const Icon* BrowserItemIcons[] = { static const Icon* BrowserItemIcons[] = {
@@ -129,6 +134,27 @@ static void
browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last); browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last);
static void browser_long_load_cb(void* context); static void browser_long_load_cb(void* context);
static void file_browser_scroll_timer_callback(void* context) {
furi_assert(context);
FileBrowser* browser = context;
with_view_model(
browser->view, FileBrowserModel * model, { model->scroll_counter++; }, true);
}
static void file_browser_view_enter_callback(void* context) {
furi_assert(context);
FileBrowser* browser = context;
with_view_model(
browser->view, FileBrowserModel * model, { model->scroll_counter = 0; }, true);
furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL);
}
static void file_browser_view_exit_callback(void* context) {
furi_assert(context);
FileBrowser* browser = context;
furi_timer_stop(browser->scroll_timer);
}
FileBrowser* file_browser_alloc(FuriString* result_path) { FileBrowser* file_browser_alloc(FuriString* result_path) {
furi_assert(result_path); furi_assert(result_path);
FileBrowser* browser = malloc(sizeof(FileBrowser)); FileBrowser* browser = malloc(sizeof(FileBrowser));
@@ -137,6 +163,11 @@ FileBrowser* file_browser_alloc(FuriString* result_path) {
view_set_context(browser->view, browser); view_set_context(browser->view, browser);
view_set_draw_callback(browser->view, file_browser_view_draw_callback); view_set_draw_callback(browser->view, file_browser_view_draw_callback);
view_set_input_callback(browser->view, file_browser_view_input_callback); view_set_input_callback(browser->view, file_browser_view_input_callback);
view_set_enter_callback(browser->view, file_browser_view_enter_callback);
view_set_exit_callback(browser->view, file_browser_view_exit_callback);
browser->scroll_timer =
furi_timer_alloc(file_browser_scroll_timer_callback, FuriTimerTypePeriodic, browser);
browser->result_path = result_path; browser->result_path = result_path;
@@ -149,6 +180,8 @@ FileBrowser* file_browser_alloc(FuriString* result_path) {
void file_browser_free(FileBrowser* browser) { void file_browser_free(FileBrowser* browser) {
furi_assert(browser); furi_assert(browser);
furi_timer_free(browser->scroll_timer);
with_view_model( with_view_model(
browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false); browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false);
@@ -468,13 +501,17 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) {
furi_string_set(filename, ". ."); furi_string_set(filename, ". .");
} }
elements_string_fit_width( size_t scroll_counter = model->scroll_counter;
canvas, filename, (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX));
if(model->item_idx == idx) { if(model->item_idx == idx) {
browser_draw_frame(canvas, i, show_scrollbar); browser_draw_frame(canvas, i, show_scrollbar);
if(scroll_counter < SCROLL_DELAY) {
scroll_counter = 0;
} else {
scroll_counter -= SCROLL_DELAY;
}
} else { } else {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
scroll_counter = 0;
} }
if(custom_icon_data) { if(custom_icon_data) {
@@ -487,8 +524,14 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) {
canvas_draw_icon( canvas_draw_icon(
canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]); canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]);
} }
canvas_draw_str( elements_scrollable_text_line(
canvas, 15, Y_OFFSET + 9 + i * FRAME_HEIGHT, furi_string_get_cstr(filename)); canvas,
15,
Y_OFFSET + 9 + i * FRAME_HEIGHT,
(show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX),
filename,
scroll_counter,
(model->item_idx != idx));
} }
if(show_scrollbar) { if(show_scrollbar) {
@@ -543,6 +586,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) {
file_browser_worker_load( file_browser_worker_load(
browser->worker, load_offset, ITEM_LIST_LEN_MAX); browser->worker, load_offset, ITEM_LIST_LEN_MAX);
} }
model->scroll_counter = 0;
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->item_idx = (model->item_idx + 1) % model->item_cnt; model->item_idx = (model->item_idx + 1) % model->item_cnt;
if(browser_is_list_load_required(model)) { if(browser_is_list_load_required(model)) {
@@ -554,6 +598,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) {
file_browser_worker_load( file_browser_worker_load(
browser->worker, load_offset, ITEM_LIST_LEN_MAX); browser->worker, load_offset, ITEM_LIST_LEN_MAX);
} }
model->scroll_counter = 0;
} }
}, },
true); true);

View File

@@ -0,0 +1,9 @@
App(
appid="locale",
name="LocaleSrv",
apptype=FlipperAppType.STARTUP,
entry_point="locale_on_system_start",
cdefines=["SRV_LOCALE"],
order=90,
sdk_headers=["locale.h"],
)

View File

@@ -0,0 +1,112 @@
#include "locale.h"
#define TAG "LocaleSrv"
LocaleMeasurementUnits locale_get_measurement_unit(void) {
return (LocaleMeasurementUnits)furi_hal_rtc_get_locale_units();
}
void locale_set_measurement_unit(LocaleMeasurementUnits format) {
furi_hal_rtc_set_locale_units((FuriHalRtcLocaleUnits)format);
}
LocaleTimeFormat locale_get_time_format(void) {
return (LocaleTimeFormat)furi_hal_rtc_get_locale_timeformat();
}
void locale_set_time_format(LocaleTimeFormat format) {
furi_hal_rtc_set_locale_timeformat((FuriHalRtcLocaleTimeFormat)format);
}
LocaleDateFormat locale_get_date_format(void) {
return (LocaleDateFormat)furi_hal_rtc_get_locale_dateformat();
}
void locale_set_date_format(LocaleDateFormat format) {
furi_hal_rtc_set_locale_dateformat((FuriHalRtcLocaleDateFormat)format);
}
float locale_fahrenheit_to_celsius(float temp_f) {
return (temp_f - 32.f) / 1.8f;
}
float locale_celsius_to_fahrenheit(float temp_c) {
return (temp_c * 1.8f + 32.f);
}
void locale_format_time(
FuriString* out_str,
const FuriHalRtcDateTime* datetime,
const LocaleTimeFormat format,
const bool show_seconds) {
furi_assert(out_str);
furi_assert(datetime);
uint8_t hours = datetime->hour;
uint8_t am_pm = 0;
if(format == LocaleTimeFormat12h) {
if(hours > 12) {
hours -= 12;
am_pm = 2;
} else {
am_pm = 1;
}
if(hours == 0) {
hours = 12;
}
}
if(show_seconds) {
furi_string_printf(out_str, "%02u:%02u:%02u", hours, datetime->minute, datetime->second);
} else {
furi_string_printf(out_str, "%02u:%02u", hours, datetime->minute);
}
if(am_pm > 0) {
furi_string_cat_printf(out_str, " %s", (am_pm == 1) ? ("AM") : ("PM"));
}
}
void locale_format_date(
FuriString* out_str,
const FuriHalRtcDateTime* datetime,
const LocaleDateFormat format,
const char* separator) {
furi_assert(out_str);
furi_assert(datetime);
furi_assert(separator);
if(format == LocaleDateFormatDMY) {
furi_string_printf(
out_str,
"%02u%s%02u%s%04u",
datetime->day,
separator,
datetime->month,
separator,
datetime->year);
} else if(format == LocaleDateFormatMDY) {
furi_string_printf(
out_str,
"%02u%s%02u%s%04u",
datetime->month,
separator,
datetime->day,
separator,
datetime->year);
} else {
furi_string_printf(
out_str,
"%04u%s%02u%s%02u",
datetime->year,
separator,
datetime->month,
separator,
datetime->day);
}
}
int32_t locale_on_system_start(void* p) {
UNUSED(p);
return 0;
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include <stdbool.h>
#include <furi.h>
#include <furi_hal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LocaleMeasurementUnitsMetric = 0, /**< Metric measurement units */
LocaleMeasurementUnitsImperial = 1, /**< Imperial measurement units */
} LocaleMeasurementUnits;
typedef enum {
LocaleTimeFormat24h = 0, /**< 24-hour format */
LocaleTimeFormat12h = 1, /**< 12-hour format */
} LocaleTimeFormat;
typedef enum {
LocaleDateFormatDMY = 0, /**< Day/Month/Year */
LocaleDateFormatMDY = 1, /**< Month/Day/Year */
LocaleDateFormatYMD = 2, /**< Year/Month/Day */
} LocaleDateFormat;
/** Get Locale measurement units
*
* @return The locale measurement units.
*/
LocaleMeasurementUnits locale_get_measurement_unit();
/** Set locale measurement units
*
* @param[in] format The locale measurements units
*/
void locale_set_measurement_unit(LocaleMeasurementUnits format);
/** Convert Fahrenheit to Celsius
*
* @param[in] temp_f The Temperature in Fahrenheit
*
* @return The Temperature in Celsius
*/
float locale_fahrenheit_to_celsius(float temp_f);
/** Convert Celsius to Fahrenheit
*
* @param[in] temp_c The Temperature in Celsius
*
* @return The Temperature in Fahrenheit
*/
float locale_celsius_to_fahrenheit(float temp_c);
/** Get Locale time format
*
* @return The locale time format.
*/
LocaleTimeFormat locale_get_time_format();
/** Set Locale Time Format
*
* @param[in] format The Locale Time Format
*/
void locale_set_time_format(LocaleTimeFormat format);
/** Format time to furi string
*
* @param[out] out_str The FuriString to store formatted time
* @param[in] datetime Pointer to the datetime
* @param[in] format The Locale Time Format
* @param[in] show_seconds The show seconds flag
*/
void locale_format_time(
FuriString* out_str,
const FuriHalRtcDateTime* datetime,
const LocaleTimeFormat format,
const bool show_seconds);
/** Get Locale DateFormat
*
* @return The Locale DateFormat.
*/
LocaleDateFormat locale_get_date_format();
/** Set Locale DateFormat
*
* @param[in] format The Locale DateFormat
*/
void locale_set_date_format(LocaleDateFormat format);
/** Format date to furi string
*
* @param[out] out_str The FuriString to store formatted date
* @param[in] datetime Pointer to the datetime
* @param[in] format The format
* @param[in] separator The separator
*/
void locale_format_date(
FuriString* out_str,
const FuriHalRtcDateTime* datetime,
const LocaleDateFormat format,
const char* separator);
#ifdef __cplusplus
}
#endif

View File

@@ -150,11 +150,16 @@ void notification_vibro_off() {
} }
void notification_sound_on(float freq, float volume) { void notification_sound_on(float freq, float volume) {
furi_hal_speaker_start(freq, volume); if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
furi_hal_speaker_start(freq, volume);
}
} }
void notification_sound_off() { void notification_sound_off() {
furi_hal_speaker_stop(); if(furi_hal_speaker_is_mine()) {
furi_hal_speaker_stop();
furi_hal_speaker_release();
}
} }
// display timer // display timer

View File

@@ -3,7 +3,7 @@ App(
name="System", name="System",
apptype=FlipperAppType.SETTINGS, apptype=FlipperAppType.SETTINGS,
entry_point="system_settings_app", entry_point="system_settings_app",
requires=["gui"], requires=["gui", "locale"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=70, order=70,
) )

View File

@@ -1,6 +1,7 @@
#include "system_settings.h" #include "system_settings.h"
#include <loader/loader.h> #include <loader/loader.h>
#include <lib/toolbox/value_index.h> #include <lib/toolbox/value_index.h>
#include <locale/locale.h>
const char* const log_level_text[] = { const char* const log_level_text[] = {
"Default", "Default",
@@ -70,6 +71,59 @@ static void heap_trace_mode_changed(VariableItem* item) {
furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]);
} }
const char* const mesurement_units_text[] = {
"Metric",
"Imperial",
};
const uint32_t mesurement_units_value[] = {
LocaleMeasurementUnitsMetric,
LocaleMeasurementUnitsImperial,
};
static void mesurement_units_changed(VariableItem* item) {
// SystemSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, mesurement_units_text[index]);
locale_set_measurement_unit(mesurement_units_value[index]);
}
const char* const time_format_text[] = {
"24h",
"12h",
};
const uint32_t time_format_value[] = {
LocaleTimeFormat24h,
LocaleTimeFormat12h,
};
static void time_format_changed(VariableItem* item) {
// SystemSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, time_format_text[index]);
locale_set_time_format(time_format_value[index]);
}
const char* const date_format_text[] = {
"D/M/Y",
"M/D/Y",
"Y/M/D",
};
const uint32_t date_format_value[] = {
LocaleDateFormatDMY,
LocaleDateFormatMDY,
LocaleDateFormatYMD,
};
static void date_format_changed(VariableItem* item) {
// SystemSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, date_format_text[index]);
locale_set_date_format(date_format_value[index]);
}
static uint32_t system_settings_exit(void* context) { static uint32_t system_settings_exit(void* context) {
UNUSED(context); UNUSED(context);
return VIEW_NONE; return VIEW_NONE;
@@ -91,6 +145,31 @@ SystemSettings* system_settings_alloc() {
uint8_t value_index; uint8_t value_index;
app->var_item_list = variable_item_list_alloc(); app->var_item_list = variable_item_list_alloc();
item = variable_item_list_add(
app->var_item_list,
"Units",
COUNT_OF(mesurement_units_text),
mesurement_units_changed,
app);
value_index = value_index_uint32(
locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value));
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, mesurement_units_text[value_index]);
item = variable_item_list_add(
app->var_item_list, "Time Format", COUNT_OF(time_format_text), time_format_changed, app);
value_index = value_index_uint32(
locale_get_time_format(), time_format_value, COUNT_OF(time_format_value));
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, time_format_text[value_index]);
item = variable_item_list_add(
app->var_item_list, "Date Format", COUNT_OF(date_format_text), date_format_changed, app);
value_index = value_index_uint32(
locale_get_date_format(), date_format_value, COUNT_OF(date_format_value));
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, date_format_text[value_index]);
item = variable_item_list_add( item = variable_item_list_add(
app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app);
value_index = value_index_uint32( value_index = value_index_uint32(

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,23 @@
Filetype: Flipper Animation
Version: 1
Width: 128
Height: 64
Passive frames: 10
Active frames: 18
Frames order: 0 1 2 1 0 1 2 1 0 1 2 3 4 5 6 5 4 7 2 8 9 10 11 10 9 10 11 12
Active cycles: 1
Frame rate: 2
Duration: 3600
Active cooldown: 7
Bubble slots: 1
Slot: 0
X: 11
Y: 19
Text: HAPPY\nHOLIDAYS!
AlignH: Right
AlignV: Center
StartFrame: 22
EndFrame: 27

View File

@@ -36,10 +36,10 @@ Min level: 1
Max level: 1 Max level: 1
Weight: 3 Weight: 3
Name: L2_Wake_up_128x64 Name: L1_Happy_holidays_128x64
Min butthurt: 0 Min butthurt: 0
Max butthurt: 12 Max butthurt: 14
Min level: 2 Min level: 1
Max level: 3 Max level: 3
Weight: 4 Weight: 4
@@ -92,6 +92,13 @@ Min level: 1
Max level: 3 Max level: 3
Weight: 3 Weight: 3
Name: L2_Wake_up_128x64
Min butthurt: 0
Max butthurt: 12
Min level: 2
Max level: 3
Weight: 4
Name: L2_Furippa2_128x64 Name: L2_Furippa2_128x64
Min butthurt: 0 Min butthurt: 0
Max butthurt: 6 Max butthurt: 6

View File

@@ -185,4 +185,79 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.330000 duty_cycle: 0.330000
data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562
#
# Model: Sharp AH-X9VEW. Doesn't have heat function.
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 307 125509 3831 1867 489 482 491 1392 461 468 485 1397 456 489 464 1411 463 466 487 1397 456 488 465 1390 463 482 481 1402 462 1402 483 471 461 1412 462 468 485 1400 464 1401 484 1397 488 1395 458 471 461 485 457 1408 487 1393 460 485 457 473 459 486 456 474 489 1394 459 471 461 485 457 473 459 488 465 466 455 490 463 467 465 480 462 468 464 482 460 470 483 1399 465 464 457 488 465 465 488 1393 460 484 458 471 461 485 457 1415 459 1405 511 408 482 489 464 466 487 1377 456 489 464 481 461 469 463 482 460 470 462 483 459 470 462 484 458 472 460 485 457 473 459 487 455 474 489 1395 458 471 461 485 457 472 460 486 456 473 459 486 456 474 458 487 455 474 458 488 465 466 487 1396 457 487 434 496 457 488 433 496 457 489 432 497 466 479 432 498 465 480 431 499 464 482 439 490 463 482 460 1394 491 1389 485 1395 490 1392 461 468 464 482 460 469 484 1399 465 1399 465 480 462 483 491 77962 300
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 345 2685 478 1170 176 29680 3832 1891 465 481 482 1400 464 465 488 1395 458 470 483 1396 457 487 466 1406 458 471 492 1389 464 481 461 1411 463 1408 456 489 464 1407 457 473 490 1392 482 1383 481 1399 486 1396 458 462 459 497 456 1409 486 1394 459 459 483 473 459 487 455 474 489 1392 461 468 464 481 461 469 463 483 459 471 482 1398 487 1379 464 481 461 485 457 1416 458 487 466 1407 457 472 460 460 482 474 489 1393 460 469 463 483 459 471 461 485 457 1408 456 488 465 481 461 469 484 1381 462 483 459 486 456 473 459 487 455 474 458 487 455 475 457 489 464 467 465 481 461 1410 485 1380 484 1397 488 1392 461 483 459 471 461 485 457 473 459 486 456 474 458 488 454 476 456 490 463 467 465 481 461 1411 464 481 440 491 462 484 437 492 461 485 436 494 459 487 434 495 458 488 433 497 456 489 432 498 486 450 482 1408 467 1389 485 1394 480 1384 459 486 456 489 464 466 487 1393 460 485 457 1415 459 1406 510
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 181 11813 3819 1899 457 488 486 1421 433 472 491 1416 438 481 461 1417 437 482 492 1416 437 467 486 1419 435 485 457 1439 435 1403 461 483 480 1408 435 484 490 1416 438 1401 484 1395 490 1392 461 467 465 480 462 1427 458 1396 457 486 456 473 459 486 456 472 481 1426 438 466 466 480 462 467 465 480 462 467 465 479 463 1426 438 481 461 484 458 1438 436 1409 486 1378 465 480 462 483 459 471 482 1425 428 475 457 487 455 474 458 487 455 1434 430 489 464 481 461 468 485 1404 439 480 462 483 459 470 462 483 459 469 463 482 460 470 462 483 459 470 462 483 459 1437 458 1381 483 1395 490 1390 463 481 461 469 463 482 460 468 464 481 461 468 464 481 461 469 463 482 460 469 463 481 461 1434 430 474 458 486 456 473 459 486 456 473 459 486 456 473 459 486 456 473 459 485 457 472 460 485 489 449 462 1434 461 1378 486 1393 481 1397 519 400 479 476 456 489 464 1431 433 486 435 495 458 487 487
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3826 1866 490 481 482 1382 461 484 490 1392 461 467 486 1396 457 471 482 1400 464 479 463 1390 463 481 482 1399 465 1398 518 401 510 1379 464 480 483 1398 456 1408 487 1392 482 1399 517 386 483 487 466 1399 486 1392 461 483 459 470 462 482 460 469 484 1397 456 473 459 459 483 472 460 484 458 471 461 483 459 1411 463 480 462 467 486 1377 487 1392 482 1396 457 486 456 472 460 486 456 473 480 1400 464 465 456 488 454 475 488 1393 460 468 464 480 462 467 486 1395 458 470 462 483 459 485 436 493 460 469 463 481 461 486 435 500 463 463 458 486 456 1397 488 1390 484 1394 480 1400 464 465 456 488 465 464 457 487 455 474 458 487 455 473 459 485 457 472 460 485 457 472 481 1399 517 401 458 497 456 473 459 485 457 487 434 495 458 471 461 484 458 488 433 502 430 507 435 500 432 498 455 1409 486 1392 482 1399 454 1409 455 488 465 480 462 467 465 480 462 1408 456 473 459 486 488
#
# Model: Electrolux ESV09CRO-B21. Doesn't have heat function.
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3092 3057 3092 4438 579 1675 545 534 576 1650 571 535 575 531 569 1656 575 1652 579 527 573 533 577 1648 572 1655 576 1651 580 526 573 532 578 1647 573 532 578 1647 573 1654 577 529 571 535 575 530 570 535 575 529 571 534 576 529 571 534 576 528 571 534 576 528 572 533 577 528 572 533 577 527 573 1651 580 526 573 532 578 527 572 532 568 537 573 531 568 536 574 1650 571 535 575 531 569 536 574 530 569 535 575 529 571 534 576 529 571 533 577 528 572 533 577 527 572 532 568 536 574 531 569 1655 576 530 570 535 575 530 570 535 575 529 571 534 576 528 572 533 577 527 573 532 578 527 572 531 569 536 574 531 569 535 575 530 570 534 576 529 571 534 576 528 571 533 577 527 573 532 567 537 573 531 569 536 574 530 570 535 575 529 571 534 576 528 571 533 577 527 572 532 578 526 573 531 569 536 574 530 570 535 575 529 571 534 576 528 572 533 577 1646 574 532 578 1646 574 1652 579 527 572 533 577 1647 573 1653 578 1675 545 534 576 1649 571
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3093 3058 3090 4441 576 1652 579 528 571 1654 577 531 579 526 573 1652 579 1649 582 525 574 1652 579 528 571 1654 577 1651 580 527 572 533 577 528 571 533 577 1649 582 1646 574 1653 578 529 581 525 574 530 580 525 574 530 580 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 532 578 1647 573 533 577 1648 572 535 575 530 569 535 575 530 580 525 574 531 579 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 1651 580 527 572 533 577 528 571 533 577 528 571 533 577 528 571 533 577 528 571 534 576 528 571 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 1649 582 525 574 1650 581 1647 573 1654 577 1651 580 1647 573 1654 577 531 579 1646 574 1653 578
#
# Model: Daikin FTE35KV1. Doesn't have heat function.
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 5045 2158 335 1768 358 690 357 723 335 716 331 1771 355 694 364 686 361 720 327 723 335 1767 359 690 357 1775 362 1770 356 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 690 357 1776 361 687 360 690 357 693 365 716 331 719 328 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 1773 364 1767 360 689 358 692 366 685 362 688 359 721 326 724 334 717 330 720 327 723 335 715 332 718 329 721 326 1777 360 1771 355 1776 361 1770 356 692 366 715 332 718 329 721 326 29460 5042 2161 332 1770 356 692 366 685 362 688 359 1774 363 685 362 688 359 721 326 694 364 1769 357 691 367 1767 360 1771 355 693 365 1769 358 1773 364 1768 359 1772 365 1766 361 688 359 691 367 1767 360 689 358 692 366 684 363 717 330 720 327 693 365 686 361 689 358 722 336 684 363 1770 357 1774 363 686 361 689 358 1774 363 686 361 1771 356 1776 361 1770 357 692 366 685 362 688 359 690 357 1776 361 687 360 690 357 1776 361 688 359 691 356 694 364 716 331 689 358 1775 362 686 361 689 358 692 366 685 362 688 359 691 356 724 334 716 331 689 358 722 336 685 362 688 359 721 326 693 365 716 331 689 358 692 366 684 363 718 329 690 357 693 365 716 331 689 358 722 336 1767 360 689 358 1774 363 686 361 1771 356 693 365 686 361 689 358 722 336 684 363 717 330 720 327 1776 361 687 360 690 357 693 365 716 331 1771 355 693 365 686 361 1772 355 1776 361 688 359 1773 364 1768 359
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 5038 2165 328 1772 365 686 361 689 358 692 366 1765 362 719 328 692 366 684 363 717 330 1771 366 684 363 1768 359 1774 363 686 361 1771 355 1776 361 1770 357 1775 362 1769 358 691 356 694 364 1767 360 691 356 694 364 686 361 689 358 692 366 685 362 1769 358 1775 362 1769 358 1774 363 1768 359 690 357 1776 361 1770 357 692 366 684 363 687 360 690 357 693 365 686 361 689 358 692 366 684 363 687 360 690 357 693 365 1766 361 1773 364 1767 360 1772 355 694 364 686 361 689 358 692 366 25151 319 3980 5041 2131 362 1769 358 693 365 686 361 689 358 1772 365 686 361 689 358 692 366 684 363 1768 359 692 366 1765 361 1772 354 694 364 1769 358 1774 363 1768 359 1773 364 1767 359 719 328 692 366 1796 331 719 328 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1768 359 1775 362 686 361 689 358 1773 364 686 361 1770 357 1777 360 1771 355 693 365 686 361 689 358 1772 365 1769 358 690 357 694 364 1767 360 691 356 694 364 686 361 689 358 723 335 1766 361 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 1771 366 684 363 687 360 690 357 693 365 1767 360 690 357 1804 333 687 360 690 357 693 365 686 361 689 358 692 366 685 362 1768 359 692 366 685 362 688 359 690 357 1774 363 688 359 691 356 1774 363 1770 356 1775 362 1769 358 691 356
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 5043 2132 361 1770 356 723 335 715 332 718 329 1774 363 715 332 719 328 722 336 714 333 1770 356 722 336 1767 360 1772 354 724 334 1769 357 1774 363 1768 358 1773 364 1767 359 720 327 723 335 1768 359 720 327 723 335 716 331 719 328 722 336 714 333 1770 356 1774 363 1769 357 1773 364 1767 360 720 327 1775 362 1769 357 721 326 725 333 717 330 720 327 723 335 716 331 719 328 722 336 714 333 717 330 720 327 723 335 1768 359 1773 364 1767 360 1772 354 724 334 717 330 720 327 723 335 29451 5041 2134 359 1772 354 724 334 717 330 720 327 1775 362 717 330 720 327 723 335 715 332 1771 355 723 335 1768 358 1773 364 715 332 1770 357 1775 362 1769 357 1774 363 1768 359 720 327 723 335 1768 359 720 327 724 334 716 331 719 328 722 336 715 332 718 329 720 327 723 335 716 331 1771 355 1776 361 718 329 721 326 1776 361 718 329 1773 364 1767 360 720 327 723 335 715 332 718 329 1774 363 1768 359 720 327 723 335 1768 358 721 326 724 334 716 331 719 328 722 336 1767 360 719 328 722 336 715 332 718 329 721 326 724 334 717 330 720 327 723 335 715 332 719 328 722 325 725 333 717 330 720 327 723 335 716 331 719 328 1774 363 716 331 1771 355 1776 361 718 329 721 326 724 334 717 330 1772 365 714 333 1770 356 722 336 715 332 718 329 721 326 724 334 717 330 719 328 1775 362 717 330 720 327 723 335 715 332 718 329 1774 363 715 332 718 329 721 326 725 333 717 330 1772 365

View File

@@ -242,3 +242,46 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.330000 duty_cycle: 0.330000
data: 4639 4406 586 418 585 393 559 447 557 447 557 1477 532 1477 532 472 532 472 532 1476 533 1476 532 1476 532 1476 532 473 555 449 555 449 555 449 555 4455 554 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 553 1455 553 450 554 450 554 1455 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 55454 4557 4458 555 449 555 449 555 450 554 450 554 1455 554 1455 553 450 554 450 554 1455 554 1455 554 1454 554 1455 554 450 554 450 554 450 555 450 554 4455 553 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 450 554 450 554 1455 554 1455 553 1455 554 450 554 450 554 450 554 1455 554 data: 4639 4406 586 418 585 393 559 447 557 447 557 1477 532 1477 532 472 532 472 532 1476 533 1476 532 1476 532 1476 532 473 555 449 555 449 555 449 555 4455 554 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 553 1455 553 450 554 450 554 1455 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 55454 4557 4458 555 449 555 449 555 450 554 450 554 1455 554 1455 553 450 554 450 554 1455 554 1455 554 1454 554 1455 554 450 554 450 554 450 555 450 554 4455 553 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 450 554 450 554 1455 554 1455 553 1455 554 450 554 450 554 450 554 1455 554
#
# Model: Edifier R1850DB
name: Power
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 46 B9 00 00
#
name: Play
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 5E A1 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 05 FA 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 49 B6 00 00
#
name: Next
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 02 FD 00 00
#
name: Prev
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 1E E1 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 10 E7 00 00
command: 41 BE 00 00

View File

@@ -1656,3 +1656,22 @@ type: parsed
protocol: RC5 protocol: RC5
address: 01 00 00 00 address: 01 00 00 00
command: 21 00 00 00 command: 21 00 00 00
#
# Model: VIZIO
name: Mute
type: parsed
protocol: NEC
address: 04 00 00 00
command: 09 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 04 00 00 00
command: 02 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 04 00 00 00
command: 03 00 00 00

View File

@@ -244,6 +244,7 @@ FEE470A4CB58
1F1A0A111B5B 1F1A0A111B5B
1F1FFE000000 1F1FFE000000
2031D1E57A3B 2031D1E57A3B
# HID Key B
204752454154 204752454154
21A600056CB0 21A600056CB0
22729A9BD40F 22729A9BD40F
@@ -292,6 +293,7 @@ FEE470A4CB58
45635EF66EF3 45635EF66EF3
476242304C53 476242304C53
484558414354 484558414354
# HID Key A
484944204953 484944204953
484A57696F4A 484A57696F4A
48734389EDC3 48734389EDC3
@@ -1226,6 +1228,7 @@ C41514DEFC07
ABFEDC124578 ABFEDC124578
046154274C11 046154274C11
5429D67E1F57 5429D67E1F57
# SMARTair Key B
E7316853E731 E7316853E731
CD7FFFF81C4A CD7FFFF81C4A
F253C30568C4 F253C30568C4
@@ -1308,4 +1311,4 @@ CE99FBC8BD26
# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K)
009FB42D98ED 009FB42D98ED
002E626E2820 002E626E2820

View File

@@ -0,0 +1,47 @@
## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic
# AA1
AEA684A6DAB23278
# key1/Kc from PicoPass 2k documentation
7665544332211000
# SAGEM
0123456789ABCDEF
# from loclass demo file.
5b7c62c491c11b39
# Kd from PicoPass 2k documentation
F0E1D2C3B4A59687
# PicoPass Default Exchange Key
5CBCF1DA45D5FB4F
# From HID multiclassSE reader
31ad7ebd2f282168
# From pastebin: https://pastebin.com/uHqpjiuU
6EFD46EFCBB3C875
E033CA419AEE43F9
# iCopy-x DRM keys
# iCL tags
2020666666668888
# iCS tags reversed from the SOs
6666202066668888
# default picopass KD / Page 0 / Book 1
FDCB5A52EA8F3090
237FF9079863DF44
5ADC25FB27181D32
83B881F2936B2E49
43644E61EE866BA5
897034143D016080
82D17B44C0122963
4895CA7DE65E2025
DADAD4C57BE271B7
E41E9EDEF5719ABF
293D275EC3AF9C7F
C3C169251B8A70FB
F41DAF58B20C8B91
28877A609EC0DD2B
66584C91EE80D5E5
C1B74D7478053AE2
# default iCLASS RFIDeas
6B65797374726B72

View File

@@ -0,0 +1,8 @@
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: SMC5326
Bit: 25
Key: 00 00 00 00 01 7D 55 80
TE: 210

View File

@@ -0,0 +1,7 @@
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940
RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617

View File

@@ -165,4 +165,6 @@ RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530
RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477
RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100
RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86
RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144
RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940
RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617

View File

@@ -0,0 +1,92 @@
# Command syntax
BadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts, but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command and more fuctional keys.
# Script file format
BadUsb app can execute only text scrips from .txt files, no compilation is required. Both `\n` and `\r\n` line endings are supported. Empty lines are allowed. You can use spaces ore tabs for line indentation.
# Command set
## Comment line
Just a single comment line. All text after REM command will be ignored by interpreter
|Command|Parameters|Notes|
|-|-|-|
|REM|Comment text||
## Delay
Pause script execution by defined time
|Command|Parameters|Notes|
|-|-|-|
|DELAY|Delay value in ms|Single delay|
|DEFAULT_DELAY|Delay value in ms|Add delay before every next command|
|DEFAULTDELAY|Delay value in ms|Same as DEFAULT_DELAY|
## Special keys
|Command|Notes|
|-|-|
|DOWNARROW / DOWN||
|LEFTARROW / LEFT||
|RIGHTARROW / RIGHT||
|UPARROW / UP||
|ENTER||
|DELETE||
|BACKSPACE||
|END||
|HOME||
|ESCAPE / ESC||
|INSERT||
|PAGEUP||
|PAGEDOWN||
|CAPSLOCK||
|NUMLOCK||
|SCROLLLOCK||
|PRINTSCREEN||
|BREAK|Pause/Break key|
|PAUSE|Pause/Break key|
|SPACE||
|TAB||
|MENU|Context menu key|
|APP|Same as MENU|
|Fx|F1-F12 keys|
## Modifier keys
Can be combined with special key command or single character
|Command|Notes|
|-|-|
|CONTROL / CTRL||
|SHIFT||
|ALT||
|WINDOWS / GUI||
|CTRL-ALT|CTRL+ALT|
|CTRL-SHIFT|CTRL+SHIFT|
|ALT-SHIFT|ALT+SHIFT|
|ALT-GUI|ALT+WIN|
|GUI-SHIFT|WIN+SHIFT|
## String
|Command|Parameters|Notes|
|-|-|-|
|STRING|Text string|Print text string|
## Repeat
|Command|Parameters|Notes|
|-|-|-|
|REPEAT|Number of additional repeats|Repeat previous command|
## ALT+Numpad input
On Windows and some Linux systems you can print character by pressing ALT key and entering its code on numpad
|Command|Parameters|Notes|
|-|-|-|
|ALTCHAR|Character code|Print single character|
|ALTSTRING|Text string|Print text string using ALT+Numpad method|
|ALTCODE|Text string|Same as ALTSTRING, presents in some Duckyscript implementations|
## SysRq
Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key)
|Command|Parameters|Notes|
|-|-|-|
|SYSRQ|Single character||
## USB device ID
You can set custom ID of Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run.
|Command|Parameters|Notes|
|-|-|-|
|ID|VID:PID Manufacturer:Product||
Example:
`ID 1234:abcd Flipper Devices:Flipper Zero`
VID and PID are hex codes and are mandatory, Manufacturer and Product are text strings and are optional.

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