# Settings Manager for MacroPad Server import os import json from typing import Any, Optional DEFAULT_SETTINGS = { "relay": { "enabled": False, "server_url": "wss://relay.macropad.example.com", "session_id": None, "password": "" }, "minimize_to_tray": True } class SettingsManager: """Manages application settings with JSON persistence.""" def __init__(self, settings_file: str): self.settings_file = settings_file self.settings = {} self.load() def load(self): """Load settings from file.""" if os.path.exists(self.settings_file): try: with open(self.settings_file, 'r', encoding='utf-8') as f: self.settings = json.load(f) except (json.JSONDecodeError, IOError): self.settings = {} # Merge with defaults to ensure all keys exist self.settings = self._merge_defaults(DEFAULT_SETTINGS, self.settings) def save(self): """Save settings to file.""" try: os.makedirs(os.path.dirname(self.settings_file), exist_ok=True) with open(self.settings_file, 'w', encoding='utf-8') as f: json.dump(self.settings, f, indent=2) return True except IOError: return False def _merge_defaults(self, defaults: dict, current: dict) -> dict: """Merge current settings with defaults, keeping current values.""" result = defaults.copy() for key, value in current.items(): if key in result: if isinstance(result[key], dict) and isinstance(value, dict): result[key] = self._merge_defaults(result[key], value) else: result[key] = value else: result[key] = value return result def get(self, key: str, default: Any = None) -> Any: """Get a setting value by key (supports dot notation).""" keys = key.split('.') value = self.settings try: for k in keys: value = value[k] return value except (KeyError, TypeError): return default def set(self, key: str, value: Any): """Set a setting value by key (supports dot notation).""" keys = key.split('.') target = self.settings for k in keys[:-1]: if k not in target: target[k] = {} target = target[k] target[keys[-1]] = value def get_relay_enabled(self) -> bool: """Check if relay server is enabled.""" return self.get('relay.enabled', False) def get_relay_url(self) -> str: """Get the relay server URL.""" return self.get('relay.server_url', '') def get_relay_session_id(self) -> Optional[str]: """Get the stored relay session ID.""" return self.get('relay.session_id') def get_relay_password(self) -> str: """Get the relay password.""" return self.get('relay.password', '') def set_relay_session_id(self, session_id: str): """Store the relay session ID.""" self.set('relay.session_id', session_id) self.save() def get_minimize_to_tray(self) -> bool: """Check if minimize to tray is enabled.""" return self.get('minimize_to_tray', True)