From 7fb1af07b8014b843cb6abda9ce2be93d14bda99 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sat, 10 Dec 2022 15:02:34 +0200 Subject: [PATCH 1/3] [FL- 3014] Untangle NFC from Unit Tests (#2106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Untangle NFC from Unit Tests * nfc tests: add log error Co-authored-by: Sergey Gavrilov Co-authored-by: gornekich Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 7 +++++-- applications/main/nfc/nfc_i.h | 4 +--- .../main/nfc/scenes/nfc_scene_generate_info.c | 9 +++++++-- .../main/nfc/scenes/nfc_scene_set_type.c | 2 +- fbt_options.py | 1 - .../main => lib}/nfc/helpers/nfc_generators.c | 17 ----------------- .../main => lib}/nfc/helpers/nfc_generators.h | 7 +++---- 7 files changed, 17 insertions(+), 30 deletions(-) rename {applications/main => lib}/nfc/helpers/nfc_generators.c (95%) rename {applications/main => lib}/nfc/helpers/nfc_generators.h (79%) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 4218482c7..e9e7b35f6 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -102,7 +102,10 @@ static bool nfc_test_digital_signal_test_encode( do { // Read test data - if(!nfc_test_read_signal_from_file(file_name)) break; + if(!nfc_test_read_signal_from_file(file_name)) { + FURI_LOG_E(TAG, "Failed to read signal from file"); + break; + } // Encode signal FURI_CRITICAL_ENTER(); diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index 57eefbf67..d49b84766 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "views/dict_attack.h" #include "views/detect_reader.h" @@ -50,9 +51,6 @@ typedef enum { NfcRpcStateEmulated, } NfcRpcState; -// Forward declaration due to circular dependency -typedef struct NfcGenerator NfcGenerator; - struct Nfc { NfcWorker* worker; ViewDispatcher* view_dispatcher; diff --git a/applications/main/nfc/scenes/nfc_scene_generate_info.c b/applications/main/nfc/scenes/nfc_scene_generate_info.c index 66900767e..7b84ae43b 100644 --- a/applications/main/nfc/scenes/nfc_scene_generate_info.c +++ b/applications/main/nfc/scenes/nfc_scene_generate_info.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../helpers/nfc_generators.h" +#include "lib/nfc/helpers/nfc_generators.h" void nfc_scene_generate_info_dialog_callback(DialogExResult result, void* context) { Nfc* nfc = context; @@ -39,7 +39,12 @@ bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, nfc->generator->next_scene); + // Switch either to NfcSceneMfClassicMenu or NfcSceneMfUltralightMenu + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); + } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); + } consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index 3e08aeb3f..cadf2eb69 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../helpers/nfc_generators.h" +#include "lib/nfc/helpers/nfc_generators.h" enum SubmenuIndex { SubmenuIndexNFCA4, diff --git a/fbt_options.py b/fbt_options.py index 7a805c996..4850389ad 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -81,7 +81,6 @@ FIRMWARE_APPS = { "basic_services", "updater_app", "unit_tests", - "nfc", ], } diff --git a/applications/main/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c similarity index 95% rename from applications/main/nfc/helpers/nfc_generators.c rename to lib/nfc/helpers/nfc_generators.c index 5f0527c6a..769b9c7b6 100644 --- a/applications/main/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -376,103 +376,86 @@ static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) { static const NfcGenerator mf_ul_generator = { .name = "Mifare Ultralight", .generator_func = nfc_generate_mf_ul_orig, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_11_generator = { .name = "Mifare Ultralight EV1 11", .generator_func = nfc_generate_mf_ul_11, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_h11_generator = { .name = "Mifare Ultralight EV1 H11", .generator_func = nfc_generate_mf_ul_h11, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_21_generator = { .name = "Mifare Ultralight EV1 21", .generator_func = nfc_generate_mf_ul_21, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_h21_generator = { .name = "Mifare Ultralight EV1 H21", .generator_func = nfc_generate_mf_ul_h21, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag203_generator = { .name = "NTAG203", .generator_func = nfc_generate_mf_ul_ntag203, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag213_generator = { .name = "NTAG213", .generator_func = nfc_generate_ntag213, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag215_generator = { .name = "NTAG215", .generator_func = nfc_generate_ntag215, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag216_generator = { .name = "NTAG216", .generator_func = nfc_generate_ntag216, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_1k_generator = { .name = "NTAG I2C 1k", .generator_func = nfc_generate_ntag_i2c_1k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_2k_generator = { .name = "NTAG I2C 2k", .generator_func = nfc_generate_ntag_i2c_2k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_plus_1k_generator = { .name = "NTAG I2C Plus 1k", .generator_func = nfc_generate_ntag_i2c_plus_1k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_plus_2k_generator = { .name = "NTAG I2C Plus 2k", .generator_func = nfc_generate_ntag_i2c_plus_2k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mifare_classic_1k_4b_uid_generator = { .name = "Mifare Classic 1k 4byte UID", .generator_func = nfc_generate_mf_classic_1k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_1k_7b_uid_generator = { .name = "Mifare Classic 1k 7byte UID", .generator_func = nfc_generate_mf_classic_1k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_4k_4b_uid_generator = { .name = "Mifare Classic 4k 4byte UID", .generator_func = nfc_generate_mf_classic_4k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_4k_7b_uid_generator = { .name = "Mifare Classic 4k 7byte UID", .generator_func = nfc_generate_mf_classic_4k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, }; const NfcGenerator* const nfc_generators[] = { diff --git a/applications/main/nfc/helpers/nfc_generators.h b/lib/nfc/helpers/nfc_generators.h similarity index 79% rename from applications/main/nfc/helpers/nfc_generators.h rename to lib/nfc/helpers/nfc_generators.h index 362a19b1e..8cee67067 100644 --- a/applications/main/nfc/helpers/nfc_generators.h +++ b/lib/nfc/helpers/nfc_generators.h @@ -1,14 +1,13 @@ #pragma once -#include "../nfc_i.h" +#include "../nfc_device.h" typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); -struct NfcGenerator { +typedef struct { const char* name; NfcGeneratorFunc generator_func; - NfcScene next_scene; -}; +} NfcGenerator; extern const NfcGenerator* const nfc_generators[]; From 9d728a1c65935d9473389fa98c7f1946935e4976 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Sat, 10 Dec 2022 16:10:51 +0300 Subject: [PATCH 2/3] Check FL ticket in PR name after merge (#2076) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add base * Add base again * Test reporting * Fix reporting * Fix secrets * Fix arguments in report * Remove depricated actions * Disable reporting on PR Co-authored-by: あく --- .github/workflows/amap_analyse.yml | 2 +- .github/workflows/build.yml | 13 ++----- .github/workflows/check_submodules.yml | 6 +-- .github/workflows/lint_c.yml | 2 +- .github/workflows/lint_python.yml | 2 +- .github/workflows/merge_report.yml | 41 ++++++++++++++++++++ .github/workflows/pvs_studio.yml | 12 +----- .github/workflows/unit_tests.yml | 2 +- scripts/get_env.py | 38 +++++++++++------- scripts/merge_report_qa.py | 53 ++++++++++++++++++++++++++ 10 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/merge_report.yml create mode 100755 scripts/merge_report_qa.py diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml index cfb1eab14..6231c5886 100644 --- a/.github/workflows/amap_analyse.yml +++ b/.github/workflows/amap_analyse.yml @@ -39,7 +39,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed5ee6bd7..99c272e60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -35,6 +35,7 @@ jobs: mkdir artifacts - name: 'Get commit details' + id: names run: | if [[ ${{ github.event_name }} == 'pull_request' ]]; then TYPE="pull" @@ -45,14 +46,6 @@ jobs: fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - name: 'Generate suffixes for comment' - id: names - run: | - echo "::set-output name=branch_name::${BRANCH_NAME}" - echo "::set-output name=commit_sha::${COMMIT_SHA}" - echo "::set-output name=default_target::${DEFAULT_TARGET}" - echo "::set-output name=suffix::${SUFFIX}" - - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} run: | @@ -143,7 +136,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: true diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index eba4affc3..2eb2027c9 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -20,7 +20,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -36,12 +36,12 @@ jobs: BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); COMMITS_IN_BRANCH="$(git rev-list --count dev)"; if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then - echo "::set-output name=fails::error"; + echo "name=fails::error" >> $GITHUB_OUTPUT echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; exit 1; fi if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then - echo "::set-output name=fails::error"; + echo "name=fails::error" >> $GITHUB_OUTPUT echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; exit 1; fi diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index 23dc6c699..71ec24ff6 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -23,7 +23,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index c2f092110..44f233db8 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -20,7 +20,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml new file mode 100644 index 000000000..b6c089056 --- /dev/null +++ b/.github/workflows/merge_report.yml @@ -0,0 +1,41 @@ +name: 'Check FL ticket in PR name' + +on: + push: + branches: + - dev +jobs: + merge_report: + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Check ticket and report' + run: | + FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh + python3 -m pip install slack_sdk + python3 scripts/merge_report_qa.py \ + ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ + ${{ secrets.QA_REPORT_SLACK_CHANNEL }} + diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 9de493a44..5473f19f0 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -25,12 +25,13 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' + id: names run: | if [[ ${{ github.event_name }} == 'pull_request' ]]; then TYPE="pull" @@ -41,15 +42,6 @@ jobs: fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - name: 'Generate suffixes for comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} - id: names - run: | - echo "::set-output name=branch_name::${BRANCH_NAME}" - echo "::set-output name=commit_sha::${COMMIT_SHA}" - echo "::set-output name=default_target::${DEFAULT_TARGET}" - echo "::set-output name=suffix::${SUFFIX}" - - name: 'Make reports directory' run: | rm -rf reports/ diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 1ca4a9c03..4eab21c87 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,7 +12,7 @@ jobs: runs-on: [self-hosted, FlipperZeroTest] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/scripts/get_env.py b/scripts/get_env.py index d05273094..e2da6eda5 100644 --- a/scripts/get_env.py +++ b/scripts/get_env.py @@ -77,28 +77,38 @@ def add_env(name, value, file): print(f"{delimeter}", file=file) -def add_envs(data, env_file, args): - add_env("COMMIT_MSG", data["commit_comment"], env_file) - add_env("COMMIT_HASH", data["commit_hash"], env_file) - add_env("COMMIT_SHA", data["commit_sha"], env_file) - add_env("SUFFIX", data["suffix"], env_file) - add_env("BRANCH_NAME", data["branch_name"], env_file) - add_env("DIST_SUFFIX", data["suffix"], env_file) - add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], env_file) +def add_set_output_var(name, value, file): + print(f"{name}={value}", file=file) + + +def add_envs(data, gh_env_file, gh_out_file, args): + add_env("COMMIT_MSG", data["commit_comment"], gh_env_file) + add_env("COMMIT_HASH", data["commit_hash"], gh_env_file) + add_env("COMMIT_SHA", data["commit_sha"], gh_env_file) + add_env("SUFFIX", data["suffix"], gh_env_file) + add_env("BRANCH_NAME", data["branch_name"], gh_env_file) + add_env("DIST_SUFFIX", data["suffix"], gh_env_file) + add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], gh_env_file) + add_set_output_var("branch_name", data["branch_name"], gh_out_file) + add_set_output_var("commit_sha", data["commit_sha"], gh_out_file) + add_set_output_var("default_target", os.getenv("DEFAULT_TARGET"), gh_out_file) + add_set_output_var("suffix", data["suffix"], gh_out_file) if args.type == "pull": - add_env("PULL_ID", data["pull_id"], env_file) - add_env("PULL_NAME", data["pull_name"], env_file) + add_env("PULL_ID", data["pull_id"], gh_env_file) + add_env("PULL_NAME", data["pull_name"], gh_env_file) def main(): args = parse_args() - event_file = open(args.event_file) + event_file = open(args.event_file, "r") event = json.load(event_file) - env_file = open(os.environ["GITHUB_ENV"], "a") + gh_env_file = open(os.environ["GITHUB_ENV"], "a") + gh_out_file = open(os.environ["GITHUB_OUTPUT"], "a") data = get_details(event, args) - add_envs(data, env_file, args) + add_envs(data, gh_env_file, gh_out_file, args) event_file.close() - env_file.close() + gh_env_file.close() + gh_out_file.close() if __name__ == "__main__": diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py new file mode 100755 index 000000000..c0707848e --- /dev/null +++ b/scripts/merge_report_qa.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import os +import re +import sys +import argparse +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("slack_token") + parser.add_argument("slack_channel") + args = parser.parse_args() + return args + + +def checkCommitMessage(msg): + regex = re.compile(r"^'?\[FL-\d+\]") + if regex.match(msg): + return True + return False + + +def reportSlack(commit_hash, slack_token, slack_channel, message): + client = WebClient(token=slack_token) + try: + client.chat_postMessage(channel="#" + slack_channel, text=message) + except SlackApiError as e: + print(e) + sys.exit(1) + + +def main(): + args = parse_args() + commit_msg = os.getenv("COMMIT_MSG") + commit_hash = os.getenv("COMMIT_HASH") + commit_sha = os.getenv("COMMIT_SHA") + commit_link = ( + "" + ) + message = "Commit " + commit_link + " merged to dev without 'FL' ticket!" + if not checkCommitMessage(commit_msg): + reportSlack(commit_hash, args.slack_token, args.slack_channel, message) + + +if __name__ == "__main__": + main() From 01e24f683793f6ea88ab344c1b8b5fcab90429cb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Dec 2022 18:30:03 +0300 Subject: [PATCH 3/3] WS: Show received signal age (#2087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: Show received signal age: by @LY2NEO with some fixes from me * WS: Signal age display GUI fixes and improvements * WeatherStation: refactor variable names * WS: GUI fixes and improvements: add icons by @Karator and apply UI changes * Weather Station: proper event flow for view redraw. Co-authored-by: あく --- .../weather_station/images/Humid_8x13.png | Bin 0 -> 3618 bytes .../weather_station/images/Timer_11x11.png | Bin 0 -> 3616 bytes .../weather_station/protocols/ws_generic.c | 17 ++++ .../weather_station/protocols/ws_generic.h | 1 + .../views/weather_station_receiver.c | 4 +- .../views/weather_station_receiver_info.c | 94 ++++++++++++++---- 6 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 applications/plugins/weather_station/images/Humid_8x13.png create mode 100644 applications/plugins/weather_station/images/Timer_11x11.png diff --git a/applications/plugins/weather_station/images/Humid_8x13.png b/applications/plugins/weather_station/images/Humid_8x13.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8c71b000699e148e9480f7ddf9795b33e2fe7f GIT binary patch literal 3618 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Timer_11x11.png b/applications/plugins/weather_station/images/Timer_11x11.png new file mode 100644 index 0000000000000000000000000000000000000000..21ad47f4b2bf69d81929187ca63d7a3d1b111ea4 GIT binary patch literal 3616 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 Y4M;=-T9{t95channel; if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Channel"); @@ -168,6 +179,12 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp } instance->humidity = (uint8_t)temp_data; + if(!flipper_format_read_uint32(flipper_format, "Ts", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing timestamp"); + break; + } + instance->timestamp = (uint32_t)temp_data; + if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Channel"); break; diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index b2a84df8e..657f8a1fc 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -29,6 +29,7 @@ struct WSBlockGeneric { uint8_t data_count_bit; uint8_t battery_low; uint8_t humidity; + uint32_t timestamp; uint8_t channel; uint8_t btn; float temp; diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index 61b152602..124065e32 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -8,7 +8,7 @@ #include #define FRAME_HEIGHT 12 -#define MAX_LEN_PX 100 +#define MAX_LEN_PX 112 #define MENU_ITEMS 4u #define UNLOCK_CNT 3 @@ -189,7 +189,7 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_set_color(canvas, ColorBlack); } canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + canvas_draw_str(canvas, 14, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } if(scrollbar) { diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index 49b447f10..c2fa00641 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -9,9 +9,11 @@ struct WSReceiverInfo { View* view; + FuriTimer* timer; }; typedef struct { + uint32_t curr_ts; FuriString* protocol_name; WSBlockGeneric* generic; } WSReceiverInfoModel; @@ -28,6 +30,10 @@ void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperForma flipper_format_read_string(fff, "Protocol", model->protocol_name); ws_block_generic_deserialize(model->generic, fff); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); }, true); } @@ -44,46 +50,76 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { "%s %db", furi_string_get_cstr(model->protocol_name), model->generic->data_count_bit); - canvas_draw_str(canvas, 5, 8, buffer); + canvas_draw_str(canvas, 0, 8, buffer); if(model->generic->channel != WS_NO_CHANNEL) { snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); - canvas_draw_str(canvas, 105, 8, buffer); + canvas_draw_str(canvas, 106, 8, buffer); } if(model->generic->id != WS_NO_ID) { snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); - canvas_draw_str(canvas, 5, 20, buffer); + canvas_draw_str(canvas, 0, 20, buffer); } if(model->generic->btn != WS_NO_BTN) { snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn); - canvas_draw_str(canvas, 62, 20, buffer); + canvas_draw_str(canvas, 57, 20, buffer); } if(model->generic->battery_low != WS_NO_BATT) { snprintf( buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); - canvas_draw_str(canvas, 90, 20, buffer); + canvas_draw_str_aligned(canvas, 126, 17, AlignRight, AlignCenter, buffer); } snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); - canvas_draw_str(canvas, 5, 32, buffer); + canvas_draw_str(canvas, 0, 32, buffer); - elements_bold_rounded_frame(canvas, 2, 37, 123, 25); + elements_bold_rounded_frame(canvas, 0, 38, 127, 25); canvas_set_font(canvas, FontPrimary); if(model->generic->temp != WS_NO_TEMPERATURE) { - canvas_draw_icon(canvas, 18, 42, &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, 63, 46, AlignRight, AlignTop, buffer); - canvas_draw_circle(canvas, 55, 45, 1); + canvas_draw_str_aligned(canvas, 47, 47, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, 38, 46, 1); } if(model->generic->humidity != WS_NO_HUMIDITY) { - canvas_draw_icon(canvas, 75, 42, &I_Humid_10x15); + canvas_draw_icon(canvas, 53, 44, &I_Humid_8x13); snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); - canvas_draw_str(canvas, 91, 54, buffer); + canvas_draw_str(canvas, 64, 55, buffer); + } + + if((int)model->generic->timestamp > 0 && model->curr_ts) { + int ts_diff = (int)model->curr_ts - (int)model->generic->timestamp; + + canvas_draw_icon(canvas, 91, 46, &I_Timer_11x11); + + if(ts_diff > 60) { + int tmp_sec = ts_diff; + int cnt_min = 1; + for(int i = 1; tmp_sec > 60; i++) { + tmp_sec = tmp_sec - 60; + cnt_min = i; + } + + if(model->curr_ts % 2 == 0) { + canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); + } else { + if(cnt_min >= 59) { + canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); + } else { + snprintf(buffer, sizeof(buffer), "%dm", cnt_min); + canvas_draw_str_aligned(canvas, 114, 51, AlignCenter, AlignCenter, buffer); + } + } + + } else { + snprintf(buffer, sizeof(buffer), "%d", ts_diff); + canvas_draw_str_aligned(canvas, 112, 51, AlignCenter, AlignCenter, buffer); + } } } @@ -98,14 +134,19 @@ bool ws_view_receiver_info_input(InputEvent* event, void* context) { return true; } -void ws_view_receiver_info_enter(void* context) { - furi_assert(context); -} - -void ws_view_receiver_info_exit(void* context) { +static void ws_view_receiver_info_enter(void* context) { furi_assert(context); WSReceiverInfo* ws_receiver_info = context; + furi_timer_start(ws_receiver_info->timer, 1000); +} + +static void ws_view_receiver_info_exit(void* context) { + furi_assert(context); + WSReceiverInfo* ws_receiver_info = context; + + furi_timer_stop(ws_receiver_info->timer); + with_view_model( ws_receiver_info->view, WSReceiverInfoModel * model, @@ -113,6 +154,20 @@ void ws_view_receiver_info_exit(void* context) { false); } +static void ws_view_receiver_info_timer(void* context) { + WSReceiverInfo* ws_receiver_info = context; + // Force redraw + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + }, + true); +} + WSReceiverInfo* ws_view_receiver_info_alloc() { WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo)); @@ -135,12 +190,17 @@ WSReceiverInfo* ws_view_receiver_info_alloc() { }, true); + ws_receiver_info->timer = + furi_timer_alloc(ws_view_receiver_info_timer, FuriTimerTypePeriodic, ws_receiver_info); + return ws_receiver_info; } void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { furi_assert(ws_receiver_info); + furi_timer_free(ws_receiver_info->timer); + with_view_model( ws_receiver_info->view, WSReceiverInfoModel * model,