From 38c98cf9f77fbd2c8cddc2187039dbd67b06cfdd Mon Sep 17 00:00:00 2001 From: AngusBell97 <124716116+AngusBell97@users.noreply.github.com> Date: Thu, 10 Sep 2026 21:17:33 +0100 Subject: [PATCH] Include supported settings in Galaxy diagnostic reports (cherry picked from commit 0466650e4ada665f3bb64236c39d59754fe830ed) --- .../mobile/js/components/TroubleshootPanel.js | 11 +++-- .../tests/test_diagnostic_registry.py | 35 ++++++++++++++ starpilot/system/the_galaxy/the_galaxy.py | 48 +++++++++++++++++-- 3 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 starpilot/system/the_galaxy/tests/test_diagnostic_registry.py diff --git a/starpilot/system/the_galaxy/assets/mobile/js/components/TroubleshootPanel.js b/starpilot/system/the_galaxy/assets/mobile/js/components/TroubleshootPanel.js index 2a70119ef2..8b69ccf5c2 100644 --- a/starpilot/system/the_galaxy/assets/mobile/js/components/TroubleshootPanel.js +++ b/starpilot/system/the_galaxy/assets/mobile/js/components/TroubleshootPanel.js @@ -6,6 +6,7 @@ function formatValue(value) { if (typeof value === "boolean") return value ? "On" : "Off" if (typeof value === "number") return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(4))) if (value === null || value === undefined) return "n/a" + if (typeof value === "object") return JSON.stringify(value) const text = String(value).trim() return text || "(empty)" } @@ -18,6 +19,7 @@ function formatLearnedValue(value) { function valuesMatch(left, right) { if (left === right) return true + if (left && right && typeof left === "object" && typeof right === "object") return JSON.stringify(left) === JSON.stringify(right) if ((left === null || left === undefined) && (right === null || right === undefined)) return true if (typeof left === "number" && typeof right === "number") return Math.abs(left - right) < 1e-9 const lt = String(left ?? "").trim() @@ -39,6 +41,7 @@ export const TroubleshootPanel = { busySection: "", error: "", onlyNonDefault: false, + search: "", vehicleStatus: { available: false, summary: "", summarySeverity: "neutral", items: [] }, snapshot: [], sections: [], @@ -50,8 +53,7 @@ export const TroubleshootPanel = { return this.sections.reduce((count, s) => count + (Array.isArray(s.items) ? s.items.filter((i) => !valuesMatch(i?.value, i?.defaultValue)).length : 0), 0) }, visibleSections() { - if (!this.onlyNonDefault) return this.sections - return this.sections.filter((s) => (Array.isArray(s.items) ? s.items.filter((i) => !valuesMatch(i?.value, i?.defaultValue)).length : 0) > 0) + return this.sections.filter((section) => this.itemsVisible(section).length > 0) }, }, mounted() { this.load() }, @@ -61,7 +63,9 @@ export const TroubleshootPanel = { isChanged(item) { return !valuesMatch(item?.value, item?.defaultValue) }, itemsVisible(section) { const items = Array.isArray(section?.items) ? section.items : [] - return this.onlyNonDefault ? items.filter((i) => !valuesMatch(i?.value, i?.defaultValue)) : items + const query = this.search.trim().toLowerCase() + return items.filter((i) => (!this.onlyNonDefault || !valuesMatch(i?.value, i?.defaultValue)) && + (!query || `${section.title} ${i.key} ${i.label} ${formatValue(i.value)}`.toLowerCase().includes(query))) }, severity(sev) { const s = String(sev || "neutral").toLowerCase() @@ -167,6 +171,7 @@ export const TroubleshootPanel = { +
Onroad: {{ isOnroad ? 'Yes' : 'No' }}
Changed Settings: {{ countNonDefault }}
diff --git a/starpilot/system/the_galaxy/tests/test_diagnostic_registry.py b/starpilot/system/the_galaxy/tests/test_diagnostic_registry.py new file mode 100644 index 0000000000..00e47a2740 --- /dev/null +++ b/starpilot/system/the_galaxy/tests/test_diagnostic_registry.py @@ -0,0 +1,35 @@ +from types import SimpleNamespace +from test_dashboard_stats import _load_server_module, FakeParams + + +def test_registry_sections_are_loggable_supported_and_read_only(monkeypatch): + s = _load_server_module() + monkeypatch.setattr(s, "params", FakeParams({"IsOnroad": False})) + monkeypatch.setattr(s, "_TROUBLESHOOT_SECTION_DEFINITIONS", []) + monkeypatch.setattr(s, "_get_param_type_info", lambda: (set(), {})) + monkeypatch.setattr(s, "_get_default_param_values", lambda: {}) + monkeypatch.setattr(s, "_get_troubleshoot_learned_values", lambda: {}) + monkeypatch.setattr(s, "_get_layout_param_metadata", lambda: {"Parent": {"label": "Parent menu"}}) + monkeypatch.setattr(s, "_get_hardware_snapshot_items", lambda: []) + monkeypatch.setattr(s, "_build_vehicle_fault_status", lambda: {"summary": "preserved"}) + monkeypatch.setattr(s, "_get_safety_snapshot_text", lambda: "stock safety") + monkeypatch.setattr(s, "_get_fingerprint_snapshot_text", lambda: "fixture") + monkeypatch.setattr(s.utilities, "get_current_lan_ip", lambda: "127.0.0.1") + monkeypatch.setattr(s, "_safe_params_get", lambda *args, **kwargs: "") + keys = ["NewSetting", "Secret", "RivianAngle", "TeslaWakeOnCAN", "ControllerActionSlots"] + monkeypatch.setattr(s, "starpilot_default_params", [(key, None, None, None) for key in keys]) + monkeypatch.setattr(s, "_params_raw", SimpleNamespace(get_key_flag=lambda key: s.ParamKeyFlag.DONT_LOG if key == "Secret" else 0)) + monkeypatch.setattr(s, "_get_has_rivian_angle_harness", lambda: False) + monkeypatch.setattr(s, "supports_tesla_can_wake", lambda params: False) + monkeypatch.setattr(s, "load_settings_catalog", lambda: [{"name": "Category", "params": [ + {"key": "NewSetting", "parent_key": "Parent"}, {"key": "NewSetting"}, + {"key": "Secret"}, {"key": "Missing"}, {"key": "TeslaWakeOnCAN"}, + {"key": "RivianAngle", "requires_capability": "HasRivianAngleHarness"}]}]) + monkeypatch.setattr(s, "_build_troubleshoot_section_payload", lambda d, *args: {**d, "items": [{"key": k} for k in d["keys"]], "resettable": True}) + result = s._build_troubleshoot_payload() + assert result["vehicleStatus"]["summary"] == "preserved" + assert result["snapshot"][0]["value"] == "stock safety" + sections = result["sections"] + assert [k for section in sections for k in section["keys"]] == ["ControllerActionSlots", "NewSetting"] + assert sections[1]["title"] == "Category › Parent menu" + assert all(section["resettable"] is False for section in sections) diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index b6a74a1eda..02f5d1e010 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -1957,22 +1957,22 @@ _RUNTIME_DEFAULT_ZERO_OK_KEYS = { _TROUBLESHOOT_SECTION_DEFINITIONS = [ { "id": "personality_settings", - "title": "Personality Profile Settings", + "title": "Longitudinal (Speed & Following) › Driving Personalities", "keys": _TROUBLESHOOT_PERSONALITY_KEYS, }, { "id": "cem_settings", - "title": "CEM Settings", + "title": "Longitudinal (Speed & Following) › Longitudinal control mode", "keys": _TROUBLESHOOT_CEM_KEYS, }, { "id": "advanced_lateral_tuning", - "title": "Advanced Lateral Tuning", + "title": "Lateral (Steering) › Advanced Lateral Tuning", "keys": _TROUBLESHOOT_ADVANCED_LATERAL_KEYS, }, { "id": "advanced_longitudinal_tuning", - "title": "Advanced Longitudinal Tuning", + "title": "Longitudinal (Speed & Following) › Advanced Longitudinal Tuning", "keys": _TROUBLESHOOT_ADVANCED_LONGITUDINAL_KEYS, }, ] @@ -4514,6 +4514,46 @@ def _build_troubleshoot_payload(): for section_definition in _TROUBLESHOOT_SECTION_DEFINITIONS ] + # Use the same category and parent metadata as Settings, rather than a second short list. + shown = {item['key'] for section in sections for item in section['items']} + registered = {key for key, *_ in starpilot_default_params} + for category in load_settings_catalog() or []: + groups = {} + for entry in category.get('params', []): + key = entry.get('key') + if key in shown or key not in registered or key.startswith('LaneCentering') or key == 'LaneCenterOffset': + continue + if (entry.get("requires_capability") == "HasRivianAngleHarness" and not _get_has_rivian_angle_harness()): + continue + if key == "TeslaWakeOnCAN" and not supports_tesla_can_wake(params): + continue + if _params_raw.get_key_flag(key) & ParamKeyFlag.DONT_LOG: + continue + parent = entry.get('parent_key') + title = category['name'] + if parent: + title += ' › ' + str(layout_metadata.get(parent, {}).get('label', parent)) + groups.setdefault(title, []).append(key) + shown.add(key) + for title, keys in groups.items(): + section = _build_troubleshoot_section_payload({'id': 'catalog_' + keys[0], 'title': title, 'keys': keys}, + value_types, default_values, layout_metadata, learned_values) + section['resettable'] = False + sections.append(section) + for title, keys in [ + ('Bluetooth Controllers', ['BluetoothEnabled', 'BluetoothDisconnectControllersOffroad', 'WheelControlsEnabled', 'ControllerActionSlots', 'WheelControlMappings']), + ('Longitudinal (Speed & Following) › Longitudinal control mode', ['ExperimentalMode', 'ConditionalExperimental', 'ConditionalChill', 'LongitudinalPersonality']), + ('Model Manager', ['Model', 'ActiveBigModel', 'ActiveSmallModel', 'ModelSortMode', 'UserFavorites'])]: + keys = [key for key in keys if key in registered and key not in shown + and not (_params_raw.get_key_flag(key) & ParamKeyFlag.DONT_LOG)] + if keys: + section = _build_troubleshoot_section_payload({'id': 'extra_' + keys[0], 'title': title, 'keys': keys}, + value_types, default_values, layout_metadata, learned_values) + section['resettable'] = False + sections.append(section) + shown.update(keys) + sections.sort(key=lambda section: section["title"]) + return _sanitize_json_value({ "vehicleStatus": _build_vehicle_fault_status(), "snapshot": snapshot_items,