From 5234a121f93f5f3fb707b8e7d4dbdc66cf5d6119 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:32:29 -0500 Subject: [PATCH] Add HKG EV App Start Climate wake Add a StarPilot vehicle toggle that selects alternate Panda firmware for Hyundai/Kia/Genesis CAN-FD EVs. The firmware keeps the HKG CAN bus active while Panda is in power save and treats the app climate-active frame, bus 1 address 0x384 with byte 3 nonzero, as a Panda boot wake source without publishing it as CAN ignition. Wire the toggle through StarPilot vehicle settings, Galaxy device settings, parameter definitions, and pandad firmware selection. Disabling the toggle selects the default Panda firmware again. Tested on a Kia EV9 and 2023 Kia EV6. EV9 remote climate active used 0x384 byte 3 equal to 0x01; EV6 remote climate active used 0x0a. Both stopped/off states observed byte 3 equal to 0x00. Toggle-off negative testing on EV9 saw the remote climate frame on CAN but pandaStates.ignitionCan stayed false. Toggle-on testing verified the active Panda signature matched panda_h7_hkg_remote.bin.signed. Follow-up testing changed the HKG path to wake-only after remote climate caused partial-car fingerprinting and Dashcam Mode; wake-only firmware was built, flashed, and verified on both devices. --- common/params_keys.h | 1 + panda/SConscript | 4 ++ panda/board/drivers/can_common.h | 15 ++++++- panda/board/main.c | 11 ++++++ panda/board/power_saving.h | 9 ++++- selfdrive/pandad/pandad.py | 34 ++++++++++------ .../ui/layouts/settings/starpilot/vehicle.py | 5 +++ starpilot/common/starpilot_utilities.py | 14 +++++-- starpilot/common/starpilot_variables.py | 4 ++ .../components/tools/device_settings.js | 39 +++++++++++++++++-- .../tools/device_settings_layout.json | 7 ++++ starpilot/system/the_galaxy/the_galaxy.py | 19 +++++++++ starpilot/ui/qt/offroad/vehicle_settings.cc | 20 ++++++++++ starpilot/ui/qt/offroad/vehicle_settings.h | 1 + tools/StarPilot/feasibleparams.txt | 1 + 15 files changed, 161 insertions(+), 23 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 12323527a..1bd4be9b6 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -289,6 +289,7 @@ inline static std::unordered_map keys = { {"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}}, {"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}}, {"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}}, + {"HKGRemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}}, {"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0"}}, {"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}}, {"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}}, diff --git a/panda/SConscript b/panda/SConscript index a607df245..9766e7f5c 100644 --- a/panda/SConscript +++ b/panda/SConscript @@ -169,10 +169,14 @@ build_project("panda", base_project_f4, "./board/main.c", []) build_project("panda_h7", base_project_h7, "./board/main.c", []) build_project("panda_remote", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"]) build_project("panda_h7_remote", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"]) +build_project("panda_hkg_remote", base_project_f4, "./board/main.c", ["-DPANDA_HKG_REMOTE_START"]) +build_project("panda_h7_hkg_remote", base_project_h7, "./board/main.c", ["-DPANDA_HKG_REMOTE_START"]) build_project("panda_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"]) build_project("panda_h7_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"]) build_project("panda_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"]) build_project("panda_h7_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"]) +build_project("panda_hkg_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_HKG_REMOTE_START", "-DPANDA_IGNORE_IGNITION_LINE"]) +build_project("panda_h7_hkg_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_HKG_REMOTE_START", "-DPANDA_IGNORE_IGNITION_LINE"]) # panda jungle fw flags = [ diff --git a/panda/board/drivers/can_common.h b/panda/board/drivers/can_common.h index 7ec89f7ba..92c0090a2 100644 --- a/panda/board/drivers/can_common.h +++ b/panda/board/drivers/can_common.h @@ -10,6 +10,10 @@ can_health_t can_health[PANDA_CAN_CNT] = {{0}, {0}, {0}}; // Ignition detected from CAN meessages bool ignition_can = false; uint32_t ignition_can_cnt = 0U; +#ifdef PANDA_HKG_REMOTE_START +bool hkg_remote_climate_wake = false; +uint32_t hkg_remote_climate_wake_cnt = 0U; +#endif bool can_silent = true; bool can_loopback = false; @@ -161,9 +165,16 @@ void can_set_forwarding(uint8_t from, uint8_t to) { #endif void ignition_can_hook(CANPacket_t *msg) { - if (msg->bus == 0U) { - int len = GET_LEN(msg); + int len = GET_LEN(msg); + #ifdef PANDA_HKG_REMOTE_START + if ((msg->bus == 1U) && (msg->addr == 0x384U) && (len == 8)) { + hkg_remote_climate_wake = msg->data[3] != 0U; + hkg_remote_climate_wake_cnt = 0U; + } + #endif + + if (msg->bus == 0U) { // GM exception // Remote-start mode uses 0xC9 bit 4 (SystemPowerMode=Run) for ignition detection. // Stock mode uses 0x1F1 bit 1 (SystemPowerMode=Run/Crank Request). diff --git a/panda/board/main.c b/panda/board/main.c index e092d6e38..284a4e392 100644 --- a/panda/board/main.c +++ b/panda/board/main.c @@ -189,6 +189,9 @@ static void tick_handler(void) { // tick drivers at 1Hz bool started = panda_ignition_line() || ignition_can; + #ifdef PANDA_HKG_REMOTE_START + started = started || hkg_remote_climate_wake; + #endif bootkick_tick(started, recent_heartbeat); // increase heartbeat counter and cap it at the uint32 limit @@ -267,11 +270,19 @@ static void tick_handler(void) { if (ignition_can_cnt > 2U) { ignition_can = false; } + #ifdef PANDA_HKG_REMOTE_START + if (hkg_remote_climate_wake_cnt > 2U) { + hkg_remote_climate_wake = false; + } + #endif // on to the next one uptime_cnt += 1U; safety_mode_cnt += 1U; ignition_can_cnt += 1U; + #ifdef PANDA_HKG_REMOTE_START + hkg_remote_climate_wake_cnt += 1U; + #endif // synchronous safety check safety_tick(¤t_safety_config); diff --git a/panda/board/power_saving.h b/panda/board/power_saving.h index 1e95c9513..91906a68e 100644 --- a/panda/board/power_saving.h +++ b/panda/board/power_saving.h @@ -9,7 +9,12 @@ void enable_can_transceivers(bool enabled) { // Leave main CAN always on for CAN-based ignition detection uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U; for(uint8_t i=1U; i<=4U; i++){ - current_board->enable_can_transceiver(i, (i == main_bus) || enabled); + bool transceiver_enabled = (i == main_bus) || enabled; + #ifdef PANDA_HKG_REMOTE_START + uint8_t hkg_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 4U : 2U; + transceiver_enabled = transceiver_enabled || (i == hkg_bus); + #endif + current_board->enable_can_transceiver(i, transceiver_enabled); } } @@ -26,7 +31,9 @@ void set_power_save_state(int state) { } else { llcan_irq_disable(cans[2]); } + #ifndef PANDA_HKG_REMOTE_START llcan_irq_disable(cans[1]); + #endif } else { print("disable power savings\n"); diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index 79d1fa95c..f08079fe0 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -13,22 +13,24 @@ from openpilot.system.hardware import HARDWARE from openpilot.common.swaglog import cloudlog -def get_selected_firmware_name(app_fn: str, remote_start: bool, ignore_ignition_line: bool) -> str: - if not remote_start and not ignore_ignition_line: +def get_selected_firmware_name(app_fn: str, remote_start: bool, hkg_remote_start: bool, ignore_ignition_line: bool) -> str: + if not remote_start and not hkg_remote_start and not ignore_ignition_line: return app_fn h7 = app_fn == "panda_h7.bin.signed" name_parts = ["panda_h7" if h7 else "panda"] - if remote_start: + if hkg_remote_start: + name_parts.extend(["hkg", "remote"]) + elif remote_start: name_parts.append("remote") if ignore_ignition_line: name_parts.append("can_ignition_only") return "_".join(name_parts) + ".bin.signed" -def get_expected_firmware_path(panda: Panda, remote_start: bool, ignore_ignition_line: bool) -> str: +def get_expected_firmware_path(panda: Panda, remote_start: bool, hkg_remote_start: bool, ignore_ignition_line: bool) -> str: app_fn = panda.get_mcu_type().config.app_fn - selected_fn = get_selected_firmware_name(app_fn, remote_start, ignore_ignition_line) + selected_fn = get_selected_firmware_name(app_fn, remote_start, hkg_remote_start, ignore_ignition_line) if selected_fn != app_fn: selected_path = os.path.join(FW_PATH, selected_fn) if os.path.isfile(selected_path): @@ -37,9 +39,9 @@ def get_expected_firmware_path(panda: Panda, remote_start: bool, ignore_ignition return os.path.join(FW_PATH, app_fn) -def get_expected_signature(panda: Panda, remote_start: bool, ignore_ignition_line: bool) -> bytes: +def get_expected_signature(panda: Panda, remote_start: bool, hkg_remote_start: bool, ignore_ignition_line: bool) -> bytes: try: - fn = get_expected_firmware_path(panda, remote_start, ignore_ignition_line) + fn = get_expected_firmware_path(panda, remote_start, hkg_remote_start, ignore_ignition_line) return Panda.get_signature_from_firmware(fn) except Exception: cloudlog.exception("Error computing expected signature") @@ -53,6 +55,13 @@ def get_remote_start_boots_comma(params: Params) -> bool: return False +def get_hkg_remote_start_boots_comma(params: Params) -> bool: + try: + return params.get_bool("HKGRemoteStartBootsComma") + except UnknownKeyName: + return False + + def get_ignore_ignition_line(params: Params) -> bool: try: return params.get_bool("IgnoreIgnitionLine") @@ -60,7 +69,7 @@ def get_ignore_ignition_line(params: Params) -> bool: return False -def flash_panda(panda_serial: str, remote_start: bool, ignore_ignition_line: bool) -> Panda: +def flash_panda(panda_serial: str, remote_start: bool, hkg_remote_start: bool, ignore_ignition_line: bool) -> Panda: try: panda = Panda(panda_serial) except PandaProtocolMismatch: @@ -68,8 +77,8 @@ def flash_panda(panda_serial: str, remote_start: bool, ignore_ignition_line: boo HARDWARE.recover_internal_panda() raise - fw_path = get_expected_firmware_path(panda, remote_start, ignore_ignition_line) - fw_signature = get_expected_signature(panda, remote_start, ignore_ignition_line) + fw_path = get_expected_firmware_path(panda, remote_start, hkg_remote_start, ignore_ignition_line) + fw_signature = get_expected_signature(panda, remote_start, hkg_remote_start, ignore_ignition_line) internal_panda = panda.is_internal() panda_version = "bootstub" if panda.bootstub else panda.get_version() @@ -153,9 +162,10 @@ def main() -> None: # Flash pandas pandas: list[Panda] = [] remote_start = get_remote_start_boots_comma(params) + hkg_remote_start = get_hkg_remote_start_boots_comma(params) ignore_ignition_line = get_ignore_ignition_line(params) for serial in panda_serials: - pandas.append(flash_panda(serial, remote_start, ignore_ignition_line)) + pandas.append(flash_panda(serial, remote_start, hkg_remote_start, ignore_ignition_line)) # Ensure internal panda is present if expected internal_pandas = [panda for panda in pandas if panda.is_internal()] @@ -207,7 +217,7 @@ def main() -> None: first_run = False # run pandad with all connected serials as arguments - if get_remote_start_boots_comma(params) or get_ignore_ignition_line(params): + if get_remote_start_boots_comma(params) or get_hkg_remote_start_boots_comma(params) or get_ignore_ignition_line(params): os.environ["BOARDD_SKIP_FW_CHECK"] = "1" else: os.environ.pop("BOARDD_SKIP_FW_CHECK", None) diff --git a/selfdrive/ui/layouts/settings/starpilot/vehicle.py b/selfdrive/ui/layouts/settings/starpilot/vehicle.py index ddbeb8488..09a7e41e6 100644 --- a/selfdrive/ui/layouts/settings/starpilot/vehicle.py +++ b/selfdrive/ui/layouts/settings/starpilot/vehicle.py @@ -209,6 +209,11 @@ class VehicleSettingsManagerView(PanelManagerView): }) if cs.isHKGCanFd and cs.hasOpenpilotLongitudinal: + toggles.append({ + "title": tr("EV Remote Climate"), + "get_state": lambda: self._controller._params.get_bool("HKGRemoteStartBootsComma"), + "set_state": lambda s: self._controller._on_panda_firmware_toggle("HKGRemoteStartBootsComma", tr("EV Remote Climate requires a Panda firmware update.")), + }) toggles.append({ "title": tr("Nostalgia Mode"), "subtitle": tr("Use the left paddle to pause openpilot acceleration and braking."), diff --git a/starpilot/common/starpilot_utilities.py b/starpilot/common/starpilot_utilities.py index f32e95898..a80f25ac4 100644 --- a/starpilot/common/starpilot_utilities.py +++ b/starpilot/common/starpilot_utilities.py @@ -173,13 +173,15 @@ def extract_zip(zip_file, extract_path): print(f"Extraction completed!") -def get_selected_panda_firmware_name(app_fn, remote_start, ignore_ignition_line): - if not remote_start and not ignore_ignition_line: +def get_selected_panda_firmware_name(app_fn, remote_start, hkg_remote_start, ignore_ignition_line): + if not remote_start and not hkg_remote_start and not ignore_ignition_line: return app_fn h7 = app_fn == "panda_h7.bin.signed" name_parts = ["panda_h7" if h7 else "panda"] - if remote_start: + if hkg_remote_start: + name_parts.extend(["hkg", "remote"]) + elif remote_start: name_parts.append("remote") if ignore_ignition_line: name_parts.append("can_ignition_only") @@ -192,6 +194,10 @@ def flash_panda(params_memory): remote_start = params.get_bool("RemoteStartBootsComma") except Exception: remote_start = False + try: + hkg_remote_start = params.get_bool("HKGRemoteStartBootsComma") + except Exception: + hkg_remote_start = False try: ignore_ignition_line = params.get_bool("IgnoreIgnitionLine") except Exception: @@ -203,7 +209,7 @@ def flash_panda(params_memory): print(f"Flashing Panda {serial}") flash_fn = None app_fn = panda.get_mcu_type().config.app_fn - selected_fn = get_selected_panda_firmware_name(app_fn, remote_start, ignore_ignition_line) + selected_fn = get_selected_panda_firmware_name(app_fn, remote_start, hkg_remote_start, ignore_ignition_line) if selected_fn != app_fn: candidate = os.path.join(FW_PATH, selected_fn) if os.path.isfile(candidate): diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index af2b9fcba..fb91f55dc 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -1392,6 +1392,10 @@ class StarPilotVariables: condition=toggle.car_make == "gm" and toggle.has_pedal, ) toggle.ignore_ignition_line = self.get_value("IgnoreIgnitionLine", condition=toggle.car_make == "gm") + toggle.hkg_remote_start_boots_comma = self.get_value( + "HKGRemoteStartBootsComma", + condition=toggle.car_make == "hyundai" and toggle.openpilot_longitudinal and bool(CP.flags & HyundaiFlags.CANFD), + ) toggle.long_pitch = self.get_value( "LongPitch", condition=toggle.openpilot_longitudinal and toggle.car_make == "gm", diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js index cea58f9b0..e1b575ce1 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js @@ -13,6 +13,7 @@ const FAVORITE_OPTION_COLLATOR = new Intl.Collator(undefined, { numeric: true, s let syncScheduled = false let lastParams = null const DYNAMIC_DEFAULT_DEP_KEYS = new Set(["AccelerationProfile", "EVTuning", "TruckTuning"]) +const PANDA_FIRMWARE_TOGGLE_KEYS = new Set(["HKGRemoteStartBootsComma"]) // Module-level state (persists across route changes) const state = reactive({ @@ -574,7 +575,13 @@ function updateFavoriteFilter(index, event) { scheduleSyncInputs() } -async function updateFavoriteValue(key, checked) { +async function updateFavoriteValue(key, checked, sourceEl = null) { + if (!confirmPandaFirmwareToggle(key, checked)) { + if (sourceEl) sourceEl.checked = !!state.values[key] + scheduleSyncInputs() + return + } + const current = state.values[key] state.values = { ...state.values, [key]: checked } state.favoriteValues = { ...state.favoriteValues, [key]: checked } @@ -583,7 +590,7 @@ async function updateFavoriteValue(key, checked) { const res = await fetch("/api/params", { method: "PUT", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ key, value: checked }), + body: JSON.stringify({ key, value: checked, ...pandaFirmwareConfirmationPayload(key) }), }) const data = await res.json() @@ -673,6 +680,25 @@ function showParamSnackbar(message, level, timeout = 2200) { }) } +function getParamDisplayLabel(key) { + return state.paramMetaByKey[key]?.label || key +} + +function confirmPandaFirmwareToggle(key, enabled) { + if (!PANDA_FIRMWARE_TOGGLE_KEYS.has(key)) return true + + const label = getParamDisplayLabel(key) + const action = enabled ? "Enable" : "Disable" + return window.confirm( + `${label} requires a Panda firmware update.\n\n` + + `${action} ${label} and flash the Panda now?` + ) +} + +function pandaFirmwareConfirmationPayload(key) { + return PANDA_FIRMWARE_TOGGLE_KEYS.has(key) ? { confirmedPandaFirmwareFlash: true } : {} +} + function syncNumericDisplay(param, rawValue) { const displayEl = document.getElementById(`ds-display-${param.key}`) if (!displayEl) return @@ -831,11 +857,16 @@ async function updateParam(key, elType) { formattedVal = coerceValueByType(el.value, param.data_type) } + if (elType === "checkbox" && !confirmPandaFirmwareToggle(key, formattedVal)) { + revertInput(key, current, elType) + return + } + try { const res = await fetch("/api/params", { method: "PUT", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ key, value: formattedVal, label: selectedLabel }), + body: JSON.stringify({ key, value: formattedVal, label: selectedLabel, ...pandaFirmwareConfirmationPayload(key) }), }) const data = await res.json() @@ -1025,7 +1056,7 @@ function renderFavoriteSlotsPanel() { class="ds-toggle ds-favorite-quick-toggle" data-favorite-value-key="${selectedKey}" checked="${() => selectedValue}" - @change="${(e) => updateFavoriteValue(selectedKey, !!e.currentTarget.checked)}" /> + @change="${(e) => updateFavoriteValue(selectedKey, !!e.currentTarget.checked, e.currentTarget)}" /> ` })} 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 36b8e2f83..be9a3ce0d 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 @@ -2520,6 +2520,13 @@ "data_type": "bool", "ui_type": "toggle" }, + { + "key": "HKGRemoteStartBootsComma", + "label": "EV Remote Climate", + "description": "Use the remote-climate Hyundai/Kia/Genesis CAN-FD panda firmware at boot.\n\nRequired for EV remote-climate startup signal behavior.", + "data_type": "bool", + "ui_type": "toggle" + }, { "key": "VoltSNG", "label": "Stop-and-Go Hack", diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index e6442ca07..ec8500b1d 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -113,6 +113,17 @@ _TESTING_GROUND_CUSTOM_RESERVED_INTERVAL_S = 15.0 _TESTING_GROUND_CUSTOM_RESERVED_PM = None _TESTING_GROUND_CUSTOM_RESERVED_LOCK = threading.Lock() _TESTING_GROUND_CUSTOM_RESERVED_LAST_PUBLISH_MONO = 0.0 +PANDA_FIRMWARE_TOGGLE_KEYS = {"HKGRemoteStartBootsComma"} +PANDA_FIRMWARE_CONFIRMATION_FIELD = "confirmedPandaFirmwareFlash" +_PANDA_FLASH_REBOOT_LOCK = threading.Lock() + + +def _flash_panda_then_reboot() -> None: + with _PANDA_FLASH_REBOOT_LOCK: + params_memory.put_bool("FlashPanda", True) + while params_memory.get_bool("FlashPanda"): + time.sleep(0.1) + HARDWARE.reboot() def _is_comma_device_runtime() -> bool: @@ -3960,6 +3971,11 @@ def setup(app): if key == "AutomaticUpdates" and params.get_bool("IsOnroad"): return jsonify({"error": "Cannot change Automatic Updates 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: + return jsonify({"error": "Panda firmware changes require confirmation before flashing."}), 409 + if key == "AllowImpossibleAcceleration": enabled = str_val.strip() in ("1", "true", "True") params.put_bool(key, enabled) @@ -4186,6 +4202,9 @@ def setup(app): response = {"message": f"Parameter '{key}' updated successfully."} updated = {} + if key in PANDA_FIRMWARE_TOGGLE_KEYS: + threading.Thread(target=_flash_panda_then_reboot, daemon=True).start() + response["message"] = f"Parameter '{key}' updated successfully. Panda flashing started; device will reboot when finished." if key == "RemapCancelToDistance" and params.get_bool("RemapCancelToDistance"): updated["RemapCancelToDistance"] = True response["message"] = "Remap Cancel Button enabled." diff --git a/starpilot/ui/qt/offroad/vehicle_settings.cc b/starpilot/ui/qt/offroad/vehicle_settings.cc index 51e57590f..676ec4b99 100644 --- a/starpilot/ui/qt/offroad/vehicle_settings.cc +++ b/starpilot/ui/qt/offroad/vehicle_settings.cc @@ -155,16 +155,19 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent, settingsList->addItem(disableOpenpilotLong); StarPilotListWidget *gmList = new StarPilotListWidget(this); + StarPilotListWidget *hkgList = new StarPilotListWidget(this); StarPilotListWidget *subaruList = new StarPilotListWidget(this); StarPilotListWidget *toyotaList = new StarPilotListWidget(this); StarPilotListWidget *vehicleInfoList = new StarPilotListWidget(this); ScrollView *gmPanel = new ScrollView(gmList, this); + ScrollView *hkgPanel = new ScrollView(hkgList, this); ScrollView *subaruPanel = new ScrollView(subaruList, this); ScrollView *toyotaPanel = new ScrollView(toyotaList, this); ScrollView *vehicleInfoPanel = new ScrollView(vehicleInfoList, this); vehiclesLayout->addWidget(gmPanel); + vehiclesLayout->addWidget(hkgPanel); vehiclesLayout->addWidget(subaruPanel); vehiclesLayout->addWidget(toyotaPanel); vehiclesLayout->addWidget(vehicleInfoPanel); @@ -179,6 +182,8 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent, {"RemapCancelToDistance", tr("Remap Cancel Button"), tr("On pedal-interceptor Bolts, treat the steering-wheel CANCEL button as an extra mappable button."), ""}, {"VoltSNG", tr("Stop-and-Go Hack"), tr("Force stop-and-go on the 2017 Chevy Volt."), ""}, + {"HKGToggles", tr("Hyundai/Kia/Genesis Settings"), tr("StarPilot features for Hyundai/Kia/Genesis vehicles."), ""}, + {"HKGRemoteStartBootsComma", tr("EV Remote Climate"), tr("Use the remote-climate Hyundai/Kia/Genesis CAN-FD panda firmware at boot.

Required for EV remote-climate startup signal behavior."), ""}, {"SubaruToggles", tr("Subaru Settings"), tr("StarPilot features for Subaru vehicles."), ""}, {"SubaruSNG", tr("Stop and Go"), tr("Stop and go for supported Subaru vehicles."), ""}, @@ -212,6 +217,14 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent, }); vehicleToggle = gmButton; + } else if (param == "HKGToggles") { + ButtonControl *hkgButton = new ButtonControl(title, tr("MANAGE"), desc); + QObject::connect(hkgButton, &ButtonControl::clicked, [vehiclesLayout, hkgPanel, this]() { + openDescriptions(forceOpenDescriptions, toggles); + vehiclesLayout->setCurrentWidget(hkgPanel); + }); + vehicleToggle = hkgButton; + } else if (param == "SubaruToggles") { ButtonControl *subaruButton = new ButtonControl(title, tr("MANAGE"), desc); QObject::connect(subaruButton, &ButtonControl::clicked, [vehiclesLayout, subaruPanel, this]() { @@ -264,6 +277,8 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent, if (gmKeys.contains(param)) { gmList->addItem(vehicleToggle); + } else if (hkgKeys.contains(param)) { + hkgList->addItem(vehicleToggle); } else if (subaruKeys.contains(param)) { subaruList->addItem(vehicleToggle); } else if (toyotaKeys.contains(param)) { @@ -322,6 +337,7 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent, }; connectPandaFlashToggle("IgnoreIgnitionLine", tr("CAN Ignition Only requires a Panda firmware update. Flash the Panda now?")); + connectPandaFlashToggle("HKGRemoteStartBootsComma", tr("EV Remote Climate requires a Panda firmware update. Flash the Panda now?")); connectPandaFlashToggle("RemoteStartBootsComma", tr("Remote Start requires a Panda firmware update. Flash the Panda now?")); openDescriptions(forceOpenDescriptions, toggles); @@ -393,6 +409,8 @@ void StarPilotVehiclesPanel::updateToggles() { if (!showAllToggles) { if (gmKeys.contains(key)) { setVisible &= parent->isGM; + } else if (hkgKeys.contains(key)) { + setVisible &= parent->isHKGCanFd && parent->hasOpenpilotLongitudinal; } else if (subaruKeys.contains(key)) { setVisible &= parent->isSubaru; } else if (toyotaKeys.contains(key)) { @@ -439,6 +457,8 @@ void StarPilotVehiclesPanel::updateToggles() { if (setVisible) { if (gmKeys.contains(key)) { toggles["GMToggles"]->setVisible(true); + } else if (hkgKeys.contains(key)) { + toggles["HKGToggles"]->setVisible(true); } else if (subaruKeys.contains(key)) { toggles["SubaruToggles"]->setVisible(true); } else if (toyotaKeys.contains(key)) { diff --git a/starpilot/ui/qt/offroad/vehicle_settings.h b/starpilot/ui/qt/offroad/vehicle_settings.h index ef240e9b2..2b4ead133 100644 --- a/starpilot/ui/qt/offroad/vehicle_settings.h +++ b/starpilot/ui/qt/offroad/vehicle_settings.h @@ -24,6 +24,7 @@ private: std::map toggles; QSet gmKeys = {"GMPedalLongitudinal", "GMDashSpoofOffsets", "IgnoreIgnitionLine", "LongPitch", "RemoteStartBootsComma", "RemapCancelToDistance", "VoltSNG"}; + QSet hkgKeys = {"HKGRemoteStartBootsComma"}; QSet longitudinalKeys = {"FrogsGoMoosTweak", "GMDashSpoofOffsets", "LongPitch", "RemapCancelToDistance", "SNGHack", "VoltSNG"}; QSet subaruKeys = {"SubaruSNG", "SubaruSNGManualParkingBrake"}; QSet toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"}; diff --git a/tools/StarPilot/feasibleparams.txt b/tools/StarPilot/feasibleparams.txt index 7b72c9510..78999ef66 100644 --- a/tools/StarPilot/feasibleparams.txt +++ b/tools/StarPilot/feasibleparams.txt @@ -114,6 +114,7 @@ ForceTorqueController FrogsGoMoosTweak GMDashSpoofOffsets GMPedalLongitudinal +HKGRemoteStartBootsComma IgnoreIgnitionLine GoatScream GoatScreamCriticalAlerts