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