Backup / restore preferences method

This commit is contained in:
Ben Meadors 2025-02-24 07:10:08 -06:00
parent c2cc3207ba
commit ada8b96842
3 changed files with 99 additions and 1 deletions

View File

@ -1597,6 +1597,81 @@ UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
}
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
{
bool success = false;
#ifdef FSCom
if (location == meshtastic_AdminMessage_BackupLocation_FLASH) {
meshtastic_BackupPreferences backup = meshtastic_BackupPreferences_init_zero;
backup.version = DEVICESTATE_CUR_VER;
backup.timestamp = getValidTime(RTCQuality::RTCQualityDevice, false);
backup.has_config = true;
backup.config = config;
backup.has_module_config = true;
backup.module_config = moduleConfig;
backup.has_channels = true;
backup.channels = channelFile;
backup.has_owner = true;
backup.owner = owner;
size_t backupSize;
pb_get_encoded_size(&backupSize, meshtastic_BackupPreferences_fields, &backup);
spiLock->lock();
FSCom.mkdir("/backups");
spiLock->unlock();
success = saveProto(backupFileName, backupSize, &meshtastic_BackupPreferences_msg, &backup);
if (success) {
LOG_INFO("Saved backup preferences");
} else {
LOG_ERROR("Failed to save backup preferences to file");
}
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
// TODO: After more mainline SD card support
}
#endif
return success;
}
bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location)
{
bool success = false;
#ifdef FSCom
if (location == meshtastic_AdminMessage_BackupLocation_FLASH) {
spiLock->lock();
if (!FSCom.exists(backupFileName)) {
spiLock->unlock();
LOG_ERROR("Could not restore. No backup file found");
return false;
} else {
spiLock->unlock();
}
meshtastic_BackupPreferences backup = meshtastic_BackupPreferences_init_zero;
success = loadProto(backupFileName, meshtastic_BackupPreferences_size, sizeof(meshtastic_BackupPreferences),
&meshtastic_BackupPreferences_msg, &backup);
if (success) {
config = backup.config;
moduleConfig = backup.module_config;
channelFile = backup.channels;
owner = backup.owner;
success = saveToDisk(SEGMENT_DEVICESTATE | SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_CHANNELS);
if (!success) {
LOG_ERROR("Failed to save restored preferences to flash");
} else {
LOG_INFO("Restored preferences from backup... Rebooting");
rebootAtMsec = millis() + 1000;
}
} else {
LOG_ERROR("Failed to restore preferences from backup file");
}
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
// TODO: After more mainline SD card support
}
return success;
#endif
}
/// Record an error that should be reported via analytics
void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename)
{

View File

@ -48,6 +48,7 @@ static constexpr const char *configFileName = "/prefs/config.proto";
static constexpr const char *uiconfigFileName = "/prefs/uiconfig.proto";
static constexpr const char *moduleConfigFileName = "/prefs/module.proto";
static constexpr const char *channelFileName = "/prefs/channels.proto";
static constexpr const char *backupFileName = "/backups/backup.proto";
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
@ -200,6 +201,9 @@ class NodeDB
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location);
private:
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
/// Find a node in our DB, create an empty NodeInfoLite if missing

View File

@ -373,6 +373,25 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#endif
break;
}
case meshtastic_AdminMessage_backup_preferences_tag: {
LOG_INFO("Client requesting to backup preferences");
if (nodeDB->backupPreferences(r->backup_preferences)) {
myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp);
} else {
myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp);
}
break;
}
case meshtastic_AdminMessage_restore_preferences_tag: {
LOG_INFO("Client requesting to restore preferences");
if (nodeDB->restorePreferences(r->backup_preferences)) {
myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp);
disableBluetooth();
} else {
myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp);
}
break;
}
#ifdef ARCH_PORTDUINO
case meshtastic_AdminMessage_exit_simulator_tag:
LOG_INFO("Exiting simulator");