Files
yaris-obd/modules/app_device_config.py
T

128 lines
4.7 KiB
Python

"""
Device configuration management (owner name, car model, avatar, sync status).
Stores in SQLite with option to sync from server (Phase 25.5a).
"""
import json
import os
from . import app_sql
def get_device_config():
"""Get current device config including sync status."""
sync_enabled = _get_sync_enabled()
config = {
"owner_name": app_sql.get_setting("device_owner_name", ""),
"car_model": app_sql.get_setting("device_car_model", ""),
"device_name": app_sql.get_setting("device_device_name", ""),
"avatar_path": app_sql.get_setting("device_avatar_path", ""),
"odometer_baseline": float(app_sql.get_setting("odometer_baseline", "0") or "0"),
"sync_enabled": sync_enabled,
}
# Fill in any missing values from config file or defaults
config = _apply_defaults(config)
return config
def update_device_config(body, errors):
"""Update device config from request body. Returns errors dict if invalid."""
if not isinstance(body, dict):
errors["error"] = "Invalid request body"
return errors
# Check if sync is enabled; if so, only allow sync-related updates
sync_enabled = _get_sync_enabled()
if sync_enabled:
# Allow toggling sync_enabled off
if "sync_enabled" in body and not body["sync_enabled"]:
_set_sync_enabled(False)
# Toggling off is always allowed — don't return error even if
# other locked fields are in the body (the JS always sends them).
return errors
# User cannot edit locked fields while syncing from server.
if any(k in body for k in ["owner_name", "car_model", "avatar_path"]):
errors["error"] = "Cannot edit device config while server sync is enabled. Server is source of truth."
return errors
# Not syncing: allow all edits
if "owner_name" in body:
value = str(body["owner_name"]).strip()
if not value or len(value) > 100:
errors["owner_name"] = "Owner name must be 1-100 characters."
else:
app_sql.set_setting("device_owner_name", value)
if "car_model" in body:
value = str(body["car_model"]).strip()
if not value or len(value) > 100:
errors["car_model"] = "Car model must be 1-100 characters."
else:
app_sql.set_setting("device_car_model", value)
if "device_name" in body:
value = str(body["device_name"]).strip()
if not value or len(value) > 100:
errors["device_name"] = "Device name must be 1-100 characters."
else:
app_sql.set_setting("device_device_name", value)
if "avatar_path" in body:
value = str(body["avatar_path"]).strip()
if not value or len(value) > 255:
errors["avatar_path"] = "Avatar path must be 1-255 characters."
else:
app_sql.set_setting("device_avatar_path", value)
if "sync_enabled" in body:
if not isinstance(body["sync_enabled"], bool):
errors["sync_enabled"] = "Must be true or false."
elif body["sync_enabled"]:
from . import app_remote_config
remote = app_remote_config.get_remote_config()
if not remote["server_url"] or not remote["device_name"] or not remote["api_key_configured"]:
errors["sync_enabled"] = "Configure server URL, device name, and API key before enabling server sync."
else:
_set_sync_enabled(body["sync_enabled"])
if "sync_enabled" in body and body["sync_enabled"] and "sync_enabled" not in errors:
_set_sync_enabled(True)
return errors
def _apply_defaults(config):
"""Fill in any empty config values from config/device.json or hardcoded defaults."""
config_file_path = os.path.join(
os.path.dirname(os.path.dirname(__file__)), "config", "device.json"
)
file_config = {}
try:
if os.path.exists(config_file_path):
with open(config_file_path, "r") as f:
file_config = json.load(f)
except (IOError, json.JSONDecodeError):
pass
# Use file config as source of truth for initial values, fallback to hardcoded
defaults = {
"owner_name": "quoije",
"car_model": "Yaris 2013",
"device_name": "Yaris OBD",
"avatar_path": "img/yaris.png",
}
for key in defaults:
if not config[key]:
config[key] = file_config.get(key, defaults[key])
return config
def _get_sync_enabled():
"""Check if device config sync from server is enabled."""
return app_sql.get_setting("device_sync_enabled", "0") == "1"
def _set_sync_enabled(enabled):
"""Set device config sync status."""
app_sql.set_setting("device_sync_enabled", "1" if enabled else "0")