From 5d45d6abb8d8b598d226636605309df6d38c4c87 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 8 Mar 2024 21:35:59 +0000 Subject: [PATCH] FindMy: Save and load state from file --- applications/system/findmy/findmy.c | 3 + applications/system/findmy/findmy_state.c | 113 ++++++++++++++---- applications/system/findmy/findmy_state.h | 17 ++- .../findmy/scenes/findmy_scene_config_mac.c | 1 + .../scenes/findmy_scene_config_packet.c | 1 + .../system/findmy/scenes/findmy_scene_main.c | 4 + 6 files changed, 110 insertions(+), 29 deletions(-) diff --git a/applications/system/findmy/findmy.c b/applications/system/findmy/findmy.c index 10019347fd..f9dc292cac 100644 --- a/applications/system/findmy/findmy.c +++ b/applications/system/findmy/findmy.c @@ -90,6 +90,7 @@ void findmy_change_broadcast_interval(FindMy* app, uint8_t value) { return; } app->state.broadcast_interval = value; + findmy_state_save(&app->state); findmy_main_update_interval(app->findmy_main, app->state.broadcast_interval); if(furi_hal_bt_extra_beacon_is_active()) { // Always check if beacon is active before changing config @@ -108,6 +109,7 @@ void findmy_change_transmit_power(FindMy* app, uint8_t value) { return; } app->state.transmit_power = value; + findmy_state_save(&app->state); if(furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_stop()); } @@ -120,6 +122,7 @@ void findmy_change_transmit_power(FindMy* app, uint8_t value) { void findmy_toggle_beacon(FindMy* app) { app->state.beacon_active = !app->state.beacon_active; + findmy_state_save(&app->state); if(furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_stop()); } diff --git a/applications/system/findmy/findmy_state.c b/applications/system/findmy/findmy_state.c index 34e9c80b22..a8d15d75eb 100644 --- a/applications/system/findmy/findmy_state.c +++ b/applications/system/findmy/findmy_state.c @@ -3,40 +3,76 @@ #include #include #include +#include bool findmy_state_load(FindMyState* out_state) { FindMyState state; - // Set default values - state.beacon_active = false; - state.broadcast_interval = 5; - state.transmit_power = 6; + // Try to load from file + bool loaded_from_file = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_file_exists(storage, FINDMY_STATE_PATH)) { + FlipperFormat* file = flipper_format_file_alloc(storage); + do { + uint32_t tmp; + FuriString* str = furi_string_alloc(); + if(!flipper_format_file_open_existing(file, FINDMY_STATE_PATH)) break; + if(!flipper_format_read_header(file, str, &tmp)) break; + if(furi_string_cmp_str(str, FINDMY_STATE_HEADER)) break; + if(tmp != FINDMY_STATE_VER) break; + + if(!flipper_format_read_bool(file, "beacon_active", &state.beacon_active, 1)) break; + + if(!flipper_format_read_uint32(file, "broadcast_interval", &tmp, 1)) break; + state.broadcast_interval = tmp; + + if(!flipper_format_read_uint32(file, "transmit_power", &tmp, 1)) break; + state.transmit_power = tmp; + + if(!flipper_format_read_hex(file, "mac", state.mac, sizeof(state.mac))) break; + + if(!flipper_format_read_hex(file, "data", state.data, sizeof(state.data))) break; + + loaded_from_file = true; + } while(0); + flipper_format_free(file); + } + furi_record_close(RECORD_STORAGE); + + // Otherwise set default values + if(!loaded_from_file) { + state.beacon_active = false; + state.broadcast_interval = 5; + state.transmit_power = 6; + + // Set default mac + uint8_t default_mac[EXTRA_BEACON_MAC_ADDR_SIZE] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; + memcpy(state.mac, default_mac, sizeof(state.mac)); + + // Set default empty AirTag data + uint8_t* data = state.data; + *data++ = 0x1E; // Length + *data++ = 0xFF; // Manufacturer Specific Data + *data++ = 0x4C; // Company ID (Apple, Inc.) + *data++ = 0x00; // ... + *data++ = 0x12; // Type (FindMy) + *data++ = 0x19; // Length + *data++ = 0x00; // Status + // Placeholder Empty Public Key without the MAC address + for(size_t i = 0; i < 22; ++i) { + *data++ = 0x00; + } + *data++ = 0x00; // First 2 bits are the version, the rest is the battery level + *data++ = 0x00; // Hint (0x00) + } + + // Sync values to config state.config.min_adv_interval_ms = state.broadcast_interval * 1000; // Converting s to ms state.config.max_adv_interval_ms = (state.broadcast_interval * 1000) + 150; state.config.adv_channel_map = GapAdvChannelMapAll; state.config.adv_power_level = GapAdvPowerLevel_0dBm + state.transmit_power; state.config.address_type = GapAddressTypePublic; - - // Set default mac - uint8_t default_mac[EXTRA_BEACON_MAC_ADDR_SIZE] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; - memcpy(state.mac, default_mac, sizeof(state.mac)); - memcpy(state.config.address, default_mac, sizeof(state.config.address)); - - // Set default empty AirTag data - uint8_t* data = state.data; - *data++ = 0x1E; // Length - *data++ = 0xFF; // Manufacturer Specific Data - *data++ = 0x4C; // Company ID (Apple, Inc.) - *data++ = 0x00; // ... - *data++ = 0x12; // Type (FindMy) - *data++ = 0x19; // Length - *data++ = 0x00; // Status - // Placeholder Empty Public Key without the MAC address - for(size_t i = 0; i < 22; ++i) { - *data++ = 0x00; - } - *data++ = 0x00; // First 2 bits are the version, the rest is the battery level - *data++ = 0x00; // Hint (0x00) + memcpy(state.config.address, state.mac, sizeof(state.config.address)); // Copy to caller state before popping stack memcpy(out_state, &state, sizeof(state)); @@ -59,3 +95,30 @@ void findmy_state_apply(FindMyState* state) { furi_check(furi_hal_bt_extra_beacon_start()); } } + +void findmy_state_save(FindMyState* state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, FINDMY_STATE_DIR); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + uint32_t tmp; + if(!flipper_format_file_open_always(file, FINDMY_STATE_PATH)) break; + if(!flipper_format_write_header_cstr(file, FINDMY_STATE_HEADER, FINDMY_STATE_VER)) break; + + if(!flipper_format_write_bool(file, "beacon_active", &state->beacon_active, 1)) break; + + tmp = state->broadcast_interval; + if(!flipper_format_write_uint32(file, "broadcast_interval", &tmp, 1)) break; + + tmp = state->transmit_power; + if(!flipper_format_write_uint32(file, "transmit_power", &tmp, 1)) break; + + if(!flipper_format_write_hex(file, "mac", state->mac, sizeof(state->mac))) break; + + if(!flipper_format_write_hex(file, "data", state->data, sizeof(state->data))) break; + } while(0); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} diff --git a/applications/system/findmy/findmy_state.h b/applications/system/findmy/findmy_state.h index 48a82fd2e4..284f2dfbc1 100644 --- a/applications/system/findmy/findmy_state.h +++ b/applications/system/findmy/findmy_state.h @@ -2,16 +2,25 @@ #include -typedef struct { - uint8_t mac[EXTRA_BEACON_MAC_ADDR_SIZE]; - uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]; - GapExtraBeaconConfig config; +#define FINDMY_STATE_HEADER "FindMy Flipper State" +#define FINDMY_STATE_VER 1 +#define FINDMY_STATE_DIR EXT_PATH("apps_data/findmy") +#define FINDMY_STATE_PATH FINDMY_STATE_DIR "/findmy_state.txt" +typedef struct { bool beacon_active; uint8_t broadcast_interval; uint8_t transmit_power; + + uint8_t mac[EXTRA_BEACON_MAC_ADDR_SIZE]; + uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]; + + // Generated from the other state values + GapExtraBeaconConfig config; } FindMyState; bool findmy_state_load(FindMyState* out_state); void findmy_state_apply(FindMyState* state); + +void findmy_state_save(FindMyState* state); diff --git a/applications/system/findmy/scenes/findmy_scene_config_mac.c b/applications/system/findmy/scenes/findmy_scene_config_mac.c index b42291a7f4..2dc67f79da 100644 --- a/applications/system/findmy/scenes/findmy_scene_config_mac.c +++ b/applications/system/findmy/scenes/findmy_scene_config_mac.c @@ -40,6 +40,7 @@ bool findmy_scene_config_mac_on_event(void* context, SceneManagerEvent event) { case ByteInputResultOk: furi_hal_bt_reverse_mac_addr(app->mac_buf); memcpy(&app->state.mac, app->mac_buf, sizeof(app->state.mac)); + findmy_state_save(&app->state); memcpy(&app->state.config.address, app->mac_buf, sizeof(app->state.config.address)); if(furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_stop()); diff --git a/applications/system/findmy/scenes/findmy_scene_config_packet.c b/applications/system/findmy/scenes/findmy_scene_config_packet.c index 121bf58b58..9d77fb1d79 100644 --- a/applications/system/findmy/scenes/findmy_scene_config_packet.c +++ b/applications/system/findmy/scenes/findmy_scene_config_packet.c @@ -40,6 +40,7 @@ bool findmy_scene_config_packet_on_event(void* context, SceneManagerEvent event) scene_manager_search_and_switch_to_previous_scene( app->scene_manager, FindMySceneConfig); memcpy(app->state.data, app->packet_buf, sizeof(app->state.data)); + findmy_state_save(&app->state); furi_check( furi_hal_bt_extra_beacon_set_data(app->state.data, sizeof(app->state.data))); findmy_main_update_type(app->findmy_main, findmy_data_get_type(app->state.data)); diff --git a/applications/system/findmy/scenes/findmy_scene_main.c b/applications/system/findmy/scenes/findmy_scene_main.c index 63932fb5c8..e70b59fc54 100644 --- a/applications/system/findmy/scenes/findmy_scene_main.c +++ b/applications/system/findmy/scenes/findmy_scene_main.c @@ -25,6 +25,8 @@ bool findmy_scene_main_on_event(void* context, SceneManagerEvent event) { findmy_toggle_beacon(app); break; case FindMyMainEventBackground: + app->state.beacon_active = true; + findmy_state_save(&app->state); if(!furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_start()); } @@ -40,6 +42,8 @@ bool findmy_scene_main_on_event(void* context, SceneManagerEvent event) { findmy_change_broadcast_interval(app, app->state.broadcast_interval - 1); break; case FindMyMainEventQuit: + app->state.beacon_active = false; + findmy_state_save(&app->state); if(furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_stop()); }