Konik.ai server support
Co-Authored-By: MoreTorque <104254025+moretore@users.noreply.github.com>
This commit is contained in:
+3
-1
@@ -5,7 +5,9 @@ from datetime import datetime, timedelta, UTC
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import get_version
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
|
||||
|
||||
API_HOST = os.getenv('API_HOST', f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}')
|
||||
|
||||
# name: jwt signature algorithm
|
||||
KEYS = {"id_rsa": "RS256",
|
||||
|
||||
@@ -9,13 +9,14 @@ from pathlib import Path
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.time_helpers import system_time_valid
|
||||
from openpilot.system.athena.registration import register
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
from openpilot.frogpilot.assets.theme_manager import ThemeManager
|
||||
from openpilot.frogpilot.common.frogpilot_backups import backup_frogpilot
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import is_FrogsGoMoo, run_cmd
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import is_FrogsGoMoo, run_cmd, use_konik_server
|
||||
from openpilot.frogpilot.common.frogpilot_variables import (
|
||||
ERROR_LOGS_PATH, FROGS_GO_MOO_PATH, HD_LOGS_PATH, THEME_SAVE_PATH,
|
||||
ERROR_LOGS_PATH, FROGS_GO_MOO_PATH, HD_LOGS_PATH, KONIK_LOGS_PATH, THEME_SAVE_PATH,
|
||||
FrogPilotVariables, get_frogpilot_toggles
|
||||
)
|
||||
|
||||
@@ -26,6 +27,15 @@ def frogpilot_boot_functions(build_metadata, params):
|
||||
FrogPilotVariables()
|
||||
ThemeManager(params, params_memory, boot_run=True).update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True)
|
||||
|
||||
if use_konik_server():
|
||||
if params.get("KonikDongleId") is not None:
|
||||
params.put("DongleId", params.get("KonikDongleId"))
|
||||
else:
|
||||
params.put("KonikDongleId", register(show_spinner=True, register_konik=True))
|
||||
params.put("DongleId", params.get("KonikDongleId"))
|
||||
elif params.get("DongleId") == params.get("KonikDongleId"):
|
||||
params.put("DongleId", params.get("StockDongleId"))
|
||||
|
||||
def boot_thread():
|
||||
while not system_time_valid():
|
||||
print("Waiting for system time to become valid...")
|
||||
@@ -40,6 +50,7 @@ def install_frogpilot(build_metadata, params):
|
||||
paths = [
|
||||
ERROR_LOGS_PATH,
|
||||
HD_LOGS_PATH,
|
||||
KONIK_LOGS_PATH,
|
||||
THEME_SAVE_PATH
|
||||
]
|
||||
for path in paths:
|
||||
|
||||
@@ -9,6 +9,7 @@ import threading
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
from functools import cache
|
||||
from pathlib import Path
|
||||
|
||||
import openpilot.system.sentry as sentry
|
||||
@@ -17,7 +18,7 @@ from cereal import log, messaging
|
||||
from openpilot.common.realtime import DT_DMON, DT_HW
|
||||
from panda import Panda
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, FROGS_GO_MOO_PATH
|
||||
from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, FROGS_GO_MOO_PATH, KONIK_PATH
|
||||
|
||||
class ThreadManager:
|
||||
def __init__(self):
|
||||
@@ -211,6 +212,11 @@ def update_json_file(path, data):
|
||||
os.replace(temp_path, path)
|
||||
|
||||
|
||||
@cache
|
||||
def use_konik_server():
|
||||
return KONIK_PATH.is_file()
|
||||
|
||||
|
||||
def wait_for_no_driver(params, sm, time_threshold=60):
|
||||
while sm["deviceState"].screenBrightnessPercent != 0 or any(proc.name == "dmonitoringd" and proc.running for proc in sm["managerState"].processes):
|
||||
sm.update()
|
||||
|
||||
@@ -67,6 +67,9 @@ FROGS_GO_MOO_PATH = Path("/persist/frogsgomoo.py")
|
||||
HD_LOGS_PATH = Path("/data/media/0/realdata_HD")
|
||||
HD_PATH = Path("/cache/use_HD")
|
||||
|
||||
KONIK_LOGS_PATH = Path("/data/media/0/realdata_konik")
|
||||
KONIK_PATH = Path("/cache/use_konik")
|
||||
|
||||
MAPD_PATH = Path("/data/media/0/osm/mapd")
|
||||
MAPS_PATH = Path("/data/media/0/osm/offline")
|
||||
|
||||
@@ -225,6 +228,17 @@ class FrogPilotVariables:
|
||||
HD_PATH.unlink()
|
||||
HARDWARE.reboot()
|
||||
|
||||
toggle.use_konik_server = device_management
|
||||
toggle.use_konik_server &= self.get_value("UseKonikServer")
|
||||
toggle.use_konik_server |= Path("/data/openpilot/not_vetted").is_file()
|
||||
|
||||
if not KONIK_PATH.is_file() and toggle.use_konik_server:
|
||||
KONIK_PATH.touch()
|
||||
HARDWARE.reboot()
|
||||
elif KONIK_PATH.is_file() and not toggle.use_konik_server:
|
||||
KONIK_PATH.unlink()
|
||||
HARDWARE.reboot()
|
||||
|
||||
stock_colors_json = (STOCK_THEME_PATH / "colors/colors.json")
|
||||
self.stock_colors = json.loads(stock_colors_json.read_text()) if stock_colors_json.is_file() else {}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent, bool
|
||||
{"HigherBitrate", tr("High-Quality Recording"), tr("<b>Save drive footage in higher video quality.</b>"), ""},
|
||||
{"LowVoltageShutdown", tr("Low-Voltage Cutoff"), tr("<b>While parked, if the battery voltage falls below the set level, the device shuts down</b> to prevent excessive battery drain."), ""},
|
||||
{"IncreaseThermalLimits", tr("Raise Temperature Limits"), QString("<b>%1</b><br><br>%2").arg(tr("WARNING: Running at higher temperatures may damage your device!")).arg(tr("<b>Allow the device to run at higher temperatures</b> before throttling or shutting down. Use only if you understand the risks!")), ""},
|
||||
{"UseKonikServer", tr("Use Konik Server"), tr("<b>Upload driving data to \"stable.konik.ai\" instead of \"connect.comma.ai\".</b>"), ""},
|
||||
|
||||
{"ScreenManagement", tr("Screen Settings"), tr("<b>Settings that control screen brightness, screen recording, and timeout duration.</b>"), "../../frogpilot/assets/toggle_icons/icon_light.png"},
|
||||
{"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("<b>The screen brightness while not driving.</b>"), ""},
|
||||
@@ -136,12 +137,14 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent, bool
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, this, &FrogPilotDevicePanel::updateToggles);
|
||||
}
|
||||
|
||||
QSet<QString> rebootKeys = {"HigherBitrate"};
|
||||
QSet<QString> rebootKeys = {"HigherBitrate", "UseKonikServer"};
|
||||
for (const QString &key : rebootKeys) {
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, [key, this](bool state) {
|
||||
QString filePath;
|
||||
if (key == "HigherBitrate") {
|
||||
filePath = "/cache/use_HD";
|
||||
} else if (key == "UseKonikServer") {
|
||||
filePath = "/cache/use_konik";
|
||||
}
|
||||
|
||||
if (!filePath.isEmpty()) {
|
||||
@@ -203,6 +206,10 @@ void FrogPilotDevicePanel::updateToggles() {
|
||||
setVisible &= params.getBool("DeviceManagement") && params.getBool("NoUploads") && !params.getBool("DisableOnroadUploads");
|
||||
}
|
||||
|
||||
else if (key == "UseKonikServer" && QFile("/data/not_vetted").exists()) {
|
||||
static_cast<ToggleControl*>(toggle)->forceOn(true);
|
||||
}
|
||||
|
||||
toggle->setVisible(setVisible);
|
||||
|
||||
if (setVisible) {
|
||||
|
||||
@@ -23,7 +23,7 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> deviceManagementKeys = {"DeviceShutdown", "HigherBitrate", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads"};
|
||||
QSet<QString> deviceManagementKeys = {"DeviceShutdown", "HigherBitrate", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "UseKonikServer"};
|
||||
QSet<QString> screenKeys = {"ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad", "StandbyMode"};
|
||||
|
||||
QSet<QString> parentKeys;
|
||||
|
||||
@@ -10,12 +10,13 @@ static QLabel *newLabel(const QString &text, const QString &type) {
|
||||
|
||||
DriveStats::DriveStats(QWidget *parent) : QFrame(parent) {
|
||||
isMetric = params.getBool("IsMetric");
|
||||
konik = useKonikServer();
|
||||
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(50, 25, 50, 20);
|
||||
|
||||
addStatsLayouts(tr("ALL TIME"), all);
|
||||
addStatsLayouts(tr("PAST WEEK"), week);
|
||||
addStatsLayouts(konik ? tr("ALL TIME (KONIK)") : tr("ALL TIME"), all);
|
||||
addStatsLayouts(konik ? tr("PAST WEEK (KONIK)") : tr("PAST WEEK"), week);
|
||||
addStatsLayouts(tr("FROGPILOT"), frogPilot, true);
|
||||
|
||||
std::optional<QString> dongleId = getDongleId();
|
||||
@@ -103,5 +104,5 @@ void DriveStats::updateStats() {
|
||||
updateStatsForLabel(json["week"].toObject(), week);
|
||||
updateFrogPilotStatsForLabel(frogPilot);
|
||||
|
||||
params.putIntNonBlocking("openpilotMinutes", json["all"].toObject()["minutes"].toDouble());
|
||||
params.putIntNonBlocking(konik ? "KonikMinutes" : "openpilotMinutes", json["all"].toObject()["minutes"].toDouble());
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ private:
|
||||
void updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels);
|
||||
|
||||
bool isMetric;
|
||||
bool konik;
|
||||
|
||||
Params params;
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ bool isFrogsGoMoo() {
|
||||
return is_FrogsGoMoo;
|
||||
}
|
||||
|
||||
bool useKonikServer() {
|
||||
static bool use_konik = QFile::exists("/cache/use_konik");
|
||||
return use_konik;
|
||||
}
|
||||
|
||||
void clearMovie(QSharedPointer<QMovie> &movie, QWidget *parent) {
|
||||
if (!movie) {
|
||||
return;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
|
||||
bool isFrogsGoMoo();
|
||||
bool useKonikServer();
|
||||
|
||||
void loadGif(const QString &gifPath, QSharedPointer<QMovie> &movie, const QSize &size, QWidget *parent);
|
||||
void loadImage(const QString &basePath, QPixmap &pixmap, QSharedPointer<QMovie> &movie, const QSize &size, QWidget *parent);
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
#include "frogpilot/ui/qt/widgets/frogpilot_controls.h"
|
||||
|
||||
namespace CommaApi {
|
||||
|
||||
const QString BASE_URL = util::getenv("API_HOST", "https://api.commadotai.com").c_str();
|
||||
const QString BASE_URL = util::getenv("API_HOST", useKonikServer() ? "https://api.konik.ai" : "https://api.commadotai.com").c_str();
|
||||
QByteArray rsa_sign(const QByteArray &data);
|
||||
QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600);
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str()));
|
||||
|
||||
pair_device = new ButtonControl(tr("Pair Device"), tr("PAIR"),
|
||||
tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
|
||||
useKonikServer() ? tr("Pair your device with Konik connect (stable.konik.ai).") : tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
|
||||
connect(pair_device, &ButtonControl::clicked, [=]() {
|
||||
PairingPopup popup(this);
|
||||
popup.exec();
|
||||
@@ -577,7 +577,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
|
||||
if (!tuningLevelConfirmed) {
|
||||
int frogpilotHours = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object().value("FrogPilotSeconds").toInt() / (60 * 60);
|
||||
int openpilotHours = params.getInt("openpilotMinutes") / 60;
|
||||
int openpilotHours = params.getInt("KonikMinutes") / 60 + params.getInt("openpilotMinutes") / 60;
|
||||
|
||||
if (frogpilotHours < 1 && openpilotHours < 100) {
|
||||
if (openpilotHours < 10) {
|
||||
|
||||
@@ -36,7 +36,7 @@ void PairingQRWidget::hideEvent(QHideEvent *event) {
|
||||
|
||||
void PairingQRWidget::refresh() {
|
||||
QString pairToken = CommaApi::create_jwt({{"pair", true}});
|
||||
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
|
||||
QString qrString = (useKonikServer() ? "https://stable.konik.ai/?pair=" : "https://connect.comma.ai/?pair=") + pairToken;
|
||||
this->updateQrCode(qrString);
|
||||
update();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
|
||||
|
||||
vlayout->addSpacing(30);
|
||||
|
||||
QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
|
||||
QLabel *title = new QLabel(tr("Pair your device to your %1 account").arg(useKonikServer() ? "Konik" : "comma"), this);
|
||||
title->setStyleSheet("font-size: 75px; color: black;");
|
||||
title->setWordWrap(true);
|
||||
vlayout->addWidget(title);
|
||||
@@ -100,9 +100,9 @@ PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
|
||||
<li style='margin-bottom: 50px;'>%2</li>
|
||||
<li style='margin-bottom: 50px;'>%3</li>
|
||||
</ol>
|
||||
)").arg(tr("Go to https://connect.comma.ai on your phone"))
|
||||
)").arg(tr("Go to https://%1 on your phone").arg(useKonikServer() ? "stable.konik.ai" : "connect.comma.ai"))
|
||||
.arg(tr("Click \"add new device\" and scan the QR code on the right"))
|
||||
.arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);
|
||||
.arg(tr("Bookmark %1 to your home screen to use it like an app").arg(useKonikServer() ? "stable.konik.ai" : "connect.comma.ai")), this);
|
||||
|
||||
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
|
||||
instructions->setWordWrap(true);
|
||||
@@ -196,7 +196,7 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
|
||||
registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;");
|
||||
finishRegistrationLayout->addWidget(registrationTitle);
|
||||
|
||||
QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
|
||||
QLabel* registrationDescription = new QLabel(useKonikServer() ? tr("Pair your device with Konik connect (stable.konik.ai).") : tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
|
||||
registrationDescription->setWordWrap(true);
|
||||
registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;");
|
||||
finishRegistrationLayout->addWidget(registrationDescription);
|
||||
|
||||
@@ -40,8 +40,10 @@ from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.version import get_build_metadata
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
|
||||
|
||||
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
|
||||
|
||||
ATHENA_HOST = os.getenv('ATHENA_HOST', f"wss://athena.{'konik.ai' if use_konik_server() else 'comma.ai'}")
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
LOCAL_PORT_WHITELIST = {22, } # SSH
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ def is_registered_device() -> bool:
|
||||
return dongle not in (None, UNREGISTERED_DONGLE_ID)
|
||||
|
||||
|
||||
def register(show_spinner=False) -> str | None:
|
||||
def register(show_spinner=False, register_konik=False) -> str | None:
|
||||
"""
|
||||
All devices built since March 2024 come with all
|
||||
info stored in /persist/. This is kept around
|
||||
@@ -45,10 +45,10 @@ def register(show_spinner=False) -> str | None:
|
||||
# Create registration token, in the future, this key will make JWTs directly
|
||||
jwt_algo, private_key, public_key = get_key_pair()
|
||||
|
||||
if not public_key:
|
||||
if not public_key and not register_konik:
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
cloudlog.warning("missing public key")
|
||||
elif dongle_id is None:
|
||||
elif dongle_id is None or register_konik:
|
||||
if show_spinner:
|
||||
spinner = Spinner()
|
||||
spinner.update("registering device")
|
||||
@@ -96,8 +96,9 @@ def register(show_spinner=False) -> str | None:
|
||||
if show_spinner:
|
||||
spinner.close()
|
||||
|
||||
if dongle_id:
|
||||
if not register_konik and dongle_id != params.get("KonikDongleId"):
|
||||
params.put("DongleId", dongle_id)
|
||||
params.put("StockDongleId", dongle_id)
|
||||
set_offroad_alert("Offroad_UnregisteredHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC)
|
||||
return dongle_id
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ namespace Path {
|
||||
return "/data/media/0/realdata_HD/";
|
||||
}
|
||||
|
||||
if (std::filesystem::exists("/cache/use_konik")) {
|
||||
return "/data/media/0/realdata_konik/";
|
||||
}
|
||||
|
||||
return "/data/media/0/realdata";
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ class Paths:
|
||||
return str(Path(Paths.comma_home()) / "media" / "0" / "realdata")
|
||||
elif Path("/cache/use_HD").is_file():
|
||||
return '/data/media/0/realdata_HD/'
|
||||
elif Path("/cache/use_konik").is_file():
|
||||
return '/data/media/0/realdata_konik/'
|
||||
else:
|
||||
return '/data/media/0/realdata/'
|
||||
|
||||
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
import os
|
||||
import requests
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
|
||||
|
||||
API_HOST = os.getenv('API_HOST', f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}')
|
||||
|
||||
# TODO: this should be merged into common.api
|
||||
|
||||
|
||||
+5
-1
@@ -22,6 +22,7 @@ Examples::
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import pprint
|
||||
import webbrowser
|
||||
@@ -32,6 +33,9 @@ from urllib.parse import parse_qs, urlencode
|
||||
from openpilot.tools.lib.api import APIError, CommaApi, UnauthorizedError
|
||||
from openpilot.tools.lib.auth_config import set_token, get_token
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
|
||||
|
||||
API_HOST = os.getenv('API_HOST', f'https://api.{'konik' if use_konik_server() else 'comma'}.ai')
|
||||
PORT = 3000
|
||||
|
||||
|
||||
@@ -66,7 +70,7 @@ def auth_redirect_link(method):
|
||||
}[method]
|
||||
|
||||
params = {
|
||||
'redirect_uri': f"https://api.comma.ai/v2/auth/{provider_id}/redirect/",
|
||||
'redirect_uri': f"{API_HOST}/v2/auth/{provider_id}/redirect/",
|
||||
'state': f'service,localhost:{PORT}',
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
print(f"{sys.argv[0]} <route> <segment> <frame number> [front|wide|driver]")
|
||||
print('example: ./fetch_image_from_route.py "02c45f73a2e5c6e9|2020-06-01--18-03-08" 3 500 driver')
|
||||
@@ -24,7 +26,7 @@ segment = int(sys.argv[2])
|
||||
frame = int(sys.argv[3])
|
||||
camera = cameras[sys.argv[4]] if len(sys.argv) > 4 and sys.argv[4] in cameras else "cameras"
|
||||
|
||||
url = f'https://api.commadotai.com/v1/route/{route}/files'
|
||||
url = f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}/v1/route/{route}/files'
|
||||
r = requests.get(url, headers={"Authorization": f"JWT {jwt}"}, timeout=10)
|
||||
assert r.status_code == 200
|
||||
print("got api response")
|
||||
|
||||
Reference in New Issue
Block a user