IQ.Pilot Release Commit @ e99f304

This commit is contained in:
IQ.Lvbs CI [bot]
2026-10-22 20:12:53 -07:00
parent 90370d565e
commit 9cb97d7e06
457 changed files with 8205 additions and 7379 deletions
+3
View File
@@ -68,6 +68,7 @@ compare_runtime*.html
iqpilot/selfdrive/pandad/pandad
iqpilot/selfdrive/pandad/tests/test_panda_safety
iqpilot/selfdrive/pandad/tests/test_ignition_hold
iqpilot/cereal/services.h
iqpilot/cereal/gen
iqpilot/cereal/messaging/bridge
@@ -180,3 +181,5 @@ iqpilot/tools/iqpilot/big_hud_preview.py
artifacts/monrovia_runtime_packages/
artifacts/iqpilot_*_private/consumer.challenge
/docs/
/dist/
nuitka-crash-report*
+5 -5
View File
@@ -1,4 +1,4 @@
IQ.Lvbs License v1.8.2
IQ.Lvbs License v1.8.3
Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved.
@@ -42,7 +42,8 @@ You may NOT:
5. Sublicense, sell, or transfer rights to the Software.
6. Use the Software and/or its source code to compete with or create a
substantially similar product.
substantially similar product that is not a personal non-commercial fork of IQ.Pilot that complies with
all other subsections of this license.
7. Use the Software in closed source software not licensed by IQ.Lvbs LLC.
@@ -54,9 +55,8 @@ OWNERSHIP
All rights, title, and interest in the Software remain exclusively with the
Copyright Holder. Any modifications, improvements, or derivative works you
create based on the Software are owned by the Copyright Holder. By
contributing modifications, you irrevocably assign all rights to the
Copyright Holder.
create based on the Software are owned by you (licensing of that work is in no manner the liability of IQ Lvbs or its associates).
Any derivative in which IQ.Pilot source code is used and modified shall remain the property of IQ Lvbs with the exception of the modifications you have added.
PROPRIETARY COMPONENTS
@@ -1 +0,0 @@
76a146832ea70711033c567d35af66df13221c2e
@@ -1,48 +0,0 @@
{
"python/_iqclosure/_vendored.json": {
"mode": 420,
"sha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570",
"size": 3
},
"python/iqpilot_private/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/iqpilot_private/konn3kt/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/iqpilot_private/konn3kt/iqlvbs/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "16ce15884bfd765c17c05da0e2474aa1e7ddd8fcf68925b2169e19042235b4b6",
"size": 469760
},
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d5afc804c71ebd0c690035cbec639f5f07112b3a7080615e6430b27a1739984f",
"size": 69608
},
"runtime": {
"entries": {
"iqpilot_private.konn3kt.iqlvbs.alc": {
"path": "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.iqlvbs.vehicle_state": {
"path": "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
}
}
},
"signatures": {
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": "3OWEWh1t7VakRznr0JljvzvODcR9GG1lU9Fx0Bu+J+m7u+U8JdOeoBSjy0DQSKPXpZ4jgi4ieVGXihJME5AsCg==",
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": "XzMSXupOCeoHX/pCV7X1kuDRw02qrCxJuuwEmSC50h9jy4ySWHk9vflvwRW3u+A6hZ9bljsK6wy0oFXZNoXcAg=="
}
}
@@ -1 +0,0 @@
zfDjghpCSIizLkZR7YlPtvZQirhuMbIYJahB/pGnNaffGIWuXGM7NPoTol0XxKdoMYvCDCu7Kj5YcAdfYoFoBQ==
@@ -1 +0,0 @@
[]
@@ -1,8 +1,94 @@
{
"bundle_version": 1792551365,
"python/_iqclosure/_vendored.json": {
"mode": 420,
"sha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570",
"size": 3
"sha256": "ca67a05945042c0f65f1b48482d9f8effa71448326f7d6ef799ec91ead96a8c9",
"size": 454
},
"python/_iqclosure/iqpilot/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/common/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/common/gpio.py": {
"mode": 420,
"sha256": "84edc2cba235b774293cbb1c52c6da74856425ff8ad99198997e23a05e784bb7",
"size": 2166
},
"python/_iqclosure/iqpilot/common/i2c.py": {
"mode": 420,
"sha256": "4af8f9da7504a548d15f18b91d71c1379cb7c05c84be031448764cc49920ce68",
"size": 2449
},
"python/_iqclosure/iqpilot/common/time_helpers.py": {
"mode": 420,
"sha256": "c14cda1f84149d5ffa63a2adec5ca24126f77c1412a78412e5e3e68a6fd12bcf",
"size": 474
},
"python/_iqclosure/iqpilot/common/utils.py": {
"mode": 420,
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
"size": 5892
},
"python/_iqclosure/iqpilot/system/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/system/hardware/__init__.py": {
"mode": 420,
"sha256": "889391bffca493e74bfc108cae2976a53b644caca13ab600061fd29e9622c627",
"size": 695
},
"python/_iqclosure/iqpilot/system/hardware/base.py": {
"mode": 420,
"sha256": "a56293f7c23e503ff62d0d3ed39f1831b5544a65b37378165934361a5ddcd304",
"size": 4966
},
"python/_iqclosure/iqpilot/system/hardware/pc/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/system/hardware/pc/hardware.py": {
"mode": 420,
"sha256": "2520895597c375ae570bd04d5f189e874c89d642f3f07bdbeb20388cb6691433",
"size": 258
},
"python/_iqclosure/iqpilot/system/hardware/tici/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
"mode": 420,
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
"size": 8016
},
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
"mode": 420,
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
"size": 25731
},
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
"mode": 420,
"sha256": "d22858982705531732d5a20a45a73795496e5bbd9e23028c24609a7e58ac7aed",
"size": 890
},
"python/_iqclosure/iqpilot/system/hardware/tici/lpa.py": {
"mode": 420,
"sha256": "2bffb84aef35892c4788e80b6d937bf2eebd15e226b72a82c4f39217667c1ca0",
"size": 53692
},
"python/_iqclosure/iqpilot/system/hardware/tici/pins.py": {
"mode": 420,
"sha256": "b57ba4262e6a6a79f2db15c27356acf3e54f579a7edd13b483b2e16d140aca6d",
"size": 678
},
"python/iqpilot_private/__init__.py": {
"mode": 420,
@@ -20,9 +106,9 @@
"size": 0
},
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "9b48803b46906943d7cb10b7e4b4f38c880f06a5c281f254a89e2254965b025f",
"size": 68080
"mode": 416,
"sha256": "4d988d95e9de50403be4ad3f733fb4a49ee153f604736736f2fa20a44e419748",
"size": 1065272
},
"runtime": {
"entries": {
@@ -33,6 +119,6 @@
}
},
"signatures": {
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": "RwstZkEGexV6fia/R9BRk+6CQ9Q6SGJopSJf04UpMpaw5fRtoPjLOrDFOt9kU8GPmCmW40gaXhJK+rimnBYnBw=="
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": "LZv8eAULL242KYGIwYc2uAfm3U2idwa3HW09LPfpVkPUK7o+sNd7RHNjUINR+tq/7QyQao9Ev8uUZCbRNxWUDA=="
}
}
@@ -1 +1 @@
TV78NXdEYQiabAzLIqS2lsZbp5bO0y4EPGwLRUMyKeEH2BStZiFfpNPvlqSn5HGI+RjmTq+p5rRUDMNofbZGBw==
+0m3OFfYq3omyTvGD2hNN6MrCbY6lxnLEwQal9thza+Baxp6D5Wi38Def/7buznDfmIgJ5QenNBfP42H1NgnAg==
@@ -1 +1,15 @@
[]
[
"iqpilot.common.gpio",
"iqpilot.common.i2c",
"iqpilot.common.time_helpers",
"iqpilot.common.utils",
"iqpilot.system.hardware",
"iqpilot.system.hardware.base",
"iqpilot.system.hardware.pc.hardware",
"iqpilot.system.hardware.tici",
"iqpilot.system.hardware.tici.amplifier",
"iqpilot.system.hardware.tici.hardware",
"iqpilot.system.hardware.tici.iwlist",
"iqpilot.system.hardware.tici.lpa",
"iqpilot.system.hardware.tici.pins"
]
@@ -27,16 +27,6 @@ def gpio_read(pin: int) -> bool | None:
return val
def gpio_export(pin: int) -> None:
if os.path.isdir(f"/sys/class/gpio/gpio{pin}"):
return
try:
with open("/sys/class/gpio/export", 'w') as f:
f.write(str(pin))
except Exception:
print(f"Failed to export gpio {pin}")
@cache
def get_irq_action(irq: int) -> list[str]:
try:
@@ -5,7 +5,6 @@ import contextlib
import subprocess
import time
import functools
from subprocess import Popen, PIPE, TimeoutExpired
import zstandard as zstd
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
@@ -32,24 +31,44 @@ class Timer:
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
_sudo_available: bool | None = None
def sudo_write(val: str, path: str) -> None:
global _sudo_available
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
return
except OSError:
pass
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
if _sudo_available is False:
return
try:
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
if _sudo_available:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
except Exception:
_sudo_available = False
def sudo_read(path: str) -> str:
global _sudo_available
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
with open(path, encoding='utf8') as f:
return f.read().strip()
except OSError:
pass
if _sudo_available is False:
return ""
try:
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
_sudo_available = result.returncode == 0
return result.stdout.strip()
except Exception:
_sudo_available = False
return ""
@@ -153,105 +172,6 @@ def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> st
return default
@contextlib.contextmanager
def managed_proc(cmd: list[str], env: dict[str, str]):
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
try:
yield proc
finally:
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except TimeoutExpired:
proc.kill()
def tabulate(tabular_data, headers=(), tablefmt="simple", floatfmt="g", stralign="left", numalign=None):
rows = [list(row) for row in tabular_data]
def fmt(val):
if isinstance(val, str):
return val
if isinstance(val, (bool, int)):
return str(val)
try:
return format(val, floatfmt)
except (TypeError, ValueError):
return str(val)
formatted = [[fmt(c) for c in row] for row in rows]
hdrs = [str(h) for h in headers] if headers else None
ncols = max((len(r) for r in formatted), default=0)
if hdrs:
ncols = max(ncols, len(hdrs))
if ncols == 0:
return ""
for r in formatted:
r.extend([""] * (ncols - len(r)))
if hdrs:
hdrs.extend([""] * (ncols - len(hdrs)))
widths = [0] * ncols
if hdrs:
for i in range(ncols):
widths[i] = len(hdrs[i])
for row in formatted:
for i in range(ncols):
widths[i] = max(widths[i], max(len(ln) for ln in row[i].split('\n')))
def _align(s, w):
if stralign == "center":
return s.center(w)
return s.ljust(w)
if tablefmt == "html":
parts = ["<table>"]
if hdrs:
parts.append("<thead>")
parts.append("<tr>" + "".join(f"<th>{h}</th>" for h in hdrs) + "</tr>")
parts.append("</thead>")
parts.append("<tbody>")
for row in formatted:
parts.append("<tr>" + "".join(f"<td>{c}</td>" for c in row) + "</tr>")
parts.append("</tbody>")
parts.append("</table>")
return "\n".join(parts)
if tablefmt == "simple_grid":
def _sep(left, mid, right):
return left + mid.join("─" * (w + 2) for w in widths) + right
top, mid_sep, bot = _sep("┌", "┬", "┐"), _sep("├", "┼", "┤"), _sep("└", "┴", "┘")
def _fmt_row(cells):
split = [c.split('\n') for c in cells]
nlines = max(len(s) for s in split)
for s in split:
s.extend([""] * (nlines - len(s)))
return ["│" + "│".join(f" {_align(split[i][li], widths[i])} " for i in range(ncols)) + "│" for li in range(nlines)]
lines = [top]
if hdrs:
lines.extend(_fmt_row(hdrs))
lines.append(mid_sep)
for ri, row in enumerate(formatted):
lines.extend(_fmt_row(row))
lines.append(mid_sep if ri < len(formatted) - 1 else bot)
return "\n".join(lines)
gap = " "
lines = []
if hdrs:
lines.append(gap.join(h.ljust(w) for h, w in zip(hdrs, widths, strict=True)))
lines.append(gap.join("-" * w for w in widths))
for row in formatted:
lines.append(gap.join(_align(row[i], widths[i]) for i in range(ncols)))
return "\n".join(lines)
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
@@ -125,16 +125,32 @@ class Amplifier:
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
def set_configs(self, configs: list[AmpConfig]) -> bool:
import threading
tries = 15
backoff = 0.
for i in range(tries):
try:
self._set_configs(configs)
return True
except OSError:
result = [None]
error = [None]
def _worker():
try:
self._set_configs(configs)
result[0] = True
except OSError as e:
error[0] = e
t = threading.Thread(target=_worker, daemon=True)
t.start()
t.join(timeout=2)
if t.is_alive():
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
continue
if result[0]:
return True
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
return False
def set_global_shutdown(self, amp_disabled: bool) -> bool:
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
if self.amplifier is not None:
self.amplifier.initialize_configuration(self.get_device_type())
# Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
try:
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
except Exception:
pass
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
gpio_init(GPIO.SOM_ST_IO, True)
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
affine_irq(3, "xhci-hcd:usb1")
try:
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessException as e:
print(str(e))
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
pass
def configure_modem(self):
from iqpilot.common.params import Params
@@ -557,7 +559,7 @@ class Tici(HardwareBase):
if self.get_device_type() in ("tici", "tizi"):
if initial_eps_apn:
subprocess.call(["mmcli", "-m", "any", f'--3gpp-set-initial-eps-bearer-settings=apn={initial_eps_apn}'])
else:
elif is_comma_profile:
subprocess.call(["mmcli", "-m", "any", '--3gpp-set-initial-eps-bearer-settings=apn='])
cmds += [
+63 -52
View File
@@ -1,8 +1,9 @@
{
"bundle_version": 1792550965,
"python/_iqclosure/_vendored.json": {
"mode": 420,
"sha256": "74f679d0ffd3a8e3225496dec9ac060c5505503c94fa04acabd0de4539c57871",
"size": 1072
"sha256": "ae4c187fb594e87be0b64ff050e3ab39cff5dace25e8f5766e3f11be6736992f",
"size": 1137
},
"python/_iqclosure/iqpilot/__init__.py": {
"mode": 420,
@@ -16,13 +17,18 @@
},
"python/_iqclosure/iqpilot/common/api/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
"size": 900
},
"python/_iqclosure/iqpilot/common/api/base.py": {
"mode": 420,
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
"size": 3451
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
"size": 4921
},
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
"mode": 420,
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
"size": 313
},
"python/_iqclosure/iqpilot/common/basedir.py": {
"mode": 420,
@@ -61,18 +67,8 @@
},
"python/_iqclosure/iqpilot/common/utils.py": {
"mode": 420,
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
"size": 5159
},
"python/_iqclosure/iqpilot/konn3kt/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/konn3kt/cloud_client.py": {
"mode": 420,
"sha256": "4d58e62f8d628a8570881f2b87c82c801b46928fbb84f8dab8a81a9b55ba4e15",
"size": 493
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
"size": 5892
},
"python/_iqclosure/iqpilot/selfdrive/__init__.py": {
"mode": 420,
@@ -116,8 +112,13 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
"mode": 420,
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
"size": 14051
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
"size": 15008
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/iqc.py": {
"mode": 420,
"sha256": "54ec8e50513edae74ddb1c28015868183cb0c103de48386292d0b7fd83bcbb44",
"size": 391
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/speed_limit_input.py": {
"mode": 420,
@@ -166,13 +167,13 @@
},
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
"mode": 420,
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
"size": 7585
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
"size": 8016
},
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
"mode": 420,
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
"size": 25601
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
"size": 25731
},
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
"mode": 420,
@@ -204,43 +205,52 @@
"sha256": "b6d70f9628b596bef9eb6509e829d9cf94b4635c6e46bb8febde4a067203a222",
"size": 113
},
"python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so": {
"mode": 416,
"sha256": "a6d54e9ef8773e31e28411012a447ef4880ed7f8d43751bb16666adceee5e0e0",
"size": 1065336
},
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "506f732da9133ba5fdf8ffcb5d7557b28de44b8b789a4994fb1bd9a7c2240641",
"size": 138936
"mode": 416,
"sha256": "47e0652a7f87850cfc9cdc28ed88b18cd36adb4da00d5e5b4c2181df7ae09089",
"size": 1130696
},
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "33358560c99c961e172a1643fb4a0ac6048f90d85551c0dea595fc1793372199",
"size": 202256
"mode": 416,
"sha256": "a5a4c36f9aa116467c10e4fe9f07b4fde58d98b97e0950a799b9ce1c83190a08",
"size": 1130920
},
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "e23916b1c8ce0dd6fe28e07d381e8545c14ab9851cc3c501cc8f08e2002df6ee",
"size": 135368
"mode": 416,
"sha256": "c518443db5595c542ec1dec089be97ee79d3a1e7e0f661dbf8d069cf243da402",
"size": 1130872
},
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "cf6a559498981b3bebb4e50ef276e8c6f0816db68534c43b3372f2d88fec9f7d",
"size": 473440
"mode": 416,
"sha256": "c7bfa17d75afa08c0f85256b461c1dd0318fadf7ca422b704673613a8ba54949",
"size": 1524504
},
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "674bb7de81a76911f536cd684b384f384b4271bdedf0591ff18c574c7378d6c7",
"size": 137560
"mode": 416,
"sha256": "b4bd7aa63d69c0db66731eff1fb668551bb5eea17f9d0a8baba01fbb1ef05ae8",
"size": 1130728
},
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b6b8c578e98f584ee221d35e08233f4a96afee3d3d765bc8ce5229040626f9c9",
"size": 135872
"mode": 416,
"sha256": "9737473c0727f844593bd539cc106aa9358e6b62288e4ceb7a44c78425fec08b",
"size": 1065272
},
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "a08e7b38bdd666bf2aa38e7528574f2bb3650cd8439edc9689b1e36b08fcf5c2",
"size": 205456
"mode": 416,
"sha256": "87c0e5b83e1f53b1984aa9debe7e6f5f8a6432b6de6c4f5d82cef01e31eb5ed8",
"size": 1130776
},
"runtime": {
"entries": {
"iqpilot_private.context.aux_inputs": {
"path": "python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.context.cot_decode": {
"path": "python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
@@ -272,12 +282,13 @@
}
},
"signatures": {
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": "3XxFsaC/nx8fXtDtpWs+7oPABYmwlIEj1Ex30U8kTDzguB6P5RJVmkFbstAiWOnVCYfLT47sMlvHeU3VTHehDA==",
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": "9SRCLPJAzPMXMJRyxmfzIR3B2uTfpRm4uzUi1UBnSufzR+ppaRxfCtLestlXWDf2nkQiiaHjF3JdPv3VhjjmBg==",
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": "KdG3SA295ptxX5WcOmdjeHUkE+f+DTRkPGcJFEXgLMPPMWSgo8aVYILT9z46JTUHd+va82ijRIAJZtJHE+wqAg==",
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": "yNV0YdFkYGTRyiBFwejPolZHv5ueyQ3XwBjKAoDEB5c80ps6dEDDoXzJBi+Pe1PS/hhVZq3rb5NqVzuJEBlPCA==",
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": "OD6pPW3nNwopVqsY0UXVyGrAREG7KNCEO59c5j6uIPuKHaSCu2z59tNG9WZ/mgfJCyhLCAN6K9BULmX2kwRTDA==",
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": "B4Pc4T9sPIuF9g35pB43YbaEzwX9h4IhPrvLzwf1HUmkPMT7knz2B1lgU0z8atz96F5OUpJSE2mG5usq5BzjAA==",
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": "KYtRL67X5HZ2vVmQfqW8XhnmDVK1vxpo2geVcZ7NXGZ3FFn5vcIweK2ZFVZoIgPfGvK5QMSONnLXPUNSfc9iCw=="
"python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so": "1CTTb0TqkzHb+66U+mwhtiB0xa5ETlJmr7Hzdl7HJjEAhZVp8LImLWJm785MsaSCkC1mr2wI81FTTCM+6KOtBg==",
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": "X1Tb8xcrygS+L0a/f0os516Q8mTPIQuCkVUNkChn77lDLc+B3UaHYiIeI7iyomzW3958NNlXo9lq+lRYw4TZBQ==",
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": "afh0sJAKVrv2p8wGD2USTgvpme2/DgFV+mb2YAchPNGtBDL+S+y5KaxKsKXUVHVN7wU36AKCj/6T777jIVz2Dg==",
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": "ewMXJE1p5pz0uADnJROdarY8Cw85Uis5rAJvpotH+rVOoVoTaw/nO4MRbCeDj6Ko042QiCo7p/91cO5VT/OjDw==",
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": "4DeG7yQsLSC1kkOTusrCebb3yxQyTXZ4BbL4YDyIk9qKtGBPmPYj0x2J6b9HpAwchheqq9uNuko2nUkW3sMvCA==",
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": "1an09hj/ok9Fk4vted3GXczjV8v+qfUsjexRpKXnFeKdQNltv6oWosjjQvtEwzkikc43mtgIKAYoAxLBiS9lDw==",
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": "zyrasLvljreehe00C+GuDffQOE2OL3RDFy3dyCofB2WfB2EMcUyIWOFk+Ybtn+jJ9DQTHH063bDkURT5yviqDw==",
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": "hgeXamMIvzc3BLu9PtmlvI2jqi+x+g37TWvbrmfd78/z8Nzw68KoVIju3rKZcHU0BaXgnhc5sxlQV0wswk+3Cg=="
}
}
@@ -1 +1 @@
NcjiBJcKitajD4QhhgJZWkc7JRypOIeWKlHvcc0gRMfLAHU7K3ogMd/ZYHUwWPaTZw0JS5zkUIPuZ7hN+chqDA==
ODnEFb70mSlY+uG5Kl/aS6lYQ1fmNusxlf3O/ed2G/lPuA21fbzKmU+9XT9a83g9oAp5sLZuBV2zECPLewWiDg==
@@ -1,5 +1,7 @@
[
"iqpilot.common.api",
"iqpilot.common.api.base",
"iqpilot.common.api.konn3kt",
"iqpilot.common.basedir",
"iqpilot.common.gpio",
"iqpilot.common.i2c",
@@ -8,13 +10,13 @@
"iqpilot.common.swaglog",
"iqpilot.common.time_helpers",
"iqpilot.common.utils",
"iqpilot.konn3kt.cloud_client",
"iqpilot.selfdrive.iqmodeld.config",
"iqpilot.selfdrive.iqmodeld.drive_profile",
"iqpilot.selfdrive.iqmodeld.emac_model_meta",
"iqpilot.selfdrive.iqmodeld.model_map_tile",
"iqpilot.selfdrive.iqmodeld.models.fetcher",
"iqpilot.selfdrive.iqmodeld.models.helpers",
"iqpilot.selfdrive.iqmodeld.models.iqc",
"iqpilot.selfdrive.iqmodeld.speed_limit_input",
"iqpilot.selfdrive.iqmodeld.temporal_state",
"iqpilot.system.hardware",
@@ -0,0 +1,26 @@
import iqpilot.common.api.konn3kt
class Api:
def __init__(self, dongle_id):
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
def request(self, method, endpoint, **params):
return self.service.request(method, endpoint, **params)
def get(self, *args, **kwargs):
return self.service.get(*args, **kwargs)
def post(self, *args, **kwargs):
return self.service.post(*args, **kwargs)
def get_token(self, payload_extra=None, expiry_hours=1):
return self.service.get_token(payload_extra, expiry_hours)
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
@@ -7,10 +7,44 @@ from functools import lru_cache
from iqpilot.system.hardware.hw import Paths
from iqpilot.system.version import get_version
# name: jwt signature algorithm
KEYS = {"id_rsa": "RS256",
"id_ecdsa": "ES256"}
_CUSTODIAN_KEY = object()
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
def _custodian_available() -> bool:
import stat
try:
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
except OSError:
return False
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
import base64
import json
import socket
nonce = base64.b64encode(os.urandom(16)).decode()
packet = json.dumps({
"v": 1,
"op": "sign",
"dongle_id": dongle_id,
"payload": payload,
"alg": alg,
"nonce": nonce,
}).encode()
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
sock.settimeout(25.0)
sock.connect(_IQKEEPER_SOCKET)
sock.send(packet)
reply_data = sock.recv(8192)
reply = json.loads(reply_data) if reply_data else {}
if reply.get("nonce") != nonce or not reply.get("ok"):
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
return reply["token"]
@lru_cache(maxsize=4)
def load_signing_key(private_key: str):
@@ -60,6 +94,9 @@ class BaseApi:
}
if payload_extra is not None:
payload.update(payload_extra)
if self.private_key is _CUSTODIAN_KEY:
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
key = load_signing_key(self.private_key) if self.private_key else self.private_key
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
@@ -73,9 +110,25 @@ class BaseApi:
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
@staticmethod
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
for key in KEYS:
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
return KEYS[key], private.read(), public.read()
priv_path = Paths.persist_root() + f'/comma/{key}'
pub_path = priv_path + '.pub'
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
continue
try:
with open(pub_path) as public:
pub = public.read()
except OSError:
continue
try:
with open(priv_path) as private:
return KEYS[key], private.read(), pub
except PermissionError:
import time
for _attempt in range(10):
if _custodian_available():
return KEYS[key], _CUSTODIAN_KEY, pub
time.sleep(0.5)
continue
return None, None, None
@@ -1,3 +1,4 @@
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
import os
from iqpilot.common.api.base import BaseApi
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
class CommaConnectApi(BaseApi):
class Konn3ktApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "openpilot-"
@@ -31,24 +31,44 @@ class Timer:
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
_sudo_available: bool | None = None
def sudo_write(val: str, path: str) -> None:
global _sudo_available
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
return
except OSError:
pass
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
if _sudo_available is False:
return
try:
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
if _sudo_available:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
except Exception:
_sudo_available = False
def sudo_read(path: str) -> str:
global _sudo_available
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
with open(path, encoding='utf8') as f:
return f.read().strip()
except OSError:
pass
if _sudo_available is False:
return ""
try:
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
_sudo_available = result.returncode == 0
return result.stdout.strip()
except Exception:
_sudo_available = False
return ""
@@ -1,17 +0,0 @@
"""
Copyright ©️ IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import os
from iqpilot.common.api.base import BaseApi
API_HOST = os.getenv('KONN3KT_API_HOST', 'https://api-iqlabs.konn3kt.com')
class Konn3ktApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "konn3kt-device-"
def get_token(self, expiry_hours=1):
return super()._get_token(expiry_hours=expiry_hours)
@@ -305,7 +305,40 @@ def get_runtime_bundle_upgrade(bundle, params: Params = None, available_bundles=
return _find_runtime_upgrade(bundle, params, available_bundles)
_forced_bundle = None
def load_default_model_session():
"""Stage the shipped default model files and return a decoded default bundle
WITHOUT persisting it as the active selection, so the next boot retries the
user's own model instead of staying on the default forever."""
bundle_dict = _load_default_bundle_dict()
ensure_default_model_files(bundle_dict)
return ModelBundle(**bundle_dict)
class forced_active_bundle:
"""Make get_active_bundle return `bundle` for the duration of the block, used to
build a runner for a session-only fallback bundle without touching Params."""
def __init__(self, bundle):
self._bundle = bundle
def __enter__(self):
global _forced_bundle
self._prev = _forced_bundle
_forced_bundle = self._bundle
return self._bundle
def __exit__(self, *exc):
global _forced_bundle
_forced_bundle = self._prev
return False
def get_active_bundle(params: Params = None):
if _forced_bundle is not None:
return _forced_bundle
params = Params() if params is None else params
try:
@@ -0,0 +1,11 @@
#!/usr/bin/env python3
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
load_private_module(__name__, "iqpilot_private.models.iqc")
except ProprietaryModuleMissing:
from iqpilot.models_private_src.iqc import * # noqa: F403
@@ -125,16 +125,32 @@ class Amplifier:
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
def set_configs(self, configs: list[AmpConfig]) -> bool:
import threading
tries = 15
backoff = 0.
for i in range(tries):
try:
self._set_configs(configs)
return True
except OSError:
result = [None]
error = [None]
def _worker():
try:
self._set_configs(configs)
result[0] = True
except OSError as e:
error[0] = e
t = threading.Thread(target=_worker, daemon=True)
t.start()
t.join(timeout=2)
if t.is_alive():
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
continue
if result[0]:
return True
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
return False
def set_global_shutdown(self, amp_disabled: bool) -> bool:
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
if self.amplifier is not None:
self.amplifier.initialize_configuration(self.get_device_type())
# Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
try:
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
except Exception:
pass
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
gpio_init(GPIO.SOM_ST_IO, True)
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
affine_irq(3, "xhci-hcd:usb1")
try:
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessException as e:
print(str(e))
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
pass
def configure_modem(self):
from iqpilot.common.params import Params
+91 -110
View File
@@ -1,8 +1,9 @@
{
"bundle_version": 1792551322,
"python/_iqclosure/_vendored.json": {
"mode": 420,
"sha256": "c4e45105dedcba6f7a4abf84a6e012517ca82cd615165e162c2025ad59020fa1",
"size": 3823
"sha256": "00a3c1ea8d86878488abd7ee5ae5dce065c4b3d0fcf43072999303067807bc63",
"size": 3668
},
"python/_iqclosure/iqpilot/__init__.py": {
"mode": 420,
@@ -16,18 +17,18 @@
},
"python/_iqclosure/iqpilot/common/api/__init__.py": {
"mode": 420,
"sha256": "97d6e0d8cbd3e47797d734322b42d04efea4d95a2dae5a56f1baef3fd847aa0d",
"size": 939
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
"size": 900
},
"python/_iqclosure/iqpilot/common/api/base.py": {
"mode": 420,
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
"size": 3451
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
"size": 4921
},
"python/_iqclosure/iqpilot/common/api/comma_connect.py": {
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
"mode": 420,
"sha256": "e7c2dd0b56fb8533c2cbc614e728a9ffc5ce7360524cfa62a02f5235bc23b9a8",
"size": 267
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
"size": 313
},
"python/_iqclosure/iqpilot/common/atlas_alerts.py": {
"mode": 420,
@@ -81,13 +82,8 @@
},
"python/_iqclosure/iqpilot/common/realtime.py": {
"mode": 420,
"sha256": "016be524079ef32df03b2c135eab8f83da60a46fc7f143620b836c65f33e39a3",
"size": 4423
},
"python/_iqclosure/iqpilot/common/spinner.py": {
"mode": 420,
"sha256": "33a2eb2aa3148492414035c5dc7150b3a69467905d7a3e58c8f65cfe70d3552c",
"size": 1371
"sha256": "0190cf1f239f8a2bda573524a72a4b6a74e7ed70aa4c1e5f72d1f1159d59cd60",
"size": 4926
},
"python/_iqclosure/iqpilot/common/steer_delay.py": {
"mode": 420,
@@ -106,23 +102,18 @@
},
"python/_iqclosure/iqpilot/common/utils.py": {
"mode": 420,
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
"size": 5159
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
"size": 5892
},
"python/_iqclosure/iqpilot/konn3kt/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/konn3kt/cloud_client.py": {
"mode": 420,
"sha256": "4d58e62f8d628a8570881f2b87c82c801b46928fbb84f8dab8a81a9b55ba4e15",
"size": 493
},
"python/_iqclosure/iqpilot/konn3kt/registration.py": {
"mode": 420,
"sha256": "e9c37e8ae0a70c3a19ca1a33b290e94658b255fab153a941f50f8a29168d75c3",
"size": 7816
"sha256": "5a2b06d919adce513fb8a7e4e6f26d48e502e92dbd89f9af7a0e67e9114ab3be",
"size": 2683
},
"python/_iqclosure/iqpilot/selfdrive/__init__.py": {
"mode": 420,
@@ -139,36 +130,16 @@
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/desire_helper.py": {
"python/_iqclosure/iqpilot/selfdrive/controls/lib/action_engine.py": {
"mode": 420,
"sha256": "5d06908d9a5358d8b27e2a37b9f234f618966004aacb1761cf16c1a1acc74ab0",
"size": 15285
"sha256": "c3e33ae73bcdab89ba32b09e18712fe91018338907e8c81af056d447c5ef04c6",
"size": 815
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/drive_helpers.py": {
"mode": 420,
"sha256": "94e202959eb80d841720ecd0581206fc4ac9d50ad2e2a6cf19f0de9914913988",
"size": 3159
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lane_change.py": {
"mode": 420,
"sha256": "0e4b4c54856d651d89e1c8896ebf8bb4f0a2efc0b8788a585094e06ecd6616d7",
"size": 8461
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lane_turn.py": {
"mode": 420,
"sha256": "d27bd9010967dd51dccc18ec005ff446ae965379b8e828ff7173331818723ec8",
"size": 4667
},
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lateral_edge_guard.py": {
"mode": 420,
"sha256": "6bb6bcb8e5323480bf1b992549279a7d4c17a1bd1bd260863cf7b61b887fa735",
"size": 10227
},
"python/_iqclosure/iqpilot/selfdrive/iqlocd/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
@@ -189,6 +160,11 @@
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/aux_inputs.py": {
"mode": 420,
"sha256": "e80699014729784ff72c64ac2cdf317a97e0f069ef99676581b1d179db5b4a0a",
"size": 493
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/big_catalog.py": {
"mode": 420,
"sha256": "7ec5df43ce10b47ce925467e61100123e828b8e72bdab20bef4c8ac0ffd3cc7a",
@@ -211,8 +187,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/daemon.py": {
"mode": 420,
"sha256": "62f51d922f6883bc094590a9d95c2c0849d858bbce30f35c2eba00ec9d3d9bad",
"size": 36325
"sha256": "2f94719d06907c4ac729b8aba063ad43079fde966813d55b6190af8d6c7da679",
"size": 37375
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/drive_profile.py": {
"mode": 420,
@@ -221,13 +197,13 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/driving_action.py": {
"mode": 420,
"sha256": "46a46ea0675ce2f815021c63f3b1ab171c7a453c499a827f2a82b2f2e55d6f58",
"size": 2484
"sha256": "83c1155d90607a2ca119ce0797474e79d741aa0399ae86a22843ad580b5c8ef7",
"size": 2644
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
"mode": 420,
"sha256": "a9f2bc8477b58a3df7196f4b40cdddf631e28051809e9bbfdd4c78148444c474",
"size": 10891
"sha256": "af83d52609ec4f2abe82f4e5f788ce4788bdc41cbf7b3de36dc63f1f598ef46c",
"size": 12107
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
"mode": 420,
@@ -256,8 +232,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py": {
"mode": 420,
"sha256": "83b0190f19e3f037c937a03b1988931729ed43d4dac92223aba0745697611ed8",
"size": 12835
"sha256": "62b13a05afbe9b5fb5d9e3d09c1491b37826ff840126c5e317a7ebf1edc28762",
"size": 12060
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_channel.py": {
"mode": 420,
@@ -281,8 +257,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/combined_artifact.py": {
"mode": 420,
"sha256": "321e7223249dc7dba93effe8e834f225fb6067abf49da17fe9a6faeb71ba8e49",
"size": 2786
"sha256": "170639c92d69e4ae2493ca961fb95e75d42be9cedb2442521fe3ac7c25cb977c",
"size": 2891
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/fetcher.py": {
"mode": 420,
@@ -291,14 +267,19 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
"mode": 420,
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
"size": 14051
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
"size": 15008
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/inference_state.py": {
"mode": 420,
"sha256": "bdfd1c1b538e2342b17d08aa6ebe0c041c2f721025bcd7334434d1139fe6063d",
"size": 268
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/iqc.py": {
"mode": 420,
"sha256": "54ec8e50513edae74ddb1c28015868183cb0c103de48386292d0b7fd83bcbb44",
"size": 391
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
@@ -306,8 +287,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/model_runner.py": {
"mode": 420,
"sha256": "e0eb9d7bff1da89d52b09596f7e7109fc25c3b9b86a89674382a7dde9e0a4199",
"size": 8555
"sha256": "c9645f9f377020a44d4a785de5930dcfc1b0028489461a94a3a1ddd10e7c7be3",
"size": 8566
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/__init__.py": {
"mode": 420,
@@ -316,13 +297,13 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/combined_split_runner.py": {
"mode": 420,
"sha256": "621968a20fec17cc69d1d68735c317251dde33168e343f0ecb692aef310861c8",
"size": 10894
"sha256": "3d6cd89b8a45b246c0fea6dfc760d22cfdfc91557f95fff115bd186da7b76678",
"size": 10897
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/fused_runner.py": {
"mode": 420,
"sha256": "25304fe7ae034d1bb8c9c86a2757704944539be6a0c69c322e75adc84f66e221",
"size": 7885
"sha256": "314023341e6fb0201c49975f1e3ed11ff074348e78bd42e893d2317e5c36ea14",
"size": 7896
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/model_types.py": {
"mode": 420,
@@ -331,8 +312,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/supercombo_runner.py": {
"mode": 420,
"sha256": "120904be1407395a0d88d28fcba1fe426a2b84e381c3c2a0e6d9665868f492fe",
"size": 10304
"sha256": "9f4a0ab397ca4c10cf72318ee39072cb38258afe0a6d31950639fcad80387434",
"size": 10315
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/supercombo_runtime.py": {
"mode": 420,
@@ -341,8 +322,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/tinygrad_runner.py": {
"mode": 420,
"sha256": "58ffe0afb2cd9527d2a1d5bbebf0471af8c8210534e266ab80488a7f89bb06ba",
"size": 7684
"sha256": "175410433d287cfd3ac93e8fae19a5da409af61dab1d1a48731eb8aa7043c18c",
"size": 7702
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/split_model_constants.py": {
"mode": 420,
@@ -516,13 +497,13 @@
},
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
"mode": 420,
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
"size": 7585
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
"size": 8016
},
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
"mode": 420,
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
"size": 25601
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
"size": 25731
},
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
"mode": 420,
@@ -575,49 +556,49 @@
"size": 119
},
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "0dcf7516014011de4b56eee175e4e299fa5fee748f6e0b72169605e098c080bc",
"size": 400040
"mode": 416,
"sha256": "fddf1c1e66a06c6815c7b3bf3b2d35f209bc245073db60542a76a56b28c28497",
"size": 1327832
},
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "39263f739980829ebf32ba43abdf32dffa2c015944808ea64cb6ff1dc210a043",
"size": 68152
"mode": 416,
"sha256": "8f552e5100f29394362210ced30a1e44a4b8227edb3f66fcf2b7dde2f3001e69",
"size": 999816
},
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "6787635f4b79687dc54fd5e3c25b72285a5a8e04e81a28b5764deac6ce2492ed",
"size": 333704
"mode": 416,
"sha256": "17dc02560735d0adf518df30d0fceb5432a1528807e44b2924d22bc4b9f61561",
"size": 1393240
},
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "a3bab600ccec9e9edb5817d5a177cd6638a8a727a8d4b25c6e722d4ad038188b",
"size": 675944
"mode": 416,
"sha256": "e99eed14d07496da9f4d085a03ae3380de93a58fadc9c8bf5247e6f07b0f57d2",
"size": 1655512
},
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "9476c1b5934125917bc0a0dceca95274ebf47d45a1ed66122cb34ba050f40e34",
"size": 597936
"mode": 416,
"sha256": "64904b6215365c7db4b2df51d1706f9483b411768bfaedd888a0e3a1701e0302",
"size": 1655480
},
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f47a9a61624b9cb5e71bfe979fbae403e04cee27db61f0ccf9b30effd1bad626",
"size": 473440
"mode": 416,
"sha256": "80ae92f06aa2562acdb6d5c88be004bbc1bf731351640f926f42a30cbde43d39",
"size": 1393208
},
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b68e259978df7824d5e992653aa1861c6fbd746e97f67c753b5f3ca5c4a93f7b",
"size": 268000
"mode": 416,
"sha256": "41fe239760108a0a2f1c3e96fb0dec4801bcbdad5b6ed5d4b92c10120715ed22",
"size": 1196408
},
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ab3543fc0fe22a5778cb79d63b3ff48031a36ce6e873c79dbfe54d73277d7f16",
"size": 206032
"mode": 416,
"sha256": "dbcb1f6103caecf536d6becfad9351381b0a9eef8742ea7df4c03c817c34e000",
"size": 1262024
},
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "fe91bb1f156c49cecb4a4707a37affdab70b7d9125ff23e4081ca9494ba2c356",
"size": 203152
"mode": 416,
"sha256": "22ef20fc1e70b73f135beb8abeb2831770cc39e0e2a44f3fc7bb44a5da811f93",
"size": 1130760
},
"runtime": {
"entries": {
@@ -628,14 +609,14 @@
}
},
"signatures": {
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "nSEzB01BtGt3un5WVEbn1E5IaHnj7/zLt4lXIYOfBd/yqMBpuUCZ48SUhJU5hTFKMyiuyk3HG/72iwDWMCo1BA==",
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "kUADWHWJJcjYKyumfG2b+XtgDgCQTMtz371MDjnngEv4zn0BnW3AlD+WfoggzBcd6FZ5DYNB8oyjea6BkQgMDA==",
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "CbUzdCkb7mORLJaOMCeyLXEkeZzr3EYA1b5doki+q9AshoXNoSOkso+Uh7fApTNr1nqSy+QNfxBm8IMtNQt8Aw==",
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "80cJCpENHXIyhxGAHRgqBEfyN7KBQ112YQMdmPe1LOiXASSTWTsu5QMUzzV3u+X5kk+1UqRC0NZ5PjoF/sFADg==",
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "x4CkpFtIxtZpt3de1im0hi7jABTdJ3mB3OSN4apkeQALCdYdhFzbVasfSrtkpN6t+/CUEx3g5cLhslEUBt9lCw==",
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": "ISg4GM8peP3ER4alhIe3+3ogJ39dNDq+prLO70AtEf/wqSfATPN09rt/kmIrveqgyTlGD2/v3uOvICnLotKlBQ==",
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": "ttGirAmlJmDTRy2MseGmR/xSK9KkjXIe9eevQ+EJ2P2itPr1XKN8rOoFXaJdUSQaVlsXi/w/yAtLcNEBf0oABA==",
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": "/v2I9Y7/OT0GhFGw8MjKWsz4hk4R/l02V5VCxD5iIYZkGBKdy9UPFcBYWjg/zpfwF+cYqwp0SmlrZOBFmykbDw==",
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": "jFsJk4sFSEtSamxV6f0dBS1kWYcFQ2KEPA+0UbDqfXf4I3+KSaJpr0CQTmfGhgIWYGxAnetunoEJMKsVrNPNDA=="
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "c7oPgelyaOkjpN3BDwHkt1UZCGuXa97ocZ4CkNtOyeBxW+7R26hldG7nOaif+nuNmnYNE/ya7ucuPwPkWJXYDA==",
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "6ypZpUAkpit8UhYLh0XqceQan9EduF4yQ4MA4GqmPvFUw7u/muSOaopK+lBaw1Xtg8yPbfR8DoGZu0uj3fsPDw==",
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "LrhYCVk6U7yOKNcVJKEBQuOfxC6cJfeUn4IA8gpwmX+ZyaV/oUvv+nDYB37MZD72KRAarhLoTXgpAU9yG7AWCw==",
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "zm5GDcrKM6wB3mr8bmmjF65QUCTjj8XtvLo1+NrP5wClZKAtVrc90Glfau8YApntZwK+uSKBeHRi2xqg4V14BQ==",
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "HgA5ik+z5dVMtSJolJQ7ERbW53vlcbG9GoDKfLN+ANluWrd4q1ezQUDvJJ5JZERQEw8N8tho6du2HsPgY65mAQ==",
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": "50yzbrZZAOWidHTH6nZ+2LMvJGJ2LV60GXN2yXdKfVERrd8BGZH8j/oOrG71lW3wf/htiZmZRLgIvEKXcUJSCw==",
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": "suTF6e1oz3Lki7qhFQ5wdHa2m5ZccMmCVEy/BZ/6wqem2fxt8C3hd0OjXiM0Xyg7R+pumDpGOiL23uh2EZDcAw==",
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": "y0YKu5/ezBo04HGeD4mQVlBpRBJdHn4Y8mBt3HNo6f5mcWg9NhJ7T0u3hmvXl1JcMUnTeDDwNkVIi52VrK+5DA==",
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": "P+fIzA2ScQzFeulAfgkx5favc2mzvlTybL+e59hBEwF+5bFE3V12DFUOEBga5Rbcv9VrvfG52dMIeysNmHHHCg=="
}
}
@@ -1 +1 @@
gmKHJty4j+pkzT4cISfbob5EVVaY0wTii9yUBeuFC4ZF5ERST59xBav/C/5rCnBhk6ednbwTWq68l0uqgc4XDA==
wRI3NynFc/uIF9F5+7fv4xjZh+64osm4CRaZhUM4JAnJXUaYYl5VDp+kZCDJw8cg/i/VKCRZMGNHq8+XE9eLAQ==
@@ -1,7 +1,7 @@
[
"iqpilot.common.api",
"iqpilot.common.api.base",
"iqpilot.common.api.comma_connect",
"iqpilot.common.api.konn3kt",
"iqpilot.common.atlas_alerts",
"iqpilot.common.basedir",
"iqpilot.common.constants",
@@ -13,19 +13,15 @@
"iqpilot.common.issue_debug",
"iqpilot.common.logging_extra",
"iqpilot.common.realtime",
"iqpilot.common.spinner",
"iqpilot.common.steer_delay",
"iqpilot.common.swaglog",
"iqpilot.common.time_helpers",
"iqpilot.common.utils",
"iqpilot.konn3kt.cloud_client",
"iqpilot.konn3kt.registration",
"iqpilot.selfdrive.controls.lib.desire_helper",
"iqpilot.selfdrive.controls.lib.action_engine",
"iqpilot.selfdrive.controls.lib.drive_helpers",
"iqpilot.selfdrive.controls.lib.helpers.lane_change",
"iqpilot.selfdrive.controls.lib.helpers.lane_turn",
"iqpilot.selfdrive.controls.lib.helpers.lateral_edge_guard",
"iqpilot.selfdrive.iqlocd.models.constants",
"iqpilot.selfdrive.iqmodeld.aux_inputs",
"iqpilot.selfdrive.iqmodeld.big_catalog",
"iqpilot.selfdrive.iqmodeld.camera",
"iqpilot.selfdrive.iqmodeld.config",
@@ -47,6 +43,7 @@
"iqpilot.selfdrive.iqmodeld.models.fetcher",
"iqpilot.selfdrive.iqmodeld.models.helpers",
"iqpilot.selfdrive.iqmodeld.models.inference_state",
"iqpilot.selfdrive.iqmodeld.models.iqc",
"iqpilot.selfdrive.iqmodeld.models.runners.model_runner",
"iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.combined_split_runner",
"iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.fused_runner",
@@ -1,9 +1,9 @@
import iqpilot.common.api.comma_connect
import iqpilot.common.api.konn3kt
class Api:
def __init__(self, dongle_id):
self.service = iqpilot.common.api.comma_connect.CommaConnectApi(dongle_id)
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
def request(self, method, endpoint, **params):
return self.service.request(method, endpoint, **params)
@@ -19,8 +19,8 @@ class Api:
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
return iqpilot.common.api.comma_connect.CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
return iqpilot.common.api.comma_connect.CommaConnectApi(None).get_key_pair()
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
@@ -7,10 +7,44 @@ from functools import lru_cache
from iqpilot.system.hardware.hw import Paths
from iqpilot.system.version import get_version
# name: jwt signature algorithm
KEYS = {"id_rsa": "RS256",
"id_ecdsa": "ES256"}
_CUSTODIAN_KEY = object()
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
def _custodian_available() -> bool:
import stat
try:
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
except OSError:
return False
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
import base64
import json
import socket
nonce = base64.b64encode(os.urandom(16)).decode()
packet = json.dumps({
"v": 1,
"op": "sign",
"dongle_id": dongle_id,
"payload": payload,
"alg": alg,
"nonce": nonce,
}).encode()
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
sock.settimeout(25.0)
sock.connect(_IQKEEPER_SOCKET)
sock.send(packet)
reply_data = sock.recv(8192)
reply = json.loads(reply_data) if reply_data else {}
if reply.get("nonce") != nonce or not reply.get("ok"):
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
return reply["token"]
@lru_cache(maxsize=4)
def load_signing_key(private_key: str):
@@ -60,6 +94,9 @@ class BaseApi:
}
if payload_extra is not None:
payload.update(payload_extra)
if self.private_key is _CUSTODIAN_KEY:
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
key = load_signing_key(self.private_key) if self.private_key else self.private_key
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
@@ -73,9 +110,25 @@ class BaseApi:
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
@staticmethod
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
for key in KEYS:
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
return KEYS[key], private.read(), public.read()
priv_path = Paths.persist_root() + f'/comma/{key}'
pub_path = priv_path + '.pub'
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
continue
try:
with open(pub_path) as public:
pub = public.read()
except OSError:
continue
try:
with open(priv_path) as private:
return KEYS[key], private.read(), pub
except PermissionError:
import time
for _attempt in range(10):
if _custodian_available():
return KEYS[key], _CUSTODIAN_KEY, pub
time.sleep(0.5)
continue
return None, None, None
@@ -1,3 +1,4 @@
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
import os
from iqpilot.common.api.base import BaseApi
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
class CommaConnectApi(BaseApi):
class Konn3ktApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "openpilot-"
@@ -62,10 +62,23 @@ def config_background_thread() -> None:
set_core_affinity(list(range(os.cpu_count() or 1)))
def _swap_active() -> bool:
try:
with open("/proc/swaps") as f:
return len(f.read().splitlines()) > 1
except Exception:
return False
def lock_memory() -> None:
"""mlockall this process so memory reclaim/compaction can't stall it. RT control
procs only (locking ui/modeld would worsen pressure). Best-effort."""
if sys.platform != 'linux' or PC:
"""mlockall this process so swap can't evict its pages and stall the RT loop.
RT control procs only (locking ui/modeld would worsen pressure). Best-effort.
No-op without active swap: with none, anonymous pages (thread stacks, heap)
are already unreclaimable, so mlockall buys nothing — it only pins every 8 MB
thread stack and MCL_FUTURE allocation in full (~400 MB/proc here) and, being
non-migratable, starves CMA. The device runs swapless by design (iqos-zram)."""
if sys.platform != 'linux' or PC or not _swap_active():
return
try:
import ctypes
@@ -1,52 +0,0 @@
import os
import subprocess
from iqpilot.common.basedir import BASEDIR
class Spinner:
def __init__(self):
try:
self.spinner_proc = subprocess.Popen(["./spinner.py"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "iqpilot", "system", "ui"),
close_fds=True)
except OSError:
self.spinner_proc = None
def __enter__(self):
return self
def update(self, spinner_text: str):
if self.spinner_proc is not None:
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
try:
self.spinner_proc.stdin.flush()
except BrokenPipeError:
pass
def update_progress(self, cur: float, total: float):
self.update(str(round(100 * cur / total)))
def close(self):
if self.spinner_proc is not None:
self.spinner_proc.kill()
try:
self.spinner_proc.communicate(timeout=2.)
except subprocess.TimeoutExpired:
print("WARNING: failed to kill spinner")
self.spinner_proc = None
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == "__main__":
import time
with Spinner() as s:
s.update("Spinner text")
time.sleep(5.0)
print("gone")
time.sleep(5.0)
@@ -31,24 +31,44 @@ class Timer:
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
_sudo_available: bool | None = None
def sudo_write(val: str, path: str) -> None:
global _sudo_available
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
return
except OSError:
pass
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
if _sudo_available is False:
return
try:
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
if _sudo_available:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
except Exception:
_sudo_available = False
def sudo_read(path: str) -> str:
global _sudo_available
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
with open(path, encoding='utf8') as f:
return f.read().strip()
except OSError:
pass
if _sudo_available is False:
return ""
try:
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
_sudo_available = result.returncode == 0
return result.stdout.strip()
except Exception:
_sudo_available = False
return ""
@@ -1,17 +0,0 @@
"""
Copyright ©️ IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import os
from iqpilot.common.api.base import BaseApi
API_HOST = os.getenv('KONN3KT_API_HOST', 'https://api-iqlabs.konn3kt.com')
class Konn3ktApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "konn3kt-device-"
def get_token(self, expiry_hours=1):
return super()._get_token(expiry_hours=expiry_hours)
@@ -1,18 +1,12 @@
#!/usr/bin/env python3
import os
import time
import json
import jwt
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
import re
import secrets
from typing import cast
from pathlib import Path
from datetime import datetime, timedelta, UTC
from iqpilot.common.api import api_get, get_key_pair
from iqpilot.common.params import Params
from iqpilot.common.spinner import Spinner
from iqpilot.system.hardware import HARDWARE
from iqpilot.system.hardware.hw import Paths
from iqpilot.common.swaglog import cloudlog
@@ -35,6 +29,10 @@ def _read_persist_dongle_id() -> str | None:
return None
def is_valid_dongle_id(dongle_id: str | None) -> bool:
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str | None:
ro = _read_persist_dongle_id()
if is_valid_dongle_id(ro):
@@ -46,8 +44,8 @@ def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = T
if v and v != UNREGISTERED_DONGLE_ID:
return v.lower() if is_valid_dongle_id(v) else v
return ro or None
def is_valid_dongle_id(dongle_id: str | None) -> bool:
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str:
p = Params() if params is None else params
dongle_id = get_cached_dongle_id(p, prefer_readonly=prefer_readonly)
@@ -57,164 +55,32 @@ def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool
p.put("DongleId", dongle_id)
cloudlog.warning(f"generated new DongleId={dongle_id} (no readonly dongle_id found)")
return dongle_id
_impl = None
def _registration_impl():
global _impl
if _impl is None:
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
_impl = load_private_module(__name__, "iqpilot_private.konn3kt.registration.reg_core")
except ProprietaryModuleMissing:
from konn3kt_private.registration import reg_core as _impl
return _impl
def ensure_dev_pairing_identity(params: Params | None = None, force_reset: bool = False) -> dict[str, str]:
p = Params() if params is None else params
persist_dir = Path(Paths.persist_root()) / "comma"
persist_dir.mkdir(parents=True, exist_ok=True)
dongle_path = persist_dir / "dongle_id"
priv_path = persist_dir / "id_rsa"
pub_path = persist_dir / "id_rsa.pub"
if force_reset:
for fp in (dongle_path, priv_path, pub_path):
try:
fp.unlink(missing_ok=True)
except Exception:
cloudlog.exception(f"failed to remove {fp}")
try:
(persist_dir / "konn3kt_prime_type").unlink(missing_ok=True)
except Exception:
pass
try:
p.remove("PrimeType")
except Exception:
pass
forced_dongle = os.getenv("KONN3KT_DEV_DONGLE_ID")
dongle_id = forced_dongle.strip().lower() if forced_dongle else None
if dongle_id and not is_valid_dongle_id(dongle_id):
cloudlog.error("KONN3KT_DEV_DONGLE_ID must be 16 hex chars")
dongle_id = None
if dongle_id is None:
existing = None
try:
existing = dongle_path.read_text().strip().lower() if dongle_path.is_file() else None
except Exception:
cloudlog.exception("failed reading existing dev dongle_id")
dongle_id = existing if is_valid_dongle_id(existing) else secrets.token_hex(8)
try:
dongle_path.write_text(dongle_id)
except Exception:
cloudlog.exception("failed writing dev dongle_id")
p.put("DongleId", dongle_id)
p.put("HardwareSerial", p.get("HardwareSerial") or f"DEV-{dongle_id}")
if force_reset or (not priv_path.is_file()) or (not pub_path.is_file()):
try:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
priv_bytes = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
pub_bytes = key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
priv_path.write_bytes(priv_bytes)
pub_path.write_bytes(pub_bytes)
except Exception:
cloudlog.exception("failed generating dev RSA keys")
raise
return {
"dongle_id": dongle_id,
"serial": p.get("HardwareSerial") or f"DEV-{dongle_id}",
"persist_dir": str(persist_dir),
}
def _normalize_imei(value: str | None) -> str:
return value or ""
return _registration_impl().ensure_dev_pairing_identity(params, force_reset)
def get_registration_identifiers(wait_timeout: float = IMEI_WAIT_TIMEOUT, show_spinner: bool = False) -> tuple[str, str, str]:
serial = HARDWARE.get_serial()
spinner = Spinner() if show_spinner else None
start_time = time.monotonic()
imei1: str | None = None
imei2: str | None = None
while time.monotonic() - start_time < wait_timeout:
try:
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
if imei1 or imei2:
break
except RuntimeError as e:
if "no modems" in str(e).lower():
cloudlog.warning("No cellular modem available, proceeding without IMEI")
break
cloudlog.exception("Error getting imei, trying again...")
except Exception:
cloudlog.exception("Error getting imei, trying again...")
time.sleep(1)
imei1 = _normalize_imei(imei1)
imei2 = _normalize_imei(imei2)
if not imei1 and not imei2:
cloudlog.warning(f"proceeding with serial-only registration for serial={serial}")
if spinner is not None:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
spinner.close()
return serial, imei1, imei2
return _registration_impl().get_registration_identifiers(wait_timeout, show_spinner)
def register(show_spinner=False) -> str | None:
params = Params()
dongle_id: str | None = get_cached_dongle_id(params, prefer_readonly=True)
if dongle_id in ("", UNREGISTERED_DONGLE_ID):
dongle_id = None
jwt_algo, private_key, public_key = get_key_pair()
if not public_key:
dongle_id = UNREGISTERED_DONGLE_ID
cloudlog.warning("missing public key")
elif dongle_id is None:
if show_spinner:
spinner = Spinner()
spinner.update("registering device")
serial, imei1, imei2 = get_registration_identifiers(wait_timeout=IMEI_WAIT_TIMEOUT, show_spinner=False)
backoff = 0
start_time = time.monotonic()
while True:
try:
register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)},
cast(str, private_key), algorithm=jwt_algo)
cloudlog.info("getting pilotauth")
cloudlog.info("getting pilotauth")
resp = api_get("v2/pilotauth/", method='POST', timeout=15,
imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)
if resp.status_code in (400, 402, 403):
cloudlog.info(f"Unable to register device, got {resp.status_code}")
dongle_id = UNREGISTERED_DONGLE_ID
else:
dongleauth = json.loads(resp.text)
dongle_id = dongleauth["dongle_id"]
break
except Exception:
cloudlog.exception("failed to authenticate")
backoff = min(backoff + 1, 15)
time.sleep(backoff)
if time.monotonic() - start_time > 60 and show_spinner:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
return UNREGISTERED_DONGLE_ID
if show_spinner:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
spinner.close()
if dongle_id:
params.put("DongleId", dongle_id)
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
set_offroad_alert("Offroad_UnregisteredHardware", False)
return dongle_id
return _registration_impl().register(show_spinner)
if __name__ == "__main__":
@@ -0,0 +1,18 @@
# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
load_private_module(__name__, "iqpilot_private.navd.action_engine")
except ProprietaryModuleMissing:
from iqpilot.cereal import custom, log
class ActionEngine:
def __init__(self):
self.desire = log.Desire.none
self.lane_change_state = log.LaneChangeState.off
self.lane_change_direction = log.LaneChangeDirection.none
self.lane_turn_direction = custom.IQTurnSignalDirection.none
self.lateral_edge_block = custom.IQLateralEdgeBlock.none
def update(self, carstate, lateral_active, lane_change_prob, nav_state=None, modeldata=None, radar_state=None):
return None
@@ -1,360 +0,0 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from __future__ import annotations
import math
from iqpilot.cereal import car, custom, log
from iqpilot.common.constants import CV
from iqpilot.common.params import Params
from iqpilot.common.realtime import DT_MDL
from iqpilot.selfdrive.controls.lib.helpers.lane_change import (
IQLaneSwapController,
AutoLaneChangeMode,
NavExitLaneChangeController,
)
from iqpilot.selfdrive.controls.lib.helpers.lateral_edge_guard import LateralEdgeGuard
from iqpilot.selfdrive.controls.lib.helpers.lane_turn import IQNavTurnController
LaneChangeState = log.LaneChangeState
LaneChangeDirection = log.LaneChangeDirection
TurnDirection = custom.IQTurnSignalDirection
LateralEdgeBlock = custom.IQLateralEdgeBlock
NavManeuverPhase = custom.IQNavState.ManeuverPhase
LANE_CHANGE_SPEED_MIN = 20 * CV.MPH_TO_MS
LANE_CHANGE_TIME_MAX = 10.0
LANE_CHANGE_CANCEL_TORQUE_FRAMES = round(0.25 / DT_MDL)
# only the rising edge reaches the model and it stays visible for the 5 s the history spans, so one
# edge per 5 s keeps the turn alive through the maneuver without stacking edges that cut the corner
TURN_DESIRE_PULSE_CYCLE_FRAMES = round(5.0 / DT_MDL)
TURN_DESIRE_PULSE_HOLD_FRAMES = TURN_DESIRE_PULSE_CYCLE_FRAMES - 1
TURN_RECOIL_HEADING_RAD = math.radians(55.0)
TURN_DESIRE_MAX_SECONDS = 25.0
TURN_DESIRE_MOVING_SPEED = 0.5
_LANE_CHANGE_DESIRES = {
(LaneChangeDirection.none, LaneChangeState.off): log.Desire.none,
(LaneChangeDirection.none, LaneChangeState.preLaneChange): log.Desire.none,
(LaneChangeDirection.none, LaneChangeState.laneChangeStarting): log.Desire.none,
(LaneChangeDirection.none, LaneChangeState.laneChangeFinishing): log.Desire.none,
(LaneChangeDirection.left, LaneChangeState.off): log.Desire.none,
(LaneChangeDirection.left, LaneChangeState.preLaneChange): log.Desire.none,
(LaneChangeDirection.left, LaneChangeState.laneChangeStarting): log.Desire.laneChangeLeft,
(LaneChangeDirection.left, LaneChangeState.laneChangeFinishing): log.Desire.laneChangeLeft,
(LaneChangeDirection.right, LaneChangeState.off): log.Desire.none,
(LaneChangeDirection.right, LaneChangeState.preLaneChange): log.Desire.none,
(LaneChangeDirection.right, LaneChangeState.laneChangeStarting): log.Desire.laneChangeRight,
(LaneChangeDirection.right, LaneChangeState.laneChangeFinishing): log.Desire.laneChangeRight,
}
_TURN_DESIRES = {
TurnDirection.none: log.Desire.none,
TurnDirection.turnLeft: log.Desire.turnLeft,
TurnDirection.turnRight: log.Desire.turnRight,
}
_PULSED_TURN_DESIRES = {
log.Desire.turnLeft,
log.Desire.turnRight,
}
def turn_desire(turn_direction) -> log.Desire:
return _TURN_DESIRES[getattr(turn_direction, "raw", turn_direction)]
def _direction_from_blinkers(carstate) -> int:
if carstate.leftBlinker:
return LaneChangeDirection.left
if carstate.rightBlinker:
return LaneChangeDirection.right
return LaneChangeDirection.none
def _steering_nudge_matches(carstate, direction: int) -> bool:
if not carstate.steeringPressed:
return False
return (
(direction == LaneChangeDirection.left and carstate.steeringTorque > 0) or
(direction == LaneChangeDirection.right and carstate.steeringTorque < 0)
)
def _opposite_direction(direction: int) -> int:
if direction == LaneChangeDirection.left:
return LaneChangeDirection.right
if direction == LaneChangeDirection.right:
return LaneChangeDirection.left
return LaneChangeDirection.none
def _blinker_matches(carstate, direction: int) -> bool:
return (
(direction == LaneChangeDirection.left and carstate.leftBlinker and not carstate.rightBlinker) or
(direction == LaneChangeDirection.right and carstate.rightBlinker and not carstate.leftBlinker)
)
def _blindspot_matches(carstate, direction: int) -> bool:
return (
(direction == LaneChangeDirection.left and carstate.leftBlindspot) or
(direction == LaneChangeDirection.right and carstate.rightBlindspot)
)
def _read_enable_bsm() -> bool:
try:
with car.CarParams.from_bytes(Params().get("CarParams")) as cp:
return bool(cp.enableBsm)
except Exception:
return False
class DesireHelper:
def __init__(self):
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
self.lane_change_timer = 0.0
self.lane_change_ll_prob = 1.0
self.lane_change_counter_torque_frames = 0
self.prev_one_blinker = False
self.prev_nav_exit_active = False
self.nav_exit_rearmed = False
self.desire = log.Desire.none
self.alc = IQLaneSwapController(self)
self.lane_turn_controller = IQNavTurnController(self)
self.nav_exit = NavExitLaneChangeController(_read_enable_bsm())
self.lateral_edge_guard = LateralEdgeGuard()
self.lateral_edge_block = LateralEdgeBlock.none
self.lane_turn_direction = TurnDirection.none
self.nav_turn_direction = TurnDirection.none
self.turn_desire_frames = 0
self.turn_desire_cycle_input = log.Desire.none
self.turn_heading_rad = 0.0
self.turn_desire_seconds = 0.0
self.turn_recoiled = False
@staticmethod
def get_lane_change_direction(carstate):
return _direction_from_blinkers(carstate)
@staticmethod
def _nav_turn_desire(nav_state):
if nav_state is None or not getattr(nav_state, "active", False):
return TurnDirection.none
if getattr(nav_state, "maneuverPhase", NavManeuverPhase.none) != NavManeuverPhase.turnActive:
return TurnDirection.none
if not getattr(nav_state, "shouldSendTurnDesire", False):
return TurnDirection.none
return getattr(nav_state, "turnDesireDirection", TurnDirection.none)
def _clear_lane_change(self) -> None:
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
def _refresh_turn_overrides(self, carstate, nav_state) -> bool:
speed_mps = carstate.vEgo
self.lane_turn_controller.update_params()
self.lane_turn_controller.update_lane_turn(
blindspot_left=carstate.leftBlindspot,
blindspot_right=carstate.rightBlindspot,
left_blinker=carstate.leftBlinker,
right_blinker=carstate.rightBlinker,
v_ego=speed_mps,
)
self.lane_turn_direction = self.lane_turn_controller.get_turn_direction()
self.nav_turn_direction = self._nav_turn_desire(nav_state)
self.nav_exit.update_params()
self.nav_exit.update(nav_state, carstate)
return bool(self.nav_exit.active)
def _reset_required(self, lateral_active: bool, nav_exit_active: bool) -> bool:
timed_out = self.lane_change_timer > LANE_CHANGE_TIME_MAX
feature_disabled = self.alc.lane_change_set_timer == AutoLaneChangeMode.OFF and not nav_exit_active
return (not lateral_active) or timed_out or feature_disabled
def _begin_from_idle(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool) -> None:
if below_speed:
return
if one_blinker and not self.prev_one_blinker:
self.lane_change_state = LaneChangeState.preLaneChange
self.lane_change_direction = _direction_from_blinkers(self._last_carstate)
self.lane_change_ll_prob = 1.0
return
if nav_exit_active and not self.prev_nav_exit_active:
self.lane_change_state = LaneChangeState.preLaneChange
self.lane_change_direction = self.nav_exit.direction
self.lane_change_ll_prob = 1.0
def _refresh_requested_direction(self, one_blinker: bool, nav_exit_active: bool) -> None:
if one_blinker:
self.lane_change_direction = _direction_from_blinkers(self._last_carstate)
elif nav_exit_active:
self.lane_change_direction = self.nav_exit.direction
def _step_pre_lane_change(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool) -> None:
self._refresh_requested_direction(one_blinker, nav_exit_active)
blindspot_detected = _blindspot_matches(self._last_carstate, self.lane_change_direction)
self.lateral_edge_block = self.lateral_edge_guard.block_for_direction(self.lane_change_direction)
lateral_edge_blocked = self.lateral_edge_block != LateralEdgeBlock.none
steering_ready = _steering_nudge_matches(self._last_carstate, self.lane_change_direction)
# an exit can need more than one lane, but only the first move may start without a driver nudge
nav_auto_start = nav_exit_active and self.nav_exit.auto_allowed and not self.nav_exit_rearmed
self.alc.update_lane_change(blindspot_detected=blindspot_detected, brake_pressed=self._last_carstate.brakePressed)
allowed_to_launch = steering_ready or self.alc.auto_lane_change_allowed or nav_auto_start
if (not (one_blinker or nav_exit_active)) or below_speed:
self._clear_lane_change()
elif allowed_to_launch and not blindspot_detected and not lateral_edge_blocked:
self.lane_change_state = LaneChangeState.laneChangeStarting
def _step_lane_change_starting(self, lane_change_prob: float) -> None:
self.lane_change_ll_prob = max(self.lane_change_ll_prob - (2.0 * DT_MDL), 0.0)
if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
self.lane_change_state = LaneChangeState.laneChangeFinishing
def _step_lane_change_finishing(self, one_blinker: bool, nav_exit_active: bool) -> None:
self.lane_change_ll_prob = min(self.lane_change_ll_prob + DT_MDL, 1.0)
if self.lane_change_ll_prob <= 0.99:
return
self.lane_change_direction = LaneChangeDirection.none
if one_blinker:
self.lane_change_state = LaneChangeState.preLaneChange
elif nav_exit_active:
self.lane_change_state = LaneChangeState.preLaneChange
self.lane_change_direction = self.nav_exit.direction
self.nav_exit_rearmed = True
else:
self.lane_change_state = LaneChangeState.off
def _driver_cancels_lane_change(self) -> bool:
if self.lane_change_state not in (LaneChangeState.laneChangeStarting, LaneChangeState.laneChangeFinishing):
self.lane_change_counter_torque_frames = 0
return False
opposite = _opposite_direction(self.lane_change_direction)
if _steering_nudge_matches(self._last_carstate, opposite):
self.lane_change_counter_torque_frames += 1
else:
self.lane_change_counter_torque_frames = 0
return (_blinker_matches(self._last_carstate, opposite) or
self.lane_change_counter_torque_frames >= LANE_CHANGE_CANCEL_TORQUE_FRAMES)
def _cancel_lane_change(self) -> None:
self._clear_lane_change()
self.lane_change_ll_prob = 1.0
self.lane_change_counter_torque_frames = 0
def _advance_lane_change_machine(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool, lane_change_prob: float) -> None:
if self._driver_cancels_lane_change():
self._cancel_lane_change()
return
if self.lane_change_state == LaneChangeState.off:
self._begin_from_idle(one_blinker, nav_exit_active, below_speed)
return
if self.lane_change_state == LaneChangeState.preLaneChange:
self._step_pre_lane_change(one_blinker, nav_exit_active, below_speed)
return
if self.lane_change_state == LaneChangeState.laneChangeStarting:
self._step_lane_change_starting(lane_change_prob)
return
if self.lane_change_state == LaneChangeState.laneChangeFinishing:
self._step_lane_change_finishing(one_blinker, nav_exit_active)
def _update_timer(self) -> None:
if self.lane_change_state in (LaneChangeState.off, LaneChangeState.preLaneChange):
self.lane_change_timer = 0.0
else:
self.lane_change_timer += DT_MDL
def _clear_turn_desire_pulse(self) -> None:
self.turn_desire_frames = 0
self.turn_desire_cycle_input = log.Desire.none
self.turn_heading_rad = 0.0
self.turn_desire_seconds = 0.0
self.turn_recoiled = False
def _measured_yaw_rate(self) -> float:
# carState.yawRate is left unpopulated by many ports, so the model pose is the primary source
rate = getattr(getattr(getattr(self, "_last_modeldata", None), "orientationRate", None), "z", None)
try:
if rate is not None and len(rate) and math.isfinite(rate[0]):
return float(rate[0])
except (TypeError, IndexError):
pass
try:
return float(getattr(self._last_carstate, "yawRate", 0.0) or 0.0)
except (TypeError, ValueError):
return 0.0
def _turn_recoiled(self, desired_output: log.Desire) -> bool:
# blinkers keep running well past the apex, so the executed heading change has to end the
# desire: one that outlives the turn keeps steering the plan into the far side of the road
if desired_output != self.turn_desire_cycle_input:
self.turn_heading_rad = 0.0
self.turn_desire_seconds = 0.0
self.turn_recoiled = False
self.turn_heading_rad += abs(self._measured_yaw_rate()) * DT_MDL
# a red light at the junction must not use up the turn before the car moves
if float(getattr(self._last_carstate, "vEgo", 0.0) or 0.0) > TURN_DESIRE_MOVING_SPEED:
self.turn_desire_seconds += DT_MDL
if self.turn_heading_rad >= TURN_RECOIL_HEADING_RAD or self.turn_desire_seconds >= TURN_DESIRE_MAX_SECONDS:
self.turn_recoiled = True
return self.turn_recoiled
def _pulse_turn_desire(self, desired_output: log.Desire) -> log.Desire:
if desired_output not in _PULSED_TURN_DESIRES:
self._clear_turn_desire_pulse()
return desired_output
recoiled = self._turn_recoiled(desired_output)
if desired_output != self.turn_desire_cycle_input:
self.turn_desire_frames = 0
self.turn_desire_cycle_input = desired_output
cycle_frame = self.turn_desire_frames % TURN_DESIRE_PULSE_CYCLE_FRAMES
self.turn_desire_frames += 1
if recoiled or cycle_frame >= TURN_DESIRE_PULSE_HOLD_FRAMES:
return log.Desire.none
return desired_output
def _pick_desire_output(self) -> None:
desired_output = log.Desire.none
if self.nav_turn_direction != TurnDirection.none:
desired_output = turn_desire(self.nav_turn_direction)
elif self.lane_turn_direction != TurnDirection.none:
desired_output = turn_desire(self.lane_turn_direction)
else:
desired_output = _LANE_CHANGE_DESIRES[(self.lane_change_direction, self.lane_change_state)]
self.desire = self._pulse_turn_desire(desired_output)
def update(self, carstate, lateral_active, lane_change_prob, nav_state=None, modeldata=None, radar_state=None):
self._last_carstate = carstate
self._last_modeldata = modeldata
self.lateral_edge_guard.update(modeldata, carstate.vEgo, DT_MDL)
self.lateral_edge_block = LateralEdgeBlock.none
one_blinker = carstate.leftBlinker != carstate.rightBlinker
below_speed = carstate.vEgo < LANE_CHANGE_SPEED_MIN
nav_exit_active = self._refresh_turn_overrides(carstate, nav_state)
if not nav_exit_active:
self.nav_exit_rearmed = False
self.alc.update_params()
if self._reset_required(lateral_active, nav_exit_active):
self._clear_lane_change()
else:
self._advance_lane_change_machine(one_blinker, nav_exit_active, below_speed, lane_change_prob)
self._update_timer()
self.prev_one_blinker = one_blinker and lateral_active
self.prev_nav_exit_active = nav_exit_active
self.alc.update_state()
self._pick_desire_output()
@@ -0,0 +1,17 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
load_private_module(__name__, "iqpilot_private.context.aux_inputs")
except ProprietaryModuleMissing:
class AuxModelInputs:
def __init__(self, input_spec: dict):
self.active = False
def poll(self) -> None:
pass
def extras(self) -> dict:
return {}
@@ -23,7 +23,7 @@ from iqpilot.common.realtime import DT_MDL, config_realtime_process
from iqpilot.common.swaglog import cloudlog
from iqpilot.common.transformations.camera import DEVICE_CAMERAS
from iqpilot.common.transformations.model import get_warp_matrix
from iqpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from iqpilot.selfdrive.controls.lib.action_engine import ActionEngine
from iqpilot.selfdrive.controls.lib.drive_helpers import (
MODEL_SMOOTHING_MAX_TOTAL_SEC,
dynamic_lat_smooth_extra_seconds,
@@ -34,7 +34,7 @@ from iqpilot.selfdrive.locationd.calibration_helpers import get_calibrated_rpy
from iqpilot.system import sentry
from iqpilot.common.steer_delay import lateral_action_delay
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle, load_default_model_session, forced_active_bundle
from iqpilot.selfdrive.iqmodeld.models.inference_state import InferenceStateBase
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import get_model_runner
from iqpilot.selfdrive.iqmodeld.camera import CameraOffsetHelper
@@ -243,13 +243,30 @@ def _merged_plan(runtime_state: "NeuralEngineState", outputs: dict[str, np.ndarr
return base_plan + (runtime_state.PLANPLUS_CONTROL * _planplus_gain(vehicle_speed)) * outputs["planplus"][0]
def _set_default_fallback_alert(active: bool) -> None:
try:
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
set_offroad_alert("Offroad_ModelFallbackDefault", active)
except Exception:
cloudlog.exception("could not update the default-model fallback alert")
class NeuralEngineState(InferenceStateBase):
frames: dict[str, RoadProjector]
def __init__(self, gpu_context: WarpContext):
super().__init__()
runner = get_model_runner()
bundle = get_active_bundle()
try:
runner = get_model_runner()
bundle = get_active_bundle()
_set_default_fallback_alert(False)
except Exception:
cloudlog.exception("active model failed to load; running the default model this session so pose stays valid")
default_bundle = load_default_model_session()
with forced_active_bundle(default_bundle):
runner = get_model_runner()
bundle = get_active_bundle()
_set_default_fallback_alert(True)
self.model_runner = runner
self.constants = runner.constants
@@ -354,7 +371,10 @@ class NeuralEngineState(InferenceStateBase):
def get_action_from_model(self, outputs: dict[str, np.ndarray], previous_action: log.ModelDataV2.Action,
lat_action_t: float, long_action_t: float, vehicle_speed: float,
lat_smooth_seconds: float | None = None) -> log.ModelDataV2.Action:
lat_smooth_seconds: float | None = None, *,
lat_active: bool = True, current_curvature: float = 0.0) -> log.ModelDataV2.Action:
if not lat_active:
previous_action.desiredCurvature = current_curvature
if lat_smooth_seconds is None:
lat_smooth_seconds = self.LAT_SMOOTH_SECONDS
@@ -568,7 +588,7 @@ class InferenceDaemon:
self._pub = PubMaster(pub_services)
self._sub = SubMaster([
"deviceState", "carState", "roadCameraState", "extrinsicsCalibration",
"driverMonitoringState", "carControl", "lateralDelay", "iqNavState", "navInstruction", "radarState",
"driverMonitoringState", "carControl", "controlsState", "lateralDelay", "iqNavState", "navInstruction", "radarState",
] + self._feature_inputs.services)
self._message_memory = DrivePacketMemory()
self._params = Params()
@@ -580,7 +600,7 @@ class InferenceDaemon:
self._car_params = self._load_car_params(demo)
self._long_action_delay = self._car_params.longitudinalActuatorDelay + self._runtime.LONG_SMOOTH_SECONDS
self._previous_action = log.ModelDataV2.Action()
self._desire_logic = DesireHelper()
self._desire_logic = ActionEngine()
self._lat_smooth_extra_sec = 0.0
self._drive_profile_state = DriveProfileState()
self._memory_resets = MemoryResetTracker()
@@ -664,7 +684,8 @@ class InferenceDaemon:
)
lat_smooth_total_sec = min(self._runtime.LAT_SMOOTH_SECONDS + self._lat_smooth_extra_sec, MODEL_SMOOTHING_MAX_TOTAL_SEC)
action = self._runtime.get_action_from_model(
outputs, self._previous_action, lat_horizon, long_horizon, vehicle_speed, lat_smooth_total_sec
outputs, self._previous_action, lat_horizon, long_horizon, vehicle_speed, lat_smooth_total_sec,
lat_active=self._sub["carControl"].latActive, current_curvature=self._sub["controlsState"].curvature,
)
self._previous_action = action
@@ -17,7 +17,10 @@ DESIRE_LEN = 8
def get_action_from_model(outputs: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
v_ego: float, lat_action_t: float, long_action_t: float,
lat_smooth_seconds: float | None = None) -> log.ModelDataV2.Action:
lat_smooth_seconds: float | None = None, *,
lat_active: bool = True, current_curvature: float = 0.0) -> log.ModelDataV2.Action:
if not lat_active:
prev_action.desiredCurvature = current_curvature
if "action" in outputs:
desired_accel = float(outputs["action"][0, 1])
desired_curvature = float(outputs["action"][0, 0]) / (max(1.0, v_ego)) ** 2
@@ -139,7 +139,18 @@ def egpu_oob_pkl_path(meta: dict) -> str:
def egpu_model_oob_pkl_path(meta: dict) -> str:
from iqpilot.system.hardware.hw import Paths
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob.pkl")
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob2.pkl")
def drop_frozen_model_oob(meta: dict) -> None:
from iqpilot.system.hardware.hw import Paths
# warp-on-dock bundles saved under the old name were built before the prune fix and replay capture-time feeds
stale = os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob.pkl")
try:
os.remove(stale)
cloudlog.warning(f"removed frozen-feed warp-on-dock artifact {stale}")
except OSError:
pass
def onnx_cache_path(meta: dict) -> str:
@@ -288,6 +299,28 @@ def patch_tinygrad_fetch_fw() -> None:
helpers.fetch_fw = fetch_fw
def patch_tinygrad_prune_views() -> None:
from tinygrad.engine import jit
from tinygrad.uop.ops import Ops
if getattr(jit.prune_linear, "_iq_patched", False):
return
# upstream prune only sees bare BUFFER args, so a kernel reading an input through a SLICE view ran once at capture and froze that input
def prune_linear(linear, needed):
kept, onetime = [], []
for si in linear.src:
si_bufs = {b for src in si.src[1:] for b in src.toposort() if b.op is Ops.BUFFER}
if not si_bufs.isdisjoint(needed):
kept.append(si)
needed |= si_bufs
else:
onetime.append(si)
return linear.replace(src=tuple(kept)), linear.replace(src=tuple(onetime))
prune_linear._iq_patched = True
jit.prune_linear = prune_linear
def patch_usb_signal_wait() -> None:
import time
@@ -3,7 +3,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
"""
from __future__ import annotations
import functools
import hashlib
import json
import os
@@ -24,26 +23,29 @@ RANGE_RETRIES = 4
PARALLEL_PARTS = max(1, int(os.environ.get("IQ_DOWNLOAD_PARTS", "3")))
def _requests_auth():
def _git_auth():
import importlib
for mod in ("iqpilot_private.models.git_auth", "iqpilot.models_private_src.git_auth",
"iqpilot.selfdrive.iqmodeld.models.git_auth"):
try:
return importlib.import_module(mod).get_requests_auth()
return importlib.import_module(mod)
except Exception:
continue
return None
def _hf():
import importlib
for mod in ("iqpilot_private.models.git_auth", "iqpilot.selfdrive.iqmodeld.models.git_auth"):
try:
m = importlib.import_module(mod)
return m.get_hf_headers(), m.hf_resolve_url
except Exception:
continue
return None, None
def _resolve_hf(hf_path: str) -> tuple[str | None, dict]:
m = _git_auth()
if m is None:
return None, {}
return m.resolve_hf(hf_path)
def requests_get(url: str, **kwargs):
m = _git_auth()
if m is None:
raise RuntimeError("no asset broker available")
return m.requests_get(url, **kwargs)
class RangesNotHonoured(Exception):
@@ -189,10 +191,9 @@ def _verify_file(tmp: str, sha256: str, size: int) -> None:
def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb=None) -> str:
import requests
headers, resolve = _hf()
if resolve is None:
url, headers = _resolve_hf(hf_path)
if url is None:
raise RuntimeError("no HF credentials available")
url = resolve(hf_path)
os.makedirs(os.path.dirname(dst), exist_ok=True)
tmp = dst + ".hfpart"
last_error: Exception | None = None
@@ -213,8 +214,10 @@ def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb
os.remove(leftover)
except Exception as e:
last_error = e
for _attempt in range(STREAM_RETRIES):
for attempt in range(STREAM_RETRIES):
try:
if attempt:
url, headers = _resolve_hf(hf_path)
have = os.path.getsize(tmp) if os.path.isfile(tmp) else 0
if size and have > size:
os.remove(tmp)
@@ -242,26 +245,6 @@ def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb
raise RuntimeError(f"HF download failed: {last_error}")
def _lfs_endpoint(base_url: str) -> str:
return base_url.split("/raw/", 1)[0] + ".git/info/lfs"
def _resolve_oid(session, base_url: str, oid: str, size: int, auth):
import requests
batch = session.post(f"{_lfs_endpoint(base_url)}/objects/batch",
data=json.dumps({"operation": "download", "transfers": ["basic"],
"objects": [{"oid": oid, "size": size}]}),
headers={"Content-Type": "application/vnd.git-lfs+json",
"Accept": "application/vnd.git-lfs+json"},
auth=auth, timeout=HTTP_TIMEOUT_S)
batch.raise_for_status()
entry = batch.json()["objects"][0]
if "actions" not in entry:
raise requests.RequestException(f"LFS object unavailable: {entry.get('error', oid)}")
action = entry["actions"]["download"]
return action["href"], action.get("header", {})
def _part_path(dst: str, oid: str) -> str:
return os.path.join(dst + ".parts", oid)
@@ -276,19 +259,28 @@ def _part_complete(path: str, oid: str, size: int) -> bool:
return digest.hexdigest() == oid
def _fetch_part(session, base_url: str, obj: dict, path: str, auth, progress) -> None:
def _fetch_part(session, obj: dict, path: str, progress) -> None:
size = int(obj["size"])
have = os.path.getsize(path) if os.path.isfile(path) else 0
if have > size:
os.remove(path)
have = 0
href, headers = _resolve_oid(session, base_url, obj["oid"], size, auth)
obj_auth = None if headers.get("Authorization") else auth
m = _git_auth()
if m is None:
raise RuntimeError("no asset broker available for LFS parts")
target = m.broker_lfs_url(obj["oid"], size)
headers = m.broker_headers() if target else {}
if not (target and headers):
raise RuntimeError("no device identity for the konn3kt asset broker")
_stream_part(session, target, headers, path, have, progress)
def _stream_part(session, href: str, headers: dict, path: str, have: int, progress) -> None:
# LFS parts are content-addressed (oid == sha256), so a half-written part can be resumed with a
# Range request and verified afterwards instead of being thrown away on every restart.
if have:
headers = {**headers, "Range": f"bytes={have}-"}
with session.get(href, headers=headers, stream=True, timeout=HTTP_TIMEOUT_S, auth=obj_auth) as r:
with session.get(href, headers=headers, stream=True, timeout=HTTP_TIMEOUT_S) as r:
r.raise_for_status()
if have and r.status_code != 206:
have = 0
@@ -300,7 +292,6 @@ def _fetch_part(session, base_url: str, obj: dict, path: str, auth, progress) ->
def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progress_cb=None) -> str:
import requests
auth = _requests_auth()
os.makedirs(dst + ".parts", exist_ok=True)
total = int(size) or sum(int(o["size"]) for o in objects)
done_bytes = sum(int(o["size"]) for o in objects if _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"])))
@@ -318,30 +309,26 @@ def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progres
with lock:
progress(n)
def fetch(base_url: str, obj: dict) -> None:
def fetch(obj: dict) -> None:
path = _part_path(dst, obj["oid"])
if _part_complete(path, obj["oid"], int(obj["size"])):
return
_fetch_part(requests.Session(), base_url, obj, path, auth, locked_progress)
_fetch_part(requests.Session(), obj, path, locked_progress)
if not _part_complete(path, obj["oid"], int(obj["size"])):
if os.path.getsize(path) >= int(obj["size"]):
os.remove(path)
raise RuntimeError(f"part {obj['oid'][:12]} incomplete or failed verification")
for base_url in MODELS_BASE_URLS:
for _attempt in range(STREAM_RETRIES):
try:
pending = [o for o in objects if not _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"]))]
got[0] = total - sum(int(o["size"]) for o in pending)
with ThreadPoolExecutor(max_workers=max(1, min(PARALLEL_PARTS, len(pending) or 1))) as pool:
for _ in pool.map(functools.partial(fetch, base_url), pending):
pass
break
except Exception as e:
last_error = e
else:
continue
break
for _attempt in range(STREAM_RETRIES):
try:
pending = [o for o in objects if not _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"]))]
got[0] = total - sum(int(o["size"]) for o in pending)
with ThreadPoolExecutor(max_workers=max(1, min(PARALLEL_PARTS, len(pending) or 1))) as pool:
for _ in pool.map(fetch, pending):
pass
break
except Exception as e:
last_error = e
else:
raise RuntimeError(f"model bundle download failed: {last_error}")
@@ -17,7 +17,8 @@ _OVERRIDE_KEYS = (
"combinedSplitArtifact",
"iqCombinedArtifact",
)
_SPLIT_ROLE_PATTERN = re.compile(r"^driving_(vision|policy|off_policy|on_policy)_(.+)_tinygrad\.pkl$")
_SPLIT_ROLE_PATTERN = re.compile(r"^driving_(vision|policy|off_policy|on_policy)_(.+)_tinygrad\.pkl(?:\.iqc)?$")
_IQC_SUFFIX = ".iqc"
def _bundle_models(bundle) -> list:
@@ -91,8 +92,9 @@ def combined_split_artifact_candidates(bundle) -> list[Path]:
def resolve_combined_split_artifact(bundle) -> Path | None:
for candidate in combined_split_artifact_candidates(bundle):
if candidate.is_file():
return candidate
for path in (candidate, candidate.with_name(candidate.name + _IQC_SUFFIX)):
if path.is_file():
return path
return None
@@ -305,7 +305,40 @@ def get_runtime_bundle_upgrade(bundle, params: Params = None, available_bundles=
return _find_runtime_upgrade(bundle, params, available_bundles)
_forced_bundle = None
def load_default_model_session():
"""Stage the shipped default model files and return a decoded default bundle
WITHOUT persisting it as the active selection, so the next boot retries the
user's own model instead of staying on the default forever."""
bundle_dict = _load_default_bundle_dict()
ensure_default_model_files(bundle_dict)
return ModelBundle(**bundle_dict)
class forced_active_bundle:
"""Make get_active_bundle return `bundle` for the duration of the block, used to
build a runner for a session-only fallback bundle without touching Params."""
def __init__(self, bundle):
self._bundle = bundle
def __enter__(self):
global _forced_bundle
self._prev = _forced_bundle
_forced_bundle = self._bundle
return self._bundle
def __exit__(self, *exc):
global _forced_bundle
_forced_bundle = self._prev
return False
def get_active_bundle(params: Params = None):
if _forced_bundle is not None:
return _forced_bundle
params = Params() if params is None else params
try:
@@ -0,0 +1,11 @@
#!/usr/bin/env python3
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
load_private_module(__name__, "iqpilot_private.models.iqc")
except ProprietaryModuleMissing:
from iqpilot.models_private_src.iqc import * # noqa: F403
@@ -2,7 +2,6 @@
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import os
import pickle as _pk
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
@@ -13,6 +12,7 @@ from iqpilot.system.hardware import TICI
from iqpilot.system.hardware.hw import Paths as _hw_paths
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle as _fetch_bundle
from iqpilot.selfdrive.iqmodeld.models.combined_artifact import has_combined_split_artifact
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
# ---- runtime type surface (native OpenCL/frame handles resolve to Any off-device) ----
if TYPE_CHECKING:
@@ -73,8 +73,7 @@ def load_artifact_metadata(metadata_filename):
if os.path.getsize(path) > _META_MAX_BYTES:
cloudlog.error(f"metadata pkl {metadata_filename} is artifact-sized, refusing to unpickle it")
return tuple({} for _ in _META_FIELDS)
with open(path, 'rb') as fh:
blob = _pk.load(fh)
blob = load_pickle(path)
return tuple(blob.get(field, {}) for field in _META_FIELDS)
except Exception:
cloudlog.exception(f"unreadable metadata pkl {metadata_filename}, continuing without it")
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
from __future__ import annotations
import pickle
from typing import Any
import numpy as np
@@ -15,6 +14,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import NumpyDict, Sh
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
def _tinygrad_imports():
@@ -64,8 +64,7 @@ class TinygradCombinedSplitRunner(ModelRunner):
if self._artifact_path is None:
raise FileNotFoundError("No IQ combined split artifact is available for the active bundle")
with open(self._artifact_path, "rb") as artifact:
runtime_package: dict[Any, Any] = pickle.load(artifact)
runtime_package: dict[Any, Any] = load_pickle(self._artifact_path)
self._meta_by_role = runtime_package.get("meta_by_role", runtime_package.get("metadata", {}))
self._policy_roles = runtime_package.get("roles", _phase_roles(self._meta_by_role))
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
from __future__ import annotations
import os
import pickle
from typing import Any
import numpy as np
@@ -15,6 +14,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import (
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
def _tinygrad_imports():
@@ -40,8 +40,7 @@ class TinygradFusedRunner(ModelRunner):
self._model_data = next(iter(self.models.values()))
pkl_path = os.path.join(CUSTOM_MODEL_PATH, self._model_data.model.artifact.fileName)
with open(pkl_path, 'rb') as f:
self._fused: dict[Any, Any] = pickle.load(f)
self._fused: dict[Any, Any] = load_pickle(pkl_path)
self._vision_meta = self._fused['metadata']['vision']
self._on_meta = self._fused['metadata']['on_policy']
@@ -5,7 +5,6 @@ from __future__ import annotations
import hashlib
import os
import pickle
import re
from typing import Any
@@ -17,6 +16,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
from iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.supercombo_runtime import TinygradSupercomboRuntime
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
def _captured_queue_depth(warp_jit: Any) -> int | None:
@@ -77,8 +77,7 @@ class TinygradSupercomboRunner(TinygradSupercomboRuntime, ModelRunner):
self._pkl_path = pkl_path
self._expected_sha256 = getattr(getattr(self._model_data.model.artifact, "downloadUri", None), "sha256", "") or ""
self._verify_artifact_file()
with open(pkl_path, 'rb') as f:
self._m: dict[Any, Any] = pickle.load(f)
self._m: dict[Any, Any] = load_pickle(pkl_path)
self._meta = self._m['metadata']
self._ish = self._meta['input_shapes']
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
from __future__ import annotations
import pickle
from dataclasses import dataclass
import numpy as np
@@ -32,6 +31,7 @@ from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelCo
from iqpilot.selfdrive.iqmodeld.config import ModelConstants
from iqpilot.selfdrive.iqmodeld.runtime.tinygrad import qcom_tensor_from_opencl_address
from iqpilot.system.hardware import TICI
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
@dataclass(frozen=True)
@@ -45,12 +45,11 @@ def _artifact_path(filename: str) -> str:
def _load_program_blob(filename: str):
with open(_artifact_path(filename), "rb") as artifact:
try:
return pickle.load(artifact)
except FileNotFoundError as exc:
assert "/dev/kgsl-3d0" not in str(exc), "Model was built on C3 or C3X, but is being loaded on PC"
raise
try:
return load_pickle(_artifact_path(filename))
except FileNotFoundError as exc:
assert "/dev/kgsl-3d0" not in str(exc), "Model was built on C3 or C3X, but is being loaded on PC"
raise
def _compile_input_plan(captured) -> dict[str, _TensorShapePlan]:
@@ -81,7 +80,7 @@ class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTiny
raise ValueError(f"Model data for type {model_type} not available.")
asset_name = self._model_data.model.artifact.fileName
assert asset_name.endswith("_tinygrad.pkl"), f"Invalid model file {asset_name} for TinygradRunner"
assert asset_name.removesuffix(".iqc").endswith("_tinygrad.pkl"), f"Invalid model file {asset_name} for TinygradRunner"
self.model_run = _load_program_blob(asset_name)
self._input_plan = _compile_input_plan(self.model_run.captured)
@@ -125,16 +125,32 @@ class Amplifier:
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
def set_configs(self, configs: list[AmpConfig]) -> bool:
import threading
tries = 15
backoff = 0.
for i in range(tries):
try:
self._set_configs(configs)
return True
except OSError:
result = [None]
error = [None]
def _worker():
try:
self._set_configs(configs)
result[0] = True
except OSError as e:
error[0] = e
t = threading.Thread(target=_worker, daemon=True)
t.start()
t.join(timeout=2)
if t.is_alive():
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
continue
if result[0]:
return True
backoff += 0.1
time.sleep(backoff)
print(f"Failed to set amp config, {tries - i - 1} retries left")
return False
def set_global_shutdown(self, amp_disabled: bool) -> bool:
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
if self.amplifier is not None:
self.amplifier.initialize_configuration(self.get_device_type())
# Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
try:
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
except Exception:
pass
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
gpio_init(GPIO.SOM_ST_IO, True)
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
affine_irq(3, "xhci-hcd:usb1")
try:
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessException as e:
print(str(e))
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
pass
def configure_modem(self):
from iqpilot.common.params import Params
@@ -1 +0,0 @@
f217b0f040cfa83f51d6e935f1646946f5992b58
@@ -1,8 +1,9 @@
{
"bundle_version": 1792395046,
"python/_iqclosure/_vendored.json": {
"mode": 420,
"sha256": "8cbd4406c95aeca668d07f6714cffdfc00d7143988901bfe86c1bb1381019996",
"size": 2050
"sha256": "93d71b5f76734cb6ceff3ead9218b21727bf47814a1a25d6be5a44994b9528fb",
"size": 1717
},
"python/_iqclosure/iqpilot/__init__.py": {
"mode": 420,
@@ -16,34 +17,24 @@
},
"python/_iqclosure/iqpilot/common/api/__init__.py": {
"mode": 420,
"sha256": "97d6e0d8cbd3e47797d734322b42d04efea4d95a2dae5a56f1baef3fd847aa0d",
"size": 939
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
"size": 900
},
"python/_iqclosure/iqpilot/common/api/base.py": {
"mode": 420,
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
"size": 3451
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
"size": 4921
},
"python/_iqclosure/iqpilot/common/api/comma_connect.py": {
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
"mode": 420,
"sha256": "e7c2dd0b56fb8533c2cbc614e728a9ffc5ce7360524cfa62a02f5235bc23b9a8",
"size": 267
},
"python/_iqclosure/iqpilot/common/atlas_alerts.py": {
"mode": 420,
"sha256": "a8b47e01f604d41d4959d4b0f5ed594c1ab90e5b0f7fb2962b5a4a71ec766598",
"size": 9411
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
"size": 313
},
"python/_iqclosure/iqpilot/common/basedir.py": {
"mode": 420,
"sha256": "58cfa5d6405903ee1acecc4544463bb8f47a66921b120c382143aa80d8539b28",
"size": 106
},
"python/_iqclosure/iqpilot/common/constants.py": {
"mode": 420,
"sha256": "fc439d59c24b95ed349b84f1a5862df3ea50e0891dbec4ea3cf5e1e0ccff6513",
"size": 434
},
"python/_iqclosure/iqpilot/common/file_helpers.py": {
"mode": 420,
"sha256": "3c661d05dea697fae741b3e65a93385e4e0f5941eae8e8f4ea64da8da90e2f52",
@@ -64,11 +55,6 @@
"sha256": "4af8f9da7504a548d15f18b91d71c1379cb7c05c84be031448764cc49920ce68",
"size": 2449
},
"python/_iqclosure/iqpilot/common/issue_debug.py": {
"mode": 420,
"sha256": "6fadd02538b85f92963f247a43dcc6f50cf4c65cdc4171f3c7e03646a76fcd1b",
"size": 1019
},
"python/_iqclosure/iqpilot/common/logging_extra.py": {
"mode": 420,
"sha256": "ce9b3b3d8498d76e1c4c0352a3e98e72759bb7f367ff8629c1329c46040595c3",
@@ -79,11 +65,6 @@
"sha256": "016be524079ef32df03b2c135eab8f83da60a46fc7f143620b836c65f33e39a3",
"size": 4423
},
"python/_iqclosure/iqpilot/common/spinner.py": {
"mode": 420,
"sha256": "33a2eb2aa3148492414035c5dc7150b3a69467905d7a3e58c8f65cfe70d3552c",
"size": 1371
},
"python/_iqclosure/iqpilot/common/swaglog.py": {
"mode": 420,
"sha256": "0a133b4ef49f80b8890f115e81273f3f3e21351ae79f92dc02abc033e6d24027",
@@ -96,8 +77,8 @@
},
"python/_iqclosure/iqpilot/common/utils.py": {
"mode": 420,
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
"size": 5159
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
"size": 5892
},
"python/_iqclosure/iqpilot/common/version.py": {
"mode": 420,
@@ -116,8 +97,8 @@
},
"python/_iqclosure/iqpilot/konn3kt/registration.py": {
"mode": 420,
"sha256": "e9c37e8ae0a70c3a19ca1a33b290e94658b255fab153a941f50f8a29168d75c3",
"size": 7816
"sha256": "5a2b06d919adce513fb8a7e4e6f26d48e502e92dbd89f9af7a0e67e9114ab3be",
"size": 2683
},
"python/_iqclosure/iqpilot/konn3kt/service_health.py": {
"mode": 420,
@@ -141,8 +122,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/car/vehicle_catalog.json": {
"mode": 420,
"sha256": "9226a2475a10088d7f472af2de9e9e3884522562db9432376c4a0def6142fe38",
"size": 122493
"sha256": "ac6ffed7e5374df5add04646af383980d251d400b0617755bd54a1fc85d21325",
"size": 124301
},
"python/_iqclosure/iqpilot/selfdrive/car/vehicle_catalog.py": {
"mode": 420,
@@ -161,8 +142,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
"mode": 420,
"sha256": "a9f2bc8477b58a3df7196f4b40cdddf631e28051809e9bbfdd4c78148444c474",
"size": 10891
"sha256": "af83d52609ec4f2abe82f4e5f788ce4788bdc41cbf7b3de36dc63f1f598ef46c",
"size": 12107
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
"mode": 420,
@@ -171,8 +152,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py": {
"mode": 420,
"sha256": "83b0190f19e3f037c937a03b1988931729ed43d4dac92223aba0745697611ed8",
"size": 12835
"sha256": "62b13a05afbe9b5fb5d9e3d09c1491b37826ff840126c5e317a7ebf1edc28762",
"size": 12060
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/__init__.py": {
"mode": 420,
@@ -186,48 +167,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
"mode": 420,
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
"size": 14051
},
"python/_iqclosure/iqpilot/selfdrive/locationd/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/locationd/calibrationd.py": {
"mode": 420,
"sha256": "6bceebb5a15895d2b6d20a28cbd14b9112f9422e0a0cf466314a7e8779da39dc",
"size": 15698
},
"python/_iqclosure/iqpilot/selfdrive/selfdrived/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/selfdrived/alertmanager.py": {
"mode": 420,
"sha256": "c601bac30951a750d3ebf3d30f06c597bacd148210b1c405d5d3eca79bb7c4e3",
"size": 2045
},
"python/_iqclosure/iqpilot/selfdrive/selfdrived/events.py": {
"mode": 420,
"sha256": "e1b113565c45271ec5310dd83940dd338ff7fa56a2bf121f864dd58cc0ba1343",
"size": 32024
},
"python/_iqclosure/iqpilot/selfdrive/ui/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/ui/feedback/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/_iqclosure/iqpilot/selfdrive/ui/feedback/feedbackd.py": {
"mode": 420,
"sha256": "7c1e6cb2c3c2c6aae91210f6cbc5c372f33c28cc3edb292ef71439b83844615d",
"size": 2615
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
"size": 15008
},
"python/_iqclosure/iqpilot/system/__init__.py": {
"mode": 420,
@@ -266,8 +207,8 @@
},
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
"mode": 420,
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
"size": 7585
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
"size": 8016
},
"python/_iqclosure/iqpilot/system/hardware/tici/esim_manager.py": {
"mode": 420,
@@ -276,8 +217,8 @@
},
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
"mode": 420,
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
"size": 25601
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
"size": 25731
},
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
"mode": 420,
@@ -316,18 +257,13 @@
},
"python/_iqclosure/iqpilot/system/manager/process.py": {
"mode": 420,
"sha256": "04bf7d945e43ed67362916884caf9ac6011ddd5dacc985f2adb282eacbdf225b",
"size": 10130
"sha256": "703663ef081e8786b0071df02d95b404b9236c6162d044a0766eb34ef3181875",
"size": 9926
},
"python/_iqclosure/iqpilot/system/manager/process_config.py": {
"mode": 420,
"sha256": "f7a4f10168ce71e490578164c08328e1e983bd9e72df955c7e34d4eda6be7086",
"size": 15379
},
"python/_iqclosure/iqpilot/system/micd.py": {
"mode": 420,
"sha256": "1a59cbe6b72fcca5723ab56dc09f52039817299071a8d8da37f04acea58776bc",
"size": 5977
"sha256": "5d5f9354ac81904097cd7c25f6ea9ee2c5dcf74ed63a3400530dddcc6a1f1660",
"size": 15410
},
"python/_iqclosure/iqpilot/system/phonegps/__init__.py": {
"mode": 420,
@@ -366,8 +302,8 @@
},
"python/_iqclosure/iqpilot/system/ui/lib/wifi_manager.py": {
"mode": 420,
"sha256": "c9fe91484344f154726b0cab62a19f58a172e4f06dfd6341ca9b179c0c4646a8",
"size": 41618
"sha256": "859d3eb2f56eb456f1d6d2df18d93c5d4990a7f4a6e75be495930a19efcb5151",
"size": 42121
},
"python/_iqclosure/iqpilot/system/version.py": {
"mode": 420,
@@ -400,29 +336,29 @@
"size": 0
},
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "7afc7118cb0e8fcfcdc552a7df224cd02dedda078d79e3484cc7c2c87209582c",
"size": 135480
"mode": 416,
"sha256": "97137b00af89cc21ec2b9c07f5887304ff0a7379619b37837384c3e6d49a96a2",
"size": 1130808
},
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "bd195bcc71dd7f15975ef2bbb28317fd7518f68c73a3d056d214b8b3446a8852",
"size": 67680
"mode": 416,
"sha256": "0674dcaf72b8137be6c75ac220001f2ea440fea92c93c5211a01e0d513772df7",
"size": 1065192
},
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "53fe8078e5388290dc36fd765c3d923b9617ac35cb7cb94c4faf8234afa69766",
"size": 204616
"mode": 416,
"sha256": "3e8e9d8d2ac2282cda1bb02e7ae366c373191bd49c49de47e09effa2f000a4f2",
"size": 1130968
},
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "c077e27d89e8b1704b4a179654690c2e0d2db08941bf8b55e511a3e524e88508",
"size": 69752
"mode": 416,
"sha256": "2135582d5a3cedf8247dce06242619e90e1d519a44548891de071b7cab1ec694",
"size": 1065144
},
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b014f9ddb6ad90a40877ff1bcf74f00f493b8b128e32025aff876cae6458ab03",
"size": 203064
"mode": 416,
"sha256": "d36c107d46199f3efc6f10b266f2658119212e184dbcdbecff09c450a6dfc381",
"size": 1065368
},
"python/iqpilot_private/konn3kt/flockd/__init__.py": {
"mode": 420,
@@ -430,14 +366,14 @@
"size": 0
},
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3a066b70e0f2755f97fa9886389dd0e50392a9a01f0dc6c8eda2655cdf44d010",
"size": 268240
"mode": 416,
"sha256": "c801140079340b2e4ab00ad925ad7bc8b5184ea7f19601643ad03f65c2dd5f09",
"size": 1196424
},
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "15d8e8b6a620c41dc6fd597ff65e6545ba4da13297355a12efabca92107e1a23",
"size": 68104
"mode": 416,
"sha256": "66129d17569ca6678ba81dd7bda5d06fd68c1a7a84c0b4e6a2a610cbaf640686",
"size": 1065144
},
"python/iqpilot_private/konn3kt/hephaestus/__init__.py": {
"mode": 420,
@@ -446,118 +382,133 @@
},
"python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip": {
"mode": 420,
"sha256": "17a7e20afa7a49c43883f54a7b7f2dd0319eac0235a2d87ee1f6b70b37ef5af3",
"size": 1379463
"sha256": "8562471927037cfce082e59054ffcdd39ca67519e8bad3f2583418f28f898960",
"size": 1379541
},
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "330b1a62d6ddc23a20c493945dcf1e68bb0144cfbe3b44f9d3e06c65ad438c02",
"size": 268072
"mode": 416,
"sha256": "16a9c7dd5865cc62f67af7f7af931adc31a82a3292f4e952c153a68b3e557e47",
"size": 1262120
},
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ae55af9d22773bcb5b497fdc7da051345341c2699590d61dae0a6ba0ab6b8072",
"size": 68104
"mode": 416,
"sha256": "8ec415342cdb92e468f477f36117ec9e9e3ed3c4e10625a133fbf1b6ffc7db7b",
"size": 999768
},
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b16d70ff35a87b2707d00de69179d672f89a8cc8c355344fa0bd395df5a85d4f",
"size": 472568
"mode": 416,
"sha256": "d940e5fa03e70641c613eb4d0a1392c4564d9393dee7226112564f6041ccc396",
"size": 1327640
},
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "9f42acb2fd579d548f05eabd6f892a27ccd0b5224e052dbe0a1caa8bcae1e15a",
"size": 68080
"mode": 416,
"sha256": "8cef60cba9aa38e086c585c786b60223d7d7b858a02ad88d19a1f434d73db9d9",
"size": 1065272
},
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "9b3de54dc169aed94abed3620e5ddc58865317c1c4865777d249e0056e459cf8",
"size": 402424
"mode": 416,
"sha256": "8e080bac97b176d2a7b271bedc3ac735c11079e8ac2a03cd2c19b4761155ccde",
"size": 1393080
},
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "987e5afd63f14b691fbc313b4ccd55d03be8f378a06829dde4cbe05d5f9d6fbc",
"size": 337616
"mode": 416,
"sha256": "bae356c2438f4c779b72c829deda319e043ce5792c624864a72b0883d6196d4a",
"size": 1262056
},
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "c2e296298df67271d0a113fd36f5af5d9fe2dd645decaffe3b8af685c8e2266e",
"size": 134424
"mode": 416,
"sha256": "a1bb5651a3f2bba40cca75070b721fb8c3e0108475e15227f4c2e218d201b4d0",
"size": 1065320
},
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3f3fd10b7394e9534738c3cfc2ae6c6a3dad8d0dd7953f009e46d017f66ef190",
"size": 68152
"mode": 416,
"sha256": "e1184e9e8d5fee473e99e0edd504784d1edbbf4b5898f1eeb14794e25d2ccb7d",
"size": 999800
},
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d2f5d3004195e660b86639e4c82402b299204db9deb161558dad9f2907abf9ed",
"size": 4311240
"mode": 416,
"sha256": "89a1ee80f07a7547ecc81e1293300969079095046b859fafd9a9871f5fe51687",
"size": 5065800
},
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "fab6aec7f68e57b725ec68f049c23af734502b2bbd31e0c582ad8147ed9fa64c",
"size": 402296
"mode": 416,
"sha256": "690af279a513a1491ec2639d6aa6bf391a5a690456458ab1c122e90664c0848f",
"size": 1393128
},
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "df46250fca25ef5e87c50c3a0caad434dd92fa893f8c9e77300d331d63cb2fc5",
"size": 271008
"mode": 416,
"sha256": "92a8bc5e07148a44920f90ed6de201c81011f77a7d38675ad8ae60ac263f3bde",
"size": 1196504
},
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "0430935f4dea6b17d1eea372e07203b74b2842cef69bcc149db5921b99ce5499",
"size": 338912
"mode": 416,
"sha256": "f68dac3600056ba38f65894285b89d266c494b210d8f40d754f6883bf42439e5",
"size": 1262040
},
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "81b0e7ee271a24ac155731b02e5d3fdc1b00e6f86afedcfb657eb3331006ad56",
"size": 136384
"mode": 416,
"sha256": "60842713081ba77c08871ddf37799b26c0934615d1e3d49d22dd677be8eaf830",
"size": 1065288
},
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "c4f596d81eee41ac1d11bbe74ff388c37ea88eab61d51148280cdfa2648af8a9",
"size": 133752
"mode": 416,
"sha256": "1b468f0e37f7647d4b3597f945b13bcdb6261277c9ee82dde1e4db0c01ae1777",
"size": 1065400
},
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d76998ffe1a7250545dc2dd79c7f0463a16c1370000273dd6eea3acd6be7b154",
"size": 67872
"mode": 416,
"sha256": "dc3f21fdd7dfc4baa6acfc884716a56de0f66974a51df1fbb8ba977b5f103ed8",
"size": 999720
},
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2c50d0a84df01de778666ea92969e28226a91b87d6da7e6ac0d419ba25743598",
"size": 137120
"mode": 416,
"sha256": "bdcb85c47639922960d380b6dd2fbf68883b4fc76de48da4d9fd93668b38cb45",
"size": 1065224
},
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "673812cb76a3dc353e7726bf940d68ee8c2b9ff3316dbddce0de972e72233150",
"size": 135816
"mode": 416,
"sha256": "0d69eab9b5c27d88d0c99a0cb6e9bfb9aa50310d4b823753644b3e3eb200ad3d",
"size": 1196440
},
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "734054caeac7fe5b17f69ee5d2ec917a93463a562cac50d584b8b6da5a7ab4ed",
"size": 204080
"mode": 416,
"sha256": "cda5e46e4746b54d25012ea5966da9778e396d74ef4bb6eac762fba4c17b6fae",
"size": 1065224
},
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3ba641937f57ce36a3663eb85df9facb97bfb5c637a776d81b63e7e74482c2d5",
"size": 201912
"mode": 416,
"sha256": "4f3666dc01af607a8860b7d26689f4790797b25633bbe5049f7a2cc3f75ea68f",
"size": 1196472
},
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "1aa10dad0406f6d71298fb017d1cb32f4516db52eb5fe97018d220171f81048e",
"size": 205248
"mode": 416,
"sha256": "b836c3f257c530f56882c15ea0b836c47bc270e57eb38424cfb763a2509a1f70",
"size": 1130696
},
"python/iqpilot_private/konn3kt/hephaestus/session_bridge.cpython-312-aarch64-linux-gnu.so": {
"mode": 416,
"sha256": "e4805ad4be15e8ba8fd3856b87161e95e627687275b97dd68f8ac8a84ae1c38d",
"size": 1065352
},
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "1fbb0bb01ce46805ca383b3ba207bf40b08647a047336fe0d00936518f241945",
"size": 135584
"mode": 416,
"sha256": "4c27b4ad1ee41a5fbd854392fc5541f9532071d87a333e2fef43a253f1c78cc1",
"size": 1065368
},
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2ae7c4618618edc31b56a8720ffcf5dea7772fd6c00ce5a68703a58990757732",
"size": 269536
"mode": 416,
"sha256": "badeebe1aea6c0be071bebc7bffa3368d528a48b517e5a079b40f9c8579803d7",
"size": 1130920
},
"python/iqpilot_private/konn3kt/registration/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/iqpilot_private/konn3kt/registration/reg_core.cpython-312-aarch64-linux-gnu.so": {
"mode": 416,
"sha256": "338b657e8e87de063ca1d04600315f4cf30ffae34be5029e6415e52d6cd4b786",
"size": 1065304
},
"python/iqpilot_private/konn3kt/telemetry/__init__.py": {
"mode": 420,
@@ -565,9 +516,9 @@
"size": 0
},
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "be7294d1034251717952a68a6831902d5ce8ea903f69b16fc3bbf7d232043d05",
"size": 540624
"mode": 416,
"sha256": "11cee4ff4e4041bbbb8ba76a0846a5eedbaee7fc172c7abba855a09e394915a1",
"size": 1393240
},
"python/iqpilot_private/konn3kt/uploaderd/__init__.py": {
"mode": 420,
@@ -575,9 +526,9 @@
"size": 0
},
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ea3c6f1c89a3411773fcbab54e73f5b6997765a42c5fbe6d12249eccc321d31e",
"size": 336264
"mode": 416,
"sha256": "2b6d4963a7370a2cac157b3ab44c02f251d7be221fbf7ec33df0b096a0ad3e3d",
"size": 1262072
},
"runtime": {
"entries": {
@@ -601,6 +552,14 @@
"path": "python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.hephaestus.iqsentryd": {
"path": "python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.hephaestus.k3wgd": {
"path": "python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.hephaestus.manage_hephaestusd": {
"path": "python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
@@ -624,36 +583,38 @@
}
},
"signatures": {
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "YpZLvLgHCf+QVH7Yo1fml62OiJu7oUVZkvT/4VScxphS4ul8GjBe+wQ+r2pFE+E1DkZ3h+QLQoI9WgqeC0HRDg==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "TVUDTKJVz5koYuF2SxNScKNChgHO3adhQdhpZtPE3IVC+zmOr/BVxGJpykyYaFx1J541DydjM3pYeiVbkBtDCA==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "zFIzelMLy+b9fNs3EDCwnc5/9R4KxGQyhACAbjqGogKDuo18t1FfDnQHzoa4vYqrkLcvVTRwgVP27/CWR5sNAA==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "RImusUqqBvtJLKEgQbzOLZRn8izo9Pzll7BaFbqqfeJCaqRGBcWzNc70YcJQ5wodEaQzaY2DLIsu07l/o0ScAQ==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "MCSdZIEfeTNKhHrL/vQChDUxRx51miixM0UAMJJ4SKkvbvmVwN56Y34B6A/aLRJvVDH8YXyUZnUCQaR8+UhgCg==",
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "YP/Op7Ol87glLrYv77mMifZyBM6d4Dj/X3wiNKIcB8ECaDCtbhzTmOwlh2eO+HZIWdxT57PCu1FEhdWlb7RaCA==",
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "uNYw3cE1Ffda+71eLKrU3sjFrpG7xNuhfTNf8X8ubGYFhp3tmAf2sp1+hTyvef/Mi+QjtF00CE0rnZ/dra98CQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "xibUheiHQQs9Cx7ygWhYYjr7/CGBMvsSv1E0IhLyejZifAnUQQ1tK0wWcfdwFrzw3iFTgO9hp55fDHXPJV2qDw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": "WguojStOM6IoIAZ3IFCmUcIJowx8UtuL8+qgn61BzvfnF2/AVS/9a+vdVvKZXwXMuZsAkKAd+GZWt4rLPUFXBw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "NqiUDgwj60ZdB6SYVIPkGh0HwFIQv2z0p0BKKingy5EyFThcKfbELMk86wYRbXAZnjkCUdTWrhxsxp9sIrSEAw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "+xL+bgAX904lJa3+fesZC3e0FAf+CbXByelJpRIjlz94eGG3h5qVgozeFalGUZYdhZpJb42SyAze4DcpaxPUAg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "kmmmMdiMMIIslmInzMjniLfmp/VP5scfuAbRAvNA9QRbmxxw4ko5nnya0VhTjS7d8hM0rRZ/McI4qAJByPpYCw==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "LLGPCnUzDdyF22dJ/qhI6gYPU8hx3xeL9LeyOEqP//gzoa4jNCJeI+HWYLz0teOSoJssiGwLJ+ZuAtuT7MIpCQ==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "kHEBEwEVZAodATQ/+/wSNyKJ5S7p2AiTqZ+kfPAmmb3ihr4xVyuy94fAkV87WIzaO1SzInB8x6SGg6UghsZiBQ==",
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": "03FhZYwHUlbfbOxYDX8CwC2gIwl6KDGRaYmCGdIx6gcg4IdtAdVyrgIw7OWDIqKuHXqlckDnf+Jh3KOClpTOAw==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "v6huZjXXSNqSNprbiFaUgC30HDcoIiyEC7A105rut+dM3KCBisvYe/adu4p4PQVS8T2qNbqnHGboZd2NHtKQCA==",
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": "aFGz1KAWpY4O/+4xXZ+/Yfq9GLQ7IEZqNNQjEtRVjxDE7XEukXK3s4dXWpeZ9pg5j1NhmcCn/bnGaR+p9JWUBQ==",
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": "gmK55AoOUqaRL4qlpQFtSYGrPkaZ+2VtN+0XqHhwY8NH8gXZUEevFhNi/yne5qdw9TPW7k1xM0kE43cNpKAEBA==",
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": "+0CBcRpRn/sXdSxnPRIV8Hv/HO0/3+dsoIbkGePdunOxWujxmPbaFYh6jADa8vrhh4AL+UDaaJQGfMjy/3CzCg==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "8CbpnBxEs/4IEKRupI+K2b//RlL8wGmmR99RUIa40XtOrq0Y2oZkk4qQWqWx0Leth3tj2euz3eSdm0OwrYerAg==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "+b6HQ5D++9rppi1E1mHVQjaMXy0tB0KLb036r4sX8JslK3pXuiP1HYP4kqpugNEBhUTvI5shcsYndKi0PBRvDA==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "1qSKc1Ge2UVIevFR4gCR+f8N3zlRoK+kRU+Db56lfzEB0f7S72mrMQpE3QD0ZRfQ+IngyYj71TueRAYTuUOZCA==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": "FMnBSL3lt83BfmjqfHGGdGl3Ivq4D5GwCXoULBsiq4uEKBO7v2ADru0poTJzjRCrPTTv8nnteWT+lGffEXO4DQ==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": "Qp3+AE9b6G1bw6IhSGAPY9NZJeqqzsVf0vLVhdQ+CWkIesNIBAWTNixW9xLZ6bxVnw4Jvk4k5n404iaOP2iPDw==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": "yK5QgYja6wGUAEDAjvnhZr7kvS72pekXRGKuDfKPFUSehFVJVx+HNwWvKNLZDE60UNyeNzCf0XG40zxsN+ufDQ==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": "DvORZ1fUs4uW/eRe6RiN8jp2tUJU2Mnn/y9M9XbNSXieKKWFsPgyvYAQS4TvhZc2+S1+cNbgoqqihu8EcFu7BA==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": "Hu5AB59owBNkFqKmvfshrOXBSDa0I02ydUifnHjiq+7tocZNrOmjnsBPbcQgSuHYPgaux5W1r0fBmwpGLs9PAA==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "CEXfY5TOPY7JyHubxBeyW+WS0mrCU6nH3HB2hglnA/xdIWrRxNo0DzkegS1LRDhNCvwqThMLX3c6DY8yR8urCw==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "Wo9GEpl8kt2tPyy/7lX9d7a2gMI0gL1XrLKtTIkhJbq78qqp/TtzZOuXty2y9NvGnoXmJdRSCRfOMBFZkY/mAA==",
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": "gYuyE8q9g3GsKXMfMoXrLCYZvz2XJDicuriUutLPgz1KBKEZDgsAVNUxfR5oztOV+OSAb11by5nb9gi9vo62BA==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "ZCpdDtvv/E9G2tkoFT027BhNaA6ZBtlwU9m7lyKiY3nXiD0hk5m2mkVnbwKXolNXBCyCiQa3YmL5fKmPDqUqBg=="
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "0qFnstW83THKbKdU/6x895gXoJLMF5kp9PhxhSoja0t/D/kQmp1QI0u7zhi3idsk8pwYzZCN8YMIRBrVALFsCg==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "eFqA9ShbE8zeqXxEaEqV0KOXKbCP/9SQifNXvRR3Y+of29oxKT+CEK5OyOdNnEjaEG+u+gTXMOd3oSsk/L+wAQ==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "Qx5fS+scNnelKqICvs1iRFY8httSCmDCRQLpB95hK4Riakydm5pwLTJF1YUR0Oz0cf4it+INwKcoUmVkAG9kBw==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "R9j9s8waY7kThuL5491x9r7C/9ROkVZWigxKaThgKC8s0Je9xIb/EFuO58reyJUVx6P37rXVU2XxSm+MvZdLCg==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "UkT0VaaRxweoTRjfzP3E38fwVRgEI1Fj/HMl6HPxuiBrOJhOSVWlJHwHKtYgFrzVBhUwNqnHrmAbdLy6ZN9LBg==",
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "2AnnmUawhW1vWt76WabuiXHM779pEE8hC5xHVEdZ7cHN34RJDg+Q1ZyO/5arv2ntek2Vp5v3F8dlRm6FpfQSBA==",
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "Aq7CLbLUNrZlTq6XWlexzlKS/31+9H+XRKzZRI7VVBpiD9VEtlcB7u49RRdcEH5gfpGZFLDIMrfS1J01yoggAg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "gXWjTUUp8r9dDyUMaXBpEUBZCTFH70Gwb8SEj+rWFV69iB1wOijDeW9P7danSn8iw3y68l6AcVV7u3y8F+trDg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": "HqaR/WsSKt1UgAFo9cg4plqPGK7719drj6AE6GwZriK6vrLZrC2KZRwNTul7RT39RsatksN3J4oNw85I5LCPAw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "ygAT4jof7Gu4vS2X+8GMic0D2QCTXCAUdU9waYi8vlyi+6hmrgbWMM/BDjDdIJoYMxjnn3zRYdN0Z6BD9PrcBg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "1yQX0yOs4u8K08p5nlyQT+1UwpH96PgMTYZlpCvNwbC8o5xex4lQJyI6lirgpHXfYltVmEgSgw8N1i0GA7yGCQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "s83uFeK7C19GQKrq5R+L3poMHRo3VCj4NMq46gWrC+ofOoRmPRgkaGSqGrDGr8m/EIMbj4bpYInkxqbjqy3AAQ==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "ETryY+pFucqOyF5iTwE4g6mnf/luxyM3VljIioWLLppDpf8WN9WVv2Qj0r4GVsAg7iwf37HOW/dvj5EtfEiPBA==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "HK2d7XJdB/NScUdK0kcpOOEwlehCjXKDiz0t3qul7TSGTw/TP7fQRfottuWhynWk3WseDpDruerOM9XZNJQzDw==",
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": "u8BScZvFva8KW03yPS1XI1VwGjLGcjMy0UMLL7X9GgksKdBKEJ/DQLU9wx3FunBk8yRQ8L/K5mq3TTGUpLn+BQ==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "6V6EGUYF2qEA6tfQeUs9oTMLDuAOkx1OpDrS3LJNNHuyw+QbK33iYGAiH07eNs2GjL8psL3zS1tbn2x4p6udAw==",
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": "QUrRj3RbxO8WaTZGn5AZBTAGQYrviI8Q9IKlX7DRyVd+kq1GwAgftTS6tDLimQNHHhLXj1ZN/wnxVqKoBrGrAg==",
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": "e0UybV5I5fAtvmF2L+1059qtu9dXCnkWUb+kdOpnUl+YXdWRwOL7TdR9TSitdNfGFRmxPfh5DwdNSToe2ZlwAw==",
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": "GKxP/VjKIdlFW7EIDu/83MOOjKhcgjk7GqsIJzuEwlsCIWlir9cOjbJB/pgwE/mnW+BPZwKMzYeeVdUXCK+rDw==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "7TUFhgkXVyx4JT5aFzlK+DIm7WxF4taRsSVabHHITFbon7XXBv7YjhTsaBTZL4d83eSfj4B7qU+1oHzFclTOBQ==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "9CBMXV4OHwtRfxXpoXvq6xJ0rb4jE54xMZUEJz47JjOBctahb3xBKpo02pNbVKnZqZEiNmPKIDqAoHCkwtbODA==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "2mJaygzUnf3XXy8z3/yrFc6OtZf8MDnRE2bn3gXx3EFo/otVuBHjyVu1Mv0H8SymIRN7I/Fn+ao+lpM7ILTWCQ==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": "ZeAj6MuMrsp+mlzimai1v31Jalb7yuClrRSsDIL2Hyh76efNxajwPOx5evu0Zk1jVznu3GLtXIE8qFwWqrXXBQ==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": "EJiZdjHFhyFThk1YZAYRa28cjbEa0jkSDmjNoMonT2yGyMVaxfkdU5OI86GR+LY+kEFxhRPsIlO8YSAp7PpqDA==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": "IaXakjeDlvwMDS7NUG0f6E/3WTqv8FwgryUtRuXYXP67bmQfrzBI/kT8e+7FH4i49fu+CyrGbS42+JhsXEpwAA==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": "30KviVv+3hNyDLKPF+IvFbWoSH7JGgL8MKQLLR7Rrndxff6xsIw9fjFrjJuWA8q/RAkO/zw/ptJOCBFLGgTQDw==",
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": "Qw7U90kMvbmWcIaQl4AwLY/NesGZmFEDT5DpRw2t+IC//yr1aZqH5NteA8oEcLnnVxlqmVkP1DMu/5Dn4aBiDA==",
"python/iqpilot_private/konn3kt/hephaestus/session_bridge.cpython-312-aarch64-linux-gnu.so": "8xFCNu6XKY52fe6xOxPM0jDOC1fkn6G/dDVct9t/WsnScSLBb7tOaPJKl+G3zk4xlttrztdK9haPoDOFlApuBw==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "xBhqIugWTyGqS1Qd8hXTcL2BX4dwY5EhO2taJEBgwKhU/o17zv7+7GufJiIsAHambrVWI06fnmUA9mZXEBSRDw==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "cHJU8JNjRlQ9mdxUsW7VU8hr7yFohd5S2xRjIF8n4mLtfA+IiwTBPWj58HOHK9QbMG2O5uyiQnGd+pINcaF2Cg==",
"python/iqpilot_private/konn3kt/registration/reg_core.cpython-312-aarch64-linux-gnu.so": "Pbz/pYFEjYy2Ywx8HLxYfSZt447S+X7of2ADDrPDjWR4qwdy3mUSUgmOR20jm0SeDlhe9GtI3kbc5I3F6GrdBw==",
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": "hMIg0GAGId13kARPXKz3kUDi20oF707izEkG2CWymliKdG+1L/8AYn6ib4whIl9NBszjy7vnmAzznbjTbmfgBA==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "Vj5Encl2ybZctTrlmZ9f5puYEBIm6ESgscF9Ee414GN88pyQnsUUhqzCeN6IaAJ9T/2tt7n/gEKSyptHCVOUCg=="
}
}
@@ -1 +1 @@
+bka3mSd+pxAt1QcXidY5AAFJXqrazscCuWg0zNSJmjsEk7/LaSXKaHcTCOpBOLN/oMF5nQCsmHnoJUwOEVsDQ==
FdPsL8Lon52Th92plPCF4TsZpz+7qfhZ7yFhrfKKVDknEnF3n+t9LchpGq3n4KuCnhfCO+dszLtoNQfSq/qkDA==
@@ -1,18 +1,14 @@
[
"iqpilot.common.api",
"iqpilot.common.api.base",
"iqpilot.common.api.comma_connect",
"iqpilot.common.atlas_alerts",
"iqpilot.common.api.konn3kt",
"iqpilot.common.basedir",
"iqpilot.common.constants",
"iqpilot.common.file_helpers",
"iqpilot.common.git_creds",
"iqpilot.common.gpio",
"iqpilot.common.i2c",
"iqpilot.common.issue_debug",
"iqpilot.common.logging_extra",
"iqpilot.common.realtime",
"iqpilot.common.spinner",
"iqpilot.common.swaglog",
"iqpilot.common.time_helpers",
"iqpilot.common.utils",
@@ -28,10 +24,6 @@
"iqpilot.selfdrive.iqmodeld.model_bundle_downloader",
"iqpilot.selfdrive.iqmodeld.models.fetcher",
"iqpilot.selfdrive.iqmodeld.models.helpers",
"iqpilot.selfdrive.locationd.calibrationd",
"iqpilot.selfdrive.selfdrived.alertmanager",
"iqpilot.selfdrive.selfdrived.events",
"iqpilot.selfdrive.ui.feedback.feedbackd",
"iqpilot.system.hardware",
"iqpilot.system.hardware.base",
"iqpilot.system.hardware.hw",
@@ -47,7 +39,6 @@
"iqpilot.system.loggerd.xattr_cache",
"iqpilot.system.manager.process",
"iqpilot.system.manager.process_config",
"iqpilot.system.micd",
"iqpilot.system.phonegps.bridge",
"iqpilot.system.sentry",
"iqpilot.system.ui.lib.networkmanager",
@@ -1,9 +1,9 @@
import iqpilot.common.api.comma_connect
import iqpilot.common.api.konn3kt
class Api:
def __init__(self, dongle_id):
self.service = iqpilot.common.api.comma_connect.CommaConnectApi(dongle_id)
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
def request(self, method, endpoint, **params):
return self.service.request(method, endpoint, **params)
@@ -19,8 +19,8 @@ class Api:
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
return iqpilot.common.api.comma_connect.CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
return iqpilot.common.api.comma_connect.CommaConnectApi(None).get_key_pair()
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
@@ -7,10 +7,44 @@ from functools import lru_cache
from iqpilot.system.hardware.hw import Paths
from iqpilot.system.version import get_version
# name: jwt signature algorithm
KEYS = {"id_rsa": "RS256",
"id_ecdsa": "ES256"}
_CUSTODIAN_KEY = object()
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
def _custodian_available() -> bool:
import stat
try:
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
except OSError:
return False
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
import base64
import json
import socket
nonce = base64.b64encode(os.urandom(16)).decode()
packet = json.dumps({
"v": 1,
"op": "sign",
"dongle_id": dongle_id,
"payload": payload,
"alg": alg,
"nonce": nonce,
}).encode()
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
sock.settimeout(25.0)
sock.connect(_IQKEEPER_SOCKET)
sock.send(packet)
reply_data = sock.recv(8192)
reply = json.loads(reply_data) if reply_data else {}
if reply.get("nonce") != nonce or not reply.get("ok"):
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
return reply["token"]
@lru_cache(maxsize=4)
def load_signing_key(private_key: str):
@@ -60,6 +94,9 @@ class BaseApi:
}
if payload_extra is not None:
payload.update(payload_extra)
if self.private_key is _CUSTODIAN_KEY:
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
key = load_signing_key(self.private_key) if self.private_key else self.private_key
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
@@ -73,9 +110,25 @@ class BaseApi:
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
@staticmethod
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
for key in KEYS:
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
return KEYS[key], private.read(), public.read()
priv_path = Paths.persist_root() + f'/comma/{key}'
pub_path = priv_path + '.pub'
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
continue
try:
with open(pub_path) as public:
pub = public.read()
except OSError:
continue
try:
with open(priv_path) as private:
return KEYS[key], private.read(), pub
except PermissionError:
import time
for _attempt in range(10):
if _custodian_available():
return KEYS[key], _CUSTODIAN_KEY, pub
time.sleep(0.5)
continue
return None, None, None
@@ -1,3 +1,4 @@
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
import os
from iqpilot.common.api.base import BaseApi
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
class CommaConnectApi(BaseApi):
class Konn3ktApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "openpilot-"
@@ -1,277 +0,0 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from bisect import insort
from collections.abc import Callable, Iterable
from dataclasses import dataclass, field
from enum import IntEnum
import iqpilot.cereal.messaging as messaging
from iqpilot.cereal import car, log
from iqpilot.common.realtime import DT_CTRL
from iqpilot.system.hardware import HARDWARE
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
VisualAlert = car.CarControl.HUDControl.VisualAlert
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
def _frames_for(seconds: float) -> int:
return int(seconds / DT_CTRL)
class Tier(IntEnum):
LOWEST = 0
LOWER = 1
LOW = 2
MID = 3
HIGH = 4
HIGHEST = 5
class Tags:
ENABLE = "enable"
PRE_ENABLE = "preEnable"
OVERRIDE_LATERAL = "overrideLateral"
OVERRIDE_LONGITUDINAL = "overrideLongitudinal"
NO_ENTRY = "noEntry"
WARNING = "warning"
USER_DISABLE = "userDisable"
SOFT_DISABLE = "softDisable"
IMMEDIATE_DISABLE = "immediateDisable"
PERMANENT = "permanent"
@dataclass(slots=True)
class AlertCard:
alert_text_1: str
alert_text_2: str
alert_status: log.SelfdriveState.AlertStatus
alert_size: log.SelfdriveState.AlertSize
priority: Tier
visual_alert: car.CarControl.HUDControl.VisualAlert
audible_alert: car.CarControl.HUDControl.AudibleAlert
duration: int
creation_delay: float = 0.0
alert_type: str = field(default="", init=False)
event_type: str | None = field(default=None, init=False)
def __init__(self,
alert_text_1: str,
alert_text_2: str,
alert_status: log.SelfdriveState.AlertStatus,
alert_size: log.SelfdriveState.AlertSize,
priority: Tier,
visual_alert: car.CarControl.HUDControl.VisualAlert,
audible_alert: car.CarControl.HUDControl.AudibleAlert,
duration: float,
creation_delay: float = 0.0):
self.alert_text_1 = alert_text_1
self.alert_text_2 = alert_text_2
self.alert_status = alert_status
self.alert_size = alert_size
self.priority = priority
self.visual_alert = visual_alert
self.audible_alert = audible_alert
self.duration = _frames_for(duration)
self.creation_delay = creation_delay
self.alert_type = ""
self.event_type = None
def __str__(self) -> str:
return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}"
AlertFactory = Callable[[car.CarParams, car.CarState, messaging.SubMaster, bool, int, log.ControlsState], AlertCard]
def car_mode_entry_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> AlertCard:
del CS, sm, metric, soft_disable_time, personality
headline = "Enable Adaptive Cruise to Engage"
if CP.brand == "honda":
headline = "Enable Main Switch to Engage"
return NoEntryCard(headline)
class EventBook(ABC):
def __init__(self):
self._live_names: list[int] = []
self._latched_names: list[int] = []
self.event_counters: dict[int, int] = {}
@property
def events(self) -> list[int]:
return self._live_names
@events.setter
def events(self, values: list[int]) -> None:
self._live_names = values
@property
def static_events(self) -> list[int]:
return self._latched_names
@static_events.setter
def static_events(self, values: list[int]) -> None:
self._latched_names = values
@property
def names(self) -> list[int]:
return list(self._live_names)
def __len__(self) -> int:
return len(self._live_names)
def add(self, event_name: int, static: bool = False) -> None:
if static:
insort(self._latched_names, event_name)
insort(self._live_names, event_name)
def clear(self) -> None:
refreshed: dict[int, int] = {}
for event_name, frames_seen in self.event_counters.items():
refreshed[event_name] = frames_seen + 1 if event_name in self._live_names else 0
self.event_counters = refreshed
self._live_names = list(self._latched_names)
def contains(self, event_type: str) -> bool:
board = self.get_events_mapping()
return any(event_type in board.get(event_name, {}) for event_name in self._live_names)
def has(self, event_name: int) -> bool:
return event_name in self._live_names
def contains_in_list(self, events_list: list[int]) -> bool:
return any(event_name in self._live_names for event_name in events_list)
def remove(self, event_name: int, static: bool = False) -> None:
if static and event_name in self._latched_names:
self._latched_names.remove(event_name)
if event_name in self._live_names:
self.event_counters[event_name] = self.event_counters.get(event_name, 0) + 1
self._live_names.remove(event_name)
def add_from_msg(self, events: Iterable) -> None:
for event in events:
insort(self._live_names, event.name.raw)
def to_msg(self):
board = self.get_events_mapping()
outbound = []
for event_name in self._live_names:
msg = self.get_event_msg_type().new_message()
msg.name = event_name
for event_kind in board.get(event_name, {}):
setattr(msg, event_kind, True)
outbound.append(msg)
return outbound
def create_alerts(self, event_types: list[str], callback_args=None):
callback_args = [] if callback_args is None else callback_args
board = self.get_events_mapping()
spawned: list[AlertCard] = []
for event_name in self._live_names:
variants = board.get(event_name, {})
for event_type in event_types:
chosen = variants.get(event_type)
if chosen is None:
continue
alert = self._realize(chosen, callback_args)
age_frames = self.event_counters.get(event_name, 0) + 1
if age_frames * DT_CTRL < alert.creation_delay:
continue
alert.alert_type = f"{self.get_event_name(event_name)}/{event_type}"
alert.event_type = event_type
spawned.append(alert)
return spawned
@staticmethod
def _realize(candidate: AlertCard | AlertFactory, callback_args: list) -> AlertCard:
return candidate if isinstance(candidate, AlertCard) else candidate(*callback_args)
@abstractmethod
def get_events_mapping(self) -> dict[int, dict[str, AlertCard | AlertFactory]]:
raise NotImplementedError
@abstractmethod
def get_event_name(self, event: int) -> str:
raise NotImplementedError
@abstractmethod
def get_event_msg_type(self):
raise NotImplementedError
def _mici_reframe(primary: str, secondary: str) -> tuple[str, str, log.SelfdriveState.AlertSize]:
if HARDWARE.get_device_type() == "mici":
return secondary, primary, AlertSize.small
return primary, secondary, AlertSize.mid
class NoEntryCard(AlertCard):
def __init__(self,
alert_text_2: str,
alert_text_1: str = "IQ.Pilot Unavailable",
visual_alert: car.CarControl.HUDControl.VisualAlert = VisualAlert.none,
priority: Tier = Tier.LOW):
primary, secondary, size = _mici_reframe(alert_text_1, alert_text_2)
super().__init__(primary, secondary, AlertStatus.normal, size, priority, visual_alert, AudibleAlert.refuse, 3.0)
class GentleDisableCard(AlertCard):
def __init__(self, alert_text_2: str):
super().__init__(
"TAKE CONTROL IMMEDIATELY",
alert_text_2,
AlertStatus.userPrompt,
AlertSize.full,
Tier.MID,
VisualAlert.steerRequired,
AudibleAlert.warningSoft,
2.0,
)
class PendingDisableCard(GentleDisableCard):
def __init__(self, alert_text_2: str):
super().__init__(alert_text_2)
self.alert_text_1 = "IQ.Pilot will disengage"
class HardDisableCard(AlertCard):
def __init__(self, alert_text_2: str):
super().__init__(
"TAKE CONTROL IMMEDIATELY",
alert_text_2,
AlertStatus.critical,
AlertSize.full,
Tier.HIGHEST,
VisualAlert.steerRequired,
AudibleAlert.warningImmediate,
4.0,
)
class ChimeCard(AlertCard):
def __init__(self, audible_alert: car.CarControl.HUDControl.AudibleAlert):
super().__init__("", "", AlertStatus.normal, AlertSize.none, Tier.MID, VisualAlert.none, audible_alert, 0.2)
class BannerCard(AlertCard):
def __init__(self, alert_text_1: str, alert_text_2: str = "", duration: float = 0.2, priority: Tier = Tier.LOWER, creation_delay: float = 0.0):
size = AlertSize.mid if alert_text_2 else AlertSize.small
super().__init__(alert_text_1, alert_text_2, AlertStatus.normal, size, priority, VisualAlert.none, AudibleAlert.none, duration, creation_delay)
class BootCard(AlertCard):
def __init__(self, alert_text_1: str, alert_text_2: str = "Always keep hands on wheel and eyes on road", alert_status=AlertStatus.normal):
if HARDWARE.get_device_type() == "mici":
compact_secondary = "" if alert_text_2 == "Always keep hands on wheel and eyes on road" else alert_text_2
super().__init__(alert_text_1, compact_secondary, alert_status, AlertSize.small, Tier.LOWER, VisualAlert.none, AudibleAlert.none, 5.0)
else:
super().__init__(alert_text_1, alert_text_2, alert_status, AlertSize.mid, Tier.LOWER, VisualAlert.none, AudibleAlert.none, 5.0)
NULL_ALERT = AlertCard("", "", AlertStatus.normal, AlertSize.none, Tier.LOWEST, VisualAlert.none, AudibleAlert.none, 0.0)
@@ -1,23 +0,0 @@
import numpy as np
# conversions
class CV:
# Speed
MPH_TO_KPH = 1.609344
KPH_TO_MPH = 1. / MPH_TO_KPH
MS_TO_KPH = 3.6
KPH_TO_MS = 1. / MS_TO_KPH
MS_TO_MPH = MS_TO_KPH * KPH_TO_MPH
MPH_TO_MS = MPH_TO_KPH * KPH_TO_MS
MS_TO_KNOTS = 1.9438
KNOTS_TO_MS = 1. / MS_TO_KNOTS
# Angle
DEG_TO_RAD = np.pi / 180.
RAD_TO_DEG = 1. / DEG_TO_RAD
# Mass
LB_TO_KG = 0.453592
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
@@ -1,36 +0,0 @@
import threading
import time
from datetime import datetime
from pathlib import Path
from iqpilot.system.hardware import PC
from iqpilot.system.hardware.hw import Paths
DEBUG_FILENAME = "iqpilot_issue_debug.txt"
DEBUG_PATH = Path(Paths.comma_home()) / "community" / DEBUG_FILENAME if PC else Path("/data/community") / DEBUG_FILENAME
_lock = threading.Lock()
_last_log_times: dict[str, float] = {}
def log_issue(tag: str, message: str) -> None:
try:
DEBUG_PATH.parent.mkdir(parents=True, exist_ok=True)
with _lock:
with open(DEBUG_PATH, "a", encoding="utf-8") as f:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
f.write(f"[{timestamp}] [{tag}] {message}\n")
except OSError:
pass
def log_issue_limited(key: str, tag: str, message: str, interval_sec: float = 1.0) -> None:
now = time.monotonic()
with _lock:
last = _last_log_times.get(key, 0.0)
if now - last < interval_sec:
return
_last_log_times[key] = now
log_issue(tag, message)
@@ -1,52 +0,0 @@
import os
import subprocess
from iqpilot.common.basedir import BASEDIR
class Spinner:
def __init__(self):
try:
self.spinner_proc = subprocess.Popen(["./spinner.py"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "iqpilot", "system", "ui"),
close_fds=True)
except OSError:
self.spinner_proc = None
def __enter__(self):
return self
def update(self, spinner_text: str):
if self.spinner_proc is not None:
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
try:
self.spinner_proc.stdin.flush()
except BrokenPipeError:
pass
def update_progress(self, cur: float, total: float):
self.update(str(round(100 * cur / total)))
def close(self):
if self.spinner_proc is not None:
self.spinner_proc.kill()
try:
self.spinner_proc.communicate(timeout=2.)
except subprocess.TimeoutExpired:
print("WARNING: failed to kill spinner")
self.spinner_proc = None
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == "__main__":
import time
with Spinner() as s:
s.update("Spinner text")
time.sleep(5.0)
print("gone")
time.sleep(5.0)
@@ -31,24 +31,44 @@ class Timer:
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
_sudo_available: bool | None = None
def sudo_write(val: str, path: str) -> None:
global _sudo_available
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
return
except OSError:
pass
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
if _sudo_available is False:
return
try:
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
if _sudo_available:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
except Exception:
_sudo_available = False
def sudo_read(path: str) -> str:
global _sudo_available
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
with open(path, encoding='utf8') as f:
return f.read().strip()
except OSError:
pass
if _sudo_available is False:
return ""
try:
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
_sudo_available = result.returncode == 0
return result.stdout.strip()
except Exception:
_sudo_available = False
return ""
@@ -1,18 +1,12 @@
#!/usr/bin/env python3
import os
import time
import json
import jwt
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
import re
import secrets
from typing import cast
from pathlib import Path
from datetime import datetime, timedelta, UTC
from iqpilot.common.api import api_get, get_key_pair
from iqpilot.common.params import Params
from iqpilot.common.spinner import Spinner
from iqpilot.system.hardware import HARDWARE
from iqpilot.system.hardware.hw import Paths
from iqpilot.common.swaglog import cloudlog
@@ -35,6 +29,10 @@ def _read_persist_dongle_id() -> str | None:
return None
def is_valid_dongle_id(dongle_id: str | None) -> bool:
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str | None:
ro = _read_persist_dongle_id()
if is_valid_dongle_id(ro):
@@ -46,8 +44,8 @@ def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = T
if v and v != UNREGISTERED_DONGLE_ID:
return v.lower() if is_valid_dongle_id(v) else v
return ro or None
def is_valid_dongle_id(dongle_id: str | None) -> bool:
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str:
p = Params() if params is None else params
dongle_id = get_cached_dongle_id(p, prefer_readonly=prefer_readonly)
@@ -57,164 +55,32 @@ def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool
p.put("DongleId", dongle_id)
cloudlog.warning(f"generated new DongleId={dongle_id} (no readonly dongle_id found)")
return dongle_id
_impl = None
def _registration_impl():
global _impl
if _impl is None:
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
try:
_impl = load_private_module(__name__, "iqpilot_private.konn3kt.registration.reg_core")
except ProprietaryModuleMissing:
from konn3kt_private.registration import reg_core as _impl
return _impl
def ensure_dev_pairing_identity(params: Params | None = None, force_reset: bool = False) -> dict[str, str]:
p = Params() if params is None else params
persist_dir = Path(Paths.persist_root()) / "comma"
persist_dir.mkdir(parents=True, exist_ok=True)
dongle_path = persist_dir / "dongle_id"
priv_path = persist_dir / "id_rsa"
pub_path = persist_dir / "id_rsa.pub"
if force_reset:
for fp in (dongle_path, priv_path, pub_path):
try:
fp.unlink(missing_ok=True)
except Exception:
cloudlog.exception(f"failed to remove {fp}")
try:
(persist_dir / "konn3kt_prime_type").unlink(missing_ok=True)
except Exception:
pass
try:
p.remove("PrimeType")
except Exception:
pass
forced_dongle = os.getenv("KONN3KT_DEV_DONGLE_ID")
dongle_id = forced_dongle.strip().lower() if forced_dongle else None
if dongle_id and not is_valid_dongle_id(dongle_id):
cloudlog.error("KONN3KT_DEV_DONGLE_ID must be 16 hex chars")
dongle_id = None
if dongle_id is None:
existing = None
try:
existing = dongle_path.read_text().strip().lower() if dongle_path.is_file() else None
except Exception:
cloudlog.exception("failed reading existing dev dongle_id")
dongle_id = existing if is_valid_dongle_id(existing) else secrets.token_hex(8)
try:
dongle_path.write_text(dongle_id)
except Exception:
cloudlog.exception("failed writing dev dongle_id")
p.put("DongleId", dongle_id)
p.put("HardwareSerial", p.get("HardwareSerial") or f"DEV-{dongle_id}")
if force_reset or (not priv_path.is_file()) or (not pub_path.is_file()):
try:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
priv_bytes = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
pub_bytes = key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
priv_path.write_bytes(priv_bytes)
pub_path.write_bytes(pub_bytes)
except Exception:
cloudlog.exception("failed generating dev RSA keys")
raise
return {
"dongle_id": dongle_id,
"serial": p.get("HardwareSerial") or f"DEV-{dongle_id}",
"persist_dir": str(persist_dir),
}
def _normalize_imei(value: str | None) -> str:
return value or ""
return _registration_impl().ensure_dev_pairing_identity(params, force_reset)
def get_registration_identifiers(wait_timeout: float = IMEI_WAIT_TIMEOUT, show_spinner: bool = False) -> tuple[str, str, str]:
serial = HARDWARE.get_serial()
spinner = Spinner() if show_spinner else None
start_time = time.monotonic()
imei1: str | None = None
imei2: str | None = None
while time.monotonic() - start_time < wait_timeout:
try:
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
if imei1 or imei2:
break
except RuntimeError as e:
if "no modems" in str(e).lower():
cloudlog.warning("No cellular modem available, proceeding without IMEI")
break
cloudlog.exception("Error getting imei, trying again...")
except Exception:
cloudlog.exception("Error getting imei, trying again...")
time.sleep(1)
imei1 = _normalize_imei(imei1)
imei2 = _normalize_imei(imei2)
if not imei1 and not imei2:
cloudlog.warning(f"proceeding with serial-only registration for serial={serial}")
if spinner is not None:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
spinner.close()
return serial, imei1, imei2
return _registration_impl().get_registration_identifiers(wait_timeout, show_spinner)
def register(show_spinner=False) -> str | None:
params = Params()
dongle_id: str | None = get_cached_dongle_id(params, prefer_readonly=True)
if dongle_id in ("", UNREGISTERED_DONGLE_ID):
dongle_id = None
jwt_algo, private_key, public_key = get_key_pair()
if not public_key:
dongle_id = UNREGISTERED_DONGLE_ID
cloudlog.warning("missing public key")
elif dongle_id is None:
if show_spinner:
spinner = Spinner()
spinner.update("registering device")
serial, imei1, imei2 = get_registration_identifiers(wait_timeout=IMEI_WAIT_TIMEOUT, show_spinner=False)
backoff = 0
start_time = time.monotonic()
while True:
try:
register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)},
cast(str, private_key), algorithm=jwt_algo)
cloudlog.info("getting pilotauth")
cloudlog.info("getting pilotauth")
resp = api_get("v2/pilotauth/", method='POST', timeout=15,
imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)
if resp.status_code in (400, 402, 403):
cloudlog.info(f"Unable to register device, got {resp.status_code}")
dongle_id = UNREGISTERED_DONGLE_ID
else:
dongleauth = json.loads(resp.text)
dongle_id = dongleauth["dongle_id"]
break
except Exception:
cloudlog.exception("failed to authenticate")
backoff = min(backoff + 1, 15)
time.sleep(backoff)
if time.monotonic() - start_time > 60 and show_spinner:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
return UNREGISTERED_DONGLE_ID
if show_spinner:
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
spinner.close()
if dongle_id:
params.put("DongleId", dongle_id)
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
set_offroad_alert("Offroad_UnregisteredHardware", False)
return dongle_id
return _registration_impl().register(show_spinner)
if __name__ == "__main__":
@@ -3941,6 +3941,86 @@
],
"req": "All"
},
"TESLA_MODEL_S_HW1|2014-2016": {
"label": "Tesla Model S (with HW1) 2014-16",
"id": "TESLA_MODEL_S_HW1",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model S (with HW1)",
"yrs": [
"2014",
"2015",
"2016"
],
"req": "All"
},
"TESLA_MODEL_S_HW2|2017-2019": {
"label": "Tesla Model S (with HW2) 2017-19",
"id": "TESLA_MODEL_S_HW2",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model S (with HW2)",
"yrs": [
"2017",
"2018",
"2019"
],
"req": "All"
},
"TESLA_MODEL_S_HW3|2020-2023": {
"label": "Tesla Model S (with HW3) 2020-23",
"id": "TESLA_MODEL_S_HW3",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model S (with HW3)",
"yrs": [
"2020",
"2021",
"2022",
"2023"
],
"req": "All"
},
"TESLA_MODEL_X_HW1|2014-2016": {
"label": "Tesla Model X (with HW1) 2014-16",
"id": "TESLA_MODEL_X_HW1",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model X (with HW1)",
"yrs": [
"2014",
"2015",
"2016"
],
"req": "All"
},
"TESLA_MODEL_X_HW2|2016-2019": {
"label": "Tesla Model X (with HW2) 2016-19",
"id": "TESLA_MODEL_X_HW2",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model X (with HW2)",
"yrs": [
"2016",
"2017",
"2018",
"2019"
],
"req": "All"
},
"TESLA_MODEL_X_HW3|2021-2023": {
"label": "Tesla Model X (with HW3) 2021-23",
"id": "TESLA_MODEL_X_HW3",
"mk": "Tesla",
"grp": "tesla",
"mdl": "Model X (with HW3)",
"yrs": [
"2021",
"2022",
"2023"
],
"req": "All"
},
"TESLA_MODEL_X|2024": {
"label": "Tesla Model X (with HW4) 2024",
"id": "TESLA_MODEL_X",

Some files were not shown because too many files have changed in this diff Show More