diff --git a/templates/index_v2.html b/templates/index_v2.html
index 92a62766..9e2c9b25 100644
--- a/templates/index_v2.html
+++ b/templates/index_v2.html
@@ -719,11 +719,12 @@
+
On-Demand: None
-
Service actions may require sudo privileges on the Pi.
+ Service actions may require sudo privileges on the Pi. Migrate Config adds new options with defaults while preserving your settings.
@@ -906,6 +907,9 @@
+
@@ -2487,6 +2491,9 @@
if (action === 'reboot_system' && !confirm('Are you sure you want to reboot the system?')) {
return;
}
+ if (action === 'migrate_config' && !confirm('This will migrate your configuration to add any new options with default values. A backup will be created automatically. Continue?')) {
+ return;
+ }
try {
const response = await fetch('/api/system/action', {
@@ -3038,6 +3045,41 @@
});
})();
+ // Music form submit
+ (function augmentMusicForm(){
+ const form = document.getElementById('music-form');
+ form.addEventListener('submit', async function(e){
+ e.preventDefault();
+ const payload = {
+ music: {
+ enabled: document.getElementById('music_enabled').checked,
+ preferred_source: document.getElementById('music_preferred_source').value,
+ YTM_COMPANION_URL: document.getElementById('ytm_companion_url').value,
+ POLLING_INTERVAL_SECONDS: parseInt(document.getElementById('music_polling_interval').value)
+ }
+ };
+ await saveConfigJson(payload);
+ });
+ })();
+
+ // Calendar form submit
+ (function augmentCalendarForm(){
+ const form = document.getElementById('calendar-form');
+ form.addEventListener('submit', async function(e){
+ e.preventDefault();
+ const calendars = document.getElementById('calendar_calendars').value.split(',').map(s => s.trim()).filter(Boolean);
+ const payload = {
+ calendar: {
+ enabled: document.getElementById('calendar_enabled').checked,
+ max_events: parseInt(document.getElementById('calendar_max_events').value),
+ update_interval: parseInt(document.getElementById('calendar_update_interval').value),
+ calendars: calendars
+ }
+ };
+ await saveConfigJson(payload);
+ });
+ })();
+
// News advanced save
async function saveNewsAdvancedSettings(){
const payload = {
diff --git a/web_interface_v2.py b/web_interface_v2.py
index d255547a..9be93db5 100644
--- a/web_interface_v2.py
+++ b/web_interface_v2.py
@@ -735,6 +735,18 @@ def system_action():
}), 400
result = subprocess.run(['git', 'pull'],
capture_output=True, text=True, cwd=str(repo_dir), check=False)
+ elif action == 'migrate_config':
+ # Run config migration script
+ repo_dir = Path(__file__).resolve().parent
+ migrate_script = repo_dir / 'migrate_config.sh'
+ if not migrate_script.exists():
+ return jsonify({
+ 'status': 'error',
+ 'message': f'Migration script not found: {migrate_script}'
+ }), 400
+
+ result = subprocess.run(['bash', str(migrate_script)],
+ cwd=str(repo_dir), capture_output=True, text=True, check=False)
else:
return jsonify({
'status': 'error',