From 70cb2caddaaa007177e611e4315e2ec5eeb185c9 Mon Sep 17 00:00:00 2001 From: Harry Wu Date: Wed, 22 Jul 2026 13:46:26 +0800 Subject: [PATCH] fix(dashy): support desktop testing and mobile layout --- dragonpilot/dashy/serverd.py | 31 ++++++++-- dragonpilot/dashy/web/dist/css/responsive.css | 59 +++++++++++++++++++ dragonpilot/dashy/web/dist/index.html | 1 + dragonpilot/settings/openpilot.developer.py | 22 ++++++- dragonpilot/settings/openpilot.toggles.py | 15 ++++- 5 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 dragonpilot/dashy/web/dist/css/responsive.css diff --git a/dragonpilot/dashy/serverd.py b/dragonpilot/dashy/serverd.py index ebac781a1..ddf2a565b 100755 --- a/dragonpilot/dashy/serverd.py +++ b/dragonpilot/dashy/serverd.py @@ -79,8 +79,10 @@ class MockParams: class AppCache: """Centralized cache for expensive operations.""" - def __init__(self): + def __init__(self, car_brand=None, openpilot_longitudinal_control=False): self._params = None + self._car_brand_override = car_brand + self._openpilot_longitudinal_control_override = openpilot_longitudinal_control self._car_params = None self._car_params_time = 0 self._context = None @@ -109,6 +111,12 @@ class AppCache: def _parse_car_params(self): """Parse CarParams from Params store.""" + if self._car_brand_override: + return { + 'brand': self._car_brand_override, + 'openpilot_longitudinal_control': self._openpilot_longitudinal_control_override, + } + result = {'brand': '', 'openpilot_longitudinal_control': False} try: # CarParams is cleared offroad/at boot; CarParamsPersistent keeps the last car's @@ -790,7 +798,10 @@ async def no_cache_middleware(request, handler): # --- Application Setup --- async def on_startup(app): """Initialize app-level resources.""" - app['cache'] = AppCache() + app['cache'] = AppCache( + car_brand=app['car_brand'], + openpilot_longitudinal_control=app['openpilot_longitudinal_control'], + ) app['ws_clients'] = set() app['publisher_task'] = asyncio.create_task(_publisher_loop(app)) logger.info("Dashy server started") @@ -808,13 +819,16 @@ async def on_cleanup(app): logger.info("Dashy server stopped") -def setup_aiohttp_app(host: str, port: int, debug: bool): +def setup_aiohttp_app(host: str, port: int, debug: bool, car_brand=None, + openpilot_longitudinal_control=False): logging.basicConfig( level=logging.DEBUG if debug else logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) app = web.Application(middlewares=[no_cache_middleware]) + app['car_brand'] = car_brand + app['openpilot_longitudinal_control'] = openpilot_longitudinal_control # API routes app.router.add_get("/api/init", init_api) @@ -846,9 +860,18 @@ def main(): parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to listen on") parser.add_argument("--port", type=int, default=5088, help="Port to listen on") parser.add_argument("--debug", action="store_true", help="Enable debug mode") + parser.add_argument("--car-brand", type=str, help="Override the car brand for desktop UI testing") + parser.add_argument("--openpilot-longitudinal-control", action="store_true", + help="Expose settings that require openpilot longitudinal control") args = parser.parse_args() - app = setup_aiohttp_app(args.host, args.port, args.debug) + app = setup_aiohttp_app( + args.host, + args.port, + args.debug, + car_brand=args.car_brand, + openpilot_longitudinal_control=args.openpilot_longitudinal_control, + ) web.run_app(app, host=args.host, port=args.port) diff --git a/dragonpilot/dashy/web/dist/css/responsive.css b/dragonpilot/dashy/web/dist/css/responsive.css new file mode 100644 index 000000000..e4fe473a6 --- /dev/null +++ b/dragonpilot/dashy/web/dist/css/responsive.css @@ -0,0 +1,59 @@ +/* Responsive overrides for the dynamically-created Dashy settings panel. */ +@media (max-width: 767px) { + /* Stack the category navigation above the settings content. */ + .hud-panel-surface .flex.flex-1.min-h-0.w-full { + flex-direction: column; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .w-48.shrink-0 { + display: flex; + width: 100%; + max-height: 3.5rem; + flex: 0 0 auto; + overflow-x: auto; + overflow-y: hidden; + padding-block: 0.25rem; + border-right: 0; + border-bottom: 1px solid color-mix(in oklab, var(--color-base-content) 10%, transparent); + scrollbar-width: none; + -webkit-overflow-scrolling: touch; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .w-48.shrink-0::-webkit-scrollbar { + display: none; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .w-48.shrink-0 > button { + width: auto; + flex: 0 0 auto; + padding: 0.625rem 0.875rem; + white-space: nowrap; + } + + /* Let cards use the complete viewport width and prevent horizontal drift. */ + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .flex-1.overflow-y-auto.p-4 { + width: 100%; + min-width: 0; + padding: 0.75rem; + overflow-x: hidden; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .flex-1.overflow-y-auto.p-4 .card { + width: 100%; + min-width: 0; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .flex-1.overflow-y-auto.p-4 .card .flex-1 { + min-width: 0; + } +} + +@media (max-width: 374px) { + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .flex-1.overflow-y-auto.p-4 { + padding: 0.5rem; + } + + .hud-panel-surface .flex.flex-1.min-h-0.w-full > .flex-1.overflow-y-auto.p-4 .p-4 { + padding: 0.75rem; + } +} diff --git a/dragonpilot/dashy/web/dist/index.html b/dragonpilot/dashy/web/dist/index.html index e29150740..6c4807350 100644 --- a/dragonpilot/dashy/web/dist/index.html +++ b/dragonpilot/dashy/web/dist/index.html @@ -9,6 +9,7 @@ Dashy by dragonpilot + diff --git a/dragonpilot/settings/openpilot.developer.py b/dragonpilot/settings/openpilot.developer.py index 5ad084b4a..2c4b1e86b 100644 --- a/dragonpilot/settings/openpilot.developer.py +++ b/dragonpilot/settings/openpilot.developer.py @@ -17,7 +17,27 @@ Notes: also still available via the existing DeveloperLayout button. """ from dragonpilot.settings import tr -from openpilot.selfdrive.ui.layouts.settings.developer import DESCRIPTIONS as _DEV_DESC + +# Keep the dashy settings schema independent from the native Raylib UI. Importing +# DeveloperLayout just for these strings initializes gui_app at module import time, +# which blocks a headless serverd process on macOS. +_DEV_DESC = { + "enable_adb": ( + "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + "See https://docs.comma.ai/how-to/connect-to-comma for more info." + ), + "ssh_key": ( + "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " + "other than your own. A comma employee will NEVER ask you to add their GitHub username." + ), + "alpha_longitudinal": ( + "WARNING: openpilot longitudinal control is in alpha for this car and may disable Automatic Emergency " + "Braking (AEB).

On this car, openpilot defaults to the car's built-in ACC instead of openpilot's " + "longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode " + "is recommended when enabling openpilot longitudinal control alpha. Changing this setting will restart " + "openpilot if the car is powered on." + ), +} _SEC = "Developer" _DASHY = "DASHY" diff --git a/dragonpilot/settings/openpilot.toggles.py b/dragonpilot/settings/openpilot.toggles.py index 835dd1016..f58b216df 100644 --- a/dragonpilot/settings/openpilot.toggles.py +++ b/dragonpilot/settings/openpilot.toggles.py @@ -16,7 +16,20 @@ duplicates because these have no flags+param_type pair. Drift detection: see system/tests/test_openpilot_mirror.py. """ from dragonpilot.settings import tr -from openpilot.selfdrive.ui.layouts.settings.toggles import DESCRIPTIONS as _TOGGLES_DESC + +# Keep this web schema importable without initializing the native Raylib UI. +_TOGGLES_DESC = { + "OpenpilotEnabledToggle": "Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.", + "DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.", + "LongitudinalPersonality": "Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button.", + "IsLdwEnabled": "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h).", + "AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.", + "RecordFront": "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", + "IsMetric": "Display speed in km/h instead of mph.", + "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", + "DisableLogging": "Disable logging service", + "DisableUpdates": "Disable update service", +} _SEC = "Openpilot" _DASHY = "DASHY"