同步移动端工程、设备控制改进与发布资料

This commit is contained in:
2026-09-26 19:21:12 +08:00
parent 912ba432cf
commit 98eadc5c8b
137 changed files with 9274 additions and 85 deletions
+36 -37
View File
@@ -15,6 +15,10 @@ from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, Response
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr, field_validator, model_validator
from app.control import DeviceControl
from app.mobile.control import MobileControl
from app.mobile.session import SessionManager
from app.mobile.bluez import BluezRuntime
from app.config.store import (
COLOR_PALETTE_LIMIT,
DEFAULT_DISPLAY,
@@ -561,6 +565,8 @@ def create_app(
staging_root=ota_staging_root,
)
control = DeviceControl(store, display, power_monitor, performance_mode)
def resolve_content(reference: dict[str, str]) -> dict[str, Any]:
checked = normalize_default_display(reference)
content_type = checked["type"]
@@ -831,8 +837,9 @@ def create_app(
},
)
@app.get("/api/status")
def get_status() -> dict:
mobile_runtime = None
def read_device_status() -> dict:
status = display.get_status()
status["service"] = {
"app_version": app.state.app_version,
@@ -855,8 +862,25 @@ def create_app(
store.config["performance_mode_enabled"]
),
}
status["mobile"] = mobile_runtime.status() if mobile_runtime else {"available": False, "connected": False, "reason": "initialization_failed"}
return status
try:
mobile = MobileControl(control, network, templates, animations, resolve_content,
apply_resolved_content, effective_default_content, set_default_content,
read_device_status, storage_monitor.get_status, library_order=library_order)
mobile_sessions = SessionManager(mobile.dispatch, mobile.identity)
mobile_runtime = BluezRuntime(mobile_sessions, enabled=os.environ.get("MATRIX_BLE_ENABLED") == "1")
app.state.mobile_control = mobile
app.state.mobile_sessions = mobile_sessions
except Exception:
logger.exception("Mobile control initialization failed; display remains available")
app.state.mobile_runtime = mobile_runtime
@app.get("/api/status")
def get_status() -> dict:
return read_device_status()
@app.get("/api/ota/status")
def get_ota_status() -> dict:
return {
@@ -1123,41 +1147,7 @@ def create_app(
def put_config(update: ConfigUpdate) -> dict:
values = update.model_dump(exclude_none=True)
try:
config = validate_config({**store.config, **values})
if "low_voltage_protection_enabled" in values:
enabled = config["low_voltage_protection_enabled"]
store.update({"low_voltage_protection_enabled": enabled})
power_monitor.set_protection_enabled(enabled)
if enabled:
power_monitor.sample_now()
if "orientation" in values:
display.set_orientation(config["orientation"])
if "brightness" in values:
display.set_brightness(config["brightness"])
if "matrix_refresh_rate_limit_hz" in values:
display.set_refresh_rate_limit(
config["matrix_refresh_rate_limit_hz"]
)
if "performance_mode_enabled" in values:
performance_mode.transact(
config["performance_mode_enabled"],
lambda enabled: store.update({"performance_mode_enabled": enabled}),
)
remaining = {
key: config[key]
for key in (
"default_font",
"default_text_size",
"preview_refresh_interval_ms",
"custom_test_color",
"workspace_order",
"animation_preview_max_concurrent",
)
if key in values
}
if remaining:
store.update(remaining)
return store.config
return control.update_config(values)
except (ConfigError, PerformanceModeError, ValueError) as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
except Exception as exc:
@@ -2023,6 +2013,8 @@ def create_app(
@app.on_event("startup")
def startup() -> None:
if mobile_runtime:
mobile_runtime.start()
performance_mode.start(store.config["performance_mode_enabled"])
media_imports.start()
power_monitor.sample_now()
@@ -2034,8 +2026,13 @@ def create_app(
network.start()
except Exception:
logger.exception("Failed to restore normal content after OTA completion")
try:
ota.schedule_kernel_recovery_after_ota()
except Exception:
logger.exception("Failed to evaluate kernel recovery after OTA completion")
ota.set_completion_callback(restore_after_ota)
ota.finalize_kernel_recovery_after_boot()
if ota.resume_or_start_monitor(restore_after_ota):
logger.warning("OTA transaction is active; keeping the update indicator visible")
elif maintenance_black:
@@ -2046,6 +2043,8 @@ def create_app(
@app.on_event("shutdown")
def shutdown() -> None:
if mobile_runtime:
mobile_runtime.close()
logger.info("Shutting down; clearing display")
media_imports.stop()
power_monitor.close()