diff --git a/common/params_keys.h b/common/params_keys.h index 145fa7f041..b384c9b41d 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -206,8 +206,6 @@ inline static std::unordered_map keys = { {"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}}, {"OnroadCyclePendingRemote", {CLEAR_ON_MANAGER_START, BOOL}}, {"ParamsVersion", {PERSISTENT, INT}}, - {"SettingsCapabilities", {PERSISTENT, STRING}}, - {"SettingsSchema", {PERSISTENT, STRING}}, {"SunnylinkCache_Roles", {PERSISTENT, STRING}}, {"SunnylinkCache_Users", {PERSISTENT, STRING}}, {"SunnylinkDongleId", {PERSISTENT, STRING}}, diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index ee82289137..437931db02 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -29,7 +29,7 @@ from openpilot.sunnypilot import get_sanitize_int_param from openpilot.sunnypilot.selfdrive.car.car_specific import CarSpecificEventsSP from openpilot.sunnypilot.selfdrive.car.cruise_helpers import CruiseHelper from openpilot.sunnypilot.selfdrive.car.intelligent_cruise_button_management.controller import IntelligentCruiseButtonManagement -from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP, EventNameSP +from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP REPLAY = "REPLAY" in os.environ SIMULATION = "SIMULATION" in os.environ diff --git a/sunnypilot/sunnylink/tools/update_settings_ui.py b/sunnypilot/sunnylink/tools/update_settings_ui.py deleted file mode 100644 index a8274ed683..0000000000 --- a/sunnypilot/sunnylink/tools/update_settings_ui.py +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env python3 -""" -Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - -This file is part of sunnypilot and is licensed under the MIT License. -See the LICENSE.md file in the root directory for more details. -""" -import json -import os -import re -import sys -from collections import OrderedDict - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -BASEDIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "..")) -SETTINGS_UI_PATH = os.path.join(SCRIPT_DIR, "..", "settings_ui.json") -PARAMS_KEYS_PATH = os.path.join(BASEDIR, "common", "params_keys.h") -TORQUE_VERSIONS_PATH = os.path.join(BASEDIR, "sunnypilot", "selfdrive", "controls", "lib", "latcontrol_torque_versions.json") - -# Hardcoded option generators (matching update_params_metadata.py logic) -ONROAD_BRIGHTNESS_OPTIONS = ( - [ - {"value": 0, "label": "Auto (Default)"}, - {"value": 1, "label": "Auto (Dark)"}, - {"value": 2, "label": "Screen Off"}, - ] - + [{"value": i, "label": f"{(i - 2) * 5} %"} for i in range(3, 23)] -) - -ONROAD_TIMER_VALUES = { - 0: 0, - 1: 3, - 2: 5, - 3: 10, - 4: 15, - 5: 30, - 6: 60, - 7: 180, - 8: 300, - 9: 600, -} - - -def parse_params_keys(path: str) -> list[str]: - """Parse param key names from params_keys.h.""" - pattern = re.compile(r'^\s*\{"(\w+)"') - keys = [] - with open(path) as f: - for line in f: - match = pattern.match(line) - if match: - keys.append(match.group(1)) - return sorted(set(keys)) - - -def collect_ui_keys(data: dict) -> set[str]: - """Walk settings_ui.json and collect all item keys.""" - keys: set[str] = set() - - def walk_items(items: list[dict]) -> None: - for item in items: - if "key" in item: - keys.add(item["key"]) - for sub_item in item.get("sub_items", []): - if "key" in sub_item: - keys.add(sub_item["key"]) - - def walk_sub_panels(sub_panels: list[dict]) -> None: - for sp in sub_panels: - walk_items(sp.get("items", [])) - - for panel in data.get("panels", []): - for section in panel.get("sections", []): - walk_items(section.get("items", [])) - walk_sub_panels(section.get("sub_panels", [])) - - for _brand, brand_data in data.get("vehicle_settings", {}).items(): - walk_items(brand_data.get("items", [])) - - return keys - - -def find_item_by_key(data: dict, target_key: str) -> dict | None: - """Find and return the item dict with matching key (mutable reference).""" - - def search_items(items: list[dict]) -> dict | None: - for item in items: - if item.get("key") == target_key: - return item - for sub_item in item.get("sub_items", []): - if sub_item.get("key") == target_key: - return sub_item - return None - - for panel in data.get("panels", []): - for section in panel.get("sections", []): - result = search_items(section.get("items", [])) - if result is not None: - return result - for sp in section.get("sub_panels", []): - result = search_items(sp.get("items", [])) - if result is not None: - return result - - for _brand, brand_data in data.get("vehicle_settings", {}).items(): - result = search_items(brand_data.get("items", [])) - if result is not None: - return result - - return None - - -def generate_torque_options(path: str) -> list[dict]: - """Generate TorqueControlTune options from torque versions JSON.""" - with open(path) as f: - versions = json.load(f) - options = [{"value": "", "label": "Default"}] - for version_key, version_data in versions.items(): - version_value = float(version_data["version"]) - options.append({"value": version_value, "label": str(version_key)}) - return options - - -def generate_onroad_timer_options() -> list[dict]: - """Generate OnroadScreenOffTimer options.""" - options = [] - for _index, seconds in sorted(ONROAD_TIMER_VALUES.items()): - if seconds == 0: - label = "Always On" - elif seconds < 60: - label = f"{seconds}s" - else: - label = f"{seconds // 60}m" - options.append({"value": seconds, "label": label}) - return options - - -def report_new_params(params_keys: list[str], ui_keys: set[str]) -> None: - """Print params in params_keys.h that are NOT in settings_ui.json.""" - missing = sorted(k for k in params_keys if k not in ui_keys) - if missing: - print(f"\n[INFO] {len(missing)} param(s) in params_keys.h without settings_ui.json entry:") - for key in missing: - print(f" - {key}") - else: - print("\n[INFO] All params_keys.h keys have settings_ui.json entries.") - - -def validate_items(data: dict) -> int: - """Validate completeness of items in settings_ui.json. Returns warning count.""" - warnings = 0 - - def check_item(item: dict, context: str) -> int: - count = 0 - key = item.get("key", "") - widget = item.get("widget", "") - - if "title" not in item: - print(f" [WARN] {context} > {key}: missing 'title' field") - count += 1 - - if item.get("title") == key: - print(f" [WARN] {context} > {key}: title is same as key (default placeholder?)") - count += 1 - - if widget in ("multiple_button", "option") and "options" not in item: - has_range = "min" in item or "max" in item - if not has_range: - print(f" [WARN] {context} > {key}: widget '{widget}' has no 'options' and no min/max") - count += 1 - - return count - - def check_items(items: list[dict], context: str) -> int: - count = 0 - for item in items: - count += check_item(item, context) - for sub_item in item.get("sub_items", []): - count += check_item(sub_item, f"{context} > sub_items") - return count - - for panel in data.get("panels", []): - panel_ctx = f"panels/{panel.get('id', '?')}" - for section in panel.get("sections", []): - section_ctx = f"{panel_ctx}/{section.get('id', '?')}" - warnings += check_items(section.get("items", []), section_ctx) - for sp in section.get("sub_panels", []): - sp_ctx = f"{section_ctx}/{sp.get('id', '?')}" - warnings += check_items(sp.get("items", []), sp_ctx) - - for brand, brand_data in data.get("vehicle_settings", {}).items(): - warnings += check_items(brand_data.get("items", []), f"vehicle_settings/{brand}") - - return warnings - - -def main() -> int: - # Load settings_ui.json preserving key order - with open(SETTINGS_UI_PATH) as f: - data = json.load(f, object_pairs_hook=OrderedDict) - - # 1. Report new params - params_keys = parse_params_keys(PARAMS_KEYS_PATH) - ui_keys = collect_ui_keys(data) - report_new_params(params_keys, ui_keys) - - # 2. Auto-sync option lists - changed = False - - # TorqueControlTune - if os.path.exists(TORQUE_VERSIONS_PATH): - new_options = generate_torque_options(TORQUE_VERSIONS_PATH) - item = find_item_by_key(data, "TorqueControlTune") - if item is not None: - old_options = item.get("options") - if old_options != new_options: - item["options"] = new_options - changed = True - print(f"\n[SYNC] TorqueControlTune: updated options ({len(new_options)} entries)") - else: - print(f"\n[SYNC] TorqueControlTune: options already up to date") - else: - print("\n[WARN] TorqueControlTune item not found in settings_ui.json") - else: - print(f"\n[WARN] Torque versions file not found: {TORQUE_VERSIONS_PATH}") - - # OnroadScreenOffBrightness - item = find_item_by_key(data, "OnroadScreenOffBrightness") - if item is not None: - old_options = item.get("options") - if old_options != ONROAD_BRIGHTNESS_OPTIONS: - item["options"] = ONROAD_BRIGHTNESS_OPTIONS - changed = True - print(f"[SYNC] OnroadScreenOffBrightness: updated options ({len(ONROAD_BRIGHTNESS_OPTIONS)} entries)") - else: - print("[SYNC] OnroadScreenOffBrightness: options already up to date") - - # OnroadScreenOffTimer - timer_options = generate_onroad_timer_options() - item = find_item_by_key(data, "OnroadScreenOffTimer") - if item is not None: - old_options = item.get("options") - if old_options != timer_options: - item["options"] = timer_options - changed = True - print(f"[SYNC] OnroadScreenOffTimer: updated options ({len(timer_options)} entries)") - else: - print("[SYNC] OnroadScreenOffTimer: options already up to date") - - # 3. Validate completeness - print("\n[VALIDATE] Checking item completeness...") - warning_count = validate_items(data) - if warning_count == 0: - print(" No warnings.") - else: - print(f" {warning_count} warning(s) found.") - - # 4. Write back only if auto-sync changed something - if changed: - with open(SETTINGS_UI_PATH, "w") as f: - json.dump(data, f, indent=2) - f.write("\n") - print(f"\n[WRITE] Updated {SETTINGS_UI_PATH}") - else: - print(f"\n[SKIP] No auto-sync changes; {SETTINGS_UI_PATH} unchanged.") - - return 0 - - -if __name__ == "__main__": - sys.exit(main())