Skip to content

Commit 3f4f152

Browse files
authored
Merge pull request #162 from chvvkumar/Dev
Add device configuration backup and restore functionality
2 parents 8223b37 + 0fcbc1e commit 3f4f152

15 files changed

Lines changed: 622 additions & 1638 deletions

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,10 @@ coverage.xml
160160
# Claude Code local settings
161161
.claude/
162162

163-
# Implementation plans (local development only)
163+
# Superpowers specs and implementation plans (local development only)
164+
docs/superpowers/
164165
docs/plans/
166+
docs/specs/
165167

166168
# AI conversation context documentation
167169
CONVERSATION_CONTEXT.md

build_info.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
#define BUILD_TIME __TIME__
1010

1111
// Git information (updated by compile script)
12-
#define GIT_COMMIT_HASH "8845feb"
13-
#define GIT_COMMIT_FULL "8845feb8cfedc9e828d4ac05d499daeab1c3773e"
14-
#define GIT_BRANCH "feat/solar-lunar-display"
12+
#define GIT_COMMIT_HASH "1f1abd5"
13+
#define GIT_COMMIT_FULL "1f1abd5bdb01707a96e570f90a548c52461d36c0"
14+
#define GIT_BRANCH "snd"
1515

1616
#endif // BUILD_INFO_H

config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ enum LogSeverity {
2323
// SYSTEM CONFIGURATION
2424
// =============================================================================
2525

26+
// Config backup/restore schema version. Bump when fields are added, renamed,
27+
// removed, or change meaning. See config_backup.cpp migrate() for the
28+
// non-additive migration hook.
29+
#define CONFIG_SCHEMA_VERSION 1
30+
2631
// Memory allocation sizes
2732
// NOTE: These values automatically control PPA hardware accelerator buffer sizes:
2833
// - PPA source buffer = FULL_IMAGE_BUFFER_SIZE

config_backup.cpp

Lines changed: 380 additions & 0 deletions
Large diffs are not rendered by default.

config_backup.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
#include <Arduino.h>
3+
4+
// Config backup/restore module. Single owner of the mapping between
5+
// ConfigStorage state and the versioned JSON backup document.
6+
namespace ConfigBackup {
7+
struct RestoreResult {
8+
bool ok = false;
9+
String error;
10+
int fileVersion = 0;
11+
int applied = 0;
12+
int skipped = 0;
13+
bool versionMismatch = false;
14+
bool secretsIncluded = false;
15+
};
16+
17+
// Serialize the full device configuration to JSON. When includeSecrets is
18+
// false the wifiPassword, mqttPassword, and haAccessToken keys are omitted.
19+
String exportJson(bool includeSecrets);
20+
21+
// Parse a backup document and apply recognized fields through ConfigStorage
22+
// setters, then saveConfig(). Does not reboot; the caller handles that.
23+
RestoreResult importJson(const String& body);
24+
}

docs/03_configuration.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Complete guide to configuring the ESP32-P4 AllSky Display firmware, covering bot
1010
- [Web UI Configuration](#web-ui-configuration)
1111
- [MQTT Configuration](#mqtt-configuration)
1212
- [Multi-Image Setup](#multi-image-setup)
13+
- [Backup and Restore](#backup-and-restore)
1314
- [Serial Commands Reference](#serial-commands-reference)
1415
- [Advanced Configuration](#advanced-configuration)
1516

@@ -650,6 +651,57 @@ mkdir -p "${OUTPUT_DIR}"
650651

651652
---
652653

654+
## Backup and Restore
655+
656+
Export the full device configuration to a file, then restore that file onto a device that has been wiped or reset. The backup captures runtime settings stored in NVS: device name, WiFi, MQTT, Home Assistant, image sources and transforms, display settings, and system thresholds. Compile-time settings are not part of a backup.
657+
658+
### Where the Controls Live
659+
660+
The controls are on the System settings page, in the "Backup and Restore" card.
661+
662+
1. Navigate to `http://allskyesp32.lan:8080/system`
663+
2. Find the "Backup and Restore" card
664+
665+
The card contains a "Download backup" button, an "Include passwords and tokens" checkbox, a file picker, and a "Restore and reboot" button.
666+
667+
### Download a Backup
668+
669+
1. Decide whether to include secrets. The "Include passwords and tokens" checkbox controls whether the WiFi password, MQTT password, and Home Assistant access token are written to the file.
670+
2. Select "Download backup". The browser saves a `.json` file named after the device.
671+
672+
Warning: when "Include passwords and tokens" is checked, the secrets are stored in plaintext in the downloaded file. There is no encryption. Store the file in a protected location. When the checkbox is unchecked, the secret keys are omitted from the file, and other identifiers such as the WiFi SSID and MQTT username are still written.
673+
674+
### Restore and Reboot
675+
676+
1. Select the file picker and choose a `.json` backup file.
677+
2. Select "Restore and reboot" and confirm the prompt.
678+
3. The device applies the recognized settings, saves them to NVS, and reboots. After the reboot, the device runs with the restored settings.
679+
680+
A restore that fails to parse the file does not reboot the device.
681+
682+
### Version Behavior
683+
684+
The backup file carries a schema version. Restore is lenient and best-effort:
685+
686+
- Recognized fields are applied.
687+
- Unknown fields are ignored.
688+
- Fields absent from the file keep their current value on the device.
689+
- A backup made on a different schema version still restores. The restore proceeds and reports a version mismatch warning.
690+
- A no-secrets backup does not erase existing credentials. Secret fields are applied only when present and non-empty, so restoring a file saved without secrets leaves the current WiFi password, MQTT password, and Home Assistant token in place.
691+
692+
### Wipe Then Restore Sequence
693+
694+
After a factory reset the web UI is not reachable until WiFi is configured, because a factory reset clears WiFi credentials. Use this order:
695+
696+
1. Factory reset the device.
697+
2. Complete WiFi setup through the captive portal so the device joins your network and the web UI becomes reachable.
698+
3. Open the System settings page and restore the backup file.
699+
4. The device reboots with the restored configuration.
700+
701+
If the backup includes secrets, the restored WiFi credentials take effect after the reboot. If the backup excludes secrets, the WiFi credentials entered during setup are retained.
702+
703+
---
704+
653705
## Serial Commands Reference
654706

655707
Connect via Serial Monitor at 9600 baud to access these commands:

docs/developer/api_reference.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,59 @@ bool isRunning();
835835
bool isOTAInProgress() const;
836836
```
837837

838+
### REST API Endpoints
839+
840+
#### GET /api/backup
841+
842+
Returns the device configuration as a JSON file attachment.
843+
844+
Query parameters:
845+
846+
| Parameter | Values | Description |
847+
|-----------|--------|-------------|
848+
| `secrets` | `0` or `1` | `1` includes passwords and tokens (WiFi password, MQTT password, Home Assistant access token). `0` omits them. |
849+
850+
Response: the configuration document with `Content-Type: application/json` and a `Content-Disposition: attachment` header. The filename is derived from the device name.
851+
852+
Example:
853+
854+
```bash
855+
# Download a backup with secrets included
856+
curl -OJ "http://allskyesp32.lan:8080/api/backup?secrets=1"
857+
858+
# Download a backup without secrets
859+
curl -OJ "http://allskyesp32.lan:8080/api/backup?secrets=0"
860+
```
861+
862+
#### POST /api/restore
863+
864+
Applies a backup file, saves the configuration, and reboots on success.
865+
866+
Request body: the raw backup file text (the JSON document returned by `GET /api/backup`). The body is read from the `plain` argument.
867+
868+
Behavior: the handler parses the body, applies every recognized field through the matching `ConfigStorage` setters, saves the configuration, then reboots the device. Unknown fields are ignored. Absent fields keep their current value. Secret fields are applied only when present and non-empty, so a no-secrets backup does not erase existing credentials.
869+
870+
Response JSON fields:
871+
872+
| Field | Type | Description |
873+
|-------|------|-------------|
874+
| `status` | string | Result of the restore. |
875+
| `message` | string | Human-readable detail. |
876+
| `applied` | number | Count of recognized fields applied. |
877+
| `skipped` | number | Count of unknown fields ignored. |
878+
| `fileVersion` | number | Schema version read from the file. Absent value is treated as 0. |
879+
| `versionMismatch` | boolean | `true` when `fileVersion` differs from the firmware schema version. The restore still proceeds. |
880+
881+
Errors: an empty or unparseable body returns HTTP 400 and does not reboot. A successful restore reboots after sending the response.
882+
883+
Example:
884+
885+
```bash
886+
# Restore a previously downloaded backup
887+
curl -X POST "http://allskyesp32.lan:8080/api/restore" \
888+
--data-binary @allsky-config-backup.json
889+
```
890+
838891
---
839892

840893
## ConfigStorage

0 commit comments

Comments
 (0)