From 9798511e2e81155b9c6c062af085db5f5650c45e Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:20:50 -0500 Subject: [PATCH] Gate PiP side camera behind Galaxy developer mode Keep the PiP side-camera contribution from PR #83 opt-in and off-road configurable. Credit to Prabhaav Pillai for the original implementation. --- selfdrive/ui/onroad/starpilot/pip_sidecam.py | 4 +-- .../the_galaxy/assets/components/sidebar.js | 26 ++++++++++++++++--- .../tools/device_settings_layout.json | 6 ++--- .../tests/test_device_settings_layout.py | 9 +++++-- starpilot/system/the_galaxy/the_galaxy.py | 21 +++++++++++++++ 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/onroad/starpilot/pip_sidecam.py b/selfdrive/ui/onroad/starpilot/pip_sidecam.py index d4f03a3da..533f28621 100644 --- a/selfdrive/ui/onroad/starpilot/pip_sidecam.py +++ b/selfdrive/ui/onroad/starpilot/pip_sidecam.py @@ -134,7 +134,7 @@ class PipSideCamera: if not force and now - self._last_param_refresh < PARAM_REFRESH_INTERVAL: return self._last_param_refresh = now - self._enabled = self._params.get_bool("PIPPreviewEnabled") + self._enabled = self._params.get_bool("PIPPreviewEnabled") and self._params.get_bool("GalaxyDeveloperMode") self._show_on_blinker = self._params.get_bool("PIPPreviewShowOnBlinker") self._show_on_bsm = self._params.get_bool("PIPPreviewShowOnBSM") try: @@ -281,4 +281,4 @@ class PipSideCamera: self.texture_y = None if self.texture_uv and self.texture_uv.id: rl.unload_texture(self.texture_uv) - self.texture_uv = None \ No newline at end of file + self.texture_uv = None diff --git a/starpilot/system/the_galaxy/assets/components/sidebar.js b/starpilot/system/the_galaxy/assets/components/sidebar.js index a86858d43..12e6f948f 100644 --- a/starpilot/system/the_galaxy/assets/components/sidebar.js +++ b/starpilot/system/the_galaxy/assets/components/sidebar.js @@ -24,7 +24,7 @@ const MENU_ITEMS = { { name: "Testing Ground", link: "/testing_ground", icon: "bi-bezier2" }, { name: "Troubleshoot", link: "/troubleshoot", icon: "bi-tools" }, { name: "V-Adj Spot Monitor", link: "/manage_v_asm", icon: "bi-eye" }, - { name: "PiP Side Camera", link: "/manage_pip_sidecam", icon: "bi-badge-hd" }, + { name: "PiP Side Camera", link: "/manage_pip_sidecam", icon: "bi-badge-hd", developerOnly: true }, { name: "Theme Maker", link: "/theme_maker", icon: "bi-palette-fill" }, { name: "Tmux Log", link: "/manage_tmux", icon: "bi-terminal" }, { name: "Backup and Restore", link: "/manage_toggles", icon: "bi-arrow-repeat" }, @@ -33,6 +33,8 @@ const MENU_ITEMS = { ], }; +let galaxyDeveloperMode = false; + function matchesPath(currentPath, link) { if (link === "/") return currentPath === "/"; if (link === "/tuning" && currentPath === "/lateral_maneuvers") return true; @@ -40,7 +42,7 @@ function matchesPath(currentPath, link) { } function buildSectionMarkup(section, links, currentPath) { - const linksMarkup = links.map((link) => { + const linksMarkup = links.filter((link) => !link.developerOnly || galaxyDeveloperMode).map((link) => { const active = matchesPath(currentPath, link.link) ? "active" : ""; return `
  • @@ -66,6 +68,21 @@ function buildSectionMarkup(section, links, currentPath) { `; } +async function refreshGalaxyDeveloperMode() { + try { + const response = await fetch("/api/params/all", { cache: "no-store" }); + if (!response.ok) return; + const values = await response.json(); + const next = Boolean(values?.GalaxyDeveloperMode); + if (next !== galaxyDeveloperMode) { + galaxyDeveloperMode = next; + renderSidebarIntoShell(); + } + } catch (error) { + console.warn("Unable to determine Galaxy Developer Mode:", error); + } +} + function bindSidebarHandlers() { const menuButton = document.getElementById("menu_button"); const underlay = document.getElementById("sidebarUnderlay"); @@ -136,6 +153,9 @@ function renderSidebarIntoShell(currentPath) { } export function Sidebar(currentPath) { - setTimeout(() => renderSidebarIntoShell(currentPath), 0); + setTimeout(() => { + renderSidebarIntoShell(currentPath); + refreshGalaxyDeveloperMode(); + }, 0); return html``; } diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json index 58e5616a9..75256e6af 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json @@ -2147,7 +2147,7 @@ "is_parent_toggle": true, "requires_nonempty_key": "PIPPreviewMask", "disabled_reason": "Configure the PiP window mask first in Galaxy > PiP Side Camera", - "settings_tier": "simple" + "settings_tier": "advanced" }, { "key": "PIPPreviewShowOnBlinker", @@ -2156,7 +2156,7 @@ "data_type": "bool", "ui_type": "toggle", "parent_key": "PIPPreviewEnabled", - "settings_tier": "simple" + "settings_tier": "advanced" }, { "key": "PIPPreviewShowOnBSM", @@ -2165,7 +2165,7 @@ "data_type": "bool", "ui_type": "toggle", "parent_key": "PIPPreviewEnabled", - "settings_tier": "simple" + "settings_tier": "advanced" }, { "key": "Compass", diff --git a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py index f3f5ce816..ae462087f 100644 --- a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py +++ b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py @@ -86,7 +86,10 @@ def test_requested_simple_and_advanced_settings_tiers(): "Wheel Controls", "Device & Data", ): - assert {param["settings_tier"] for param in sections[section_name].values()} == {"simple"} + params = sections[section_name].values() + if section_name == "Visual (Display & UI)": + params = [param for param in params if not param["key"].startswith("PIPPreview")] + assert {param["settings_tier"] for param in params} == {"simple"} for key in ("AlwaysOnLateral", "LaneChanges", "QOLLateral"): assert lateral[key]["settings_tier"] == "simple" @@ -161,7 +164,9 @@ def test_pip_preview_is_under_driving_screen_widgets_and_configured_only_in_gala assert visual["PIPPreviewEnabled"]["parent_key"] == "CustomUI" assert visual["PIPPreviewShowOnBlinker"]["parent_key"] == "PIPPreviewEnabled" assert visual["PIPPreviewShowOnBSM"]["parent_key"] == "PIPPreviewEnabled" - assert visual["PIPPreviewEnabled"]["settings_tier"] == "simple" + assert visual["PIPPreviewEnabled"]["settings_tier"] == "advanced" + assert visual["PIPPreviewShowOnBlinker"]["settings_tier"] == "advanced" + assert visual["PIPPreviewShowOnBSM"]["settings_tier"] == "advanced" assert _declared_default("PIPPreviewEnabled") == "0" assert _declared_default("PIPPreviewShowOnBlinker") == "0" diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index 53a38ade2..6a9bdc9dd 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -97,6 +97,7 @@ GITLAB_SUBMISSIONS_PROJECT_ID = "71992109" GITLAB_TOKEN = os.environ.get("GITLAB_TOKEN", "") LEGACY_LATERAL_METHOD_API_PREFIX = "/api/" + "".join(("f", "t", "m")) VASM_CONFIGURATION_KEYS = {"VASMEnabled", "VASMConfidenceThreshold", "VASMSmoothSeconds", "VASMAnnotationConfig"} +PIP_PREVIEW_CONFIGURATION_KEYS = {"PIPPreviewEnabled", "PIPPreviewMask", "PIPPreviewShowOnBlinker", "PIPPreviewShowOnBSM"} MODEL_SMOOTHING_KEYS = {"LatSmoothSeconds", "LongSmoothSeconds"} GALAXY_DEPS_PATH = "/data/galaxy_deps" @@ -4511,6 +4512,12 @@ def setup(app): if key in VASM_CONFIGURATION_KEYS and params.get_bool("IsOnroad"): return jsonify({"error": "Cannot change V-ASM configuration while driving."}), 403 + if key in PIP_PREVIEW_CONFIGURATION_KEYS: + if not params.get_bool("GalaxyDeveloperMode"): + return jsonify({"error": "PiP Side Camera is available only with Galaxy Developer Mode enabled."}), 403 + if params.get_bool("IsOnroad"): + return jsonify({"error": "Cannot change PiP Side Camera configuration while driving."}), 403 + if key in PANDA_FIRMWARE_TOGGLE_KEYS and params.get_bool("IsOnroad"): return jsonify({"error": "Cannot flash Panda firmware while driving."}), 403 if key in PANDA_FIRMWARE_TOGGLE_KEYS and data.get(PANDA_FIRMWARE_CONFIRMATION_FIELD) is not True: @@ -7716,6 +7723,10 @@ def setup(app): @app.route("/api/pip_preview/snapshot", methods=["GET"]) def pip_preview_snapshot(): + if not params.get_bool("GalaxyDeveloperMode"): + return jsonify({"error": "PiP Side Camera is available only with Galaxy Developer Mode enabled."}), 403 + if params.get_bool("IsOnroad"): + return jsonify({"error": "Camera snapshots are unavailable while driving."}), 403 jpeg = _get_live_driver_jpeg() if jpeg is not None: return Response(jpeg, mimetype="image/jpeg") @@ -7723,10 +7734,16 @@ def setup(app): @app.route("/api/pip_preview/config", methods=["GET"]) def pip_preview_get_config(): + if not params.get_bool("GalaxyDeveloperMode"): + return jsonify({"error": "PiP Side Camera is available only with Galaxy Developer Mode enabled."}), 403 return jsonify(_decode_json_object(params.get("PIPPreviewMask"))) @app.route("/api/pip_preview/config", methods=["POST"]) def pip_preview_save_config(): + if not params.get_bool("GalaxyDeveloperMode"): + return jsonify({"error": "PiP Side Camera is available only with Galaxy Developer Mode enabled."}), 403 + if params.get_bool("IsOnroad"): + return jsonify({"error": "Cannot change PiP Side Camera configuration while driving."}), 403 try: config = _normalize_pip_preview_config(request.get_json(silent=True)) except ValueError as exc: @@ -7738,6 +7755,10 @@ def setup(app): @app.route("/api/pip_preview/config", methods=["DELETE"]) def pip_preview_delete_config(): + if not params.get_bool("GalaxyDeveloperMode"): + return jsonify({"error": "PiP Side Camera is available only with Galaxy Developer Mode enabled."}), 403 + if params.get_bool("IsOnroad"): + return jsonify({"error": "Cannot change PiP Side Camera configuration while driving."}), 403 params.put("PIPPreviewMask", {}) params.put_bool("PIPPreviewEnabled", False) update_starpilot_toggles()