mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 12:58:36 -07:00
FindMy: Save and load state from file
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user