FindMy: Save and load state from file

This commit is contained in:
Willy-JL
2024-03-08 21:35:59 +00:00
parent 7c48c6164a
commit 5d45d6abb8
6 changed files with 110 additions and 29 deletions

View File

@@ -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());
}

View File

@@ -3,40 +3,76 @@
#include <string.h>
#include <stddef.h>
#include <furi_hal_bt.h>
#include <flipper_format/flipper_format.h>
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);
}

View File

@@ -2,16 +2,25 @@
#include <extra_beacon.h>
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);

View File

@@ -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());

View File

@@ -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));

View File

@@ -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());
}